]> granicus.if.org Git - libevent/blobdiff - http.c
Optimize arc4random_uniform() (by syncing with OpenBSD implementation)
[libevent] / http.c
diff --git a/http.c b/http.c
index 77abd4267d825fe1612d9b4f7bcb895c0603bcea..43879f39152a449b70c3a0ea89c97b882a1ad5e4 100644 (file)
--- a/http.c
+++ b/http.c
@@ -28,6 +28,8 @@
 #include "event2/event-config.h"
 #include "evconfig-private.h"
 
+#define member_size(type, member) sizeof(((type *)0)->member)
+
 #ifdef EVENT__HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
 #include "event2/http_struct.h"
 #include "event2/http_compat.h"
 #include "event2/util.h"
+#include "event2/ws.h"
 #include "event2/listener.h"
 #include "log-internal.h"
 #include "util-internal.h"
@@ -177,10 +180,10 @@ fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
 
 extern int debug;
 
-static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
+static evutil_socket_t create_bind_socket_nonblock(struct evutil_addrinfo *, int reuse);
 static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
 static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
-static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri);
+static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri, unsigned flags);
 static int evhttp_associate_new_request_with_connection(
        struct evhttp_connection *evcon);
 static void evhttp_connection_start_detectclose(
@@ -195,7 +198,7 @@ static void evhttp_read_header(struct evhttp_connection *evcon,
 static int evhttp_add_header_internal(struct evkeyvalq *headers,
     const char *key, const char *value);
 static const char *evhttp_response_phrase_internal(int code);
-static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t);
+static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t, struct bufferevent *bev);
 static void evhttp_write_buffer(struct evhttp_connection *,
     void (*)(struct evhttp_connection *, void *), void *);
 static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
@@ -417,6 +420,7 @@ evhttp_response_needs_body(struct evhttp_request *req)
        return (req->response_code != HTTP_NOCONTENT &&
                req->response_code != HTTP_NOTMODIFIED &&
                (req->response_code < 100 || req->response_code >= 200) &&
+               req->type != EVHTTP_REQ_CONNECT &&
                req->type != EVHTTP_REQ_HEAD);
 }
 
