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