This update completes the factoring out of database management into database.c,
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.12 2007-03-15 16:50:56 quinn Exp $
3  */
4
5 #include <stdio.h>
6 #include <sys/socket.h>
7 #include <sys/types.h>
8 #include <sys/uio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <strings.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <netdb.h>
15 #include <errno.h>
16 #include <assert.h>
17
18 #if HAVE_CONFIG_H
19 #include <cconfig.h>
20 #endif
21
22 #include <netinet/in.h>
23
24 #include <yaz/yaz-util.h>
25 #include <yaz/comstack.h>
26 #include <netdb.h>
27
28 #include "util.h"
29 #include "eventl.h"
30 #include "pazpar2.h"
31 #include "http.h"
32 #include "http_command.h"
33
34 static void proxy_io(IOCHAN i, int event);
35 static struct http_channel *http_create(void);
36 static void http_destroy(IOCHAN i);
37
38 extern IOCHAN channel_list;
39
40 static struct sockaddr_in *proxy_addr = 0; // If this is set, we proxy normal HTTP requests
41 static char proxy_url[256] = "";
42 static char myurl[256] = "";
43 static struct http_buf *http_buf_freelist = 0;
44 static struct http_channel *http_channel_freelist = 0;
45
46 static struct http_buf *http_buf_create()
47 {
48     struct http_buf *r;
49
50     if (http_buf_freelist)
51     {
52         r = http_buf_freelist;
53         http_buf_freelist = http_buf_freelist->next;
54     }
55     else
56         r = xmalloc(sizeof(struct http_buf));
57     r->offset = 0;
58     r->len = 0;
59     r->next = 0;
60     return r;
61 }
62
63 static void http_buf_destroy(struct http_buf *b)
64 {
65     b->next = http_buf_freelist;
66     http_buf_freelist = b;
67 }
68
69 static void http_buf_destroy_queue(struct http_buf *b)
70 {
71     struct http_buf *p;
72     while (b)
73     {
74         p = b->next;
75         http_buf_destroy(b);
76         b = p;
77     }
78 }
79
80 #ifdef GAGA
81 // Calculate length of chain
82 static int http_buf_len(struct http_buf *b)
83 {
84     int sum = 0;
85     for (; b; b = b->next)
86         sum += b->len;
87     return sum;
88 }
89 #endif
90
91 static struct http_buf *http_buf_bybuf(char *b, int len)
92 {
93     struct http_buf *res = 0;
94     struct http_buf **p = &res;
95
96     while (len)
97     {
98         int tocopy = len;
99         if (tocopy > HTTP_BUF_SIZE)
100             tocopy = HTTP_BUF_SIZE;
101         *p = http_buf_create();
102         memcpy((*p)->buf, b, tocopy);
103         (*p)->len = tocopy;
104         len -= tocopy;
105         b += tocopy;
106         p = &(*p)->next;
107     }
108     return res;
109 }
110
111 // Add a (chain of) buffers to the end of an existing queue.
112 static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
113 {
114     while (*queue)
115         queue = &(*queue)->next;
116     *queue = b;
117 }
118
119 static struct http_buf *http_buf_bywrbuf(WRBUF wrbuf)
120 {
121     // Heavens to Betsy (buf)!
122     return http_buf_bybuf(wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
123 }
124
125 // Non-destructively collapse chain of buffers into a string (max *len)
126 // Return
127 static int http_buf_peek(struct http_buf *b, char *buf, int len)
128 {
129     int rd = 0;
130     while (b && rd < len)
131     {
132         int toread = len - rd;
133         if (toread > b->len)
134             toread = b->len;
135         memcpy(buf + rd, b->buf + b->offset, toread);
136         rd += toread;
137         b = b->next;
138     }
139     buf[rd] = '\0';
140     return rd;
141 }
142
143 // Ddestructively munch up to len  from head of queue.
144 static int http_buf_read(struct http_buf **b, char *buf, int len)
145 {
146     int rd = 0;
147     while ((*b) && rd < len)
148     {
149         int toread = len - rd;
150         if (toread > (*b)->len)
151             toread = (*b)->len;
152         memcpy(buf + rd, (*b)->buf + (*b)->offset, toread);
153         rd += toread;
154         if (toread < (*b)->len)
155         {
156             (*b)->len -= toread;
157             (*b)->offset += toread;
158             break;
159         }
160         else
161         {
162             struct http_buf *n = (*b)->next;
163             http_buf_destroy(*b);
164             *b = n;
165         }
166     }
167     buf[rd] = '\0';
168     return rd;
169 }
170
171 // Buffers may overlap.
172 static void urldecode(char *i, char *o)
173 {
174     while (*i)
175     {
176         if (*i == '+')
177         {
178             *(o++) = ' ';
179             i++;
180         }
181         else if (*i == '%')
182         {
183             i++;
184             sscanf(i, "%2hhx", o);
185             i += 2;
186             o++;
187         }
188         else
189             *(o++) = *(i++);
190     }
191     *o = '\0';
192 }
193
194 // Warning: Buffers may not overlap
195 void urlencode(const char *i, char *o)
196 {
197     while (*i)
198     {
199         if (strchr(" /:", *i))
200         {
201             sprintf(o, "%%%.2X", (int) *i);
202             o += 3;
203         }
204         else
205             *(o++) = *i;
206         i++;
207     }
208     *o = '\0';
209 }
210
211 void http_addheader(struct http_response *r, const char *name, const char *value)
212 {
213     struct http_channel *c = r->channel;
214     struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
215     h->name = nmem_strdup(c->nmem, name);
216     h->value = nmem_strdup(c->nmem, value);
217     h->next = r->headers;
218     r->headers = h;
219 }
220
221 char *http_argbyname(struct http_request *r, char *name)
222 {
223     struct http_argument *p;
224     if (!name)
225         return 0;
226     for (p = r->arguments; p; p = p->next)
227         if (!strcmp(p->name, name))
228             return p->value;
229     return 0;
230 }
231
232 char *http_headerbyname(struct http_header *h, char *name)
233 {
234     for (; h; h = h->next)
235         if (!strcmp(h->name, name))
236             return h->value;
237     return 0;
238 }
239
240 struct http_response *http_create_response(struct http_channel *c)
241 {
242     struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
243     strcpy(r->code, "200");
244     r->msg = "OK";
245     r->channel = c;
246     r->headers = 0;
247     r->payload = 0;
248     return r;
249 }
250
251 // Check if buf contains a package (minus payload)
252 static int package_check(const char *buf)
253 {
254     int len = 0;
255     while (*buf) // Check if we have a sequence of lines terminated by an empty line
256     {
257         char *b = strstr(buf, "\r\n");
258
259         if (!b)
260             return 0;
261
262         len += (b - buf) + 2;
263         if (b == buf)
264             return len;
265         buf = b + 2;
266     }
267     return 0;
268 }
269
270 // Check if we have a request. Return 0 or length
271 // (including trailing CRNL) FIXME: Does not deal gracefully with requests
272 // carrying payload but this is kind of OK since we will reject anything
273 // other than an empty GET
274 static int request_check(struct http_buf *queue)
275 {
276     char tmp[4096];
277
278     http_buf_peek(queue, tmp, 4096);
279     return package_check(tmp);
280 }
281
282 struct http_response *http_parse_response_buf(struct http_channel *c, const char *buf, int len)
283 {
284     char tmp[4096];
285     struct http_response *r = http_create_response(c);
286     char *p, *p2;
287     struct http_header **hp = &r->headers;
288
289     if (len >= 4096)
290         return 0;
291     memcpy(tmp, buf, len);
292     for (p = tmp; *p && *p != ' '; p++) // Skip HTTP version
293         ;
294     p++;
295     // Response code
296     for (p2 = p; *p2 && *p2 != ' ' && p2 - p < 3; p2++)
297         r->code[p2 - p] = *p2;
298     if (!(p = strstr(tmp, "\r\n")))
299         return 0;
300     p += 2;
301     while (*p)
302     {
303         if (!(p2 = strstr(p, "\r\n")))
304             return 0;
305         if (p == p2) // End of headers
306             break;
307         else
308         {
309             struct http_header *h = *hp = nmem_malloc(c->nmem, sizeof(*h));
310             char *value = strchr(p, ':');
311             if (!value)
312                 return 0;
313             *(value++) = '\0';
314             h->name = nmem_strdup(c->nmem, p);
315             while (isspace(*value))
316                 value++;
317             if (value >= p2)  // Empty header;
318             {
319                 h->value = "";
320                 p = p2 + 2;
321                 continue;
322             }
323             *p2 = '\0';
324             h->value = nmem_strdup(c->nmem, value);
325             h->next = 0;
326             hp = &h->next;
327             p = p2 + 2;
328         }
329     }
330     return r;
331 }
332
333 struct http_request *http_parse_request(struct http_channel *c, struct http_buf **queue,
334         int len)
335 {
336     struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
337     char *p, *p2;
338     char tmp[4096];
339     char *buf = tmp;
340
341     if (len > 4096)
342         return 0;
343     if (http_buf_read(queue, buf, len) < len)
344         return 0;
345
346     r->search = "";
347     r->channel = c;
348     r->arguments = 0;
349     r->headers = 0;
350     // Parse first line
351     for (p = buf, p2 = r->method; *p && *p != ' ' && p - buf < 19; p++)
352         *(p2++) = *p;
353     if (*p != ' ')
354     {
355         yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
356         return 0;
357     }
358     *p2 = '\0';
359
360     if (!(buf = strchr(buf, ' ')))
361     {
362         yaz_log(YLOG_WARN, "Syntax error in request (1)");
363         return 0;
364     }
365     buf++;
366     if (!(p = strchr(buf, ' ')))
367     {
368         yaz_log(YLOG_WARN, "Syntax error in request (2)");
369         return 0;
370     }
371     *(p++) = '\0';
372     if ((p2 = strchr(buf, '?'))) // Do we have arguments?
373         *(p2++) = '\0';
374     r->path = nmem_strdup(c->nmem, buf);
375     if (p2)
376     {
377         r->search = nmem_strdup(c->nmem, p2);
378         // Parse Arguments
379         while (*p2)
380         {
381             struct http_argument *a;
382             char *equal = strchr(p2, '=');
383             char *eoa = strchr(p2, '&');
384             if (!equal)
385             {
386                 yaz_log(YLOG_WARN, "Expected '=' in argument");
387                 return 0;
388             }
389             if (!eoa)
390                 eoa = equal + strlen(equal); // last argument
391             else
392                 *(eoa++) = '\0';
393             a = nmem_malloc(c->nmem, sizeof(struct http_argument));
394             *(equal++) = '\0';
395             a->name = nmem_strdup(c->nmem, p2);
396             urldecode(equal, equal);
397             a->value = nmem_strdup(c->nmem, equal);
398             a->next = r->arguments;
399             r->arguments = a;
400             p2 = eoa;
401         }
402     }
403     buf = p;
404
405     if (strncmp(buf, "HTTP/", 5))
406         strcpy(r->http_version, "1.0");
407     else
408     {
409         buf += 5;
410         if (!(p = strstr(buf, "\r\n")))
411             return 0;
412         *(p++) = '\0';
413         p++;
414         strcpy(r->http_version, buf);
415         buf = p;
416     }
417     strcpy(c->version, r->http_version);
418
419     r->headers = 0;
420     while (*buf)
421     {
422         if (!(p = strstr(buf, "\r\n")))
423             return 0;
424         if (p == buf)
425             break;
426         else
427         {
428             struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
429             if (!(p2 = strchr(buf, ':')))
430                 return 0;
431             *(p2++) = '\0';
432             h->name = nmem_strdup(c->nmem, buf);
433             while (isspace(*p2))
434                 p2++;
435             if (p2 >= p) // Empty header?
436             {
437                 buf = p + 2;
438                 continue;
439             }
440             *p = '\0';
441             h->value = nmem_strdup(c->nmem, p2);
442             h->next = r->headers;
443             r->headers = h;
444             buf = p + 2;
445         }
446     }
447
448     return r;
449 }
450
451 static struct http_buf *http_serialize_response(struct http_channel *c,
452         struct http_response *r)
453 {
454     struct http_header *h;
455
456     wrbuf_rewind(c->wrbuf);
457     wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
458     for (h = r->headers; h; h = h->next)
459         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
460     if (r->payload)
461     {
462         wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ?
463                 (int) strlen(r->payload) : 0);
464         wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
465     }
466     wrbuf_puts(c->wrbuf, "\r\n");
467
468     if (r->payload)
469         wrbuf_puts(c->wrbuf, r->payload);
470
471     return http_buf_bywrbuf(c->wrbuf);
472 }
473
474 // Serialize a HTTP request
475 static struct http_buf *http_serialize_request(struct http_request *r)
476 {
477     struct http_channel *c = r->channel;
478     struct http_header *h;
479     struct http_argument *a;
480
481     wrbuf_rewind(c->wrbuf);
482     wrbuf_printf(c->wrbuf, "%s %s", r->method, r->path);
483
484     if (r->arguments)
485     {
486         wrbuf_putc(c->wrbuf, '?');
487         for (a = r->arguments; a; a = a->next) {
488             if (a != r->arguments)
489                 wrbuf_putc(c->wrbuf, '&');
490             wrbuf_printf(c->wrbuf, "%s=%s", a->name, a->value);
491         }
492     }
493
494     wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
495
496     for (h = r->headers; h; h = h->next)
497         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
498
499     wrbuf_puts(c->wrbuf, "\r\n");
500     
501     return http_buf_bywrbuf(c->wrbuf);
502 }
503
504
505 static int http_weshouldproxy(struct http_request *rq)
506 {
507     if (proxy_addr && !strstr(rq->path, "search.pz2"))
508         return 1;
509     return 0;
510 }
511
512 static int http_proxy(struct http_request *rq)
513 {
514     struct http_channel *c = rq->channel;
515     struct http_proxy *p = c->proxy;
516     struct http_header *hp;
517     struct http_buf *requestbuf;
518
519     if (!p) // This is a new connection. Create a proxy channel
520     {
521         int sock;
522         struct protoent *pe;
523         int one = 1;
524         int flags;
525
526         if (!(pe = getprotobyname("tcp"))) {
527             abort();
528         }
529         if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
530         {
531             yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
532             return -1;
533         }
534         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
535                         &one, sizeof(one)) < 0)
536             abort();
537         if ((flags = fcntl(sock, F_GETFL, 0)) < 0) 
538             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
539         if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
540             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
541         if (connect(sock, (struct sockaddr *) proxy_addr, sizeof(*proxy_addr)) < 0)
542             if (errno != EINPROGRESS)
543             {
544                 yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
545                 return -1;
546             }
547
548         p = xmalloc(sizeof(struct http_proxy));
549         p->oqueue = 0;
550         p->channel = c;
551         p->first_response = 1;
552         c->proxy = p;
553         // We will add EVENT_OUTPUT below
554         p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT);
555         iochan_setdata(p->iochan, p);
556         p->iochan->next = channel_list;
557         channel_list = p->iochan;
558     }
559
560     // Modify Host: header
561     for (hp = rq->headers; hp; hp = hp->next)
562         if (!strcmp(hp->name, "Host"))
563             break;
564     if (!hp)
565     {
566         yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
567         return -1;
568     }
569     hp->value = nmem_strdup(c->nmem, proxy_url);
570     requestbuf = http_serialize_request(rq);
571     http_buf_enqueue(&p->oqueue, requestbuf);
572     iochan_setflag(p->iochan, EVENT_OUTPUT);
573     return 0;
574 }
575
576 void http_send_response(struct http_channel *ch)
577 {
578     struct http_response *rs = ch->response;
579     struct http_buf *hb;
580
581     assert(rs);
582     hb = http_serialize_response(ch, rs);
583     if (!hb)
584     {
585         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
586         http_destroy(ch->iochan);
587     }
588     else
589     {
590         http_buf_enqueue(&ch->oqueue, hb);
591         iochan_setflag(ch->iochan, EVENT_OUTPUT);
592         ch->state = Http_Idle;
593     }
594 }
595
596 static void http_io(IOCHAN i, int event)
597 {
598     struct http_channel *hc = iochan_getdata(i);
599
600     switch (event)
601     {
602         int res, reqlen;
603         struct http_buf *htbuf;
604
605         case EVENT_INPUT:
606             htbuf = http_buf_create();
607             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
608             if (res == -1 && errno == EAGAIN)
609             {
610                 http_buf_destroy(htbuf);
611                 return;
612             }
613             if (res <= 0)
614             {
615                 http_buf_destroy(htbuf);
616                 http_destroy(i);
617                 return;
618             }
619             if (res > 0)
620             {
621                 htbuf->buf[res] = '\0';
622                 htbuf->len = res;
623                 http_buf_enqueue(&hc->iqueue, htbuf);
624             }
625
626             if (hc->state == Http_Busy)
627                 return;
628             if ((reqlen = request_check(hc->iqueue)) <= 2)
629                 return;
630
631             nmem_reset(hc->nmem);
632             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
633             {
634                 yaz_log(YLOG_WARN, "Failed to parse request");
635                 http_destroy(i);
636                 return;
637             }
638             hc->response = 0;
639             yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
640                     hc->request->path,
641                     *hc->request->search ? "?" : "",
642                     hc->request->search);
643             if (http_weshouldproxy(hc->request))
644                 http_proxy(hc->request);
645             else
646             {
647                 // Execute our business logic!
648                 hc->state = Http_Busy;
649                 http_command(hc);
650             }
651             if (hc->iqueue)
652             {
653                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
654                 iochan_setevent(i, EVENT_INPUT);
655             }
656
657             break;
658
659         case EVENT_OUTPUT:
660             if (hc->oqueue)
661             {
662                 struct http_buf *wb = hc->oqueue;
663                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
664                 if (res <= 0)
665                 {
666                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
667                     http_destroy(i);
668                     return;
669                 }
670                 if (res == wb->len)
671                 {
672                     hc->oqueue = hc->oqueue->next;
673                     http_buf_destroy(wb);
674                 }
675                 else
676                 {
677                     wb->len -= res;
678                     wb->offset += res;
679                 }
680                 if (!hc->oqueue) {
681                     if (!strcmp(hc->version, "1.0"))
682                     {
683                         http_destroy(i);
684                         return;
685                     }
686                     else
687                     {
688                         iochan_clearflag(i, EVENT_OUTPUT);
689                         if (hc->iqueue)
690                             iochan_setevent(hc->iochan, EVENT_INPUT);
691                     }
692                 }
693             }
694
695             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
696                 http_destroy(i); // Server closed; we're done
697             break;
698         default:
699             yaz_log(YLOG_WARN, "Unexpected event on connection");
700             http_destroy(i);
701     }
702 }
703
704 // If this hostname contains our proxy host as a prefix, replace with myurl
705 static char *sub_hostname(struct http_channel *c, char *buf)
706 {
707     char tmp[1024];
708     if (strlen(buf) > 1023)
709         return buf;
710     if (strncmp(buf, "http://", 7))
711         return buf;
712     if (!strncmp(buf + 7, proxy_url, strlen(proxy_url)))
713     {
714         strcpy(tmp, myurl);
715         strcat(tmp, buf + strlen(proxy_url) + 7);
716         return nmem_strdup(c->nmem, tmp);
717     }
718     return buf;
719 }
720
721 // Handles I/O on a client connection to a backend web server (proxy mode)
722 static void proxy_io(IOCHAN pi, int event)
723 {
724     struct http_proxy *pc = iochan_getdata(pi);
725     struct http_channel *hc = pc->channel;
726
727     switch (event)
728     {
729         int res;
730         struct http_buf *htbuf;
731
732         case EVENT_INPUT:
733             htbuf = http_buf_create();
734             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
735             if (res == 0 || (res < 0 && errno != EINPROGRESS))
736             {
737                 if (hc->oqueue)
738                 {
739                     yaz_log(YLOG_WARN, "Proxy read came up short");
740                     // Close channel and alert client HTTP channel that we're gone
741                     http_buf_destroy(htbuf);
742                     close(iochan_getfd(pi));
743                     iochan_destroy(pi);
744                     pc->iochan = 0;
745                 }
746                 else
747                 {
748                     http_destroy(hc->iochan);
749                     return;
750                 }
751             }
752             else
753             {
754                 htbuf->buf[res] = '\0';
755                 htbuf->offset = 0;
756                 htbuf->len = res;
757                 if (pc->first_response) // Check if this is a redirect
758                 {
759                     int len;
760                     if ((len = package_check(htbuf->buf)))
761                     {
762                         struct http_response *res = http_parse_response_buf(hc, htbuf->buf, len);
763                         if (res)
764                         {
765                             struct http_header *h;
766                             for (h = res->headers; h; h = h->next)
767                                 if (!strcmp(h->name, "Location"))
768                                 {
769                                     // We found a location header. Rewrite it.
770                                     struct http_buf *buf;
771                                     h->value = sub_hostname(hc, h->value);
772                                     buf = http_serialize_response(hc, res);
773                                     yaz_log(YLOG_LOG, "Proxy rewrite");
774                                     http_buf_enqueue(&hc->oqueue, buf);
775                                     htbuf->offset = len;
776                                     break;
777                                 }
778                         }
779                     }
780                     pc->first_response = 0;
781                 }
782                 // Write any remaining payload
783                 if (htbuf->len - htbuf->offset > 0)
784                     http_buf_enqueue(&hc->oqueue, htbuf);
785             }
786             iochan_setflag(hc->iochan, EVENT_OUTPUT);
787             break;
788         case EVENT_OUTPUT:
789             if (!(htbuf = pc->oqueue))
790             {
791                 iochan_clearflag(pi, EVENT_OUTPUT);
792                 return;
793             }
794             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
795             if (res <= 0)
796             {
797                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
798                 http_destroy(hc->iochan);
799                 return;
800             }
801             if (res == htbuf->len)
802             {
803                 struct http_buf *np = htbuf->next;
804                 http_buf_destroy(htbuf);
805                 pc->oqueue = np;
806             }
807             else
808             {
809                 htbuf->len -= res;
810                 htbuf->offset += res;
811             }
812
813             if (!pc->oqueue) {
814                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
815             }
816             break;
817         default:
818             yaz_log(YLOG_WARN, "Unexpected event on connection");
819             http_destroy(hc->iochan);
820     }
821 }
822
823 // Cleanup channel
824 static void http_destroy(IOCHAN i)
825 {
826     struct http_channel *s = iochan_getdata(i);
827
828     if (s->proxy)
829     {
830         if (s->proxy->iochan)
831         {
832             close(iochan_getfd(s->proxy->iochan));
833             iochan_destroy(s->proxy->iochan);
834         }
835         http_buf_destroy_queue(s->proxy->oqueue);
836         xfree(s->proxy);
837     }
838     s->next = http_channel_freelist;
839     http_channel_freelist = s;
840     close(iochan_getfd(i));
841     iochan_destroy(i);
842 }
843
844 static struct http_channel *http_create(void)
845 {
846     struct http_channel *r = http_channel_freelist;
847
848     if (r)
849     {
850         http_channel_freelist = r->next;
851         nmem_reset(r->nmem);
852         wrbuf_rewind(r->wrbuf);
853     }
854     else
855     {
856         r = xmalloc(sizeof(struct http_channel));
857         r->nmem = nmem_create();
858         r->wrbuf = wrbuf_alloc();
859     }
860     r->proxy = 0;
861     r->iochan = 0;
862     r->iqueue = r->oqueue = 0;
863     r->state = Http_Idle;
864     r->request = 0;
865     r->response = 0;
866     return r;
867 }
868
869
870 /* Accept a new command connection */
871 static void http_accept(IOCHAN i, int event)
872 {
873     struct sockaddr_in addr;
874     int fd = iochan_getfd(i);
875     socklen_t len;
876     int s;
877     IOCHAN c;
878     int flags;
879     struct http_channel *ch;
880
881     len = sizeof addr;
882     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
883     {
884         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
885         return;
886     }
887     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
888         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
889     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
890         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
891
892     yaz_log(YLOG_DEBUG, "New command connection");
893     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
894
895     ch = http_create();
896     ch->iochan = c;
897     iochan_setdata(c, ch);
898
899     c->next = channel_list;
900     channel_list = c;
901 }
902
903 /* Create a http-channel listener, syntax [host:]port */
904 void http_init(const char *addr)
905 {
906     IOCHAN c;
907     int l;
908     struct protoent *p;
909     struct sockaddr_in myaddr;
910     int one = 1;
911     const char *pp;
912     int port;
913
914     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
915
916     memset(&myaddr, 0, sizeof myaddr);
917     myaddr.sin_family = AF_INET;
918     pp = strchr(addr, ':');
919     if (pp)
920     {
921         int len = pp - addr;
922         char hostname[128];
923         struct hostent *he;
924
925         strncpy(hostname, addr, len);
926         hostname[len] = '\0';
927         if (!(he = gethostbyname(hostname)))
928         {
929             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
930             exit(1);
931         }
932         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
933         port = atoi(pp + 1);
934     }
935     else
936     {
937         port = atoi(addr);
938         myaddr.sin_addr.s_addr = INADDR_ANY;
939     }
940     myaddr.sin_port = htons(port);
941
942     if (!(p = getprotobyname("tcp"))) {
943         abort();
944     }
945     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
946         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
947     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
948                     &one, sizeof(one)) < 0)
949         abort();
950
951     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
952         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
953     if (listen(l, SOMAXCONN) < 0) 
954         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
955
956     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
957     c->next = channel_list;
958     channel_list = c;
959 }
960
961 void http_set_proxyaddr(char *host, char *base_url)
962 {
963     char *p;
964     int port;
965     struct hostent *he;
966
967     strcpy(myurl, base_url);
968     strcpy(proxy_url, host);
969     p = strchr(host, ':');
970     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
971     if (p) {
972         port = atoi(p + 1);
973         *p = '\0';
974     }
975     else
976         port = 80;
977     if (!(he = gethostbyname(host))) 
978     {
979         fprintf(stderr, "Failed to lookup '%s'\n", host);
980         exit(1);
981     }
982     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
983     proxy_addr->sin_family = he->h_addrtype;
984     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
985     proxy_addr->sin_port = htons(port);
986 }
987
988 /*
989  * Local variables:
990  * c-basic-offset: 4
991  * indent-tabs-mode: nil
992  * End:
993  * vim: shiftwidth=4 tabstop=8 expandtab
994  */