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