X-Git-Url: http://git.indexdata.com/?p=pazpar2-moved-to-github.git;a=blobdiff_plain;f=src%2Fhttp.c;h=5f5534d7b484444790ae18aa0606e0d106d61d72;hp=456d69b8cedabc8f8e3d5a7025a20dbcfcaa4ece;hb=37ae4386bd764d62ae43c68df1560be55ff813ca;hpb=a9f6149504fc21f892e8a578b7eb1fefce406ddf diff --git a/src/http.c b/src/http.c index 456d69b..5f5534d 100644 --- a/src/http.c +++ b/src/http.c @@ -1,5 +1,5 @@ /* This file is part of Pazpar2. - Copyright (C) 2006-2011 Index Data + Copyright (C) Index Data Pazpar2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -27,7 +27,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #ifdef WIN32 -#include +#include +#include typedef int socklen_t; #endif @@ -54,14 +55,6 @@ typedef int socklen_t; #include #include -#if HAVE_NETINET_IN_H -#include -#endif - -#if HAVE_ARPA_INET_H -#include -#endif - #include #include #include @@ -70,6 +63,7 @@ typedef int socklen_t; #include "ppmutex.h" #include "session.h" #include "http.h" +#include "parameters.h" #define MAX_HTTP_HEADER 4096 @@ -87,6 +81,7 @@ struct http_buf struct http_buf *next; }; +static int log_level_post = 0; static void proxy_io(IOCHAN i, int event); static struct http_channel *http_channel_create(http_server_t http_server, @@ -96,6 +91,12 @@ static void http_channel_destroy(IOCHAN i); static http_server_t http_server_create(void); static void http_server_incref(http_server_t hs); +#ifdef WIN32 +#define CLOSESOCKET(x) closesocket(x) +#else +#define CLOSESOCKET(x) close(x) +#endif + struct http_server { YAZ_MUTEX mutex; @@ -606,7 +607,8 @@ struct http_request *http_parse_request(struct http_channel *c, r->content_len = start + len - buf; r->content_buf = buf; - if (!yaz_strcmp_del("application/x-www-form-urlencoded", + if (content_type && + !yaz_strcmp_del("application/x-www-form-urlencoded", content_type, "; ")) { http_parse_arguments(r, c->nmem, r->content_buf); @@ -621,6 +623,7 @@ static struct http_buf *http_serialize_response(struct http_channel *c, struct http_header *h; wrbuf_rewind(c->wrbuf); + wrbuf_printf(c->wrbuf, "HTTP/%s %s %s\r\n", c->version, r->code, r->msg); for (h = r->headers; h; h = h->next) wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value); @@ -649,6 +652,13 @@ static struct http_buf *http_serialize_response(struct http_channel *c, if (r->payload) wrbuf_puts(c->wrbuf, r->payload); + if (global_parameters.dump_records > 1) + { + FILE *lf = yaz_log_file(); + yaz_log(YLOG_LOG, "Response:"); + fwrite(wrbuf_buf(c->wrbuf), 1, wrbuf_len(c->wrbuf), lf); + fputc('\n', lf); + } return http_buf_bywrbuf(c->http_server, c->wrbuf); } @@ -689,12 +699,12 @@ static int http_weshouldproxy(struct http_request *rq) } -struct http_header * http_header_append(struct http_channel *ch, - struct http_header * hp, - const char *name, +struct http_header * http_header_append(struct http_channel *ch, + struct http_header * hp, + const char *name, const char *value) { - struct http_header *hpnew = 0; + struct http_header *hpnew = 0; if (!hp | !ch) return 0; @@ -706,18 +716,18 @@ struct http_header * http_header_append(struct http_channel *ch, hpnew = nmem_malloc(ch->nmem, sizeof *hpnew); hpnew->name = nmem_strdup(ch->nmem, name); hpnew->value = nmem_strdup(ch->nmem, value); - + hpnew->next = 0; hp->next = hpnew; hp = hp->next; - + return hpnew; } return hp; } - + static int is_inprogress(void) { #ifdef WIN32 @@ -728,7 +738,7 @@ static int is_inprogress(void) return 1; #endif return 0; -} +} static void enable_nonblock(int sock) { @@ -738,7 +748,7 @@ static void enable_nonblock(int sock) if (ioctlsocket(sock, FIONBIO, &flags) < 0) yaz_log(YLOG_FATAL|YLOG_ERRNO, "ioctlsocket"); #else - if ((flags = fcntl(sock, F_GETFL, 0)) < 0) + if ((flags = fcntl(sock, F_GETFL, 0)) < 0) yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl"); if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2"); @@ -751,7 +761,6 @@ static int http_proxy(struct http_request *rq) struct http_proxy *p = c->proxy; struct http_header *hp; struct http_buf *requestbuf; - char server_port[16] = ""; struct conf_server *ser = c->server; if (!p) // This is a new connection. Create a proxy channel @@ -773,10 +782,10 @@ static int http_proxy(struct http_request *rq) abort(); enable_nonblock(sock); if (connect(sock, (struct sockaddr *) - c->server->http_server->proxy_addr, + c->server->http_server->proxy_addr, sizeof(*c->server->http_server->proxy_addr)) < 0) { - if (!is_inprogress()) + if (!is_inprogress()) { yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect"); return -1; @@ -787,11 +796,15 @@ static int http_proxy(struct http_request *rq) p->channel = c; p->first_response = 1; c->proxy = p; - // We will add EVENT_OUTPUT below p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT, "http_proxy"); iochan_setdata(p->iochan, p); - iochan_add(ser->iochan_man, p->iochan); + if (iochan_add(ser->iochan_man, p->iochan, 5)) + { + iochan_destroy(p->iochan); + xfree(p); + return -1; + } } // Do _not_ modify Host: header, just checking it's existence @@ -801,27 +814,26 @@ static int http_proxy(struct http_request *rq) yaz_log(YLOG_WARN, "Failed to find Host header in proxy"); return -1; } - + // Add new header about paraz2 version, host, remote client address, etc. { char server_via[128]; hp = rq->headers; - hp = http_header_append(c, hp, + hp = http_header_append(c, hp, "X-Pazpar2-Version", PACKAGE_VERSION); - hp = http_header_append(c, hp, + hp = http_header_append(c, hp, "X-Pazpar2-Server-Host", ser->host); - sprintf(server_port, "%d", ser->port); - hp = http_header_append(c, hp, - "X-Pazpar2-Server-Port", server_port); - yaz_snprintf(server_via, sizeof(server_via), - "1.1 %s:%s (%s/%s)", - ser->host ? ser->host : "@", - server_port, PACKAGE_NAME, PACKAGE_VERSION); + hp = http_header_append(c, hp, + "X-Pazpar2-Server-Port", ser->port); + yaz_snprintf(server_via, sizeof(server_via), + "1.1 %s:%s (%s/%s)", + ser->host, ser->port, + PACKAGE_NAME, PACKAGE_VERSION); hp = http_header_append(c, hp, "Via" , server_via); hp = http_header_append(c, hp, "X-Forwarded-For", c->addr); } - + requestbuf = http_serialize_request(rq); http_buf_enqueue(&p->oqueue, requestbuf); @@ -834,6 +846,16 @@ void http_send_response(struct http_channel *ch) struct http_response *rs = ch->response; struct http_buf *hb; + yaz_timing_stop(ch->yt); + if (ch->request) + { + yaz_log(YLOG_LOG, "Response: %6.5f %d %s%s%s ", + yaz_timing_get_real(ch->yt), + iochan_getfd(ch->iochan), + ch->request->path, + *ch->request->search ? "?" : "", + ch->request->search); + } assert(rs); hb = http_serialize_response(ch, rs); if (!hb) @@ -874,7 +896,7 @@ static void http_io(IOCHAN i, int event) { int res, reqlen; struct http_buf *htbuf; - + htbuf = http_buf_create(hc->http_server); res = recv(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1, 0); if (res == -1 && errno == EAGAIN) @@ -929,6 +951,7 @@ static void http_io(IOCHAN i, int event) fflush(hc->http_server->record_file); } #endif + yaz_timing_start(hc->yt); if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen))) { yaz_log(YLOG_WARN, "Failed to parse request"); @@ -936,12 +959,14 @@ static void http_io(IOCHAN i, int event) return; } hc->response = 0; - yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method, + yaz_log(YLOG_LOG, "Request: - %d %s %s%s%s", + iochan_getfd(i), + hc->request->method, hc->request->path, *hc->request->search ? "?" : "", hc->request->search); - if (hc->request->content_buf) - yaz_log(YLOG_LOG, "%s", hc->request->content_buf); + if (hc->request->content_buf && log_level_post) + yaz_log(log_level_post, "%s", hc->request->content_buf); if (http_weshouldproxy(hc->request)) http_proxy(hc->request); else @@ -980,7 +1005,6 @@ static void http_io(IOCHAN i, int event) (long long) iochan_getfd(i), sz); fwrite(wb->buf, 1, wb->offset + wb->len, hc->http_server->record_file); - fputc('\n', hc->http_server->record_file); fflush(hc->http_server->record_file); } #endif @@ -1007,7 +1031,7 @@ static void http_io(IOCHAN i, int event) } } } - if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) + if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) http_channel_destroy(i); // Server closed; we're done } else @@ -1040,11 +1064,7 @@ static void proxy_io(IOCHAN pi, int event) yaz_log(YLOG_WARN, "Proxy read came up short"); // Close channel and alert client HTTP channel that we're gone http_buf_destroy(hc->http_server, htbuf); -#ifdef WIN32 - closesocket(iochan_getfd(pi)); -#else - close(iochan_getfd(pi)); -#endif + CLOSESOCKET(iochan_getfd(pi)); iochan_destroy(pi); pc->iochan = 0; } @@ -1079,7 +1099,7 @@ static void proxy_io(IOCHAN pi, int event) return; } if (res == htbuf->len) - { + { struct http_buf *np = htbuf->next; http_buf_destroy(hc->http_server, htbuf); pc->oqueue = np; @@ -1114,16 +1134,13 @@ static void http_channel_destroy(IOCHAN i) { if (s->proxy->iochan) { -#ifdef WIN32 - closesocket(iochan_getfd(s->proxy->iochan)); -#else - close(iochan_getfd(s->proxy->iochan)); -#endif + CLOSESOCKET(iochan_getfd(s->proxy->iochan)); iochan_destroy(s->proxy->iochan); } http_buf_destroy_queue(s->http_server, s->proxy->oqueue); xfree(s->proxy); } + yaz_timing_destroy(&s->yt); http_buf_destroy_queue(s->http_server, s->iqueue); http_buf_destroy_queue(s->http_server, s->oqueue); http_fire_observers(s); @@ -1133,11 +1150,8 @@ static void http_channel_destroy(IOCHAN i) http_server_destroy(http_server); -#ifdef WIN32 - closesocket(iochan_getfd(i)); -#else - close(iochan_getfd(i)); -#endif + CLOSESOCKET(iochan_getfd(i)); + iochan_destroy(i); nmem_destroy(s->nmem); wrbuf_destroy(s->wrbuf); @@ -1166,6 +1180,7 @@ static struct http_channel *http_channel_create(http_server_t hs, r->keep_alive = 0; r->request = 0; r->response = 0; + strcpy(r->version, "1.0"); if (!addr) { yaz_log(YLOG_WARN, "Invalid HTTP forward address"); @@ -1173,6 +1188,7 @@ static struct http_channel *http_channel_create(http_server_t hs, } strcpy(r->addr, addr); r->observers = 0; + r->yt = yaz_timing_create(); return r; } @@ -1180,116 +1196,162 @@ static struct http_channel *http_channel_create(http_server_t hs, /* Accept a new command connection */ static void http_accept(IOCHAN i, int event) { - struct sockaddr_in addr; + char host[256]; + struct sockaddr_storage addr; int fd = iochan_getfd(i); - socklen_t len; + socklen_t len = sizeof addr; int s; IOCHAN c; struct http_channel *ch; struct conf_server *server = iochan_getdata(i); - len = sizeof addr; if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "accept"); return; } + if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, 0, 0, + NI_NUMERICHOST)) + { + yaz_log(YLOG_WARN|YLOG_ERRNO, "getnameinfo"); + CLOSESOCKET(s); + return; + } enable_nonblock(s); yaz_log(YLOG_DEBUG, "New command connection"); - c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, "http_session_socket"); - - ch = http_channel_create(server->http_server, inet_ntoa(addr.sin_addr), - server); + c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, + "http_session_socket"); + + ch = http_channel_create(server->http_server, host, server); ch->iochan = c; iochan_setdata(c, ch); - iochan_add(server->iochan_man, c); + if (iochan_add(server->iochan_man, c, 0)) + { + yaz_log(YLOG_WARN, "Refusing incoming HTTP connection"); + http_channel_destroy(c); + } } /* Create a http-channel listener, syntax [host:]port */ -int http_init(const char *addr, struct conf_server *server, - const char *record_fname) +int http_init(struct conf_server *server, const char *record_fname) { IOCHAN c; - int l; - struct protoent *p; - struct sockaddr_in myaddr; + int s = -1; int one = 1; - const char *pp; - short port; FILE *record_file = 0; + struct addrinfo hints, *af = 0, *ai; + int error; + int ipv6_only = -1; + + yaz_log(YLOG_LOG, "HTTP listener %s:%s", server->host, server->port); + + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + + if (!strcmp(server->host, "@")) + { + ipv6_only = 0; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(0, server->port, &hints, &af); + } + else + error = getaddrinfo(server->host, server->port, &hints, &af); - yaz_log(YLOG_LOG, "HTTP listener %s", addr); - - - if (record_fname) + if (error) { - record_file = fopen(record_fname, "wb"); - if (!record_file) + yaz_log(YLOG_FATAL, "Failed to resolve %s: %s", server->host, + gai_strerror(error)); + return 1; + } + for (ai = af; ai; ai = ai->ai_next) + { + if (ai->ai_family == AF_INET6) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname); - return 1; + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s != -1) + break; } } - - memset(&myaddr, 0, sizeof myaddr); - myaddr.sin_family = AF_INET; - pp = strchr(addr, ':'); - if (pp) + if (s == -1) { - WRBUF w = wrbuf_alloc(); - struct hostent *he; - - wrbuf_write(w, addr, pp - addr); - wrbuf_puts(w, ""); - - he = gethostbyname(wrbuf_cstr(w)); - wrbuf_destroy(w); - if (!he) + for (ai = af; ai; ai = ai->ai_next) { - yaz_log(YLOG_FATAL, "Unable to resolve '%s'", addr); - return 1; + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s != -1) + break; } - memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length); - port = atoi(pp + 1); } - else + if (s == -1) { - port = atoi(addr); - myaddr.sin_addr.s_addr = INADDR_ANY; + yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket"); + freeaddrinfo(af); + return 1; } - - myaddr.sin_port = htons(port); - - if (!(p = getprotobyname("tcp"))) { + if (ipv6_only >= 0 && ai->ai_family == AF_INET6 && + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only))) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt IPV6_V6ONLY %s:%s %d", + server->host, server->port, ipv6_only); + freeaddrinfo(af); + CLOSESOCKET(s); return 1; } - if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0) - yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket"); - if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*) - &one, sizeof(one)) < 0) + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt SO_REUSEADDR %s:%s", + server->host, server->port); + freeaddrinfo(af); + CLOSESOCKET(s); return 1; - - if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) + } + if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind %s:%s", + server->host, server->port); + freeaddrinfo(af); + CLOSESOCKET(s); return 1; } - if (listen(l, SOMAXCONN) < 0) + freeaddrinfo(af); + if (listen(s, SOMAXCONN) < 0) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen %s:%s", + server->host, server->port); + CLOSESOCKET(s); return 1; } - server->http_server = http_server_create(); + if (record_fname) + { + record_file = fopen(record_fname, "wb"); + if (!record_file) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname); + CLOSESOCKET(s); + return 1; + } + } - server->http_server->record_file = record_file; - server->http_server->listener_socket = l; + c = iochan_create(s, http_accept, EVENT_INPUT|EVENT_EXCEPT, "http_server"); + if (iochan_add(server->iochan_man, c, 0)) + { + yaz_log(YLOG_WARN, "Can not create HTTP binding socket"); + iochan_destroy(c); + return -1; + } - c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT, "http_server"); + server->http_server = http_server_create(); + server->http_server->record_file = record_file; + server->http_server->listener_socket = s; iochan_setdata(c, server); - iochan_add(server->iochan_man, c); return 0; } @@ -1403,8 +1465,10 @@ http_server_t http_server_create(void) hs->proxy_addr = 0; hs->ref_count = 1; hs->http_sessions = 0; - hs->record_file = 0; + + log_level_post = yaz_log_module_level("post"); + return hs; }