@@ -497,7 +501,8 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
     struct evhttp_request *req)
 {
        const char *method;
-       ev_uint16_t flags;
+       /* NOTE: some version of GCC reports a warning that flags may be uninitialized, hence assignment */
+       ev_uint16_t flags = 0;
 
        evhttp_remove_header(req->output_headers, "Proxy-Connection");
 
@@ -541,6 +546,9 @@ evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
 static int
 evhttp_is_request_connection_close(struct evhttp_request *req)
 {
+       if (req->type == EVHTTP_REQ_CONNECT)
+               return 0;
+
        return
                evhttp_is_connection_close(req->flags, req->input_headers) ||
                evhttp_is_connection_close(req->flags, req->output_headers);
@@ -561,7 +569,7 @@ evhttp_maybe_add_date_header(struct evkeyvalq *headers)
 {
        if (evhttp_find_header(headers, "Date") == NULL) {
                char date[50];
-               if (sizeof(date) - evutil_date_rfc1123(date, sizeof(date), NULL) > 0) {
+               if ((signed)sizeof(date) > evutil_date_rfc1123(date, sizeof(date), NULL)) {
                        evhttp_add_header(headers, "Date", date);
                }
        }
@@ -733,7 +741,7 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
                 * case may happen when a browser keeps a persistent
                 * connection open and we timeout on the read.  when
                 * the request is still being used for sending, we
-                * need to disassociated it from the connection here.
+                * need to disassociate it from the connection here.
                 */
                if (!req->userdone) {
                        /* remove it so that it will not be freed */
@@ -868,11 +876,16 @@ evhttp_connection_fail_(struct evhttp_connection *evcon,
        evhttp_request_free_(evcon, req);
 
        /* reset the connection */
-       evhttp_connection_reset_(evcon);
+       evhttp_connection_reset_(evcon, 1);
 
        /* We are trying the next request that was queued on us */
        if (TAILQ_FIRST(&evcon->requests) != NULL)
                evhttp_connection_connect_(evcon);
+       else
+               if ((evcon->flags & EVHTTP_CON_OUTGOING) &&
+                   (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+                       evhttp_connection_free(evcon);
+               }
 
        /* The call to evhttp_connection_reset_ overwrote errno.
         * Let's restore the original errno, so that the user's
@@ -923,7 +936,7 @@ evhttp_connection_done(struct evhttp_connection *evcon)
 
                /* check if we got asked to close the connection */
                if (need_close)
-                       evhttp_connection_reset_(evcon);
+                       evhttp_connection_reset_(evcon, 1);
 
                if (TAILQ_FIRST(&evcon->requests) != NULL) {
                        /*
@@ -1257,7 +1270,7 @@ evhttp_read_cb(struct bufferevent *bufev, void *arg)
                                __func__, EV_SIZE_ARG(total_len)));
 #endif
 
-                       evhttp_connection_reset_(evcon);
+                       evhttp_connection_reset_(evcon, 1);
                }
                break;
        case EVCON_DISCONNECTED:
@@ -1307,13 +1320,10 @@ void
 evhttp_connection_free(struct evhttp_connection *evcon)
 {
        struct evhttp_request *req;
-       int need_close = 0;
 
        /* notify interested parties that this connection is going down */
-       if (evcon->fd != -1) {
-               if (evhttp_connected(evcon) && evcon->closecb != NULL)
-                       (*evcon->closecb)(evcon, evcon->closecb_arg);
-       }
+       if (evhttp_connected(evcon) && evcon->closecb != NULL)
+               (*evcon->closecb)(evcon, evcon->closecb_arg);
 
        /* remove all requests that might be queued on this
         * connection.  for server connections, this should be empty.
@@ -1327,6 +1337,7 @@ evhttp_connection_free(struct evhttp_connection *evcon)
        if (evcon->http_server != NULL) {
                struct evhttp *http = evcon->http_server;
                TAILQ_REMOVE(&http->connections, evcon, next);
+               http->connection_cnt--;
        }
 
        if (event_initialized(&evcon->retry_ev)) {
@@ -1338,26 +1349,20 @@ evhttp_connection_free(struct evhttp_connection *evcon)
            &evcon->read_more_deferred_cb);
 
        if (evcon->bufev != NULL) {
-               need_close =
-                       !(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE);
-               if (evcon->fd == -1)
-                       evcon->fd = bufferevent_getfd(evcon->bufev);
-
                bufferevent_free(evcon->bufev);
        }
 
-       if (evcon->fd != -1) {
-               shutdown(evcon->fd, EVUTIL_SHUT_WR);
-               if (need_close)
-                       evutil_closesocket(evcon->fd);
-       }
-
        if (evcon->bind_address != NULL)
                mm_free(evcon->bind_address);
 
        if (evcon->address != NULL)
                mm_free(evcon->address);
 
+#ifndef _WIN32
+       if (evcon->unixsocket != NULL)
+               mm_free(evcon->unixsocket);
+#endif
+
        mm_free(evcon);
 }
 
@@ -1410,18 +1415,21 @@ evhttp_request_dispatch(struct evhttp_connection* evcon)
        evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
 }
 
-/* Reset our connection state: disables reading/writing, closes our fd (if
-* any), clears out buffers, and puts us in state DISCONNECTED. */
-void
-evhttp_connection_reset_(struct evhttp_connection *evcon)
+/** Hard-reset our connection state
+ *
+ * This will:
+ * - reset fd
+ * - clears out buffers
+ * - call closecb
+ */
+static void
+evhttp_connection_reset_hard_(struct evhttp_connection *evcon)
 {
        struct evbuffer *tmp;
        int err;
 
-       bufferevent_setcb(evcon->bufev, NULL, NULL, NULL, NULL);
-
        /* XXXX This is not actually an optimal fix.  Instead we ought to have
-          an API for "stop connecting", or use bufferevent_setfd to turn off
+          an API for "stop connecting", or use bufferevent_replacefd to turn off
           connecting.  But for Libevent 2.0, this seems like a minimal change
           least likely to disrupt the rest of the bufferevent and http code.
 
@@ -1433,19 +1441,12 @@ evhttp_connection_reset_(struct evhttp_connection *evcon)
        */
        bufferevent_disable_hard_(evcon->bufev, EV_READ|EV_WRITE);
 
-       if (evcon->fd == -1)
-               evcon->fd = bufferevent_getfd(evcon->bufev);
+       /* inform interested parties about connection close */
+       if (evhttp_connected(evcon) && evcon->closecb != NULL)
+               (*evcon->closecb)(evcon, evcon->closecb_arg);
 
-       if (evcon->fd != -1) {
-               /* inform interested parties about connection close */
-               if (evhttp_connected(evcon) && evcon->closecb != NULL)
-                       (*evcon->closecb)(evcon, evcon->closecb_arg);
-
-               shutdown(evcon->fd, EVUTIL_SHUT_WR);
-               evutil_closesocket(evcon->fd);
-               evcon->fd = -1;
-       }
-       err = bufferevent_setfd(evcon->bufev, -1);
+       /** FIXME: manipulating with fd is unwanted */
+       err = bufferevent_replacefd(evcon->bufev, -1);
        EVUTIL_ASSERT(!err && "setfd");
 
        /* we need to clean up any buffered data */
@@ -1455,9 +1456,26 @@ evhttp_connection_reset_(struct evhttp_connection *evcon)
        tmp = bufferevent_get_input(evcon->bufev);
        err = evbuffer_drain(tmp, -1);
        EVUTIL_ASSERT(!err && "drain input");
+}
 
-       evcon->flags &= ~EVHTTP_CON_READING_ERROR;
+/** Reset our connection state
+ *
+ * This will:
+ * - disables reading/writing
+ * - puts us in DISCONNECTED state
+ *
+ * @param hard - hard reset will (@see evhttp_connection_reset_hard_())
+ */
+void
+evhttp_connection_reset_(struct evhttp_connection *evcon, int hard)
+{
+       bufferevent_setcb(evcon->bufev, NULL, NULL, NULL, NULL);
 
+       if (hard) {
+               evhttp_connection_reset_hard_(evcon);
+       }
+
+       evcon->flags &= ~EVHTTP_CON_READING_ERROR;
        evcon->state = EVCON_DISCONNECTED;
 }
 
@@ -1488,8 +1506,10 @@ static void
 evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
 {
        struct evcon_requestq requests;
+       EVUTIL_ASSERT(evcon->flags & EVHTTP_CON_OUTGOING);
+
+       evhttp_connection_reset_(evcon, 1);
 
-       evhttp_connection_reset_(evcon);
        if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
                struct timeval tv_retry = evcon->initial_retry_timeout;
                int i;
@@ -1535,6 +1555,12 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
                request->cb(request, request->cb_arg);
                evhttp_request_free_auto(request);
        }
+
+       if (TAILQ_FIRST(&evcon->requests) == NULL
+         && (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+               evhttp_connection_free(evcon);
+       }
+
 }
 
 static void
@@ -1567,16 +1593,13 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
        struct evhttp_connection *evcon = arg;
        struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
 
-       if (evcon->fd == -1)
-               evcon->fd = bufferevent_getfd(bufev);
-
        switch (evcon->state) {
        case EVCON_CONNECTING:
                if (what & BEV_EVENT_TIMEOUT) {
                        event_debug(("%s: connection timeout for \"%s:%d\" on "
                                EV_SOCK_FMT,
                                __func__, evcon->address, evcon->port,
-                               EV_SOCK_ARG(evcon->fd)));
+                               EV_SOCK_ARG(bufferevent_getfd(bufev))));
                        evhttp_connection_cb_cleanup(evcon);
                        return;
                }
@@ -1612,7 +1635,7 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
                 * disconnected.
                 */
                EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
-               evhttp_connection_reset_(evcon);
+               evhttp_connection_reset_(evcon, 1);
 
                /*
                 * If we have no more requests that need completion
@@ -1658,11 +1681,6 @@ static void
 evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
 {
        struct evhttp_connection *evcon = arg;
-       int error;
-       ev_socklen_t errsz = sizeof(error);
-
-       if (evcon->fd == -1)
-               evcon->fd = bufferevent_getfd(bufev);
 
        if (!(what & BEV_EVENT_CONNECTED)) {
                /* some operating systems return ECONNREFUSED immediately
@@ -1677,34 +1695,10 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
                return;
        }
 
-       if (evcon->fd == -1) {
-               event_debug(("%s: bufferevent_getfd returned -1",
-                       __func__));
-               goto cleanup;
-       }
-
-       /* Check if the connection completed */
-       if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
-                      &errsz) == -1) {
-               event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT,
-                       __func__, evcon->address, evcon->port,
-                       EV_SOCK_ARG(evcon->fd)));
-               goto cleanup;
-       }
-
-       if (error) {
-               event_debug(("%s: connect failed for \"%s:%d\" on "
-                       EV_SOCK_FMT": %s",
-                       __func__, evcon->address, evcon->port,
-                       EV_SOCK_ARG(evcon->fd),
-                       evutil_socket_error_to_string(error)));
-               goto cleanup;
-       }
-
        /* We are connected to the server now */
        event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n",
                        __func__, evcon->address, evcon->port,
-                       EV_SOCK_ARG(evcon->fd)));
+                       EV_SOCK_ARG(bufferevent_getfd(bufev))));
 
        /* Reset the retry count as we were successful in connecting */
        evcon->retry_cnt = 0;
