2 * $Id: http.c,v 1.12 2007-03-15 16:50:56 quinn Exp $
6 #include <sys/socket.h>
22 #include <netinet/in.h>
24 #include <yaz/yaz-util.h>
25 #include <yaz/comstack.h>
32 #include "http_command.h"
34 static void proxy_io(IOCHAN i, int event);
35 static struct http_channel *http_create(void);
36 static void http_destroy(IOCHAN i);
38 extern IOCHAN channel_list;
40 static struct sockaddr_in *proxy_addr = 0; // If this is set, we proxy normal HTTP requests
41 static char proxy_url[256] = "";
42 static char myurl[256] = "";
43 static struct http_buf *http_buf_freelist = 0;
44 static struct http_channel *http_channel_freelist = 0;
46 static struct http_buf *http_buf_create()
50 if (http_buf_freelist)
52 r = http_buf_freelist;
53 http_buf_freelist = http_buf_freelist->next;
56 r = xmalloc(sizeof(struct http_buf));
63 static void http_buf_destroy(struct http_buf *b)
65 b->next = http_buf_freelist;
66 http_buf_freelist = b;
69 static void http_buf_destroy_queue(struct http_buf *b)
81 // Calculate length of chain
82 static int http_buf_len(struct http_buf *b)
85 for (; b; b = b->next)
91 static struct http_buf *http_buf_bybuf(char *b, int len)
93 struct http_buf *res = 0;
94 struct http_buf **p = &res;
99 if (tocopy > HTTP_BUF_SIZE)
100 tocopy = HTTP_BUF_SIZE;
101 *p = http_buf_create();
102 memcpy((*p)->buf, b, tocopy);
111 // Add a (chain of) buffers to the end of an existing queue.
112 static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
115 queue = &(*queue)->next;
119 static struct http_buf *http_buf_bywrbuf(WRBUF wrbuf)
121 // Heavens to Betsy (buf)!
122 return http_buf_bybuf(wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
125 // Non-destructively collapse chain of buffers into a string (max *len)
127 static int http_buf_peek(struct http_buf *b, char *buf, int len)
130 while (b && rd < len)
132 int toread = len - rd;
135 memcpy(buf + rd, b->buf + b->offset, toread);
143 // Ddestructively munch up to len from head of queue.
144 static int http_buf_read(struct http_buf **b, char *buf, int len)
147 while ((*b) && rd < len)
149 int toread = len - rd;
150 if (toread > (*b)->len)
152 memcpy(buf + rd, (*b)->buf + (*b)->offset, toread);
154 if (toread < (*b)->len)
157 (*b)->offset += toread;
162 struct http_buf *n = (*b)->next;
163 http_buf_destroy(*b);
171 // Buffers may overlap.
172 static void urldecode(char *i, char *o)
184 sscanf(i, "%2hhx", o);
194 // Warning: Buffers may not overlap
195 void urlencode(const char *i, char *o)
199 if (strchr(" /:", *i))
201 sprintf(o, "%%%.2X", (int) *i);
211 void http_addheader(struct http_response *r, const char *name, const char *value)
213 struct http_channel *c = r->channel;
214 struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
215 h->name = nmem_strdup(c->nmem, name);
216 h->value = nmem_strdup(c->nmem, value);
217 h->next = r->headers;
221 char *http_argbyname(struct http_request *r, char *name)
223 struct http_argument *p;
226 for (p = r->arguments; p; p = p->next)
227 if (!strcmp(p->name, name))
232 char *http_headerbyname(struct http_header *h, char *name)
234 for (; h; h = h->next)
235 if (!strcmp(h->name, name))
240 struct http_response *http_create_response(struct http_channel *c)
242 struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
243 strcpy(r->code, "200");
251 // Check if buf contains a package (minus payload)
252 static int package_check(const char *buf)
255 while (*buf) // Check if we have a sequence of lines terminated by an empty line
257 char *b = strstr(buf, "\r\n");
262 len += (b - buf) + 2;
270 // Check if we have a request. Return 0 or length
271 // (including trailing CRNL) FIXME: Does not deal gracefully with requests
272 // carrying payload but this is kind of OK since we will reject anything
273 // other than an empty GET
274 static int request_check(struct http_buf *queue)
278 http_buf_peek(queue, tmp, 4096);
279 return package_check(tmp);
282 struct http_response *http_parse_response_buf(struct http_channel *c, const char *buf, int len)
285 struct http_response *r = http_create_response(c);
287 struct http_header **hp = &r->headers;
291 memcpy(tmp, buf, len);
292 for (p = tmp; *p && *p != ' '; p++) // Skip HTTP version
296 for (p2 = p; *p2 && *p2 != ' ' && p2 - p < 3; p2++)
297 r->code[p2 - p] = *p2;
298 if (!(p = strstr(tmp, "\r\n")))
303 if (!(p2 = strstr(p, "\r\n")))
305 if (p == p2) // End of headers
309 struct http_header *h = *hp = nmem_malloc(c->nmem, sizeof(*h));
310 char *value = strchr(p, ':');
314 h->name = nmem_strdup(c->nmem, p);
315 while (isspace(*value))
317 if (value >= p2) // Empty header;
324 h->value = nmem_strdup(c->nmem, value);
333 struct http_request *http_parse_request(struct http_channel *c, struct http_buf **queue,
336 struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
343 if (http_buf_read(queue, buf, len) < len)
351 for (p = buf, p2 = r->method; *p && *p != ' ' && p - buf < 19; p++)
355 yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
360 if (!(buf = strchr(buf, ' ')))
362 yaz_log(YLOG_WARN, "Syntax error in request (1)");
366 if (!(p = strchr(buf, ' ')))
368 yaz_log(YLOG_WARN, "Syntax error in request (2)");
372 if ((p2 = strchr(buf, '?'))) // Do we have arguments?
374 r->path = nmem_strdup(c->nmem, buf);
377 r->search = nmem_strdup(c->nmem, p2);
381 struct http_argument *a;
382 char *equal = strchr(p2, '=');
383 char *eoa = strchr(p2, '&');
386 yaz_log(YLOG_WARN, "Expected '=' in argument");
390 eoa = equal + strlen(equal); // last argument
393 a = nmem_malloc(c->nmem, sizeof(struct http_argument));
395 a->name = nmem_strdup(c->nmem, p2);
396 urldecode(equal, equal);
397 a->value = nmem_strdup(c->nmem, equal);
398 a->next = r->arguments;
405 if (strncmp(buf, "HTTP/", 5))
406 strcpy(r->http_version, "1.0");
410 if (!(p = strstr(buf, "\r\n")))
414 strcpy(r->http_version, buf);
417 strcpy(c->version, r->http_version);
422 if (!(p = strstr(buf, "\r\n")))
428 struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
429 if (!(p2 = strchr(buf, ':')))
432 h->name = nmem_strdup(c->nmem, buf);
435 if (p2 >= p) // Empty header?
441 h->value = nmem_strdup(c->nmem, p2);
442 h->next = r->headers;
451 static struct http_buf *http_serialize_response(struct http_channel *c,
452 struct http_response *r)
454 struct http_header *h;
456 wrbuf_rewind(c->wrbuf);
457 wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
458 for (h = r->headers; h; h = h->next)
459 wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
462 wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ?
463 (int) strlen(r->payload) : 0);
464 wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
466 wrbuf_puts(c->wrbuf, "\r\n");
469 wrbuf_puts(c->wrbuf, r->payload);
471 return http_buf_bywrbuf(c->wrbuf);
474 // Serialize a HTTP request
475 static struct http_buf *http_serialize_request(struct http_request *r)
477 struct http_channel *c = r->channel;
478 struct http_header *h;
479 struct http_argument *a;
481 wrbuf_rewind(c->wrbuf);
482 wrbuf_printf(c->wrbuf, "%s %s", r->method, r->path);
486 wrbuf_putc(c->wrbuf, '?');
487 for (a = r->arguments; a; a = a->next) {
488 if (a != r->arguments)
489 wrbuf_putc(c->wrbuf, '&');
490 wrbuf_printf(c->wrbuf, "%s=%s", a->name, a->value);
494 wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
496 for (h = r->headers; h; h = h->next)
497 wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
499 wrbuf_puts(c->wrbuf, "\r\n");
501 return http_buf_bywrbuf(c->wrbuf);
505 static int http_weshouldproxy(struct http_request *rq)
507 if (proxy_addr && !strstr(rq->path, "search.pz2"))
512 static int http_proxy(struct http_request *rq)
514 struct http_channel *c = rq->channel;
515 struct http_proxy *p = c->proxy;
516 struct http_header *hp;
517 struct http_buf *requestbuf;
519 if (!p) // This is a new connection. Create a proxy channel
526 if (!(pe = getprotobyname("tcp"))) {
529 if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
531 yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
534 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
535 &one, sizeof(one)) < 0)
537 if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
538 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
539 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
540 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
541 if (connect(sock, (struct sockaddr *) proxy_addr, sizeof(*proxy_addr)) < 0)
542 if (errno != EINPROGRESS)
544 yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
548 p = xmalloc(sizeof(struct http_proxy));
551 p->first_response = 1;
553 // We will add EVENT_OUTPUT below
554 p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT);
555 iochan_setdata(p->iochan, p);
556 p->iochan->next = channel_list;
557 channel_list = p->iochan;
560 // Modify Host: header
561 for (hp = rq->headers; hp; hp = hp->next)
562 if (!strcmp(hp->name, "Host"))
566 yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
569 hp->value = nmem_strdup(c->nmem, proxy_url);
570 requestbuf = http_serialize_request(rq);
571 http_buf_enqueue(&p->oqueue, requestbuf);
572 iochan_setflag(p->iochan, EVENT_OUTPUT);
576 void http_send_response(struct http_channel *ch)
578 struct http_response *rs = ch->response;
582 hb = http_serialize_response(ch, rs);
585 yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
586 http_destroy(ch->iochan);
590 http_buf_enqueue(&ch->oqueue, hb);
591 iochan_setflag(ch->iochan, EVENT_OUTPUT);
592 ch->state = Http_Idle;
596 static void http_io(IOCHAN i, int event)
598 struct http_channel *hc = iochan_getdata(i);
603 struct http_buf *htbuf;
606 htbuf = http_buf_create();
607 res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
608 if (res == -1 && errno == EAGAIN)
610 http_buf_destroy(htbuf);
615 http_buf_destroy(htbuf);
621 htbuf->buf[res] = '\0';
623 http_buf_enqueue(&hc->iqueue, htbuf);
626 if (hc->state == Http_Busy)
628 if ((reqlen = request_check(hc->iqueue)) <= 2)
631 nmem_reset(hc->nmem);
632 if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
634 yaz_log(YLOG_WARN, "Failed to parse request");
639 yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
641 *hc->request->search ? "?" : "",
642 hc->request->search);
643 if (http_weshouldproxy(hc->request))
644 http_proxy(hc->request);
647 // Execute our business logic!
648 hc->state = Http_Busy;
653 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
654 iochan_setevent(i, EVENT_INPUT);
662 struct http_buf *wb = hc->oqueue;
663 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
666 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
672 hc->oqueue = hc->oqueue->next;
673 http_buf_destroy(wb);
681 if (!strcmp(hc->version, "1.0"))
688 iochan_clearflag(i, EVENT_OUTPUT);
690 iochan_setevent(hc->iochan, EVENT_INPUT);
695 if (!hc->oqueue && hc->proxy && !hc->proxy->iochan)
696 http_destroy(i); // Server closed; we're done
699 yaz_log(YLOG_WARN, "Unexpected event on connection");
704 // If this hostname contains our proxy host as a prefix, replace with myurl
705 static char *sub_hostname(struct http_channel *c, char *buf)
708 if (strlen(buf) > 1023)
710 if (strncmp(buf, "http://", 7))
712 if (!strncmp(buf + 7, proxy_url, strlen(proxy_url)))
715 strcat(tmp, buf + strlen(proxy_url) + 7);
716 return nmem_strdup(c->nmem, tmp);
721 // Handles I/O on a client connection to a backend web server (proxy mode)
722 static void proxy_io(IOCHAN pi, int event)
724 struct http_proxy *pc = iochan_getdata(pi);
725 struct http_channel *hc = pc->channel;
730 struct http_buf *htbuf;
733 htbuf = http_buf_create();
734 res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
735 if (res == 0 || (res < 0 && errno != EINPROGRESS))
739 yaz_log(YLOG_WARN, "Proxy read came up short");
740 // Close channel and alert client HTTP channel that we're gone
741 http_buf_destroy(htbuf);
742 close(iochan_getfd(pi));
748 http_destroy(hc->iochan);
754 htbuf->buf[res] = '\0';
757 if (pc->first_response) // Check if this is a redirect
760 if ((len = package_check(htbuf->buf)))
762 struct http_response *res = http_parse_response_buf(hc, htbuf->buf, len);
765 struct http_header *h;
766 for (h = res->headers; h; h = h->next)
767 if (!strcmp(h->name, "Location"))
769 // We found a location header. Rewrite it.
770 struct http_buf *buf;
771 h->value = sub_hostname(hc, h->value);
772 buf = http_serialize_response(hc, res);
773 yaz_log(YLOG_LOG, "Proxy rewrite");
774 http_buf_enqueue(&hc->oqueue, buf);
780 pc->first_response = 0;
782 // Write any remaining payload
783 if (htbuf->len - htbuf->offset > 0)
784 http_buf_enqueue(&hc->oqueue, htbuf);
786 iochan_setflag(hc->iochan, EVENT_OUTPUT);
789 if (!(htbuf = pc->oqueue))
791 iochan_clearflag(pi, EVENT_OUTPUT);
794 res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
797 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
798 http_destroy(hc->iochan);
801 if (res == htbuf->len)
803 struct http_buf *np = htbuf->next;
804 http_buf_destroy(htbuf);
810 htbuf->offset += res;
814 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
818 yaz_log(YLOG_WARN, "Unexpected event on connection");
819 http_destroy(hc->iochan);
824 static void http_destroy(IOCHAN i)
826 struct http_channel *s = iochan_getdata(i);
830 if (s->proxy->iochan)
832 close(iochan_getfd(s->proxy->iochan));
833 iochan_destroy(s->proxy->iochan);
835 http_buf_destroy_queue(s->proxy->oqueue);
838 s->next = http_channel_freelist;
839 http_channel_freelist = s;
840 close(iochan_getfd(i));
844 static struct http_channel *http_create(void)
846 struct http_channel *r = http_channel_freelist;
850 http_channel_freelist = r->next;
852 wrbuf_rewind(r->wrbuf);
856 r = xmalloc(sizeof(struct http_channel));
857 r->nmem = nmem_create();
858 r->wrbuf = wrbuf_alloc();
862 r->iqueue = r->oqueue = 0;
863 r->state = Http_Idle;
870 /* Accept a new command connection */
871 static void http_accept(IOCHAN i, int event)
873 struct sockaddr_in addr;
874 int fd = iochan_getfd(i);
879 struct http_channel *ch;
882 if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
884 yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
887 if ((flags = fcntl(s, F_GETFL, 0)) < 0)
888 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
889 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
890 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
892 yaz_log(YLOG_DEBUG, "New command connection");
893 c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
897 iochan_setdata(c, ch);
899 c->next = channel_list;
903 /* Create a http-channel listener, syntax [host:]port */
904 void http_init(const char *addr)
909 struct sockaddr_in myaddr;
914 yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
916 memset(&myaddr, 0, sizeof myaddr);
917 myaddr.sin_family = AF_INET;
918 pp = strchr(addr, ':');
925 strncpy(hostname, addr, len);
926 hostname[len] = '\0';
927 if (!(he = gethostbyname(hostname)))
929 yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
932 memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
938 myaddr.sin_addr.s_addr = INADDR_ANY;
940 myaddr.sin_port = htons(port);
942 if (!(p = getprotobyname("tcp"))) {
945 if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
946 yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
947 if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
948 &one, sizeof(one)) < 0)
951 if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0)
952 yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
953 if (listen(l, SOMAXCONN) < 0)
954 yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
956 c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
957 c->next = channel_list;
961 void http_set_proxyaddr(char *host, char *base_url)
967 strcpy(myurl, base_url);
968 strcpy(proxy_url, host);
969 p = strchr(host, ':');
970 yaz_log(YLOG_DEBUG, "Proxying for %s", host);
977 if (!(he = gethostbyname(host)))
979 fprintf(stderr, "Failed to lookup '%s'\n", host);
982 proxy_addr = xmalloc(sizeof(struct sockaddr_in));
983 proxy_addr->sin_family = he->h_addrtype;
984 memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
985 proxy_addr->sin_port = htons(port);
991 * indent-tabs-mode: nil
993 * vim: shiftwidth=4 tabstop=8 expandtab