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