@@ -1749,7 +1743,7 @@ evhttp_parse_http_version(const char *version, struct evhttp_request *req)
        int n = sscanf(version, "HTTP/%d.%d%c", &major, &minor, &ch);
        if (n != 2 || major > 1) {
                event_debug(("%s: bad version %s on message %p from %s",
-                       __func__, version, req, req->remote_host));
+                       __func__, version, (void *)req, req->remote_host));
                return (-1);
        }
        req->major = major;
@@ -1998,19 +1992,30 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
 
                ext_method.method = method;
                ext_method.type = 0;
+               ext_method.flags = 0;
 
                if (req->evcon->ext_method_cmp &&
                    req->evcon->ext_method_cmp(&ext_method) == 0) {
-                       /* TODO: make sure the other fields in ext_method are
+                       /* make sure the other fields in ext_method are
                         * not changed by the callback.
                         */
+                       if (strcmp(ext_method.method, method) != 0) {
+                               event_warn("%s: modifying the 'method' field of ext_method_cmp's "
+                                       "parameter is not allowed", __func__);
+                               return -1;
+                       }
+                       if (ext_method.flags != 0) {
+                               event_warn("%s: modifying the 'flags' field of ext_method_cmp's "
+                                       "parameter is not allowed", __func__);
+                               return -1;
+                       }
                        type = ext_method.type;
                }
        }
 
        if (!type) {
                event_debug(("%s: bad method %s on request %p from %s",
-                           __func__, method, req, req->remote_host));
+                           __func__, method, (void *)req, req->remote_host));
                /* No error yet; we'll give a better error later when
                 * we see that req->type is unsupported. */
        }
@@ -2026,7 +2031,7 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
        }
 
        if (type == EVHTTP_REQ_CONNECT) {
-               if ((req->uri_elems = evhttp_uri_parse_authority(req->uri)) == NULL) {
+               if ((req->uri_elems = evhttp_uri_parse_authority(req->uri, 0)) == NULL) {
                        return -1;
                }
        } else {
@@ -2348,7 +2353,8 @@ evhttp_get_body_length(struct evhttp_request *req)
 static int
 evhttp_method_may_have_body_(struct evhttp_connection *evcon, enum evhttp_cmd_type type)
 {
-       ev_uint16_t flags;
+       /* NOTE: some version of GCC reports a warning that flags may be uninitialized, hence assignment */
+       ev_uint16_t flags = 0;
        evhttp_method_(evcon, type, &flags);
        return (flags & EVHTTP_METHOD_HAS_BODY) ? 1 : 0;
 }
@@ -2422,7 +2428,7 @@ evhttp_read_firstline(struct evhttp_connection *evcon,
        if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
                /* Error while reading, terminate */
                event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
-                       __func__, EV_SOCK_ARG(evcon->fd)));
+                       __func__, EV_SOCK_ARG(bufferevent_getfd(evcon->bufev))));
                evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
                return;
        } else if (res == MORE_DATA_EXPECTED) {
@@ -2439,7 +2445,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
                   struct evhttp_request *req)
 {
        enum message_read_status res;
-       evutil_socket_t fd = evcon->fd;
+       evutil_socket_t fd = bufferevent_getfd(evcon->bufev);
 
        res = evhttp_parse_headers_(req, bufferevent_get_input(evcon->bufev));
        if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
@@ -2517,22 +2523,16 @@ evhttp_connection_new(const char *address, ev_uint16_t port)
        return (evhttp_connection_base_new(NULL, NULL, address, port));
 }
 
-struct evhttp_connection *
-evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
-    const char *address, ev_uint16_t port)
+static struct evhttp_connection *
+evhttp_connection_new_(struct event_base *base, struct bufferevent* bev)
 {
-       struct evhttp_connection *evcon = NULL;
-
-       event_debug(("Attempting connection to %s:%d\n", address, port));
+       struct evhttp_connection *evcon;
 
        if ((evcon = mm_calloc(1, sizeof(struct evhttp_connection))) == NULL) {
                event_warn("%s: calloc failed", __func__);
                goto error;
        }
 
-       evcon->fd = -1;
-       evcon->port = port;
-
        evcon->max_headers_size = EV_SIZE_MAX;
        evcon->max_body_size = EV_SIZE_MAX;
 
@@ -2543,13 +2543,8 @@ evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_bas
 
        evcon->retry_cnt = evcon->retry_max = 0;
 
-       if ((evcon->address = mm_strdup(address)) == NULL) {
-               event_warn("%s: strdup failed", __func__);
-               goto error;
-       }
-
        if (bev == NULL) {
-               if (!(bev = bufferevent_socket_new(base, -1, 0))) {
+               if (!(bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE))) {
                        event_warn("%s: bufferevent_socket_new failed", __func__);
                        goto error;
                }
@@ -2572,7 +2567,6 @@ evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_bas
            bufferevent_get_priority(bev),
            evhttp_deferred_read_cb, evcon);
 
-       evcon->dns_base = dnsbase;
        evcon->ai_family = AF_UNSPEC;
 
        return (evcon);
@@ -2583,6 +2577,63 @@ evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_bas
        return (NULL);
 }
 
