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