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