Reorganized source tree
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.1 2006-12-20 20:47:16 quinn Exp $
3  */
4
5 #include <stdio.h>
6 #include <sys/socket.h>
7 #include <sys/types.h>
8 #include <sys/uio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <strings.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <netdb.h>
15 #include <errno.h>
16 #include <assert.h>
17
18 #include <yaz/yaz-util.h>
19 #include <yaz/comstack.h>
20 #include <netdb.h>
21
22 #include "command.h"
23 #include "util.h"
24 #include "eventl.h"
25 #include "pazpar2.h"
26 #include "http.h"
27 #include "http_command.h"
28
29 static void proxy_io(IOCHAN i, int event);
30 static struct http_channel *http_create(void);
31 static void http_destroy(IOCHAN i);
32
33 extern IOCHAN channel_list;
34
35 static struct sockaddr_in *proxy_addr = 0; // If this is set, we proxy normal HTTP requests
36 static char proxy_url[256] = "";
37 static struct http_buf *http_buf_freelist = 0;
38 static struct http_channel *http_channel_freelist = 0;
39
40 static struct http_buf *http_buf_create()
41 {
42     struct http_buf *r;
43
44     if (http_buf_freelist)
45     {
46         r = http_buf_freelist;
47         http_buf_freelist = http_buf_freelist->next;
48     }
49     else
50         r = xmalloc(sizeof(struct http_buf));
51     r->offset = 0;
52     r->len = 0;
53     r->next = 0;
54     return r;
55 }
56
57 static void http_buf_destroy(struct http_buf *b)
58 {
59     b->next = http_buf_freelist;
60     http_buf_freelist = b;
61 }
62
63 static void http_buf_destroy_queue(struct http_buf *b)
64 {
65     struct http_buf *p;
66     while (b)
67     {
68         p = b->next;
69         http_buf_destroy(b);
70         b = p;
71     }
72 }
73
74 #ifdef GAGA
75 // Calculate length of chain
76 static int http_buf_len(struct http_buf *b)
77 {
78     int sum = 0;
79     for (; b; b = b->next)
80         sum += b->len;
81     return sum;
82 }
83 #endif
84
85 static struct http_buf *http_buf_bybuf(char *b, int len)
86 {
87     struct http_buf *res = 0;
88     struct http_buf **p = &res;
89
90     while (len)
91     {
92         *p = http_buf_create();
93         int tocopy = len;
94         if (tocopy > HTTP_BUF_SIZE)
95             tocopy = HTTP_BUF_SIZE;
96         memcpy((*p)->buf, b, tocopy);
97         (*p)->len = tocopy;
98         len -= tocopy;
99         b += tocopy;
100         p = &(*p)->next;
101     }
102     return res;
103 }
104
105 // Add a (chain of) buffers to the end of an existing queue.
106 static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
107 {
108     while (*queue)
109         queue = &(*queue)->next;
110     *queue = b;
111 }
112
113 static struct http_buf *http_buf_bywrbuf(WRBUF wrbuf)
114 {
115     // Heavens to Betsy (buf)!
116     return http_buf_bybuf(wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
117 }
118
119 // Non-destructively collapse chain of buffers into a string (max *len)
120 // Return
121 static int http_buf_peek(struct http_buf *b, char *buf, int len)
122 {
123     int rd = 0;
124     while (b && rd < len)
125     {
126         int toread = len - rd;
127         if (toread > b->len)
128             toread = b->len;
129         memcpy(buf + rd, b->buf + b->offset, toread);
130         rd += toread;
131         b = b->next;
132     }
133     buf[rd] = '\0';
134     return rd;
135 }
136
137 // Ddestructively munch up to len  from head of queue.
138 static int http_buf_read(struct http_buf **b, char *buf, int len)
139 {
140     int rd = 0;
141     while ((*b) && rd < len)
142     {
143         int toread = len - rd;
144         if (toread > (*b)->len)
145             toread = (*b)->len;
146         memcpy(buf + rd, (*b)->buf + (*b)->offset, toread);
147         rd += toread;
148         if (toread < (*b)->len)
149         {
150             (*b)->len -= toread;
151             (*b)->offset += toread;
152             break;
153         }
154         else
155         {
156             struct http_buf *n = (*b)->next;
157             http_buf_destroy(*b);
158             *b = n;
159         }
160     }
161     buf[rd] = '\0';
162     return rd;
163 }
164
165 void static urldecode(char *i, char *o)
166 {
167     while (*i)
168     {
169         if (*i == '+')
170         {
171             *(o++) = ' ';
172             i++;
173         }
174         else if (*i == '%')
175         {
176             i++;
177             sscanf(i, "%2hhx", o);
178             i += 2;
179             o++;
180         }
181         else
182             *(o++) = *(i++);
183     }
184     *o = '\0';
185 }
186
187 void http_addheader(struct http_response *r, const char *name, const char *value)
188 {
189     struct http_channel *c = r->channel;
190     struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
191     h->name = nmem_strdup(c->nmem, name);
192     h->value = nmem_strdup(c->nmem, value);
193     h->next = r->headers;
194     r->headers = h;
195 }
196
197 char *http_argbyname(struct http_request *r, char *name)
198 {
199     struct http_argument *p;
200     if (!name)
201         return 0;
202     for (p = r->arguments; p; p = p->next)
203         if (!strcmp(p->name, name))
204             return p->value;
205     return 0;
206 }
207
208 char *http_headerbyname(struct http_request *r, char *name)
209 {
210     struct http_header *p;
211     for (p = r->headers; p; p = p->next)
212         if (!strcmp(p->name, name))
213             return p->value;
214     return 0;
215 }
216
217 struct http_response *http_create_response(struct http_channel *c)
218 {
219     struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
220     strcpy(r->code, "200");
221     r->msg = "OK";
222     r->channel = c;
223     r->headers = 0;
224     r->payload = 0;
225     return r;
226 }
227
228 // Check if we have a complete request. Return 0 or length (including trailing newline)
229 // FIXME: Does not deal gracefully with requests carrying payload
230 // but this is kind of OK since we will reject anything other than an empty GET
231 static int request_check(struct http_buf *queue)
232 {
233     char tmp[4096];
234     int len = 0;
235     char *buf = tmp;
236
237     http_buf_peek(queue, tmp, 4096);
238     while (*buf) // Check if we have a sequence of lines terminated by an empty line
239     {
240         char *b = strstr(buf, "\r\n");
241
242         if (!b)
243             return 0;
244
245         len += (b - buf) + 2;
246         if (b == buf)
247             return len;
248         buf = b + 2;
249     }
250     return 0;
251 }
252
253 struct http_request *http_parse_request(struct http_channel *c, struct http_buf **queue,
254         int len)
255 {
256     struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
257     char *p, *p2;
258     char tmp[4096];
259     char *buf = tmp;
260
261     if (len > 4096)
262         return 0;
263     if (http_buf_read(queue, buf, len) < len)
264         return 0;
265
266     r->channel = c;
267     r->arguments = 0;
268     r->headers = 0;
269     // Parse first line
270     for (p = buf, p2 = r->method; *p && *p != ' ' && p - buf < 19; p++)
271         *(p2++) = *p;
272     if (*p != ' ')
273     {
274         yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
275         return 0;
276     }
277     *p2 = '\0';
278
279     if (!(buf = strchr(buf, ' ')))
280     {
281         yaz_log(YLOG_WARN, "Syntax error in request (1)");
282         return 0;
283     }
284     buf++;
285     if (!(p = strchr(buf, ' ')))
286     {
287         yaz_log(YLOG_WARN, "Syntax error in request (2)");
288         return 0;
289     }
290     *(p++) = '\0';
291     if ((p2 = strchr(buf, '?'))) // Do we have arguments?
292         *(p2++) = '\0';
293     r->path = nmem_strdup(c->nmem, buf);
294     if (p2)
295     {
296         // Parse Arguments
297         while (*p2)
298         {
299             struct http_argument *a;
300             char *equal = strchr(p2, '=');
301             char *eoa = strchr(p2, '&');
302             if (!equal)
303             {
304                 yaz_log(YLOG_WARN, "Expected '=' in argument");
305                 return 0;
306             }
307             if (!eoa)
308                 eoa = equal + strlen(equal); // last argument
309             else
310                 *(eoa++) = '\0';
311             a = nmem_malloc(c->nmem, sizeof(struct http_argument));
312             *(equal++) = '\0';
313             a->name = nmem_strdup(c->nmem, p2);
314             urldecode(equal, equal);
315             a->value = nmem_strdup(c->nmem, equal);
316             a->next = r->arguments;
317             r->arguments = a;
318             p2 = eoa;
319         }
320     }
321     buf = p;
322
323     if (strncmp(buf, "HTTP/", 5))
324         strcpy(r->http_version, "1.0");
325     else
326     {
327         buf += 5;
328         if (!(p = strstr(buf, "\r\n")))
329             return 0;
330         *(p++) = '\0';
331         p++;
332         strcpy(r->http_version, buf);
333         buf = p;
334     }
335     strcpy(c->version, r->http_version);
336
337     r->headers = 0;
338     while (*buf)
339     {
340         if (!(p = strstr(buf, "\r\n")))
341             return 0;
342         if (p == buf)
343             break;
344         else
345         {
346             struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
347             if (!(p2 = strchr(buf, ':')))
348                 return 0;
349             *(p2++) = '\0';
350             h->name = nmem_strdup(c->nmem, buf);
351             while (isspace(*p2))
352                 p2++;
353             if (p2 >= p) // Empty header?
354             {
355                 buf = p + 2;
356                 continue;
357             }
358             *p = '\0';
359             h->value = nmem_strdup(c->nmem, p2);
360             h->next = r->headers;
361             r->headers = h;
362             buf = p + 2;
363         }
364     }
365
366     return r;
367 }
368
369
370 static struct http_buf *http_serialize_response(struct http_channel *c,
371         struct http_response *r)
372 {
373     wrbuf_rewind(c->wrbuf);
374     struct http_header *h;
375
376     wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
377     for (h = r->headers; h; h = h->next)
378         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
379     wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ? (int) strlen(r->payload) : 0);
380     wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
381     wrbuf_puts(c->wrbuf, "\r\n");
382
383     if (r->payload)
384         wrbuf_puts(c->wrbuf, r->payload);
385
386     return http_buf_bywrbuf(c->wrbuf);
387 }
388
389 // Serialize a HTTP request
390 static struct http_buf *http_serialize_request(struct http_request *r)
391 {
392     struct http_channel *c = r->channel;
393     wrbuf_rewind(c->wrbuf);
394     struct http_header *h;
395     struct http_argument *a;
396
397     wrbuf_printf(c->wrbuf, "%s %s", r->method, r->path);
398
399     if (r->arguments)
400     {
401         wrbuf_putc(c->wrbuf, '?');
402         for (a = r->arguments; a; a = a->next) {
403             if (a != r->arguments)
404                 wrbuf_putc(c->wrbuf, '&');
405             wrbuf_printf(c->wrbuf, "%s=%s", a->name, a->value);
406         }
407     }
408
409     wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
410
411     for (h = r->headers; h; h = h->next)
412         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
413
414     wrbuf_puts(c->wrbuf, "\r\n");
415     
416     return http_buf_bywrbuf(c->wrbuf);
417 }
418
419
420 static int http_weshouldproxy(struct http_request *rq)
421 {
422     if (proxy_addr && !strstr(rq->path, "search.pz2"))
423         return 1;
424     return 0;
425 }
426
427 static int http_proxy(struct http_request *rq)
428 {
429     struct http_channel *c = rq->channel;
430     struct http_proxy *p = c->proxy;
431     struct http_header *hp;
432     struct http_buf *requestbuf;
433
434     if (!p) // This is a new connection. Create a proxy channel
435     {
436         int sock;
437         struct protoent *pe;
438         int one = 1;
439         int flags;
440
441         if (!(pe = getprotobyname("tcp"))) {
442             abort();
443         }
444         if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
445         {
446             yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
447             return -1;
448         }
449         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
450                         &one, sizeof(one)) < 0)
451             abort();
452         if ((flags = fcntl(sock, F_GETFL, 0)) < 0) 
453             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
454         if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
455             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
456         if (connect(sock, (struct sockaddr *) proxy_addr, sizeof(*proxy_addr)) < 0)
457             if (errno != EINPROGRESS)
458             {
459                 yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
460                 return -1;
461             }
462
463         p = xmalloc(sizeof(struct http_proxy));
464         p->oqueue = 0;
465         p->channel = c;
466         c->proxy = p;
467         // We will add EVENT_OUTPUT below
468         p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT);
469         iochan_setdata(p->iochan, p);
470         p->iochan->next = channel_list;
471         channel_list = p->iochan;
472     }
473
474     // Modify Host: header
475     for (hp = rq->headers; hp; hp = hp->next)
476         if (!strcmp(hp->name, "Host"))
477             break;
478     if (!hp)
479     {
480         yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
481         return -1;
482     }
483     hp->value = nmem_strdup(c->nmem, proxy_url);
484     requestbuf = http_serialize_request(rq);
485     http_buf_enqueue(&p->oqueue, requestbuf);
486     iochan_setflag(p->iochan, EVENT_OUTPUT);
487     return 0;
488 }
489
490 void http_send_response(struct http_channel *ch)
491 {
492     struct http_response *rs = ch->response;
493     assert(rs);
494     struct http_buf *hb = http_serialize_response(ch, rs);
495     if (!hb)
496     {
497         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
498         http_destroy(ch->iochan);
499     }
500     else
501     {
502         http_buf_enqueue(&ch->oqueue, hb);
503         iochan_setflag(ch->iochan, EVENT_OUTPUT);
504         ch->state = Http_Idle;
505     }
506 }
507
508 static void http_io(IOCHAN i, int event)
509 {
510     struct http_channel *hc = iochan_getdata(i);
511
512     switch (event)
513     {
514         int res, reqlen;
515         struct http_buf *htbuf;
516
517         case EVENT_INPUT:
518             htbuf = http_buf_create();
519             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
520             if (res <= 0 && errno != EAGAIN)
521             {
522                 http_buf_destroy(htbuf);
523                 http_destroy(i);
524                 return;
525             }
526             if (res > 0)
527             {
528                 htbuf->buf[res] = '\0';
529                 htbuf->len = res;
530                 http_buf_enqueue(&hc->iqueue, htbuf);
531             }
532
533             if (hc->state == Http_Busy)
534                 return;
535
536             if ((reqlen = request_check(hc->iqueue)) <= 2)
537                 return;
538
539             nmem_reset(hc->nmem);
540             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
541             {
542                 yaz_log(YLOG_WARN, "Failed to parse request");
543                 http_destroy(i);
544                 return;
545             }
546             hc->response = 0;
547             yaz_log(YLOG_LOG, "Request: %s %s v %s", hc->request->method,
548                     hc->request->path, hc->request->http_version);
549             if (http_weshouldproxy(hc->request))
550                 http_proxy(hc->request);
551             else
552             {
553                 // Execute our business logic!
554                 hc->state = Http_Busy;
555                 http_command(hc);
556             }
557             if (hc->iqueue)
558             {
559                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
560                 iochan_setevent(i, EVENT_INPUT);
561             }
562
563             break;
564
565         case EVENT_OUTPUT:
566             if (hc->oqueue)
567             {
568                 struct http_buf *wb = hc->oqueue;
569                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
570                 if (res <= 0)
571                 {
572                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
573                     http_destroy(i);
574                     return;
575                 }
576                 if (res == wb->len)
577                 {
578                     hc->oqueue = hc->oqueue->next;
579                     http_buf_destroy(wb);
580                 }
581                 else
582                 {
583                     wb->len -= res;
584                     wb->offset += res;
585                 }
586                 if (!hc->oqueue) {
587                     if (!strcmp(hc->version, "1.0"))
588                     {
589                         http_destroy(i);
590                         return;
591                     }
592                     else
593                     {
594                         iochan_clearflag(i, EVENT_OUTPUT);
595                         if (hc->iqueue)
596                             iochan_setevent(hc->iochan, EVENT_INPUT);
597                     }
598                 }
599             }
600
601             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
602                 http_destroy(i); // Server closed; we're done
603             break;
604         default:
605             yaz_log(YLOG_WARN, "Unexpected event on connection");
606             http_destroy(i);
607     }
608 }
609
610 // Handles I/O on a client connection to a backend web server (proxy mode)
611 static void proxy_io(IOCHAN pi, int event)
612 {
613     struct http_proxy *pc = iochan_getdata(pi);
614     struct http_channel *hc = pc->channel;
615
616     switch (event)
617     {
618         int res;
619         struct http_buf *htbuf;
620
621         case EVENT_INPUT:
622             htbuf = http_buf_create();
623             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
624             if (res == 0 || (res < 0 && errno != EINPROGRESS))
625             {
626                 if (hc->oqueue)
627                 {
628                     yaz_log(YLOG_WARN, "Proxy read came up short");
629                     // Close channel and alert client HTTP channel that we're gone
630                     http_buf_destroy(htbuf);
631                     close(iochan_getfd(pi));
632                     iochan_destroy(pi);
633                     pc->iochan = 0;
634                 }
635                 else
636                 {
637                     http_destroy(hc->iochan);
638                     return;
639                 }
640             }
641             else
642             {
643                 htbuf->buf[res] = '\0';
644                 htbuf->len = res;
645                 http_buf_enqueue(&hc->oqueue, htbuf);
646             }
647             iochan_setflag(hc->iochan, EVENT_OUTPUT);
648             break;
649         case EVENT_OUTPUT:
650             if (!(htbuf = pc->oqueue))
651             {
652                 iochan_clearflag(pi, EVENT_OUTPUT);
653                 return;
654             }
655             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
656             if (res <= 0)
657             {
658                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
659                 http_destroy(hc->iochan);
660                 return;
661             }
662             if (res == htbuf->len)
663             {
664                 struct http_buf *np = htbuf->next;
665                 http_buf_destroy(htbuf);
666                 pc->oqueue = np;
667             }
668             else
669             {
670                 htbuf->len -= res;
671                 htbuf->offset += res;
672             }
673
674             if (!pc->oqueue) {
675                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
676             }
677             break;
678         default:
679             yaz_log(YLOG_WARN, "Unexpected event on connection");
680             http_destroy(hc->iochan);
681     }
682 }
683
684 // Cleanup channel
685 static void http_destroy(IOCHAN i)
686 {
687     struct http_channel *s = iochan_getdata(i);
688
689     if (s->proxy)
690     {
691         if (s->proxy->iochan)
692         {
693             close(iochan_getfd(s->proxy->iochan));
694             iochan_destroy(s->proxy->iochan);
695         }
696         http_buf_destroy_queue(s->proxy->oqueue);
697         xfree(s->proxy);
698     }
699     s->next = http_channel_freelist;
700     http_channel_freelist = s;
701     close(iochan_getfd(i));
702     iochan_destroy(i);
703 }
704
705 static struct http_channel *http_create(void)
706 {
707     struct http_channel *r = http_channel_freelist;
708
709     if (r)
710     {
711         http_channel_freelist = r->next;
712         nmem_reset(r->nmem);
713         wrbuf_rewind(r->wrbuf);
714     }
715     else
716     {
717         r = xmalloc(sizeof(struct http_channel));
718         r->nmem = nmem_create();
719         r->wrbuf = wrbuf_alloc();
720     }
721     r->proxy = 0;
722     r->iochan = 0;
723     r->iqueue = r->oqueue = 0;
724     r->state = Http_Idle;
725     r->request = 0;
726     r->response = 0;
727     return r;
728 }
729
730
731 /* Accept a new command connection */
732 static void http_accept(IOCHAN i, int event)
733 {
734     struct sockaddr_in addr;
735     int fd = iochan_getfd(i);
736     socklen_t len;
737     int s;
738     IOCHAN c;
739     int flags;
740     struct http_channel *ch;
741
742     len = sizeof addr;
743     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
744     {
745         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
746         return;
747     }
748     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
749         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
750     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
751         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
752
753     yaz_log(YLOG_LOG, "New command connection");
754     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
755
756     ch = http_create();
757     ch->iochan = c;
758     iochan_setdata(c, ch);
759
760     c->next = channel_list;
761     channel_list = c;
762 }
763
764 /* Create a http-channel listener, syntax [host:]port */
765 void http_init(const char *addr)
766 {
767     IOCHAN c;
768     int l;
769     struct protoent *p;
770     struct sockaddr_in myaddr;
771     int one = 1;
772     const char *pp;
773     int port;
774
775     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
776
777     bzero(&myaddr, sizeof myaddr);
778     myaddr.sin_family = AF_INET;
779     pp = strchr(addr, ':');
780     if (pp)
781     {
782         int len = pp - addr;
783         char hostname[128];
784         struct hostent *he;
785
786         strncpy(hostname, addr, len);
787         hostname[len] = '\0';
788         if (!(he = gethostbyname(hostname)))
789         {
790             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
791             exit(1);
792         }
793         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
794         port = atoi(pp + 1);
795     }
796     else
797     {
798         port = atoi(addr);
799         myaddr.sin_addr.s_addr = INADDR_ANY;
800     }
801     myaddr.sin_port = htons(port);
802
803     if (!(p = getprotobyname("tcp"))) {
804         abort();
805     }
806     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
807         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
808     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
809                     &one, sizeof(one)) < 0)
810         abort();
811
812     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
813         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
814     if (listen(l, SOMAXCONN) < 0) 
815         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
816
817     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
818     c->next = channel_list;
819     channel_list = c;
820 }
821
822 void http_set_proxyaddr(char *host)
823 {
824     char *p;
825     int port;
826     struct hostent *he;
827
828     strcpy(proxy_url, host);
829     p = strchr(host, ':');
830     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
831     if (p) {
832         port = atoi(p + 1);
833         *p = '\0';
834     }
835     else
836         port = 80;
837     if (!(he = gethostbyname(host))) 
838     {
839         fprintf(stderr, "Failed to lookup '%s'\n", host);
840         exit(1);
841     }
842     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
843     proxy_addr->sin_family = he->h_addrtype;
844     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
845     proxy_addr->sin_port = htons(port);
846 }
847
848 /*
849  * Local variables:
850  * c-basic-offset: 4
851  * indent-tabs-mode: nil
852  * End:
853  * vim: shiftwidth=4 tabstop=8 expandtab
854  */