+#ifndef _WIN32
+struct evhttp_connection *
+evhttp_connection_base_bufferevent_unix_new(struct event_base *base, struct bufferevent* bev, const char *unixsocket)
+{
+       struct evhttp_connection *evcon;
+
+       if (strlen(unixsocket) >= member_size(struct sockaddr_un, sun_path)) {
+               event_warn("%s: unix socket too long", __func__);
+               return NULL;
+       }
+
+       evcon = evhttp_connection_new_(base, bev);
+       if (evcon == NULL)
+               goto error;
+
+       if ((evcon->unixsocket = mm_strdup(unixsocket)) == NULL) {
+               event_warn("%s: strdup failed", __func__);
+               goto error;
+       }
+
+       evcon->ai_family = AF_UNIX;
+
+       return (evcon);
+ error:
+       if (evcon != NULL)
+               evhttp_connection_free(evcon);
+       return (NULL);
+}
+#endif
+
+struct evhttp_connection *
+evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
+    const char *address, unsigned short port)
+{
+       struct evhttp_connection *evcon;
+
+       event_debug(("Attempting connection to %s:%d\n", address, port));
+
+       evcon = evhttp_connection_new_(base, bev);
+       if (evcon == NULL)
+               goto error;
+
+       if ((evcon->address = mm_strdup(address)) == NULL) {
+               event_warn("%s: strdup failed", __func__);
+               goto error;
+       }
+       evcon->port = port;
+       evcon->dns_base = dnsbase;
+
+       return (evcon);
+error:
+       if (evcon != NULL)
+               evhttp_connection_free(evcon);
+       return (NULL);
+}
+
+
 struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
 {
        return evcon->bufev;
@@ -2725,7 +2776,7 @@ evhttp_connection_set_closecb(struct evhttp_connection *evcon,
 
 void
 evhttp_connection_get_peer(struct evhttp_connection *evcon,
-    char **address, ev_uint16_t *port)
+    const char **address, ev_uint16_t *port)
 {
        *address = evcon->address;
        *port = evcon->port;
@@ -2748,24 +2799,30 @@ evhttp_connection_connect_(struct evhttp_connection *evcon)
        if (evcon->state == EVCON_CONNECTING)
                return (0);
 
-       evhttp_connection_reset_(evcon);
+       /* Do not do hard reset, since this will reset the fd, but someone may
+        * change some options for it (i.e. setsockopt(), #875)
+        *
+        * However don't think that this options will be preserved for all
+        * connection lifetime, they will be reseted in the following cases:
+        * - evhttp_connection_set_local_address()
+        * - evhttp_connection_set_local_port()
+        * - evhttp_connection_set_retries()
+        * */
+       evhttp_connection_reset_(evcon, 0);
 
        EVUTIL_ASSERT(!(evcon->flags & EVHTTP_CON_INCOMING));
        evcon->flags |= EVHTTP_CON_OUTGOING;
 
        if (evcon->bind_address || evcon->bind_port) {
-               evcon->fd = bind_socket(
-                       evcon->bind_address, evcon->bind_port, 0 /*reuse*/);
-               if (evcon->fd == -1) {
+               int fd = bind_socket(evcon->bind_address, evcon->bind_port,
+                       0 /*reuse*/);
+               if (fd == -1) {
                        event_debug(("%s: failed to bind to \"%s\"",
                                __func__, evcon->bind_address));
                        return (-1);
                }
 
-               if (bufferevent_setfd(evcon->bufev, evcon->fd))
-                       return (-1);
-       } else {
-               if (bufferevent_setfd(evcon->bufev, -1))
+               if (bufferevent_replacefd(evcon->bufev, fd))
                        return (-1);
        }
 
@@ -2791,14 +2848,23 @@ evhttp_connection_connect_(struct evhttp_connection *evcon)
                        socklen = sizeof(struct sockaddr_in6);
                }
                ret = bufferevent_socket_connect(evcon->bufev, sa, socklen);
-       } else {
+       }
+#ifndef _WIN32
+       else if (evcon->unixsocket) {
+               struct sockaddr_un sockaddr;
+               sockaddr.sun_family = AF_UNIX;
+               strcpy(sockaddr.sun_path, evcon->unixsocket);
+               ret = bufferevent_socket_connect(evcon->bufev, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
+       }
+#endif
+       else {
                ret = bufferevent_socket_connect_hostname(evcon->bufev,
                                evcon->dns_base, evcon->ai_family, address, evcon->port);
        }
 
        if (ret < 0) {
                evcon->state = old_state;
-               event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
+               event_sock_warn(bufferevent_getfd(evcon->bufev), "%s: connection to \"%s\" failed",
                    __func__, evcon->address);
                /* some operating systems return ECONNREFUSED immediately
                 * when connecting to a local address.  the cleanup is going
@@ -2969,36 +3035,72 @@ evhttp_send_done(struct evhttp_connection *evcon, void *arg)
 /*
  * Returns an error page.
  */
-
 void
 evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
 {
-
-#define ERR_FORMAT "<HTML><HEAD>\n" \
-           "<TITLE>%d %s</TITLE>\n" \
-           "</HEAD><BODY>\n" \
-           "<H1>%s</H1>\n" \
-           "</BODY></HTML>\n"
+#define ERR_FORMAT "<html><head>" \
+       "<title>%d %s</title>" \
+       "</head><body>" \
+       "<h1>%d %s</h1>%s" \
+       "</body></html>"
 
        struct evbuffer *buf = evbuffer_new();
+       struct evhttp *http = req->evcon->http_server;
+
        if (buf == NULL) {
                /* if we cannot allocate memory; we just drop the connection */
                evhttp_connection_free(req->evcon);
                return;
        }
-       if (reason == NULL) {
-               reason = evhttp_response_phrase_internal(error);
-       }
 
        evhttp_response_code_(req, error, reason);
 
-       evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
+       /* Output error using callback for connection's evhttp, if available */
+       if ((http->errorcb == NULL) ||
+           ((*http->errorcb)(req, buf, error, reason, http->errorcbarg) < 0))
+       {
+               const char *heading = evhttp_response_phrase_internal(error);
+
+               evbuffer_drain(buf, evbuffer_get_length(buf));
+               evbuffer_add_printf(buf, ERR_FORMAT,
+                  error, heading, error, heading,
+                  (reason ? reason : ""));
+       }
 
        evhttp_send_page_(req, buf);
 
        evbuffer_free(buf);
 #undef ERR_FORMAT
 }
+static void
+evhttp_send_notfound(struct evhttp_request *req, const char *url)
+{
+#define REASON_FORMAT "<p>The requested URL %s was not found on this server.</p>"
+       char   *escaped_url = NULL;
+       char   *reason = NULL;
+       size_t reason_len;
+
+       url = (url != NULL ? url : req->uri);
+       if (url != NULL)
+               escaped_url = evhttp_htmlescape(url);
+
+       if (escaped_url != NULL) {
+               reason_len = strlen(REASON_FORMAT)+strlen(escaped_url)+1;
+               reason = mm_malloc(reason_len);
+       }
+
+       if ((escaped_url != NULL) && (reason != NULL))
+               evutil_snprintf(reason, reason_len, REASON_FORMAT, escaped_url);
+
+       evhttp_send_error(req, HTTP_NOTFOUND, reason);
+
+       if (reason != NULL)
+               mm_free(reason);
+       if (escaped_url != NULL)
+               mm_free(escaped_url);
+#undef REASON_FORMAT
+}
+
 
 /* Requires that headers and response code are already set up */
 
@@ -3090,6 +3192,31 @@ evhttp_send_reply_chunk_with_cb(struct evhttp_request *req, struct evbuffer *dat
        evhttp_write_buffer(evcon, cb, arg);
 }
 
+struct bufferevent *
+evhttp_start_ws_(struct evhttp_request *req)
+{
+       struct evhttp_connection *evcon = req->evcon;
+       struct bufferevent *bufev;
+
+       evhttp_response_code_(req, HTTP_SWITCH_PROTOCOLS, "Switching Protocols");
+
+       if (req->evcon == NULL)
+               return NULL;
+
+       evhttp_make_header(req->evcon, req);
+       evhttp_write_buffer(req->evcon, NULL, NULL);
+
+       TAILQ_REMOVE(&evcon->requests, req, next);
+
+       bufev = evcon->bufev;
+       evcon->bufev = NULL;
+       evcon->closecb = NULL;
+
+       evhttp_request_free(req);
+       evhttp_connection_free(evcon);
+       return bufev;
+}
+
 void
 evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
 {
@@ -3428,7 +3555,6 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
     int is_whole_uri, unsigned flags)
 {
        char *line=NULL;
-       char *argument;
        char *p;
        const char *query_part;
        int result = -1;
@@ -3456,12 +3582,12 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
                goto error;
        }
 
-       p = argument = line;
+       p = line;
        while (p != NULL && *p != '\0') {
                char *key, *value, *decoded_value;
-               argument = strsep(&p, "&");
+               int err;
 
-               value = argument;
+               value = strsep(&p, "&");
                key = strsep(&value, "=");
                if (flags & EVHTTP_URI_QUERY_NONCONFORMANT) {
                        if (value == NULL)
@@ -3482,8 +3608,10 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
                event_debug(("Query Param: %s -> %s\n", key, decoded_value));
                if (flags & EVHTTP_URI_QUERY_LAST_VAL)
                        evhttp_remove_header(headers, key);
-               evhttp_add_header_internal(headers, key, decoded_value);
+               err = evhttp_add_header_internal(headers, key, decoded_value);
                mm_free(decoded_value);
+               if (err)
+                       goto error;
        }
 
        result = 0;
@@ -3683,49 +3811,23 @@ evhttp_handle_request(struct evhttp_request *req, void *arg)
        if (http->gencb) {
                (*http->gencb)(req, http->gencbarg);
                return;
-       } else {
-               /* We need to send a 404 here */
-#define ERR_FORMAT "<html><head>" \
-                   "<title>404 Not Found</title>" \
-                   "</head><body>" \
-                   "<h1>Not Found</h1>" \
-                   "<p>The requested URL %s was not found on this server.</p>"\
-                   "</body></html>\n"
-
-               char *escaped_html;
-               struct evbuffer *buf;
-
-               if ((escaped_html = evhttp_htmlescape(req->uri)) == NULL) {
-                       evhttp_connection_free(req->evcon);
-                       return;
-               }
-
-               if ((buf = evbuffer_new()) == NULL) {
-                       mm_free(escaped_html);
-                       evhttp_connection_free(req->evcon);
-                       return;
-               }
-
-               evhttp_response_code_(req, HTTP_NOTFOUND, "Not Found");
-
-               evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
-
-               mm_free(escaped_html);
-
-               evhttp_send_page_(req, buf);
-
-               evbuffer_free(buf);
-#undef ERR_FORMAT
-       }
+       } else
+               evhttp_send_notfound(req, NULL);
 }
 
 /* Listener callback when a connection arrives at a server. */
 static void
 accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
 {
-       struct evhttp *http = arg;
+       struct evhttp_bound_socket *bound = arg;
+
+       struct evhttp *http = bound->http;
 
-       evhttp_get_request(http, nfd, peer_sa, peer_socklen);
+       struct bufferevent *bev = NULL;
+       if (bound->bevcb)
+               bev = bound->bevcb(http->base, bound->bevcbarg);
+
+       evhttp_get_request(http, nfd, peer_sa, peer_socklen, bev);
 }
 
 int
@@ -3821,9 +3923,11 @@ evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener)
                return (NULL);
 
        bound->listener = listener;
+       bound->bevcb = NULL;
+       bound->http = http;
        TAILQ_INSERT_TAIL(&http->sockets, bound, next);
 
-       evconnlistener_set_cb(listener, accept_socket_cb, http);
+       evconnlistener_set_cb(listener, accept_socket_cb, bound);
        return bound;
 }
 
