Happy new year
[pazpar2-moved-to-github.git] / src / http.c
index 74fdf66..5f5534d 100644 (file)
@@ -1,5 +1,5 @@
 /* This file is part of Pazpar2.
-   Copyright (C) 2006-2013 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
@@ -81,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,
@@ -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);
@@ -655,6 +657,7 @@ static struct http_buf *http_serialize_response(struct http_channel *c,
         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);
 }
@@ -758,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
@@ -794,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
@@ -818,13 +824,12 @@ static int http_proxy(struct http_request *rq)
                                 "X-Pazpar2-Version", PACKAGE_VERSION);
         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);
+                                "X-Pazpar2-Server-Port", ser->port);
         yaz_snprintf(server_via, sizeof(server_via),
                      "1.1 %s:%s (%s/%s)",
-                     ser->host ? ser->host : "@",
-                     server_port, PACKAGE_NAME, PACKAGE_VERSION);
+                     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);
     }
@@ -841,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)
@@ -936,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");
@@ -943,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
@@ -987,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
@@ -1123,6 +1140,7 @@ static void http_channel_destroy(IOCHAN i)
         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);
@@ -1170,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;
 }
 
@@ -1178,21 +1197,21 @@ static struct http_channel *http_channel_create(http_server_t hs,
 static void http_accept(IOCHAN i, int event)
 {
     char host[256];
-    struct sockaddr addr;
+    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, &addr, &len)) < 0)
+    if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
     {
         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
         return;
     }
-    if (getnameinfo(&addr, len, host, sizeof(host)-1, 0, 0, NI_NUMERICHOST))
+    if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, 0, 0,
+        NI_NUMERICHOST))
     {
         yaz_log(YLOG_WARN|YLOG_ERRNO, "getnameinfo");
         CLOSESOCKET(s);
@@ -1204,27 +1223,28 @@ static void http_accept(IOCHAN i, int event)
     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;
+    int s = -1;
     int one = 1;
-    const char *pp;
     FILE *record_file = 0;
-    struct addrinfo hints, *ai = 0;
+    struct addrinfo hints, *af = 0, *ai;
     int error;
     int ipv6_only = -1;
 
-    yaz_log(YLOG_LOG, "HTTP listener %s", addr);
+    yaz_log(YLOG_LOG, "HTTP listener %s:%s", server->host, server->port);
 
     hints.ai_flags = 0;
     hints.ai_family = AF_UNSPEC;
@@ -1235,70 +1255,76 @@ int http_init(const char *addr, struct conf_server *server,
     hints.ai_canonname      = NULL;
     hints.ai_next           = NULL;
 
-    pp = strchr(addr, ':');
-    if (pp)
+    if (!strcmp(server->host, "@"))
     {
-        WRBUF w = wrbuf_alloc();
-        wrbuf_write(w, addr, pp - addr);
-        if (!strcmp(wrbuf_cstr(w), "@"))
-        {   /* IPV4 + IPV6 .. same as YAZ */
-            ipv6_only = 0;
-            hints.ai_flags = AI_PASSIVE;
-            hints.ai_family = AF_INET6;
-            error = getaddrinfo(0, pp + 1, &hints, &ai);
-        }
-        else
-            error = getaddrinfo(wrbuf_cstr(w), pp + 1, &hints, &ai);
-        wrbuf_destroy(w);
-    }
-    else
-    {
-        /* default for no host given is IPV4 */
+        ipv6_only = 0;
         hints.ai_flags = AI_PASSIVE;
-        hints.ai_family = AF_INET;
-        error = getaddrinfo(0, addr, &hints, &ai);
+        error = getaddrinfo(0, server->port, &hints, &af);
     }
+    else
+        error = getaddrinfo(server->host, server->port, &hints, &af);
+
     if (error)
     {
-        yaz_log(YLOG_FATAL, "Failed to resolve %s: %s", addr,
+        yaz_log(YLOG_FATAL, "Failed to resolve %s: %s", server->host,
                 gai_strerror(error));
         return 1;
     }
-    l = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-    if (l < 0)
+    for (ai = af; ai; ai = ai->ai_next)
+    {
+        if (ai->ai_family == AF_INET6)
+        {
+            s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+            if (s != -1)
+                break;
+        }
+    }
+    if (s == -1)
+    {
+        for (ai = af; ai; ai = ai->ai_next)
+        {
+            s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+            if (s != -1)
+                break;
+        }
+    }
+    if (s == -1)
     {
         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
-        freeaddrinfo(ai);
+        freeaddrinfo(af);
         return 1;
     }
-    if (ipv6_only >= 0 &&
-        setsockopt(l, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)))
+    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 %d", addr,
-            ipv6_only);
-        freeaddrinfo(ai);
-        CLOSESOCKET(l);
+        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 (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
     {
-        yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt SO_REUSEADDR %s", addr);
-        freeaddrinfo(ai);
-        CLOSESOCKET(l);
+        yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt SO_REUSEADDR %s:%s",
+                server->host, server->port);
+        freeaddrinfo(af);
+        CLOSESOCKET(s);
         return 1;
     }
-    if (bind(l, ai->ai_addr, ai->ai_addrlen) < 0)
+    if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0)
     {
-        yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind %s", addr);
-        freeaddrinfo(ai);
-        CLOSESOCKET(l);
+        yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind %s:%s",
+                server->host, server->port);
+        freeaddrinfo(af);
+        CLOSESOCKET(s);
         return 1;
     }
-    freeaddrinfo(ai);
-    if (listen(l, SOMAXCONN) < 0)
+    freeaddrinfo(af);
+    if (listen(s, SOMAXCONN) < 0)
     {
-        yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen %s", addr);
-        CLOSESOCKET(l);
+        yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen %s:%s",
+                server->host, server->port);
+        CLOSESOCKET(s);
         return 1;
     }
 
@@ -1308,19 +1334,24 @@ int http_init(const char *addr, struct conf_server *server,
         if (!record_file)
         {
             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname);
-            CLOSESOCKET(l);
+            CLOSESOCKET(s);
             return 1;
         }
     }
-    server->http_server = http_server_create();
 
-    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;
 }
 
@@ -1434,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;
 }