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