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