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