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