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