@@ -3839,6 +3943,14 @@ evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound)
        return bound->listener;
 }
 
+void
+evhttp_bound_set_bevcb(struct evhttp_bound_socket *bound,
+    struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg)
+{
+       bound->bevcb = cb;
+       bound->bevcbarg = cbarg;
+}
+
 void
 evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound)
 {
@@ -3873,6 +3985,7 @@ evhttp_new_object(void)
        TAILQ_INIT(&http->sockets);
        TAILQ_INIT(&http->callbacks);
        TAILQ_INIT(&http->connections);
+       TAILQ_INIT(&http->ws_sessions);
        TAILQ_INIT(&http->virtualhosts);
        TAILQ_INIT(&http->aliases);
 
@@ -3917,6 +4030,7 @@ evhttp_free(struct evhttp* http)
 {
        struct evhttp_cb *http_cb;
        struct evhttp_connection *evcon;
+       struct evws_connection *evws;
        struct evhttp_bound_socket *bound;
        struct evhttp* vhost;
        struct evhttp_server_alias *alias;
@@ -3935,6 +4049,10 @@ evhttp_free(struct evhttp* http)
                evhttp_connection_free(evcon);
        }
 
+       while ((evws = TAILQ_FIRST(&http->ws_sessions)) != NULL) {
+               evws_connection_free(evws);
+       }
+
        while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
                TAILQ_REMOVE(&http->callbacks, http_cb, next);
                mm_free(http_cb->what);
@@ -4083,6 +4201,21 @@ evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size)
                http->default_max_body_size = max_body_size;
 }
 
