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