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