+void
+evhttp_set_max_connections(struct evhttp* http, int max_connections)
+{
+       if (max_connections < 0)
+               http->connection_max = 0;
+       else
+               http->connection_max = max_connections;
+}
+
+int
+evhttp_get_connection_count(struct evhttp* http)
+{
+       return http->connection_cnt;
+}
+
 void
 evhttp_set_default_content_type(struct evhttp *http,
        const char *content_type) {
@@ -4173,6 +4306,14 @@ evhttp_set_newreqcb(struct evhttp *http,
        http->newreqcb = cb;
        http->newreqcbarg = cbarg;
 }
+void
+evhttp_set_errorcb(struct evhttp *http,
+    int (*cb)(struct evhttp_request *, struct evbuffer *, int, const char *, void *),
+    void *cbarg)
+{
+       http->errorcb = cb;
+       http->errorcbarg = cbarg;
+}
 
 /*
  * Request related functions
@@ -4322,7 +4463,7 @@ evhttp_request_set_on_complete_cb(struct evhttp_request *req,
 const char *
 evhttp_request_get_uri(const struct evhttp_request *req) {
        if (req->uri == NULL)
-               event_debug(("%s: request %p has no uri\n", __func__, req));
+               event_debug(("%s: request %p has no uri\n", __func__, (void *)req));
        return (req->uri);
 }
 
@@ -4330,7 +4471,7 @@ const struct evhttp_uri *
 evhttp_request_get_evhttp_uri(const struct evhttp_request *req) {
        if (req->uri_elems == NULL)
                event_debug(("%s: request %p has no uri elems\n",
-                           __func__, req));
+                           __func__, (void *)req));
        return (req->uri_elems);
 }
 
@@ -4422,37 +4563,57 @@ struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req)
 static struct evhttp_connection*
 evhttp_get_request_connection(
        struct evhttp* http,
-       evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
+       evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen,
+       struct bufferevent* bev)
 {
        struct evhttp_connection *evcon;
-       char *hostname = NULL, *portname = NULL;
-       struct bufferevent* bev = NULL;
 
 #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
        if (sa->sa_family == AF_UNIX) {
-               struct sockaddr_un *sun = (struct sockaddr_un *)sa;
-               sun->sun_path[0] = '\0';
+               struct sockaddr_un *sa_un = (struct sockaddr_un *)sa;
+               sa_un->sun_path[0] = '\0';
        }
 #endif
 
-       name_from_addr(sa, salen, &hostname, &portname);
-       if (hostname == NULL || portname == NULL) {
-               if (hostname) mm_free(hostname);
-               if (portname) mm_free(portname);
-               return (NULL);
+#ifndef _WIN32
+       if (sa->sa_family == AF_UNIX) {
+               struct sockaddr_un *sockaddr = (struct sockaddr_un *)sa;
+
+               event_debug(("%s: new request from unix socket on "
+                       EV_SOCK_FMT"\n", __func__, EV_SOCK_ARG(fd)));
+
+               /* we need a connection object to put the http request on */
+               if (!bev && http->bevcb != NULL) {
+                       bev = (*http->bevcb)(http->base, http->bevcbarg);
+               }
+
+               evcon = evhttp_connection_base_bufferevent_unix_new(http->base,
+                       bev, sockaddr->sun_path);
        }
+       else
+#endif
+       {
+               char *hostname = NULL, *portname = NULL;
+
+               name_from_addr(sa, salen, &hostname, &portname);
+               if (hostname == NULL || portname == NULL) {
+                       if (hostname) mm_free(hostname);
+                       if (portname) mm_free(portname);
+                       return (NULL);
+               }
 
-       event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
-               __func__, hostname, portname, EV_SOCK_ARG(fd)));
+               event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
+                       __func__, hostname, portname, EV_SOCK_ARG(fd)));
 
