getting the paraz2 server host and port info from the HTTP request, and mirroring...
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.14 2007-03-28 12:05:18 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_host[64] = "";
554     char server_port[16] = "";
555
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     // Modify Host: header, but getting the host and port info first
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     {
610         char * colon = 0;
611         
612         if((colon = strchr(hp->value, ':'))){
613             int collen = colon - hp->value;
614             strncpy(server_host, hp->value, (collen < 64) ? collen : 64 );
615             strncpy(server_port, colon + 1, 16);
616         } else {
617             strncpy(server_host, hp->value, 64);
618             strncpy(server_port, hp->value, 16);
619         }
620     }
621   
622     hp->value = nmem_strdup(c->nmem, proxy_url);
623
624     // Add new header about paraz2 version, host, remote client address, etc.
625     {
626
627         hp = rq->headers;
628         hp = http_header_append(c, hp, 
629                                 PACKAGE_NAME "-version", PACKAGE_VERSION);
630         hp = http_header_append(c, hp, 
631                                 PACKAGE_NAME "-server-host", server_host);
632         //sprintf(server_port, "%d",  ser->port);
633         hp = http_header_append(c, hp, 
634                                 PACKAGE_NAME "-server-port", server_port);
635         hp = http_header_append(c, hp, 
636                                 PACKAGE_NAME "-remote-addr", 
637                                 c->iochan->addr_str);
638     }
639
640     requestbuf = http_serialize_request(rq);
641     http_buf_enqueue(&p->oqueue, requestbuf);
642     iochan_setflag(p->iochan, EVENT_OUTPUT);
643     return 0;
644 }
645
646 void http_send_response(struct http_channel *ch)
647 {
648     struct http_response *rs = ch->response;
649     struct http_buf *hb;
650
651     assert(rs);
652     hb = http_serialize_response(ch, rs);
653     if (!hb)
654     {
655         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
656         http_destroy(ch->iochan);
657     }
658     else
659     {
660         http_buf_enqueue(&ch->oqueue, hb);
661         iochan_setflag(ch->iochan, EVENT_OUTPUT);
662         ch->state = Http_Idle;
663     }
664 }
665
666 static void http_io(IOCHAN i, int event)
667 {
668     struct http_channel *hc = iochan_getdata(i);
669
670     switch (event)
671     {
672         int res, reqlen;
673         struct http_buf *htbuf;
674
675         case EVENT_INPUT:
676             htbuf = http_buf_create();
677             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
678             if (res == -1 && errno == EAGAIN)
679             {
680                 http_buf_destroy(htbuf);
681                 return;
682             }
683             if (res <= 0)
684             {
685                 http_buf_destroy(htbuf);
686                 http_destroy(i);
687                 return;
688             }
689             if (res > 0)
690             {
691                 htbuf->buf[res] = '\0';
692                 htbuf->len = res;
693                 http_buf_enqueue(&hc->iqueue, htbuf);
694             }
695
696             if (hc->state == Http_Busy)
697                 return;
698             if ((reqlen = request_check(hc->iqueue)) <= 2)
699                 return;
700
701             nmem_reset(hc->nmem);
702             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
703             {
704                 yaz_log(YLOG_WARN, "Failed to parse request");
705                 http_destroy(i);
706                 return;
707             }
708             hc->response = 0;
709             yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
710                     hc->request->path,
711                     *hc->request->search ? "?" : "",
712                     hc->request->search);
713             if (http_weshouldproxy(hc->request))
714                 http_proxy(hc->request);
715             else
716             {
717                 // Execute our business logic!
718                 hc->state = Http_Busy;
719                 http_command(hc);
720             }
721             if (hc->iqueue)
722             {
723                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
724                 iochan_setevent(i, EVENT_INPUT);
725             }
726
727             break;
728
729         case EVENT_OUTPUT:
730             if (hc->oqueue)
731             {
732                 struct http_buf *wb = hc->oqueue;
733                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
734                 if (res <= 0)
735                 {
736                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
737                     http_destroy(i);
738                     return;
739                 }
740                 if (res == wb->len)
741                 {
742                     hc->oqueue = hc->oqueue->next;
743                     http_buf_destroy(wb);
744                 }
745                 else
746                 {
747                     wb->len -= res;
748                     wb->offset += res;
749                 }
750                 if (!hc->oqueue) {
751                     if (!strcmp(hc->version, "1.0"))
752                     {
753                         http_destroy(i);
754                         return;
755                     }
756                     else
757                     {
758                         iochan_clearflag(i, EVENT_OUTPUT);
759                         if (hc->iqueue)
760                             iochan_setevent(hc->iochan, EVENT_INPUT);
761                     }
762                 }
763             }
764
765             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
766                 http_destroy(i); // Server closed; we're done
767             break;
768         default:
769             yaz_log(YLOG_WARN, "Unexpected event on connection");
770             http_destroy(i);
771     }
772 }
773
774 // If this hostname contains our proxy host as a prefix, replace with myurl
775 static char *sub_hostname(struct http_channel *c, char *buf)
776 {
777     char tmp[1024];
778     if (strlen(buf) > 1023)
779         return buf;
780     if (strncmp(buf, "http://", 7))
781         return buf;
782     if (!strncmp(buf + 7, proxy_url, strlen(proxy_url)))
783     {
784         strcpy(tmp, myurl);
785         strcat(tmp, buf + strlen(proxy_url) + 7);
786         return nmem_strdup(c->nmem, tmp);
787     }
788     return buf;
789 }
790
791 // Handles I/O on a client connection to a backend web server (proxy mode)
792 static void proxy_io(IOCHAN pi, int event)
793 {
794     struct http_proxy *pc = iochan_getdata(pi);
795     struct http_channel *hc = pc->channel;
796
797     switch (event)
798     {
799         int res;
800         struct http_buf *htbuf;
801
802         case EVENT_INPUT:
803             htbuf = http_buf_create();
804             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
805             if (res == 0 || (res < 0 && errno != EINPROGRESS))
806             {
807                 if (hc->oqueue)
808                 {
809                     yaz_log(YLOG_WARN, "Proxy read came up short");
810                     // Close channel and alert client HTTP channel that we're gone
811                     http_buf_destroy(htbuf);
812                     close(iochan_getfd(pi));
813                     iochan_destroy(pi);
814                     pc->iochan = 0;
815                 }
816                 else
817                 {
818                     http_destroy(hc->iochan);
819                     return;
820                 }
821             }
822             else
823             {
824                 htbuf->buf[res] = '\0';
825                 htbuf->offset = 0;
826                 htbuf->len = res;
827                 if (pc->first_response) // Check if this is a redirect
828                 {
829                     int len;
830                     if ((len = package_check(htbuf->buf)))
831                     {
832                         struct http_response *res = http_parse_response_buf(hc, htbuf->buf, len);
833                         if (res)
834                         {
835                             struct http_header *h;
836                             for (h = res->headers; h; h = h->next)
837                                 if (!strcmp(h->name, "Location"))
838                                 {
839                                     // We found a location header. Rewrite it.
840                                     struct http_buf *buf;
841                                     h->value = sub_hostname(hc, h->value);
842                                     buf = http_serialize_response(hc, res);
843                                     yaz_log(YLOG_LOG, "Proxy rewrite");
844                                     http_buf_enqueue(&hc->oqueue, buf);
845                                     htbuf->offset = len;
846                                     break;
847                                 }
848                         }
849                     }
850                     pc->first_response = 0;
851                 }
852                 // Write any remaining payload
853                 if (htbuf->len - htbuf->offset > 0)
854                     http_buf_enqueue(&hc->oqueue, htbuf);
855             }
856             iochan_setflag(hc->iochan, EVENT_OUTPUT);
857             break;
858         case EVENT_OUTPUT:
859             if (!(htbuf = pc->oqueue))
860             {
861                 iochan_clearflag(pi, EVENT_OUTPUT);
862                 return;
863             }
864             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
865             if (res <= 0)
866             {
867                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
868                 http_destroy(hc->iochan);
869                 return;
870             }
871             if (res == htbuf->len)
872             {
873                 struct http_buf *np = htbuf->next;
874                 http_buf_destroy(htbuf);
875                 pc->oqueue = np;
876             }
877             else
878             {
879                 htbuf->len -= res;
880                 htbuf->offset += res;
881             }
882
883             if (!pc->oqueue) {
884                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
885             }
886             break;
887         default:
888             yaz_log(YLOG_WARN, "Unexpected event on connection");
889             http_destroy(hc->iochan);
890     }
891 }
892
893 // Cleanup channel
894 static void http_destroy(IOCHAN i)
895 {
896     struct http_channel *s = iochan_getdata(i);
897
898     if (s->proxy)
899     {
900         if (s->proxy->iochan)
901         {
902             close(iochan_getfd(s->proxy->iochan));
903             iochan_destroy(s->proxy->iochan);
904         }
905         http_buf_destroy_queue(s->proxy->oqueue);
906         xfree(s->proxy);
907     }
908     s->next = http_channel_freelist;
909     http_channel_freelist = s;
910     close(iochan_getfd(i));
911     iochan_destroy(i);
912 }
913
914 static struct http_channel *http_create(void)
915 {
916     struct http_channel *r = http_channel_freelist;
917
918     if (r)
919     {
920         http_channel_freelist = r->next;
921         nmem_reset(r->nmem);
922         wrbuf_rewind(r->wrbuf);
923     }
924     else
925     {
926         r = xmalloc(sizeof(struct http_channel));
927         r->nmem = nmem_create();
928         r->wrbuf = wrbuf_alloc();
929     }
930     r->proxy = 0;
931     r->iochan = 0;
932     r->iqueue = r->oqueue = 0;
933     r->state = Http_Idle;
934     r->request = 0;
935     r->response = 0;
936     return r;
937 }
938
939
940 /* Accept a new command connection */
941 static void http_accept(IOCHAN i, int event)
942 {
943     struct sockaddr_in addr;
944     int fd = iochan_getfd(i);
945     socklen_t len;
946     int s;
947     IOCHAN c;
948     int flags;
949     struct http_channel *ch;
950
951     len = sizeof addr;
952     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
953     {
954         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
955         return;
956     }
957     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
958         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
959     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
960         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
961
962     yaz_log(YLOG_DEBUG, "New command connection");
963     c = iochan_create(s, &addr, http_io, EVENT_INPUT | EVENT_EXCEPT);
964
965     ch = http_create();
966     ch->iochan = c;
967     iochan_setdata(c, ch);
968
969     c->next = channel_list;
970     channel_list = c;
971 }
972
973 /* Create a http-channel listener, syntax [host:]port */
974 void http_init(const char *addr)
975 {
976     IOCHAN c;
977     int l;
978     struct protoent *p;
979     struct sockaddr_in myaddr;
980     int one = 1;
981     const char *pp;
982     int port;
983
984     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
985
986     memset(&myaddr, 0, sizeof myaddr);
987     myaddr.sin_family = AF_INET;
988     pp = strchr(addr, ':');
989     if (pp)
990     {
991         int len = pp - addr;
992         char hostname[128];
993         struct hostent *he;
994
995         strncpy(hostname, addr, len);
996         hostname[len] = '\0';
997         if (!(he = gethostbyname(hostname)))
998         {
999             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
1000             exit(1);
1001         }
1002         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
1003         port = atoi(pp + 1);
1004     }
1005     else
1006     {
1007         port = atoi(addr);
1008         myaddr.sin_addr.s_addr = INADDR_ANY;
1009     }
1010     myaddr.sin_port = htons(port);
1011
1012     if (!(p = getprotobyname("tcp"))) {
1013         abort();
1014     }
1015     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
1016         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
1017     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
1018                     &one, sizeof(one)) < 0)
1019         abort();
1020
1021     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
1022         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
1023     if (listen(l, SOMAXCONN) < 0) 
1024         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
1025
1026     c = iochan_create(l, &myaddr, http_accept, EVENT_INPUT | EVENT_EXCEPT);
1027     c->next = channel_list;
1028     channel_list = c;
1029 }
1030
1031 void http_set_proxyaddr(char *host, char *base_url)
1032 {
1033     char *p;
1034     int port;
1035     struct hostent *he;
1036
1037     strcpy(myurl, base_url);
1038     strcpy(proxy_url, host);
1039     p = strchr(host, ':');
1040     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
1041     if (p) {
1042         port = atoi(p + 1);
1043         *p = '\0';
1044     }
1045     else
1046         port = 80;
1047     if (!(he = gethostbyname(host))) 
1048     {
1049         fprintf(stderr, "Failed to lookup '%s'\n", host);
1050         exit(1);
1051     }
1052     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
1053     proxy_addr->sin_family = he->h_addrtype;
1054     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
1055     proxy_addr->sin_port = htons(port);
1056 }
1057
1058 /*
1059  * Local variables:
1060  * c-basic-offset: 4
1061  * indent-tabs-mode: nil
1062  * End:
1063  * vim: shiftwidth=4 tabstop=8 expandtab
1064  */