URL decode variable names. Not just values. If not, things will break
[pazpar2-moved-to-github.git] / src / http.c
1 /* $Id: http.c,v 1.35 2007-06-26 13:01:07 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(a->name, a->name);
425             urldecode(equal, equal);
426             a->value = nmem_strdup(c->nmem, equal);
427             a->next = r->arguments;
428             r->arguments = a;
429             p2 = eoa;
430         }
431     }
432     buf = p;
433
434     if (strncmp(buf, "HTTP/", 5))
435         strcpy(r->http_version, "1.0");
436     else
437     {
438         buf += 5;
439         if (!(p = strstr(buf, "\r\n")))
440             return 0;
441         *(p++) = '\0';
442         p++;
443         strcpy(r->http_version, buf);
444         buf = p;
445     }
446     strcpy(c->version, r->http_version);
447
448     r->headers = 0;
449     while (*buf)
450     {
451         if (!(p = strstr(buf, "\r\n")))
452             return 0;
453         if (p == buf)
454             break;
455         else
456         {
457             struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
458             if (!(p2 = strchr(buf, ':')))
459                 return 0;
460             *(p2++) = '\0';
461             h->name = nmem_strdup(c->nmem, buf);
462             while (isspace(*p2))
463                 p2++;
464             if (p2 >= p) // Empty header?
465             {
466                 buf = p + 2;
467                 continue;
468             }
469             *p = '\0';
470             h->value = nmem_strdup(c->nmem, p2);
471             h->next = r->headers;
472             r->headers = h;
473             buf = p + 2;
474         }
475     }
476
477     return r;
478 }
479
480 static struct http_buf *http_serialize_response(struct http_channel *c,
481         struct http_response *r)
482 {
483     struct http_header *h;
484
485     wrbuf_rewind(c->wrbuf);
486     wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
487     for (h = r->headers; h; h = h->next)
488         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
489     if (r->payload)
490     {
491         wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ?
492                 (int) strlen(r->payload) : 0);
493         wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
494         if (1)
495         {
496             xmlDoc *doc = xmlParseMemory(r->payload, strlen(r->payload));
497             if (doc)
498             {
499                 xmlFreeDoc(doc);
500             }
501             else
502             {
503                 yaz_log(YLOG_WARN, "Sending non-wellformed "
504                         "response (bug #1162");
505                 yaz_log(YLOG_WARN, "payload: %s", r->payload);
506             }
507         }
508     }
509     wrbuf_puts(c->wrbuf, "\r\n");
510
511     if (r->payload)
512         wrbuf_puts(c->wrbuf, r->payload);
513
514     return http_buf_bywrbuf(c->wrbuf);
515 }
516
517 // Serialize a HTTP request
518 static struct http_buf *http_serialize_request(struct http_request *r)
519 {
520     struct http_channel *c = r->channel;
521     struct http_header *h;
522     struct http_argument *a;
523
524     wrbuf_rewind(c->wrbuf);
525     wrbuf_printf(c->wrbuf, "%s %s", r->method, r->path);
526
527     if (r->arguments)
528     {
529         wrbuf_putc(c->wrbuf, '?');
530         for (a = r->arguments; a; a = a->next) {
531             if (a != r->arguments)
532                 wrbuf_putc(c->wrbuf, '&');
533             wrbuf_printf(c->wrbuf, "%s=%s", a->name, a->value);
534         }
535     }
536
537     wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
538
539     for (h = r->headers; h; h = h->next)
540         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
541
542     wrbuf_puts(c->wrbuf, "\r\n");
543     
544     return http_buf_bywrbuf(c->wrbuf);
545 }
546
547
548 static int http_weshouldproxy(struct http_request *rq)
549 {
550     if (proxy_addr && !strstr(rq->path, "search.pz2"))
551         return 1;
552     return 0;
553 }
554
555
556 struct http_header * http_header_append(struct http_channel *ch, 
557                                         struct http_header * hp, 
558                                         const char *name, 
559                                         const char *value)
560 {
561     struct http_header *hpnew = 0; 
562
563     if (!hp | !ch)
564         return 0;
565
566     while (hp && hp->next)
567         hp = hp->next;
568
569     if(name && strlen(name)&& value && strlen(value)){
570         hpnew = nmem_malloc(ch->nmem, sizeof *hpnew);
571         hpnew->name = nmem_strdup(ch->nmem, name);
572         hpnew->value = nmem_strdup(ch->nmem, value);
573         
574         hpnew->next = 0;
575         hp->next = hpnew;
576         hp = hp->next;
577         
578         return hpnew;
579     }
580
581     return hp;
582 }
583
584     
585
586 static int http_proxy(struct http_request *rq)
587 {
588     struct http_channel *c = rq->channel;
589     struct http_proxy *p = c->proxy;
590     struct http_header *hp;
591     struct http_buf *requestbuf;
592     char server_via[128] = "";
593     char server_port[16] = "";
594     struct conf_server *ser = global_parameters.server;
595
596     if (!p) // This is a new connection. Create a proxy channel
597     {
598         int sock;
599         struct protoent *pe;
600         int one = 1;
601         int flags;
602
603         if (!(pe = getprotobyname("tcp"))) {
604             abort();
605         }
606         if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
607         {
608             yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
609             return -1;
610         }
611         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
612                         &one, sizeof(one)) < 0)
613             abort();
614         if ((flags = fcntl(sock, F_GETFL, 0)) < 0) 
615             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
616         if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
617             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
618         if (connect(sock, (struct sockaddr *) proxy_addr, 
619                     sizeof(*proxy_addr)) < 0)
620             if (errno != EINPROGRESS)
621             {
622                 yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
623                 return -1;
624             }
625
626         p = xmalloc(sizeof(struct http_proxy));
627         p->oqueue = 0;
628         p->channel = c;
629         p->first_response = 1;
630         c->proxy = p;
631         // We will add EVENT_OUTPUT below
632         p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT);
633         iochan_setdata(p->iochan, p);
634         pazpar2_add_channel(p->iochan);
635     }
636
637     // Do _not_ modify Host: header, just checking it's existence
638     for (hp = rq->headers; hp; hp = hp->next)
639         if (!strcmp(hp->name, "Host"))
640             break;
641     if (!hp)
642     {
643         yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
644         return -1;
645     }
646     
647     // Add new header about paraz2 version, host, remote client address, etc.
648     {
649         hp = rq->headers;
650         hp = http_header_append(c, hp, 
651                                 "X-Pazpar2-Version", PACKAGE_VERSION);
652         hp = http_header_append(c, hp, 
653                                 "X-Pazpar2-Server-Host", ser->host);
654         sprintf(server_port, "%d",  ser->port);
655         hp = http_header_append(c, hp, 
656                                 "X-Pazpar2-Server-Port", server_port);
657         sprintf(server_via,  "1.1 %s:%s (%s/%s)",  
658                 ser->host, server_port, PACKAGE_NAME, PACKAGE_VERSION);
659         hp = http_header_append(c, hp, "Via" , server_via);
660         hp = http_header_append(c, hp, "X-Forwarded-For", c->addr);
661     }
662     
663     requestbuf = http_serialize_request(rq);
664     http_buf_enqueue(&p->oqueue, requestbuf);
665     iochan_setflag(p->iochan, EVENT_OUTPUT);
666     return 0;
667 }
668
669 void http_send_response(struct http_channel *ch)
670 {
671     struct http_response *rs = ch->response;
672     struct http_buf *hb;
673
674     assert(rs);
675     hb = http_serialize_response(ch, rs);
676     if (!hb)
677     {
678         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
679         http_destroy(ch->iochan);
680     }
681     else
682     {
683         http_buf_enqueue(&ch->oqueue, hb);
684         iochan_setflag(ch->iochan, EVENT_OUTPUT);
685         ch->state = Http_Idle;
686     }
687 }
688
689 static void http_io(IOCHAN i, int event)
690 {
691     struct http_channel *hc = iochan_getdata(i);
692
693     switch (event)
694     {
695         int res, reqlen;
696         struct http_buf *htbuf;
697
698         case EVENT_INPUT:
699             htbuf = http_buf_create();
700             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
701             if (res == -1 && errno == EAGAIN)
702             {
703                 http_buf_destroy(htbuf);
704                 return;
705             }
706             if (res <= 0)
707             {
708                 http_buf_destroy(htbuf);
709                 http_destroy(i);
710                 return;
711             }
712             htbuf->buf[res] = '\0';
713             htbuf->len = res;
714             http_buf_enqueue(&hc->iqueue, htbuf);
715
716             if (hc->state == Http_Busy)
717                 return;
718             if ((reqlen = request_check(hc->iqueue)) <= 2)
719                 return;
720
721             nmem_reset(hc->nmem);
722             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
723             {
724                 yaz_log(YLOG_WARN, "Failed to parse request");
725                 http_destroy(i);
726                 return;
727             }
728             hc->response = 0;
729             yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
730                     hc->request->path,
731                     *hc->request->search ? "?" : "",
732                     hc->request->search);
733             if (http_weshouldproxy(hc->request))
734                 http_proxy(hc->request);
735             else
736             {
737                 // Execute our business logic!
738                 hc->state = Http_Busy;
739                 http_command(hc);
740             }
741             if (hc->iqueue)
742             {
743                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
744                 iochan_setevent(i, EVENT_INPUT);
745             }
746
747             break;
748
749         case EVENT_OUTPUT:
750             if (hc->oqueue)
751             {
752                 struct http_buf *wb = hc->oqueue;
753                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
754                 if (res <= 0)
755                 {
756                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
757                     http_destroy(i);
758                     return;
759                 }
760                 if (res == wb->len)
761                 {
762                     hc->oqueue = hc->oqueue->next;
763                     http_buf_destroy(wb);
764                 }
765                 else
766                 {
767                     wb->len -= res;
768                     wb->offset += res;
769                 }
770                 if (!hc->oqueue) {
771                     if (!strcmp(hc->version, "1.0"))
772                     {
773                         http_destroy(i);
774                         return;
775                     }
776                     else
777                     {
778                         iochan_clearflag(i, EVENT_OUTPUT);
779                         if (hc->iqueue)
780                             iochan_setevent(hc->iochan, EVENT_INPUT);
781                     }
782                 }
783             }
784
785             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
786                 http_destroy(i); // Server closed; we're done
787             break;
788         default:
789             yaz_log(YLOG_WARN, "Unexpected event on connection");
790             http_destroy(i);
791     }
792 }
793
794 #ifdef GAGA
795 // If this hostname contains our proxy host as a prefix, replace with myurl
796 static char *sub_hostname(struct http_channel *c, char *buf)
797 {
798     char tmp[1024];
799     if (strlen(buf) > 1023)
800         return buf;
801     if (strncmp(buf, "http://", 7))
802         return buf;
803     if (!strncmp(buf + 7, proxy_url, strlen(proxy_url)))
804     {
805         strcpy(tmp, myurl);
806         strcat(tmp, buf + strlen(proxy_url) + 7);
807         return nmem_strdup(c->nmem, tmp);
808     }
809     return buf;
810 }
811 #endif
812
813 // Handles I/O on a client connection to a backend web server (proxy mode)
814 static void proxy_io(IOCHAN pi, int event)
815 {
816     struct http_proxy *pc = iochan_getdata(pi);
817     struct http_channel *hc = pc->channel;
818
819     switch (event)
820     {
821         int res;
822         struct http_buf *htbuf;
823
824         case EVENT_INPUT:
825             htbuf = http_buf_create();
826             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
827             if (res == 0 || (res < 0 && errno != EINPROGRESS))
828             {
829                 if (hc->oqueue)
830                 {
831                     yaz_log(YLOG_WARN, "Proxy read came up short");
832                     // Close channel and alert client HTTP channel that we're gone
833                     http_buf_destroy(htbuf);
834                     close(iochan_getfd(pi));
835                     iochan_destroy(pi);
836                     pc->iochan = 0;
837                 }
838                 else
839                 {
840                     http_destroy(hc->iochan);
841                     return;
842                 }
843             }
844             else
845             {
846                 htbuf->buf[res] = '\0';
847                 htbuf->offset = 0;
848                 htbuf->len = res;
849 #ifdef GAGA
850                 if (pc->first_response) // Check if this is a redirect
851                 {
852                     int len;
853                     if ((len = package_check(htbuf->buf)))
854                     {
855                         struct http_response *res = http_parse_response_buf(hc, htbuf->buf, len);
856                         if (res)
857                         {
858                             struct http_header *h;
859                             for (h = res->headers; h; h = h->next)
860                                 if (!strcmp(h->name, "Location"))
861                                 {
862                                     // We found a location header. Rewrite it.
863                                     struct http_buf *buf;
864                                     h->value = sub_hostname(hc, h->value);
865                                     buf = http_serialize_response(hc, res);
866                                     yaz_log(YLOG_LOG, "Proxy rewrite");
867                                     http_buf_enqueue(&hc->oqueue, buf);
868                                     htbuf->offset = len;
869                                     break;
870                                 }
871                         }
872                     }
873                     pc->first_response = 0;
874                 }
875 #endif
876                 // Write any remaining payload
877                 if (htbuf->len - htbuf->offset > 0)
878                     http_buf_enqueue(&hc->oqueue, htbuf);
879             }
880             iochan_setflag(hc->iochan, EVENT_OUTPUT);
881             break;
882         case EVENT_OUTPUT:
883             if (!(htbuf = pc->oqueue))
884             {
885                 iochan_clearflag(pi, EVENT_OUTPUT);
886                 return;
887             }
888             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
889             if (res <= 0)
890             {
891                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
892                 http_destroy(hc->iochan);
893                 return;
894             }
895             if (res == htbuf->len)
896             {
897                 struct http_buf *np = htbuf->next;
898                 http_buf_destroy(htbuf);
899                 pc->oqueue = np;
900             }
901             else
902             {
903                 htbuf->len -= res;
904                 htbuf->offset += res;
905             }
906
907             if (!pc->oqueue) {
908                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
909             }
910             break;
911         default:
912             yaz_log(YLOG_WARN, "Unexpected event on connection");
913             http_destroy(hc->iochan);
914     }
915 }
916
917 static void http_fire_observers(struct http_channel *c);
918 static void http_destroy_observers(struct http_channel *c);
919
920 // Cleanup channel
921 static void http_destroy(IOCHAN i)
922 {
923     struct http_channel *s = iochan_getdata(i);
924
925     if (s->proxy)
926     {
927         if (s->proxy->iochan)
928         {
929             close(iochan_getfd(s->proxy->iochan));
930             iochan_destroy(s->proxy->iochan);
931         }
932         http_buf_destroy_queue(s->proxy->oqueue);
933         xfree(s->proxy);
934     }
935     http_buf_destroy_queue(s->iqueue);
936     http_buf_destroy_queue(s->oqueue);
937     http_fire_observers(s);
938     http_destroy_observers(s);
939     s->next = http_channel_freelist;
940     http_channel_freelist = s;
941     close(iochan_getfd(i));
942     iochan_destroy(i);
943 }
944
945 static struct http_channel *http_create(const char *addr)
946 {
947     struct http_channel *r = http_channel_freelist;
948
949     if (r)
950     {
951         http_channel_freelist = r->next;
952         nmem_reset(r->nmem);
953         wrbuf_rewind(r->wrbuf);
954     }
955     else
956     {
957         r = xmalloc(sizeof(struct http_channel));
958         r->nmem = nmem_create();
959         r->wrbuf = wrbuf_alloc();
960     }
961     r->proxy = 0;
962     r->iochan = 0;
963     r->iqueue = r->oqueue = 0;
964     r->state = Http_Idle;
965     r->request = 0;
966     r->response = 0;
967     if (!addr)
968     {
969         yaz_log(YLOG_WARN, "Invalid HTTP forward address");
970         exit(1);
971     }
972     strcpy(r->addr, addr);
973     r->observers = 0;
974     return r;
975 }
976
977
978 /* Accept a new command connection */
979 static void http_accept(IOCHAN i, int event)
980 {
981     struct sockaddr_in addr;
982     int fd = iochan_getfd(i);
983     socklen_t len;
984     int s;
985     IOCHAN c;
986     int flags;
987     struct http_channel *ch;
988
989     len = sizeof addr;
990     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
991     {
992         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
993         return;
994     }
995     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
996         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
997     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
998         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
999
1000     yaz_log(YLOG_DEBUG, "New command connection");
1001     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
1002     
1003     ch = http_create(inet_ntoa(addr.sin_addr));
1004     ch->iochan = c;
1005     iochan_setdata(c, ch);
1006
1007     pazpar2_add_channel(c);
1008 }
1009
1010 /* Create a http-channel listener, syntax [host:]port */
1011 void http_init(const char *addr)
1012 {
1013     IOCHAN c;
1014     int l;
1015     struct protoent *p;
1016     struct sockaddr_in myaddr;
1017     int one = 1;
1018     const char *pp;
1019     int port;
1020
1021     yaz_log(YLOG_LOG, "HTTP listener %s", addr);
1022
1023     memset(&myaddr, 0, sizeof myaddr);
1024     myaddr.sin_family = AF_INET;
1025     pp = strchr(addr, ':');
1026     if (pp)
1027     {
1028         int len = pp - addr;
1029         char hostname[128];
1030         struct hostent *he;
1031
1032         strncpy(hostname, addr, len);
1033         hostname[len] = '\0';
1034         if (!(he = gethostbyname(hostname))){
1035             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
1036             exit(1);
1037         }
1038         
1039         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
1040         port = atoi(pp + 1);
1041
1042         yaz_log(YLOG_LOG, "HTTP address  %s:%d", 
1043                 "" == he->h_addr_list[0] ? he->h_addr_list[0] : "127.0.0.1" , 
1044                     port);
1045
1046     }
1047     else
1048     {
1049         port = atoi(addr);
1050         myaddr.sin_addr.s_addr = INADDR_ANY;
1051     }
1052
1053     myaddr.sin_port = htons(port);
1054
1055     if (!(p = getprotobyname("tcp"))) {
1056         abort();
1057     }
1058     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
1059         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
1060     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
1061                     &one, sizeof(one)) < 0)
1062         abort();
1063
1064     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
1065     {
1066         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
1067         exit(1);
1068     }
1069     if (listen(l, SOMAXCONN) < 0) 
1070     {
1071         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
1072         exit(1);
1073     }
1074
1075     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
1076     pazpar2_add_channel(c);
1077 }
1078
1079 void http_set_proxyaddr(char *host, char *base_url)
1080 {
1081     char *p;
1082     int port;
1083     struct hostent *he;
1084
1085     strcpy(myurl, base_url);
1086     strcpy(proxy_url, host);
1087     p = strchr(host, ':');
1088     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
1089     yaz_log(YLOG_LOG, "HTTP backend  %s", proxy_url);
1090     if (p) {
1091         port = atoi(p + 1);
1092         *p = '\0';
1093     }
1094     else
1095         port = 80;
1096     if (!(he = gethostbyname(host))) 
1097     {
1098         fprintf(stderr, "Failed to lookup '%s'\n", host);
1099         exit(1);
1100     }
1101     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
1102     proxy_addr->sin_family = he->h_addrtype;
1103     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
1104     proxy_addr->sin_port = htons(port);
1105 }
1106
1107 static void http_fire_observers(struct http_channel *c)
1108 {
1109     http_channel_observer_t p = c->observers;
1110     while (p)
1111     {
1112         p->destroy(p->data, c);
1113         p = p->next;
1114     }
1115 }
1116
1117 static void http_destroy_observers(struct http_channel *c)
1118 {
1119     while (c->observers)
1120     {
1121         http_channel_observer_t obs = c->observers;
1122         c->observers = obs->next;
1123         xfree(obs);
1124     }
1125 }
1126
1127 http_channel_observer_t http_add_observer(struct http_channel *c, void *data,
1128                                           http_channel_destroy_t des)
1129 {
1130     http_channel_observer_t obs = xmalloc(sizeof(*obs));
1131     obs->chan = c;
1132     obs->data = data;
1133     obs->destroy= des;
1134     obs->next = c->observers;
1135     c->observers = obs;
1136     return obs;
1137 }
1138
1139 void http_remove_observer(http_channel_observer_t obs)
1140 {
1141     struct http_channel *c = obs->chan;
1142     http_channel_observer_t found, *p = &c->observers;
1143     while (*p != obs)
1144         p = &(*p)->next;
1145     found = *p;
1146     assert(found);
1147     *p = (*p)->next;
1148     xfree(found);
1149 }
1150
1151 struct http_channel *http_channel_observer_chan(http_channel_observer_t obs)
1152 {
1153     return obs->chan;
1154 }
1155
1156 /*
1157  * Local variables:
1158  * c-basic-offset: 4
1159  * indent-tabs-mode: nil
1160  * End:
1161  * vim: shiftwidth=4 tabstop=8 expandtab
1162  */