-       /* we need a connection object to put the http request on */
-       if (http->bevcb != NULL) {
-               bev = (*http->bevcb)(http->base, http->bevcbarg);
+               /* we need a connection object to put the http request on */
+               if (!bev && http->bevcb != NULL) {
+                       bev = (*http->bevcb)(http->base, http->bevcbarg);
+               }
+               evcon = evhttp_connection_base_bufferevent_new(
+                       http->base, NULL, bev, hostname, atoi(portname));
+               mm_free(hostname);
+               mm_free(portname);
        }
-       evcon = evhttp_connection_base_bufferevent_new(
-               http->base, NULL, bev, hostname, atoi(portname));
-       mm_free(hostname);
-       mm_free(portname);
        if (evcon == NULL)
                return (NULL);
 
@@ -4464,9 +4625,7 @@ evhttp_get_request_connection(
        evcon->flags |= EVHTTP_CON_INCOMING;
        evcon->state = EVCON_READING_FIRSTLINE;
 
-       evcon->fd = fd;
-
-       if (bufferevent_setfd(evcon->bufev, fd))
+       if (bufferevent_replacefd(evcon->bufev, fd))
                goto err;
        if (bufferevent_enable(evcon->bufev, EV_READ))
                goto err;
@@ -4489,10 +4648,12 @@ evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
        if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
                return (-1);
 
-       if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
-               event_warn("%s: strdup", __func__);
-               evhttp_request_free(req);
-               return (-1);
+       if (evcon->address != NULL) {
+               if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
+                       event_warn("%s: strdup", __func__);
+                       evhttp_request_free(req);
+                       return (-1);
+               }
        }
        req->remote_port = evcon->port;
 
@@ -4521,11 +4682,12 @@ evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
 
 static void
 evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
-    struct sockaddr *sa, ev_socklen_t salen)
+    struct sockaddr *sa, ev_socklen_t salen,
+    struct bufferevent *bev)
 {
        struct evhttp_connection *evcon;
 
-       evcon = evhttp_get_request_connection(http, fd, sa, salen);
+       evcon = evhttp_get_request_connection(http, fd, sa, salen, bev);
        if (evcon == NULL) {
                event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
                    __func__, EV_SOCK_ARG(fd));
@@ -4546,8 +4708,30 @@ evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
        evcon->http_server = http;
        evcon->ext_method_cmp = http->ext_method_cmp;
        TAILQ_INSERT_TAIL(&http->connections, evcon, next);
+       http->connection_cnt++;
+
+       /* send "service unavailable" if we've reached the connection limit */
+       if (http->connection_max && http->connection_max < http->connection_cnt) {
+               struct evhttp_request *req;
+
+               if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL) {
+                       evhttp_connection_free(evcon);
+                       return;
+               }
+
+               req->evcon = evcon;     /* the request owns the connection */
+               req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+               req->kind = EVHTTP_REQUEST;
+               /* note, req->remote_host not needed since we don't read */
+
+               TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+               /* send error to client */
+               evcon->state = EVCON_WRITING;
+               bufferevent_enable(evcon->bufev, EV_READ); /* enable close events */
+               evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
 
-       if (evhttp_associate_new_request_with_connection(evcon) == -1)
+       } else if (evhttp_associate_new_request_with_connection(evcon) == -1)
                evhttp_connection_free(evcon);
 }
 
@@ -4593,9 +4777,8 @@ name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
 }
 
 /* Create a non-blocking socket and bind it */
-/* todo: rename this function */
 static evutil_socket_t
-bind_socket_ai(struct evutil_addrinfo *ai, int reuse)
+create_bind_socket_nonblock(struct evutil_addrinfo *ai, int reuse)
 {
        evutil_socket_t fd;
 
@@ -4669,14 +4852,14 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
 
        /* just create an unbound socket */
        if (address == NULL && port == 0)
-               return bind_socket_ai(NULL, 0);
+               return create_bind_socket_nonblock(NULL, 0);
 
        aitop = make_addrinfo(address, port);
 
        if (aitop == NULL)
                return (-1);
 
-       fd = bind_socket_ai(aitop, reuse);
+       fd = create_bind_socket_nonblock(aitop, reuse);
 
        evutil_freeaddrinfo(aitop);
 
@@ -4689,6 +4872,9 @@ struct evhttp_uri {
        char *userinfo; /* userinfo (typically username:pass), or NULL */
        char *host; /* hostname, IP address, or NULL */
        int port; /* port, or zero */
+#ifndef _WIN32
+       char *unixsocket; /* unix domain socket or NULL */
+#endif
        char *path; /* path, or "". */
        char *query; /* query, or NULL */
        char *fragment; /* fragment or NULL */
@@ -4830,9 +5016,11 @@ bracket_addr_ok(const char *s, const char *eos)
 }
 
 static int
-parse_authority(struct evhttp_uri *uri, char *s, char *eos)
+parse_authority(struct evhttp_uri *uri, char *s, char *eos, unsigned *flags)
 {
+       size_t len;
        char *cp, *port;
+
        EVUTIL_ASSERT(eos);
        if (eos == s) {
                uri->host = mm_strdup("");
@@ -4858,6 +5046,20 @@ parse_authority(struct evhttp_uri *uri, char *s, char *eos)
        } else {
                cp = s;
        }
+
+#ifndef _WIN32
+       if (*flags & EVHTTP_URI_UNIX_SOCKET && !strncmp(cp, "unix:", 5)) {
+               char *e = strchr(cp + 5, ':');
+               if (e) {
+                       *e = '\0';
+                       uri->unixsocket = mm_strdup(cp + 5);
+                       return 0;
+               } else {
+                       return -1;
+               }
+       }
+#endif
+
        /* Optionally, we end with ":port" */
        for (port=eos-1; port >= cp && EVUTIL_ISDIGIT_(*port); --port)
                ;
@@ -4872,22 +5074,31 @@ parse_authority(struct evhttp_uri *uri, char *s, char *eos)
        /* Now, cp..eos holds the "host" port, which can be an IPv4Address,
         * an IP-Literal, or a reg-name */
        EVUTIL_ASSERT(eos >= cp);
+       len = eos-cp;
        if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
                /* IPv6address, IP-Literal, or junk. */
                if (! bracket_addr_ok(cp, eos))
                        return -1;
+               if (*flags & EVHTTP_URI_HOST_STRIP_BRACKETS)
+                       len = eos-cp-2;
        } else {
                /* Make sure the host part is ok. */
                if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */
                        return -1;
        }
-       uri->host = mm_malloc(eos-cp+1);
+
+       uri->host = mm_malloc(len+1);
        if (uri->host == NULL) {
                event_warn("%s: malloc", __func__);
                return -1;
        }
-       memcpy(uri->host, cp, eos-cp);
-       uri->host[eos-cp] = '\0';
+       if (*cp == '[' && *flags & EVHTTP_URI_HOST_STRIP_BRACKETS) {
+               memcpy(uri->host, cp+1, len);
+               *flags |= _EVHTTP_URI_HOST_HAS_BRACKETS;
+       } else {
+               memcpy(uri->host, cp, len);
+       }
+       uri->host[len] = '\0';
        return 0;
 
 }
