Fixed problem with detection of end-of-stream for HTTP socket. Problem
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.2 2006-12-20 23:31:24 adam 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 == -1 && errno == EAGAIN)
521             {
522                 http_buf_destroy(htbuf);
523                 return;
524             }
525             if (res <= 0)
526             {
527                 http_buf_destroy(htbuf);
528                 http_destroy(i);
529                 return;
530             }
531             if (res > 0)
532             {
533                 htbuf->buf[res] = '\0';
534                 htbuf->len = res;
535                 http_buf_enqueue(&hc->iqueue, htbuf);
536             }
537
538             if (hc->state == Http_Busy)
539                 return;
540             if ((reqlen = request_check(hc->iqueue)) <= 2)
541                 return;
542
543             nmem_reset(hc->nmem);
544             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
545             {
546                 yaz_log(YLOG_WARN, "Failed to parse request");
547                 http_destroy(i);
548                 return;
549             }
550             hc->response = 0;
551             yaz_log(YLOG_LOG, "Request: %s %s v %s", hc->request->method,
552                     hc->request->path, hc->request->http_version);
553             if (http_weshouldproxy(hc->request))
554                 http_proxy(hc->request);
555             else
556             {
557                 // Execute our business logic!
558                 hc->state = Http_Busy;
559                 http_command(hc);
560             }
561             if (hc->iqueue)
562             {
563                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
564                 iochan_setevent(i, EVENT_INPUT);
565             }
566
567             break;
568
569         case EVENT_OUTPUT:
570             if (hc->oqueue)
571             {
572                 struct http_buf *wb = hc->oqueue;
573                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
574                 if (res <= 0)
575                 {
576                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
577                     http_destroy(i);
578                     return;
579                 }
580                 if (res == wb->len)
581                 {
582                     hc->oqueue = hc->oqueue->next;
583                     http_buf_destroy(wb);
584                 }
585                 else
586                 {
587                     wb->len -= res;
588                     wb->offset += res;
589                 }
590                 if (!hc->oqueue) {
591                     if (!strcmp(hc->version, "1.0"))
592                     {
593                         http_destroy(i);
594                         return;
595                     }
596                     else
597                     {
598                         iochan_clearflag(i, EVENT_OUTPUT);
599                         if (hc->iqueue)
600                             iochan_setevent(hc->iochan, EVENT_INPUT);
601                     }
602                 }
603             }
604
605             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
606                 http_destroy(i); // Server closed; we're done
607             break;
608         default:
609             yaz_log(YLOG_WARN, "Unexpected event on connection");
610             http_destroy(i);
611     }
612 }
613
614 // Handles I/O on a client connection to a backend web server (proxy mode)
615 static void proxy_io(IOCHAN pi, int event)
616 {
617     struct http_proxy *pc = iochan_getdata(pi);
618     struct http_channel *hc = pc->channel;
619
620     switch (event)
621     {
622         int res;
623         struct http_buf *htbuf;
624
625         case EVENT_INPUT:
626             htbuf = http_buf_create();
627             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
628             if (res == 0 || (res < 0 && errno != EINPROGRESS))
629             {
630                 if (hc->oqueue)
631                 {
632                     yaz_log(YLOG_WARN, "Proxy read came up short");
633                     // Close channel and alert client HTTP channel that we're gone
634                     http_buf_destroy(htbuf);
635                     close(iochan_getfd(pi));
636                     iochan_destroy(pi);
637                     pc->iochan = 0;
638                 }
639                 else
640                 {
641                     http_destroy(hc->iochan);
642                     return;
643                 }
644             }
645             else
646             {
647                 htbuf->buf[res] = '\0';
648                 htbuf->len = res;
649                 http_buf_enqueue(&hc->oqueue, htbuf);
650             }
651             iochan_setflag(hc->iochan, EVENT_OUTPUT);
652             break;
653         case EVENT_OUTPUT:
654             if (!(htbuf = pc->oqueue))
655             {
656                 iochan_clearflag(pi, EVENT_OUTPUT);
657                 return;
658             }
659             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
660             if (res <= 0)
661             {
662                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
663                 http_destroy(hc->iochan);
664                 return;
665             }
666             if (res == htbuf->len)
667             {
668                 struct http_buf *np = htbuf->next;
669                 http_buf_destroy(htbuf);
670                 pc->oqueue = np;
671             }
672             else
673             {
674                 htbuf->len -= res;
675                 htbuf->offset += res;
676             }
677
678             if (!pc->oqueue) {
679                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
680             }
681             break;
682         default:
683             yaz_log(YLOG_WARN, "Unexpected event on connection");
684             http_destroy(hc->iochan);
685     }
686 }
687
688 // Cleanup channel
689 static void http_destroy(IOCHAN i)
690 {
691     struct http_channel *s = iochan_getdata(i);
692
693     if (s->proxy)
694     {
695         if (s->proxy->iochan)
696         {
697             close(iochan_getfd(s->proxy->iochan));
698             iochan_destroy(s->proxy->iochan);
699         }
700         http_buf_destroy_queue(s->proxy->oqueue);
701         xfree(s->proxy);
702     }
703     s->next = http_channel_freelist;
704     http_channel_freelist = s;
705     close(iochan_getfd(i));
706     iochan_destroy(i);
707 }
708
709 static struct http_channel *http_create(void)
710 {
711     struct http_channel *r = http_channel_freelist;
712
713     if (r)
714     {
715         http_channel_freelist = r->next;
716         nmem_reset(r->nmem);
717         wrbuf_rewind(r->wrbuf);
718     }
719     else
720     {
721         r = xmalloc(sizeof(struct http_channel));
722         r->nmem = nmem_create();
723         r->wrbuf = wrbuf_alloc();
724     }
725     r->proxy = 0;
726     r->iochan = 0;
727     r->iqueue = r->oqueue = 0;
728     r->state = Http_Idle;
729     r->request = 0;
730     r->response = 0;
731     return r;
732 }
733
734
735 /* Accept a new command connection */
736 static void http_accept(IOCHAN i, int event)
737 {
738     struct sockaddr_in addr;
739     int fd = iochan_getfd(i);
740     socklen_t len;
741     int s;
742     IOCHAN c;
743     int flags;
744     struct http_channel *ch;
745
746     len = sizeof addr;
747     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
748     {
749         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
750         return;
751     }
752     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
753         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
754     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
755         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
756
757     yaz_log(YLOG_LOG, "New command connection");
758     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
759
760     ch = http_create();
761     ch->iochan = c;
762     iochan_setdata(c, ch);
763
764     c->next = channel_list;
765     channel_list = c;
766 }
767
768 /* Create a http-channel listener, syntax [host:]port */
769 void http_init(const char *addr)
770 {
771     IOCHAN c;
772     int l;
773     struct protoent *p;
774     struct sockaddr_in myaddr;
775     int one = 1;
776     const char *pp;
777     int port;
778
779     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
780
781     bzero(&myaddr, sizeof myaddr);
782     myaddr.sin_family = AF_INET;
783     pp = strchr(addr, ':');
784     if (pp)
785     {
786         int len = pp - addr;
787         char hostname[128];
788         struct hostent *he;
789
790         strncpy(hostname, addr, len);
791         hostname[len] = '\0';
792         if (!(he = gethostbyname(hostname)))
793         {
794             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
795             exit(1);
796         }
797         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
798         port = atoi(pp + 1);
799     }
800     else
801     {
802         port = atoi(addr);
803         myaddr.sin_addr.s_addr = INADDR_ANY;
804     }
805     myaddr.sin_port = htons(port);
806
807     if (!(p = getprotobyname("tcp"))) {
808         abort();
809     }
810     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
811         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
812     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
813                     &one, sizeof(one)) < 0)
814         abort();
815
816     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
817         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
818     if (listen(l, SOMAXCONN) < 0) 
819         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
820
821     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
822     c->next = channel_list;
823     channel_list = c;
824 }
825
826 void http_set_proxyaddr(char *host)
827 {
828     char *p;
829     int port;
830     struct hostent *he;
831
832     strcpy(proxy_url, host);
833     p = strchr(host, ':');
834     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
835     if (p) {
836         port = atoi(p + 1);
837         *p = '\0';
838     }
839     else
840         port = 80;
841     if (!(he = gethostbyname(host))) 
842     {
843         fprintf(stderr, "Failed to lookup '%s'\n", host);
844         exit(1);
845     }
846     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
847     proxy_addr->sin_family = he->h_addrtype;
848     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
849     proxy_addr->sin_port = htons(port);
850 }
851
852 /*
853  * Local variables:
854  * c-basic-offset: 4
855  * indent-tabs-mode: nil
856  * End:
857  * vim: shiftwidth=4 tabstop=8 expandtab
858  */