Changed the way the forwarded HTTP headers are computed. Now pazpar2 sends
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.15 2007-03-29 09:16:34 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                                 PACKAGE_NAME "-version", PACKAGE_VERSION);
614         hp = http_header_append(c, hp, 
615                                 PACKAGE_NAME "-server-host", ser->myurl);
616         sprintf(server_port, "%d",  ser->port);
617         hp = http_header_append(c, hp, 
618                                 PACKAGE_NAME "-server-port", server_port);
619         sprintf(server_via,  "1.1 %s:%s (%s/%s)",  
620                 ser->myurl, server_port, PACKAGE_NAME, PACKAGE_VERSION);
621         hp = http_header_append(c, hp, 
622                                 "Via" , server_via);
623         //hp = http_header_append(c, hp,"Client-ip", 
624         //                        c->iochan->addr_str);
625         hp = http_header_append(c, hp,"X-Forwarded-For", 
626                                 c->iochan->addr_str);
627       }
628
629     requestbuf = http_serialize_request(rq);
630     http_buf_enqueue(&p->oqueue, requestbuf);
631     iochan_setflag(p->iochan, EVENT_OUTPUT);
632     return 0;
633 }
634
635 void http_send_response(struct http_channel *ch)
636 {
637     struct http_response *rs = ch->response;
638     struct http_buf *hb;
639
640     assert(rs);
641     hb = http_serialize_response(ch, rs);
642     if (!hb)
643     {
644         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
645         http_destroy(ch->iochan);
646     }
647     else
648     {
649         http_buf_enqueue(&ch->oqueue, hb);
650         iochan_setflag(ch->iochan, EVENT_OUTPUT);
651         ch->state = Http_Idle;
652     }
653 }
654
655 static void http_io(IOCHAN i, int event)
656 {
657     struct http_channel *hc = iochan_getdata(i);
658
659     switch (event)
660     {
661         int res, reqlen;
662         struct http_buf *htbuf;
663
664         case EVENT_INPUT:
665             htbuf = http_buf_create();
666             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
667             if (res == -1 && errno == EAGAIN)
668             {
669                 http_buf_destroy(htbuf);
670                 return;
671             }
672             if (res <= 0)
673             {
674                 http_buf_destroy(htbuf);
675                 http_destroy(i);
676                 return;
677             }
678             if (res > 0)
679             {
680                 htbuf->buf[res] = '\0';
681                 htbuf->len = res;
682                 http_buf_enqueue(&hc->iqueue, htbuf);
683             }
684
685             if (hc->state == Http_Busy)
686                 return;
687             if ((reqlen = request_check(hc->iqueue)) <= 2)
688                 return;
689
690             nmem_reset(hc->nmem);
691             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
692             {
693                 yaz_log(YLOG_WARN, "Failed to parse request");
694                 http_destroy(i);
695                 return;
696             }
697             hc->response = 0;
698             yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
699                     hc->request->path,
700                     *hc->request->search ? "?" : "",
701                     hc->request->search);
702             if (http_weshouldproxy(hc->request))
703                 http_proxy(hc->request);
704             else
705             {
706                 // Execute our business logic!
707                 hc->state = Http_Busy;
708                 http_command(hc);
709             }
710             if (hc->iqueue)
711             {
712                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
713                 iochan_setevent(i, EVENT_INPUT);
714             }
715
716             break;
717
718         case EVENT_OUTPUT:
719             if (hc->oqueue)
720             {
721                 struct http_buf *wb = hc->oqueue;
722                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
723                 if (res <= 0)
724                 {
725                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
726                     http_destroy(i);
727                     return;
728                 }
729                 if (res == wb->len)
730                 {
731                     hc->oqueue = hc->oqueue->next;
732                     http_buf_destroy(wb);
733                 }
734                 else
735                 {
736                     wb->len -= res;
737                     wb->offset += res;
738                 }
739                 if (!hc->oqueue) {
740                     if (!strcmp(hc->version, "1.0"))
741                     {
742                         http_destroy(i);
743                         return;
744                     }
745                     else
746                     {
747                         iochan_clearflag(i, EVENT_OUTPUT);
748                         if (hc->iqueue)
749                             iochan_setevent(hc->iochan, EVENT_INPUT);
750                     }
751                 }
752             }
753
754             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
755                 http_destroy(i); // Server closed; we're done
756             break;
757         default:
758             yaz_log(YLOG_WARN, "Unexpected event on connection");
759             http_destroy(i);
760     }
761 }
762
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
780 // Handles I/O on a client connection to a backend web server (proxy mode)
781 static void proxy_io(IOCHAN pi, int event)
782 {
783     struct http_proxy *pc = iochan_getdata(pi);
784     struct http_channel *hc = pc->channel;
785
786     switch (event)
787     {
788         int res;
789         struct http_buf *htbuf;
790
791         case EVENT_INPUT:
792             htbuf = http_buf_create();
793             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
794             if (res == 0 || (res < 0 && errno != EINPROGRESS))
795             {
796                 if (hc->oqueue)
797                 {
798                     yaz_log(YLOG_WARN, "Proxy read came up short");
799                     // Close channel and alert client HTTP channel that we're gone
800                     http_buf_destroy(htbuf);
801                     close(iochan_getfd(pi));
802                     iochan_destroy(pi);
803                     pc->iochan = 0;
804                 }
805                 else
806                 {
807                     http_destroy(hc->iochan);
808                     return;
809                 }
810             }
811             else
812             {
813                 htbuf->buf[res] = '\0';
814                 htbuf->offset = 0;
815                 htbuf->len = res;
816                 if (pc->first_response) // Check if this is a redirect
817                 {
818                     int len;
819                     if ((len = package_check(htbuf->buf)))
820                     {
821                         struct http_response *res = http_parse_response_buf(hc, htbuf->buf, len);
822                         if (res)
823                         {
824                             struct http_header *h;
825                             for (h = res->headers; h; h = h->next)
826                                 if (!strcmp(h->name, "Location"))
827                                 {
828                                     // We found a location header. Rewrite it.
829                                     struct http_buf *buf;
830                                     h->value = sub_hostname(hc, h->value);
831                                     buf = http_serialize_response(hc, res);
832                                     yaz_log(YLOG_LOG, "Proxy rewrite");
833                                     http_buf_enqueue(&hc->oqueue, buf);
834                                     htbuf->offset = len;
835                                     break;
836                                 }
837                         }
838                     }
839                     pc->first_response = 0;
840                 }
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 is %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     if (p) {
1031         port = atoi(p + 1);
1032         *p = '\0';
1033     }
1034     else
1035         port = 80;
1036     if (!(he = gethostbyname(host))) 
1037     {
1038         fprintf(stderr, "Failed to lookup '%s'\n", host);
1039         exit(1);
1040     }
1041     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
1042     proxy_addr->sin_family = he->h_addrtype;
1043     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
1044     proxy_addr->sin_port = htons(port);
1045 }
1046
1047 /*
1048  * Local variables:
1049  * c-basic-offset: 4
1050  * indent-tabs-mode: nil
1051  * End:
1052  * vim: shiftwidth=4 tabstop=8 expandtab
1053  */