@@ -5023,7 +5234,7 @@ evhttp_uri_parse_with_flags(const char *source_uri, unsigned flags)
                readp += 2;
                authority = readp;
                path = end_of_authority(readp);
-               if (parse_authority(uri, authority, path) < 0)
+               if (parse_authority(uri, authority, path, &uri->flags) < 0)
                        goto err;
                readp = path;
                got_authority = 1;
@@ -5102,7 +5313,7 @@ err:
 }
 
 static struct evhttp_uri *
-evhttp_uri_parse_authority(char *source_uri)
+evhttp_uri_parse_authority(char *source_uri, unsigned flags)
 {
        struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
        char *end;
@@ -5112,10 +5323,10 @@ evhttp_uri_parse_authority(char *source_uri)
                goto err;
        }
        uri->port = -1;
-       uri->flags = 0;
+       uri->flags = flags;
 
        end = end_of_authority(source_uri);
-       if (parse_authority(uri, source_uri, end) < 0)
+       if (parse_authority(uri, source_uri, end, &uri->flags) < 0)
                goto err;
 
        uri->path = mm_strdup("");
@@ -5142,6 +5353,9 @@ evhttp_uri_free(struct evhttp_uri *uri)
        URI_FREE_STR_(scheme);
        URI_FREE_STR_(userinfo);
        URI_FREE_STR_(host);
+#ifndef _WIN32
+       URI_FREE_STR_(unixsocket);
+#endif
        URI_FREE_STR_(path);
        URI_FREE_STR_(query);
        URI_FREE_STR_(fragment);
@@ -5170,11 +5384,26 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
                URI_ADD_(scheme);
                evbuffer_add(tmp, ":", 1);
        }
+#ifndef _WIN32
+       if (uri->unixsocket) {
+               evbuffer_add(tmp, "//", 2);
+               if (uri->userinfo)
+                       evbuffer_add_printf(tmp, "%s@", uri->userinfo);
+               evbuffer_add_printf(tmp, "unix:%s:", uri->unixsocket);
+       }
+       else
+#endif
        if (uri->host) {
                evbuffer_add(tmp, "//", 2);
                if (uri->userinfo)
                        evbuffer_add_printf(tmp,"%s@", uri->userinfo);
-               URI_ADD_(host);
+               if (uri->flags & _EVHTTP_URI_HOST_HAS_BRACKETS) {
+                       evbuffer_add(tmp, "[", 1);
+                       URI_ADD_(host);
+                       evbuffer_add(tmp, "]", 1);
+               } else {
+                       URI_ADD_(host);
+               }
                if (uri->port >= 0)
                        evbuffer_add_printf(tmp,":%d", uri->port);
 
@@ -5204,7 +5433,7 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
                evbuffer_free(tmp);
                return NULL;
        }
-               evbuffer_remove(tmp, buf, joined_size);
+       evbuffer_remove(tmp, buf, joined_size);
 
        output = buf;
 err:
@@ -5229,6 +5458,13 @@ evhttp_uri_get_host(const struct evhttp_uri *uri)
 {
        return uri->host;
 }
+#ifndef _WIN32
+const char *
+evhttp_uri_get_unixsocket(const struct evhttp_uri *uri)
+{
+       return uri->unixsocket;
+}
+#endif
 int
 evhttp_uri_get_port(const struct evhttp_uri *uri)
 {
@@ -5283,19 +5519,49 @@ evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo)
 int
 evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
 {
+       size_t len = 0;
+
        if (host) {
+               len = strlen(host);
+
                if (host[0] == '[') {
-                       if (! bracket_addr_ok(host, host+strlen(host)))
+                       if (! bracket_addr_ok(host, host+len))
                                return -1;
                } else {
-                       if (! regname_ok(host, host+strlen(host)))
+                       if (! regname_ok(host, host+len))
                                return -1;
                }
        }
 
-       URI_SET_STR_(host);
+       if (host && host[0] == '[' && uri->flags & EVHTTP_URI_HOST_STRIP_BRACKETS) {
+               char *new_host;
+
+               len -= 2;
+               new_host = mm_realloc(uri->host, len+1);
+               if (!new_host) {
+                       free(uri->host);
+                       uri->host = NULL;
+               } else {
+                       memcpy(new_host, host+1, len);
+                       new_host[len] = '\0';
+                       uri->host = new_host;
+               }
+               uri->flags |= _EVHTTP_URI_HOST_HAS_BRACKETS;
+       } else {
+               URI_SET_STR_(host);
+               uri->flags &= ~_EVHTTP_URI_HOST_HAS_BRACKETS;
+       }
+
+       return 0;
+}
+#ifndef _WIN32
+int
+evhttp_uri_set_unixsocket(struct evhttp_uri *uri, const char *unixsocket)
+{
+       URI_SET_STR_(unixsocket);
        return 0;
 }
+#endif
 int
 evhttp_uri_set_port(struct evhttp_uri *uri, int port)
 {