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