Squashed 'third_party/libevent/' content from commit d8ceae8

Change-Id: I84e1b23561eb225b9f288717b028e6bc9e5a27e1
git-subtree-dir: third_party/libevent
git-subtree-split: d8ceae81b83f3d0a56e2f41d4c429b2702a8e302
diff --git a/http.c b/http.c
new file mode 100644
index 0000000..377597e
--- /dev/null
+++ b/http.c
@@ -0,0 +1,4529 @@
+/*
+ * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu>
+ * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "event2/event-config.h"
+
+#ifdef _EVENT_HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef _EVENT_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef _EVENT_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+
+#ifndef WIN32
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+#include <sys/queue.h>
+
+#ifdef _EVENT_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef _EVENT_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef _EVENT_HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#endif
+#include <signal.h>
+#include <time.h>
+#ifdef _EVENT_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef _EVENT_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#undef timeout_pending
+#undef timeout_initialized
+
+#include "strlcpy-internal.h"
+#include "event2/http.h"
+#include "event2/event.h"
+#include "event2/buffer.h"
+#include "event2/bufferevent.h"
+#include "event2/bufferevent_compat.h"
+#include "event2/http_struct.h"
+#include "event2/http_compat.h"
+#include "event2/util.h"
+#include "event2/listener.h"
+#include "log-internal.h"
+#include "util-internal.h"
+#include "http-internal.h"
+#include "mm-internal.h"
+#include "bufferevent-internal.h"
+
+#ifndef _EVENT_HAVE_GETNAMEINFO
+#define NI_MAXSERV 32
+#define NI_MAXHOST 1025
+
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
+#define NI_NUMERICSERV 2
+#endif
+
+static int
+fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
+	size_t hostlen, char *serv, size_t servlen, int flags)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+	if (serv != NULL) {
+		char tmpserv[16];
+		evutil_snprintf(tmpserv, sizeof(tmpserv),
+		    "%d", ntohs(sin->sin_port));
+		if (strlcpy(serv, tmpserv, servlen) >= servlen)
+			return (-1);
+	}
+
+	if (host != NULL) {
+		if (flags & NI_NUMERICHOST) {
+			if (strlcpy(host, inet_ntoa(sin->sin_addr),
+			    hostlen) >= hostlen)
+				return (-1);
+			else
+				return (0);
+		} else {
+			struct hostent *hp;
+			hp = gethostbyaddr((char *)&sin->sin_addr,
+			    sizeof(struct in_addr), AF_INET);
+			if (hp == NULL)
+				return (-2);
+
+			if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
+				return (-1);
+			else
+				return (0);
+		}
+	}
+	return (0);
+}
+
+#endif
+
+#define REQ_VERSION_BEFORE(req, major_v, minor_v)			\
+	((req)->major < (major_v) ||					\
+	    ((req)->major == (major_v) && (req)->minor < (minor_v)))
+
+#define REQ_VERSION_ATLEAST(req, major_v, minor_v)			\
+	((req)->major > (major_v) ||					\
+	    ((req)->major == (major_v) && (req)->minor >= (minor_v)))
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+extern int debug;
+
+static evutil_socket_t bind_socket_ai(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 int evhttp_associate_new_request_with_connection(
+	struct evhttp_connection *evcon);
+static void evhttp_connection_start_detectclose(
+	struct evhttp_connection *evcon);
+static void evhttp_connection_stop_detectclose(
+	struct evhttp_connection *evcon);
+static void evhttp_request_dispatch(struct evhttp_connection* evcon);
+static void evhttp_read_firstline(struct evhttp_connection *evcon,
+				  struct evhttp_request *req);
+static void evhttp_read_header(struct evhttp_connection *evcon,
+    struct evhttp_request *req);
+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_write_buffer(struct evhttp_connection *,
+    void (*)(struct evhttp_connection *, void *), void *);
+static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
+
+/* callbacks for bufferevent */
+static void evhttp_read_cb(struct bufferevent *, void *);
+static void evhttp_write_cb(struct bufferevent *, void *);
+static void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
+static int evhttp_decode_uri_internal(const char *uri, size_t length,
+    char *ret, int decode_plus);
+static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
+		  const char *hostname);
+
+#ifndef _EVENT_HAVE_STRSEP
+/* strsep replacement for platforms that lack it.  Only works if
+ * del is one character long. */
+static char *
+strsep(char **s, const char *del)
+{
+	char *d, *tok;
+	EVUTIL_ASSERT(strlen(del) == 1);
+	if (!s || !*s)
+		return NULL;
+	tok = *s;
+	d = strstr(tok, del);
+	if (d) {
+		*d = '\0';
+		*s = d + 1;
+	} else
+		*s = NULL;
+	return tok;
+}
+#endif
+
+static size_t
+html_replace(const char ch, const char **escaped)
+{
+	switch (ch) {
+	case '<':
+		*escaped = "&lt;";
+		return 4;
+	case '>':
+		*escaped = "&gt;";
+		return 4;
+	case '"':
+		*escaped = "&quot;";
+		return 6;
+	case '\'':
+		*escaped = "&#039;";
+		return 6;
+	case '&':
+		*escaped = "&amp;";
+		return 5;
+	default:
+		break;
+	}
+
+	return 1;
+}
+
+/*
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ */
+
+char *
+evhttp_htmlescape(const char *html)
+{
+	size_t i;
+	size_t new_size = 0, old_size = 0;
+	char *escaped_html, *p;
+
+	if (html == NULL)
+		return (NULL);
+
+	old_size = strlen(html);
+	for (i = 0; i < old_size; ++i) {
+		const char *replaced = NULL;
+		const size_t replace_size = html_replace(html[i], &replaced);
+		if (replace_size > EV_SIZE_MAX - new_size) {
+			event_warn("%s: html_replace overflow", __func__);
+			return (NULL);
+		}
+		new_size += replace_size;
+	}
+
+	if (new_size == EV_SIZE_MAX)
+		return (NULL);
+	p = escaped_html = mm_malloc(new_size + 1);
+	if (escaped_html == NULL) {
+		event_warn("%s: malloc(%lu)", __func__,
+		           (unsigned long)(new_size + 1));
+		return (NULL);
+	}
+	for (i = 0; i < old_size; ++i) {
+		const char *replaced = &html[i];
+		const size_t len = html_replace(html[i], &replaced);
+		memcpy(p, replaced, len);
+		p += len;
+	}
+
+	*p = '\0';
+
+	return (escaped_html);
+}
+
+/** Given an evhttp_cmd_type, returns a constant string containing the
+ * equivalent HTTP command, or NULL if the evhttp_command_type is
+ * unrecognized. */
+static const char *
+evhttp_method(enum evhttp_cmd_type type)
+{
+	const char *method;
+
+	switch (type) {
+	case EVHTTP_REQ_GET:
+		method = "GET";
+		break;
+	case EVHTTP_REQ_POST:
+		method = "POST";
+		break;
+	case EVHTTP_REQ_HEAD:
+		method = "HEAD";
+		break;
+	case EVHTTP_REQ_PUT:
+		method = "PUT";
+		break;
+	case EVHTTP_REQ_DELETE:
+		method = "DELETE";
+		break;
+	case EVHTTP_REQ_OPTIONS:
+		method = "OPTIONS";
+		break;
+	case EVHTTP_REQ_TRACE:
+		method = "TRACE";
+		break;
+	case EVHTTP_REQ_CONNECT:
+		method = "CONNECT";
+		break;
+	case EVHTTP_REQ_PATCH:
+		method = "PATCH";
+		break;
+	default:
+		method = NULL;
+		break;
+	}
+
+	return (method);
+}
+
+/**
+ * Determines if a response should have a body.
+ * Follows the rules in RFC 2616 section 4.3.
+ * @return 1 if the response MUST have a body; 0 if the response MUST NOT have
+ *     a body.
+ */
+static int
+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_HEAD);
+}
+
+/** Helper: adds the event 'ev' with the timeout 'timeout', or with
+ * default_timeout if timeout is -1.
+ */
+static int
+evhttp_add_event(struct event *ev, int timeout, int default_timeout)
+{
+	if (timeout != 0) {
+		struct timeval tv;
+
+		evutil_timerclear(&tv);
+		tv.tv_sec = timeout != -1 ? timeout : default_timeout;
+		return event_add(ev, &tv);
+	} else {
+		return event_add(ev, NULL);
+	}
+}
+
+/** Helper: called after we've added some data to an evcon's bufferevent's
+ * output buffer.  Sets the evconn's writing-is-done callback, and puts
+ * the bufferevent into writing mode.
+ */
+static void
+evhttp_write_buffer(struct evhttp_connection *evcon,
+    void (*cb)(struct evhttp_connection *, void *), void *arg)
+{
+	event_debug(("%s: preparing to write buffer\n", __func__));
+
+	/* Set call back */
+	evcon->cb = cb;
+	evcon->cb_arg = arg;
+
+	/* Disable the read callback: we don't actually care about data;
+	 * we only care about close detection.  (We don't disable reading,
+	 * since we *do* want to learn about any close events.) */
+	bufferevent_setcb(evcon->bufev,
+	    NULL, /*read*/
+	    evhttp_write_cb,
+	    evhttp_error_cb,
+	    evcon);
+
+	bufferevent_enable(evcon->bufev, EV_WRITE);
+}
+
+static void
+evhttp_send_continue_done(struct evhttp_connection *evcon, void *arg)
+{
+	bufferevent_disable(evcon->bufev, EV_WRITE);
+}
+
+static void
+evhttp_send_continue(struct evhttp_connection *evcon,
+			struct evhttp_request *req)
+{
+	bufferevent_enable(evcon->bufev, EV_WRITE);
+	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
+			"HTTP/%d.%d 100 Continue\r\n\r\n",
+			req->major, req->minor);
+	evcon->cb = evhttp_send_continue_done;
+	evcon->cb_arg = NULL;
+	bufferevent_setcb(evcon->bufev,
+	    evhttp_read_cb,
+	    evhttp_write_cb,
+	    evhttp_error_cb,
+	    evcon);
+}
+
+/** Helper: returns true iff evconn is in any connected state. */
+static int
+evhttp_connected(struct evhttp_connection *evcon)
+{
+	switch (evcon->state) {
+	case EVCON_DISCONNECTED:
+	case EVCON_CONNECTING:
+		return (0);
+	case EVCON_IDLE:
+	case EVCON_READING_FIRSTLINE:
+	case EVCON_READING_HEADERS:
+	case EVCON_READING_BODY:
+	case EVCON_READING_TRAILER:
+	case EVCON_WRITING:
+	default:
+		return (1);
+	}
+}
+
+/* Create the headers needed for an outgoing HTTP request, adds them to
+ * the request's header list, and writes the request line to the
+ * connection's output buffer.
+ */
+static void
+evhttp_make_header_request(struct evhttp_connection *evcon,
+    struct evhttp_request *req)
+{
+	const char *method;
+
+	evhttp_remove_header(req->output_headers, "Proxy-Connection");
+
+	/* Generate request line */
+	method = evhttp_method(req->type);
+	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
+	    "%s %s HTTP/%d.%d\r\n",
+	    method, req->uri, req->major, req->minor);
+
+	/* Add the content length on a post or put request if missing */
+	if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
+	    evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+		char size[22];
+		evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
+		    EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
+		evhttp_add_header(req->output_headers, "Content-Length", size);
+	}
+}
+
+/** Return true if the list of headers in 'headers', intepreted with respect
+ * to flags, means that we should send a "connection: close" when the request
+ * is done. */
+static int
+evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
+{
+	if (flags & EVHTTP_PROXY_REQUEST) {
+		/* proxy connection */
+		const char *connection = evhttp_find_header(headers, "Proxy-Connection");
+		return (connection == NULL || evutil_ascii_strcasecmp(connection, "keep-alive") != 0);
+	} else {
+		const char *connection = evhttp_find_header(headers, "Connection");
+		return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0);
+	}
+}
+
+/* Return true iff 'headers' contains 'Connection: keep-alive' */
+static int
+evhttp_is_connection_keepalive(struct evkeyvalq* headers)
+{
+	const char *connection = evhttp_find_header(headers, "Connection");
+	return (connection != NULL
+	    && evutil_ascii_strncasecmp(connection, "keep-alive", 10) == 0);
+}
+
+/* Add a correct "Date" header to headers, unless it already has one. */
+static void
+evhttp_maybe_add_date_header(struct evkeyvalq *headers)
+{
+	if (evhttp_find_header(headers, "Date") == NULL) {
+		char date[50];
+#ifndef WIN32
+		struct tm cur;
+#endif
+		struct tm *cur_p;
+		time_t t = time(NULL);
+#ifdef WIN32
+		cur_p = gmtime(&t);
+#else
+		gmtime_r(&t, &cur);
+		cur_p = &cur;
+#endif
+		if (strftime(date, sizeof(date),
+			"%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
+			evhttp_add_header(headers, "Date", date);
+		}
+	}
+}
+
+/* Add a "Content-Length" header with value 'content_length' to headers,
+ * unless it already has a content-length or transfer-encoding header. */
+static void
+evhttp_maybe_add_content_length_header(struct evkeyvalq *headers,
+    size_t content_length)
+{
+	if (evhttp_find_header(headers, "Transfer-Encoding") == NULL &&
+	    evhttp_find_header(headers,	"Content-Length") == NULL) {
+		char len[22];
+		evutil_snprintf(len, sizeof(len), EV_SIZE_FMT,
+		    EV_SIZE_ARG(content_length));
+		evhttp_add_header(headers, "Content-Length", len);
+	}
+}
+
+/*
+ * Create the headers needed for an HTTP reply in req->output_headers,
+ * and write the first HTTP response for req line to evcon.
+ */
+static void
+evhttp_make_header_response(struct evhttp_connection *evcon,
+    struct evhttp_request *req)
+{
+	int is_keepalive = evhttp_is_connection_keepalive(req->input_headers);
+	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
+	    "HTTP/%d.%d %d %s\r\n",
+	    req->major, req->minor, req->response_code,
+	    req->response_code_line);
+
+	if (req->major == 1) {
+		if (req->minor >= 1)
+			evhttp_maybe_add_date_header(req->output_headers);
+
+		/*
+		 * if the protocol is 1.0; and the connection was keep-alive
+		 * we need to add a keep-alive header, too.
+		 */
+		if (req->minor == 0 && is_keepalive)
+			evhttp_add_header(req->output_headers,
+			    "Connection", "keep-alive");
+
+		if ((req->minor >= 1 || is_keepalive) &&
+		    evhttp_response_needs_body(req)) {
+			/*
+			 * we need to add the content length if the
+			 * user did not give it, this is required for
+			 * persistent connections to work.
+			 */
+			evhttp_maybe_add_content_length_header(
+				req->output_headers,
+				evbuffer_get_length(req->output_buffer));
+		}
+	}
+
+	/* Potentially add headers for unidentified content. */
+	if (evhttp_response_needs_body(req)) {
+		if (evhttp_find_header(req->output_headers,
+			"Content-Type") == NULL) {
+			evhttp_add_header(req->output_headers,
+			    "Content-Type", "text/html; charset=ISO-8859-1");
+		}
+	}
+
+	/* if the request asked for a close, we send a close, too */
+	if (evhttp_is_connection_close(req->flags, req->input_headers)) {
+		evhttp_remove_header(req->output_headers, "Connection");
+		if (!(req->flags & EVHTTP_PROXY_REQUEST))
+		    evhttp_add_header(req->output_headers, "Connection", "close");
+		evhttp_remove_header(req->output_headers, "Proxy-Connection");
+	}
+}
+
+/** Generate all headers appropriate for sending the http request in req (or
+ * the response, if we're sending a response), and write them to evcon's
+ * bufferevent. Also writes all data from req->output_buffer */
+static void
+evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+	struct evkeyval *header;
+	struct evbuffer *output = bufferevent_get_output(evcon->bufev);
+
+	/*
+	 * Depending if this is a HTTP request or response, we might need to
+	 * add some new headers or remove existing headers.
+	 */
+	if (req->kind == EVHTTP_REQUEST) {
+		evhttp_make_header_request(evcon, req);
+	} else {
+		evhttp_make_header_response(evcon, req);
+	}
+
+	TAILQ_FOREACH(header, req->output_headers, next) {
+		evbuffer_add_printf(output, "%s: %s\r\n",
+		    header->key, header->value);
+	}
+	evbuffer_add(output, "\r\n", 2);
+
+	if (evbuffer_get_length(req->output_buffer) > 0) {
+		/*
+		 * For a request, we add the POST data, for a reply, this
+		 * is the regular data.
+		 */
+		/* XXX We might want to support waiting (a limited amount of
+		   time) for a continue status line from the server before
+		   sending POST/PUT message bodies. */
+		evbuffer_add_buffer(output, req->output_buffer);
+	}
+}
+
+void
+evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon,
+    ev_ssize_t new_max_headers_size)
+{
+	if (new_max_headers_size<0)
+		evcon->max_headers_size = EV_SIZE_MAX;
+	else
+		evcon->max_headers_size = new_max_headers_size;
+}
+void
+evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
+    ev_ssize_t new_max_body_size)
+{
+	if (new_max_body_size<0)
+		evcon->max_body_size = EV_UINT64_MAX;
+	else
+		evcon->max_body_size = new_max_body_size;
+}
+
+static int
+evhttp_connection_incoming_fail(struct evhttp_request *req,
+    enum evhttp_connection_error error)
+{
+	switch (error) {
+	case EVCON_HTTP_TIMEOUT:
+	case EVCON_HTTP_EOF:
+		/*
+		 * these are cases in which we probably should just
+		 * close the connection and not send a reply.  this
+		 * 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.
+		 */
+		if (!req->userdone) {
+			/* remove it so that it will not be freed */
+			TAILQ_REMOVE(&req->evcon->requests, req, next);
+			/* indicate that this request no longer has a
+			 * connection object
+			 */
+			req->evcon = NULL;
+		}
+		return (-1);
+	case EVCON_HTTP_INVALID_HEADER:
+	case EVCON_HTTP_BUFFER_ERROR:
+	case EVCON_HTTP_REQUEST_CANCEL:
+	default:	/* xxx: probably should just error on default */
+		/* the callback looks at the uri to determine errors */
+		if (req->uri) {
+			mm_free(req->uri);
+			req->uri = NULL;
+		}
+		if (req->uri_elems) {
+			evhttp_uri_free(req->uri_elems);
+			req->uri_elems = NULL;
+		}
+
+		/*
+		 * the callback needs to send a reply, once the reply has
+		 * been send, the connection should get freed.
+		 */
+		(*req->cb)(req, req->cb_arg);
+	}
+
+	return (0);
+}
+
+/* Called when evcon has experienced a (non-recoverable? -NM) error, as
+ * given in error. If it's an outgoing connection, reset the connection,
+ * retry any pending requests, and inform the user.  If it's incoming,
+ * delegates to evhttp_connection_incoming_fail(). */
+void
+evhttp_connection_fail(struct evhttp_connection *evcon,
+    enum evhttp_connection_error error)
+{
+	struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
+	void (*cb)(struct evhttp_request *, void *);
+	void *cb_arg;
+	EVUTIL_ASSERT(req != NULL);
+
+	bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
+
+	if (evcon->flags & EVHTTP_CON_INCOMING) {
+		/*
+		 * for incoming requests, there are two different
+		 * failure cases.  it's either a network level error
+		 * or an http layer error. for problems on the network
+		 * layer like timeouts we just drop the connections.
+		 * For HTTP problems, we might have to send back a
+		 * reply before the connection can be freed.
+		 */
+		if (evhttp_connection_incoming_fail(req, error) == -1)
+			evhttp_connection_free(evcon);
+		return;
+	}
+
+	/* when the request was canceled, the callback is not executed */
+	if (error != EVCON_HTTP_REQUEST_CANCEL) {
+		/* save the callback for later; the cb might free our object */
+		cb = req->cb;
+		cb_arg = req->cb_arg;
+	} else {
+		cb = NULL;
+		cb_arg = NULL;
+	}
+
+	/* do not fail all requests; the next request is going to get
+	 * send over a new connection.   when a user cancels a request,
+	 * all other pending requests should be processed as normal
+	 */
+	TAILQ_REMOVE(&evcon->requests, req, next);
+	evhttp_request_free(req);
+
+	/* reset the connection */
+	evhttp_connection_reset(evcon);
+
+	/* We are trying the next request that was queued on us */
+	if (TAILQ_FIRST(&evcon->requests) != NULL)
+		evhttp_connection_connect(evcon);
+
+	/* inform the user */
+	if (cb != NULL)
+		(*cb)(NULL, cb_arg);
+}
+
+/* Bufferevent callback: invoked when any data has been written from an
+ * http connection's bufferevent */
+static void
+evhttp_write_cb(struct bufferevent *bufev, void *arg)
+{
+	struct evhttp_connection *evcon = arg;
+
+	/* Activate our call back */
+	if (evcon->cb != NULL)
+		(*evcon->cb)(evcon, evcon->cb_arg);
+}
+
+/**
+ * Advance the connection state.
+ * - If this is an outgoing connection, we've just processed the response;
+ *   idle or close the connection.
+ * - If this is an incoming connection, we've just processed the request;
+ *   respond.
+ */
+static void
+evhttp_connection_done(struct evhttp_connection *evcon)
+{
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+	int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
+
+	if (con_outgoing) {
+		/* idle or close the connection */
+		int need_close;
+		TAILQ_REMOVE(&evcon->requests, req, next);
+		req->evcon = NULL;
+
+		evcon->state = EVCON_IDLE;
+
+		need_close =
+		    evhttp_is_connection_close(req->flags, req->input_headers)||
+		    evhttp_is_connection_close(req->flags, req->output_headers);
+
+		/* check if we got asked to close the connection */
+		if (need_close)
+			evhttp_connection_reset(evcon);
+
+		if (TAILQ_FIRST(&evcon->requests) != NULL) {
+			/*
+			 * We have more requests; reset the connection
+			 * and deal with the next request.
+			 */
+			if (!evhttp_connected(evcon))
+				evhttp_connection_connect(evcon);
+			else
+				evhttp_request_dispatch(evcon);
+		} else if (!need_close) {
+			/*
+			 * The connection is going to be persistent, but we
+			 * need to detect if the other side closes it.
+			 */
+			evhttp_connection_start_detectclose(evcon);
+		}
+	} else {
+		/*
+		 * incoming connection - we need to leave the request on the
+		 * connection so that we can reply to it.
+		 */
+		evcon->state = EVCON_WRITING;
+	}
+
+	/* notify the user of the request */
+	(*req->cb)(req, req->cb_arg);
+
+	/* if this was an outgoing request, we own and it's done. so free it.
+	 * unless the callback specifically requested to own the request.
+	 */
+	if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) {
+		evhttp_request_free(req);
+	}
+}
+
+/*
+ * Handles reading from a chunked request.
+ *   return ALL_DATA_READ:
+ *     all data has been read
+ *   return MORE_DATA_EXPECTED:
+ *     more data is expected
+ *   return DATA_CORRUPTED:
+ *     data is corrupted
+ *   return REQUEST_CANCELED:
+ *     request was canceled by the user calling evhttp_cancel_request
+ *   return DATA_TOO_LONG:
+ *     ran over the maximum limit
+ */
+
+static enum message_read_status
+evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
+{
+	if (req == NULL || buf == NULL) {
+	    return DATA_CORRUPTED;
+	}
+
+	while (1) {
+		size_t buflen;
+
+		if ((buflen = evbuffer_get_length(buf)) == 0) {
+			break;
+		}
+
+		/* evbuffer_get_length returns size_t, but len variable is ssize_t,
+		 * check for overflow conditions */
+		if (buflen > EV_SSIZE_MAX) {
+			return DATA_CORRUPTED;
+		}
+
+		if (req->ntoread < 0) {
+			/* Read chunk size */
+			ev_int64_t ntoread;
+			char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
+			char *endp;
+			int error;
+			if (p == NULL)
+				break;
+			/* the last chunk is on a new line? */
+			if (strlen(p) == 0) {
+				mm_free(p);
+				continue;
+			}
+			ntoread = evutil_strtoll(p, &endp, 16);
+			error = (*p == '\0' ||
+			    (*endp != '\0' && *endp != ' ') ||
+			    ntoread < 0);
+			mm_free(p);
+			if (error) {
+				/* could not get chunk size */
+				return (DATA_CORRUPTED);
+			}
+
+			/* ntoread is signed int64, body_size is unsigned size_t, check for under/overflow conditions */
+			if ((ev_uint64_t)ntoread > EV_SIZE_MAX - req->body_size) {
+			    return DATA_CORRUPTED;
+			}
+
+			if (req->body_size + (size_t)ntoread > req->evcon->max_body_size) {
+				/* failed body length test */
+				event_debug(("Request body is too long"));
+				return (DATA_TOO_LONG);
+			}
+
+			req->body_size += (size_t)ntoread;
+			req->ntoread = ntoread;
+			if (req->ntoread == 0) {
+				/* Last chunk */
+				return (ALL_DATA_READ);
+			}
+			continue;
+		}
+
+		/* req->ntoread is signed int64, len is ssize_t, based on arch,
+		 * ssize_t could only be 32b, check for these conditions */
+		if (req->ntoread > EV_SSIZE_MAX) {
+			return DATA_CORRUPTED;
+		}
+
+		/* don't have enough to complete a chunk; wait for more */
+		if (req->ntoread > 0 && buflen < (ev_uint64_t)req->ntoread)
+			return (MORE_DATA_EXPECTED);
+
+		/* Completed chunk */
+		evbuffer_remove_buffer(buf, req->input_buffer, (size_t)req->ntoread);
+		req->ntoread = -1;
+		if (req->chunk_cb != NULL) {
+			req->flags |= EVHTTP_REQ_DEFER_FREE;
+			(*req->chunk_cb)(req, req->cb_arg);
+			evbuffer_drain(req->input_buffer,
+			    evbuffer_get_length(req->input_buffer));
+			req->flags &= ~EVHTTP_REQ_DEFER_FREE;
+			if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
+				return (REQUEST_CANCELED);
+			}
+		}
+	}
+
+	return (MORE_DATA_EXPECTED);
+}
+
+static void
+evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+	struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
+
+	switch (evhttp_parse_headers(req, buf)) {
+	case DATA_CORRUPTED:
+	case DATA_TOO_LONG:
+		evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+		break;
+	case ALL_DATA_READ:
+		bufferevent_disable(evcon->bufev, EV_READ);
+		evhttp_connection_done(evcon);
+		break;
+	case MORE_DATA_EXPECTED:
+	case REQUEST_CANCELED: /* ??? */
+	default:
+		bufferevent_enable(evcon->bufev, EV_READ);
+		break;
+	}
+}
+
+static void
+evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+	struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
+
+	if (req->chunked) {
+		switch (evhttp_handle_chunked_read(req, buf)) {
+		case ALL_DATA_READ:
+			/* finished last chunk */
+			evcon->state = EVCON_READING_TRAILER;
+			evhttp_read_trailer(evcon, req);
+			return;
+		case DATA_CORRUPTED:
+		case DATA_TOO_LONG:/*separate error for this? XXX */
+			/* corrupted data */
+			evhttp_connection_fail(evcon,
+			    EVCON_HTTP_INVALID_HEADER);
+			return;
+		case REQUEST_CANCELED:
+			/* request canceled */
+			evhttp_request_free(req);
+			return;
+		case MORE_DATA_EXPECTED:
+		default:
+			break;
+		}
+	} else if (req->ntoread < 0) {
+		/* Read until connection close. */
+		if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) {
+			evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+			return;
+		}
+
+		req->body_size += evbuffer_get_length(buf);
+		evbuffer_add_buffer(req->input_buffer, buf);
+	} else if (req->chunk_cb != NULL || evbuffer_get_length(buf) >= (size_t)req->ntoread) {
+		/* XXX: the above get_length comparison has to be fixed for overflow conditions! */
+		/* We've postponed moving the data until now, but we're
+		 * about to use it. */
+		size_t n = evbuffer_get_length(buf);
+
+		if (n > (size_t) req->ntoread)
+			n = (size_t) req->ntoread;
+		req->ntoread -= n;
+		req->body_size += n;
+		evbuffer_remove_buffer(buf, req->input_buffer, n);
+	}
+
+	if (req->body_size > req->evcon->max_body_size ||
+	    (!req->chunked && req->ntoread >= 0 &&
+		(size_t)req->ntoread > req->evcon->max_body_size)) {
+		/* XXX: The above casted comparison must checked for overflow */
+		/* failed body length test */
+		event_debug(("Request body is too long"));
+		evhttp_connection_fail(evcon,
+				       EVCON_HTTP_INVALID_HEADER);
+		return;
+	}
+
+	if (evbuffer_get_length(req->input_buffer) > 0 && req->chunk_cb != NULL) {
+		req->flags |= EVHTTP_REQ_DEFER_FREE;
+		(*req->chunk_cb)(req, req->cb_arg);
+		req->flags &= ~EVHTTP_REQ_DEFER_FREE;
+		evbuffer_drain(req->input_buffer,
+		    evbuffer_get_length(req->input_buffer));
+		if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
+			evhttp_request_free(req);
+			return;
+		}
+	}
+
+	if (req->ntoread == 0) {
+		bufferevent_disable(evcon->bufev, EV_READ);
+		/* Completed content length */
+		evhttp_connection_done(evcon);
+		return;
+	}
+
+	/* Read more! */
+	bufferevent_enable(evcon->bufev, EV_READ);
+}
+
+#define get_deferred_queue(evcon)		\
+	(event_base_get_deferred_cb_queue((evcon)->base))
+
+/*
+ * Gets called when more data becomes available
+ */
+
+static void
+evhttp_read_cb(struct bufferevent *bufev, void *arg)
+{
+	struct evhttp_connection *evcon = arg;
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+	/* Cancel if it's pending. */
+	event_deferred_cb_cancel(get_deferred_queue(evcon),
+	    &evcon->read_more_deferred_cb);
+
+	switch (evcon->state) {
+	case EVCON_READING_FIRSTLINE:
+		evhttp_read_firstline(evcon, req);
+		/* note the request may have been freed in
+		 * evhttp_read_body */
+		break;
+	case EVCON_READING_HEADERS:
+		evhttp_read_header(evcon, req);
+		/* note the request may have been freed in
+		 * evhttp_read_body */
+		break;
+	case EVCON_READING_BODY:
+		evhttp_read_body(evcon, req);
+		/* note the request may have been freed in
+		 * evhttp_read_body */
+		break;
+	case EVCON_READING_TRAILER:
+		evhttp_read_trailer(evcon, req);
+		break;
+	case EVCON_IDLE:
+		{
+#ifdef USE_DEBUG
+			struct evbuffer *input;
+			size_t total_len;
+
+			input = bufferevent_get_input(evcon->bufev);
+			total_len = evbuffer_get_length(input);
+			event_debug(("%s: read "EV_SIZE_FMT
+				" bytes in EVCON_IDLE state,"
+				" resetting connection",
+				__func__, EV_SIZE_ARG(total_len)));
+#endif
+
+			evhttp_connection_reset(evcon);
+		}
+		break;
+	case EVCON_DISCONNECTED:
+	case EVCON_CONNECTING:
+	case EVCON_WRITING:
+	default:
+		event_errx(1, "%s: illegal connection state %d",
+			   __func__, evcon->state);
+	}
+}
+
+static void
+evhttp_deferred_read_cb(struct deferred_cb *cb, void *data)
+{
+	struct evhttp_connection *evcon = data;
+	evhttp_read_cb(evcon->bufev, evcon);
+}
+
+static void
+evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
+{
+	/* This is after writing the request to the server */
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+	EVUTIL_ASSERT(req != NULL);
+
+	EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
+
+	/* We are done writing our header and are now expecting the response */
+	req->kind = EVHTTP_RESPONSE;
+
+	evhttp_start_read(evcon);
+}
+
+/*
+ * Clean up a connection object
+ */
+
+void
+evhttp_connection_free(struct evhttp_connection *evcon)
+{
+	struct evhttp_request *req;
+
+	/* 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);
+	}
+
+	/* remove all requests that might be queued on this
+	 * connection.  for server connections, this should be empty.
+	 * because it gets dequeued either in evhttp_connection_done or
+	 * evhttp_connection_fail.
+	 */
+	while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
+		TAILQ_REMOVE(&evcon->requests, req, next);
+		evhttp_request_free(req);
+	}
+
+	if (evcon->http_server != NULL) {
+		struct evhttp *http = evcon->http_server;
+		TAILQ_REMOVE(&http->connections, evcon, next);
+	}
+
+	if (event_initialized(&evcon->retry_ev)) {
+		event_del(&evcon->retry_ev);
+		event_debug_unassign(&evcon->retry_ev);
+	}
+
+	if (evcon->bufev != NULL)
+		bufferevent_free(evcon->bufev);
+
+	event_deferred_cb_cancel(get_deferred_queue(evcon),
+	    &evcon->read_more_deferred_cb);
+
+	if (evcon->fd != -1) {
+		shutdown(evcon->fd, EVUTIL_SHUT_WR);
+		evutil_closesocket(evcon->fd);
+	}
+
+	if (evcon->bind_address != NULL)
+		mm_free(evcon->bind_address);
+
+	if (evcon->address != NULL)
+		mm_free(evcon->address);
+
+	mm_free(evcon);
+}
+
+void
+evhttp_connection_set_local_address(struct evhttp_connection *evcon,
+    const char *address)
+{
+	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
+	if (evcon->bind_address)
+		mm_free(evcon->bind_address);
+	if ((evcon->bind_address = mm_strdup(address)) == NULL)
+		event_warn("%s: strdup", __func__);
+}
+
+void
+evhttp_connection_set_local_port(struct evhttp_connection *evcon,
+    ev_uint16_t port)
+{
+	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
+	evcon->bind_port = port;
+}
+
+static void
+evhttp_request_dispatch(struct evhttp_connection* evcon)
+{
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+	/* this should not usually happy but it's possible */
+	if (req == NULL)
+		return;
+
+	/* delete possible close detection events */
+	evhttp_connection_stop_detectclose(evcon);
+
+	/* we assume that the connection is connected already */
+	EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
+
+	evcon->state = EVCON_WRITING;
+
+	/* Create the header from the store arguments */
+	evhttp_make_header(evcon, req);
+
+	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)
+{
+	struct evbuffer *tmp;
+
+	/* 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
+	   connecting.  But for Libevent 2.0, this seems like a minimal change
+	   least likely to disrupt the rest of the bufferevent and http code.
+
+	   Why is this here?  If the fd is set in the bufferevent, and the
+	   bufferevent is connecting, then you can't actually stop the
+	   bufferevent from trying to connect with bufferevent_disable().  The
+	   connect will never trigger, since we close the fd, but the timeout
+	   might.  That caused an assertion failure in evhttp_connection_fail.
+	*/
+	bufferevent_disable_hard(evcon->bufev, EV_READ|EV_WRITE);
+
+	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;
+	}
+
+	/* we need to clean up any buffered data */
+	tmp = bufferevent_get_output(evcon->bufev);
+	evbuffer_drain(tmp, evbuffer_get_length(tmp));
+	tmp = bufferevent_get_input(evcon->bufev);
+	evbuffer_drain(tmp, evbuffer_get_length(tmp));
+
+	evcon->state = EVCON_DISCONNECTED;
+}
+
+static void
+evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
+{
+	evcon->flags |= EVHTTP_CON_CLOSEDETECT;
+
+	bufferevent_enable(evcon->bufev, EV_READ);
+}
+
+static void
+evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
+{
+	evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+
+	bufferevent_disable(evcon->bufev, EV_READ);
+}
+
+static void
+evhttp_connection_retry(evutil_socket_t fd, short what, void *arg)
+{
+	struct evhttp_connection *evcon = arg;
+
+	evcon->state = EVCON_DISCONNECTED;
+	evhttp_connection_connect(evcon);
+}
+
+static void
+evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
+{
+	struct evcon_requestq requests;
+
+	if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
+		evtimer_assign(&evcon->retry_ev, evcon->base, evhttp_connection_retry, evcon);
+		/* XXXX handle failure from evhttp_add_event */
+		evhttp_add_event(&evcon->retry_ev,
+		    MIN(3600, 2 << evcon->retry_cnt),
+		    HTTP_CONNECT_TIMEOUT);
+		evcon->retry_cnt++;
+		return;
+	}
+	evhttp_connection_reset(evcon);
+
+	/*
+	 * User callback can do evhttp_make_request() on the same
+	 * evcon so new request will be added to evcon->requests.  To
+	 * avoid freeing it prematurely we iterate over the copy of
+	 * the queue.
+	 */
+	TAILQ_INIT(&requests);
+	while (TAILQ_FIRST(&evcon->requests) != NULL) {
+		struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
+		TAILQ_REMOVE(&evcon->requests, request, next);
+		TAILQ_INSERT_TAIL(&requests, request, next);
+	}
+
+	/* for now, we just signal all requests by executing their callbacks */
+	while (TAILQ_FIRST(&requests) != NULL) {
+		struct evhttp_request *request = TAILQ_FIRST(&requests);
+		TAILQ_REMOVE(&requests, request, next);
+		request->evcon = NULL;
+
+		/* we might want to set an error here */
+		request->cb(request, request->cb_arg);
+		evhttp_request_free(request);
+	}
+}
+
+static void
+evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
+{
+	struct evhttp_connection *evcon = arg;
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+
+	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)));
+			evhttp_connection_cb_cleanup(evcon);
+			return;
+		}
+		break;
+
+	case EVCON_READING_BODY:
+		if (!req->chunked && req->ntoread < 0
+		    && what == (BEV_EVENT_READING|BEV_EVENT_EOF)) {
+			/* EOF on read can be benign */
+			evhttp_connection_done(evcon);
+			return;
+		}
+		break;
+
+	case EVCON_DISCONNECTED:
+	case EVCON_IDLE:
+	case EVCON_READING_FIRSTLINE:
+	case EVCON_READING_HEADERS:
+	case EVCON_READING_TRAILER:
+	case EVCON_WRITING:
+	default:
+		break;
+	}
+
+	/* when we are in close detect mode, a read error means that
+	 * the other side closed their connection.
+	 */
+	if (evcon->flags & EVHTTP_CON_CLOSEDETECT) {
+		evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+		EVUTIL_ASSERT(evcon->http_server == NULL);
+		/* For connections from the client, we just
+		 * reset the connection so that it becomes
+		 * disconnected.
+		 */
+		EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
+		evhttp_connection_reset(evcon);
+		return;
+	}
+
+	if (what & BEV_EVENT_TIMEOUT) {
+		evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+	} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
+		evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+	} else {
+		evhttp_connection_fail(evcon, EVCON_HTTP_BUFFER_ERROR);
+	}
+}
+
+/*
+ * Event callback for asynchronous connection attempt.
+ */
+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 (!(what & BEV_EVENT_CONNECTED)) {
+		/* some operating systems return ECONNREFUSED immediately
+		 * when connecting to a local address.  the cleanup is going
+		 * to reschedule this function call.
+		 */
+#ifndef WIN32
+		if (errno == ECONNREFUSED)
+			goto cleanup;
+#endif
+		evhttp_error_cb(bufev, what, arg);
+		return;
+	}
+
+	/* 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)));
+
+	/* Reset the retry count as we were successful in connecting */
+	evcon->retry_cnt = 0;
+	evcon->state = EVCON_IDLE;
+
+	/* reset the bufferevent cbs */
+	bufferevent_setcb(evcon->bufev,
+	    evhttp_read_cb,
+	    evhttp_write_cb,
+	    evhttp_error_cb,
+	    evcon);
+
+	if (evcon->timeout == -1)
+		bufferevent_settimeout(evcon->bufev,
+		    HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
+	else {
+		struct timeval tv;
+		tv.tv_sec = evcon->timeout;
+		tv.tv_usec = 0;
+		bufferevent_set_timeouts(evcon->bufev, &tv, &tv);
+	}
+
+	/* try to start requests that have queued up on this connection */
+	evhttp_request_dispatch(evcon);
+	return;
+
+ cleanup:
+	evhttp_connection_cb_cleanup(evcon);
+}
+
+/*
+ * Check if we got a valid response code.
+ */
+
+static int
+evhttp_valid_response_code(int code)
+{
+	if (code == 0)
+		return (0);
+
+	return (1);
+}
+
+static int
+evhttp_parse_http_version(const char *version, struct evhttp_request *req)
+{
+	int major, minor;
+	char ch;
+	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));
+		return (-1);
+	}
+	req->major = major;
+	req->minor = minor;
+	return (0);
+}
+
+/* Parses the status line of a web server */
+
+static int
+evhttp_parse_response_line(struct evhttp_request *req, char *line)
+{
+	char *protocol;
+	char *number;
+	const char *readable = "";
+
+	protocol = strsep(&line, " ");
+	if (line == NULL)
+		return (-1);
+	number = strsep(&line, " ");
+	if (line != NULL)
+		readable = line;
+
+	if (evhttp_parse_http_version(protocol, req) < 0)
+		return (-1);
+
+	req->response_code = atoi(number);
+	if (!evhttp_valid_response_code(req->response_code)) {
+		event_debug(("%s: bad response code \"%s\"",
+			__func__, number));
+		return (-1);
+	}
+
+	if ((req->response_code_line = mm_strdup(readable)) == NULL) {
+		event_warn("%s: strdup", __func__);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/* Parse the first line of a HTTP request */
+
+static int
+evhttp_parse_request_line(struct evhttp_request *req, char *line)
+{
+	char *method;
+	char *uri;
+	char *version;
+	const char *hostname;
+	const char *scheme;
+
+	/* Parse the request line */
+	method = strsep(&line, " ");
+	if (line == NULL)
+		return (-1);
+	uri = strsep(&line, " ");
+	if (line == NULL)
+		return (-1);
+	version = strsep(&line, " ");
+	if (line != NULL)
+		return (-1);
+
+	/* First line */
+	if (strcmp(method, "GET") == 0) {
+		req->type = EVHTTP_REQ_GET;
+	} else if (strcmp(method, "POST") == 0) {
+		req->type = EVHTTP_REQ_POST;
+	} else if (strcmp(method, "HEAD") == 0) {
+		req->type = EVHTTP_REQ_HEAD;
+	} else if (strcmp(method, "PUT") == 0) {
+		req->type = EVHTTP_REQ_PUT;
+	} else if (strcmp(method, "DELETE") == 0) {
+		req->type = EVHTTP_REQ_DELETE;
+	} else if (strcmp(method, "OPTIONS") == 0) {
+		req->type = EVHTTP_REQ_OPTIONS;
+	} else if (strcmp(method, "TRACE") == 0) {
+		req->type = EVHTTP_REQ_TRACE;
+	} else if (strcmp(method, "PATCH") == 0) {
+		req->type = EVHTTP_REQ_PATCH;
+	} else {
+		req->type = _EVHTTP_REQ_UNKNOWN;
+		event_debug(("%s: bad method %s on request %p from %s",
+			__func__, method, req, req->remote_host));
+		/* No error yet; we'll give a better error later when
+		 * we see that req->type is unsupported. */
+	}
+
+	if (evhttp_parse_http_version(version, req) < 0)
+		return (-1);
+
+	if ((req->uri = mm_strdup(uri)) == NULL) {
+		event_debug(("%s: mm_strdup", __func__));
+		return (-1);
+	}
+
+	if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
+		    EVHTTP_URI_NONCONFORMANT)) == NULL) {
+		return -1;
+	}
+
+	/* If we have an absolute-URI, check to see if it is an http request
+	   for a known vhost or server alias. If we don't know about this
+	   host, we consider it a proxy request. */
+	scheme = evhttp_uri_get_scheme(req->uri_elems);
+	hostname = evhttp_uri_get_host(req->uri_elems);
+	if (scheme && (!evutil_ascii_strcasecmp(scheme, "http") ||
+		       !evutil_ascii_strcasecmp(scheme, "https")) &&
+	    hostname &&
+	    !evhttp_find_vhost(req->evcon->http_server, NULL, hostname))
+		req->flags |= EVHTTP_PROXY_REQUEST;
+
+	return (0);
+}
+
+const char *
+evhttp_find_header(const struct evkeyvalq *headers, const char *key)
+{
+	struct evkeyval *header;
+
+	TAILQ_FOREACH(header, headers, next) {
+		if (evutil_ascii_strcasecmp(header->key, key) == 0)
+			return (header->value);
+	}
+
+	return (NULL);
+}
+
+void
+evhttp_clear_headers(struct evkeyvalq *headers)
+{
+	struct evkeyval *header;
+
+	for (header = TAILQ_FIRST(headers);
+	    header != NULL;
+	    header = TAILQ_FIRST(headers)) {
+		TAILQ_REMOVE(headers, header, next);
+		mm_free(header->key);
+		mm_free(header->value);
+		mm_free(header);
+	}
+}
+
+/*
+ * Returns 0,  if the header was successfully removed.
+ * Returns -1, if the header could not be found.
+ */
+
+int
+evhttp_remove_header(struct evkeyvalq *headers, const char *key)
+{
+	struct evkeyval *header;
+
+	TAILQ_FOREACH(header, headers, next) {
+		if (evutil_ascii_strcasecmp(header->key, key) == 0)
+			break;
+	}
+
+	if (header == NULL)
+		return (-1);
+
+	/* Free and remove the header that we found */
+	TAILQ_REMOVE(headers, header, next);
+	mm_free(header->key);
+	mm_free(header->value);
+	mm_free(header);
+
+	return (0);
+}
+
+static int
+evhttp_header_is_valid_value(const char *value)
+{
+	const char *p = value;
+
+	while ((p = strpbrk(p, "\r\n")) != NULL) {
+		/* we really expect only one new line */
+		p += strspn(p, "\r\n");
+		/* we expect a space or tab for continuation */
+		if (*p != ' ' && *p != '\t')
+			return (0);
+	}
+	return (1);
+}
+
+int
+evhttp_add_header(struct evkeyvalq *headers,
+    const char *key, const char *value)
+{
+	event_debug(("%s: key: %s val: %s\n", __func__, key, value));
+
+	if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
+		/* drop illegal headers */
+		event_debug(("%s: dropping illegal header key\n", __func__));
+		return (-1);
+	}
+
+	if (!evhttp_header_is_valid_value(value)) {
+		event_debug(("%s: dropping illegal header value\n", __func__));
+		return (-1);
+	}
+
+	return (evhttp_add_header_internal(headers, key, value));
+}
+
+static int
+evhttp_add_header_internal(struct evkeyvalq *headers,
+    const char *key, const char *value)
+{
+	struct evkeyval *header = mm_calloc(1, sizeof(struct evkeyval));
+	if (header == NULL) {
+		event_warn("%s: calloc", __func__);
+		return (-1);
+	}
+	if ((header->key = mm_strdup(key)) == NULL) {
+		mm_free(header);
+		event_warn("%s: strdup", __func__);
+		return (-1);
+	}
+	if ((header->value = mm_strdup(value)) == NULL) {
+		mm_free(header->key);
+		mm_free(header);
+		event_warn("%s: strdup", __func__);
+		return (-1);
+	}
+
+	TAILQ_INSERT_TAIL(headers, header, next);
+
+	return (0);
+}
+
+/*
+ * Parses header lines from a request or a response into the specified
+ * request object given an event buffer.
+ *
+ * Returns
+ *   DATA_CORRUPTED      on error
+ *   MORE_DATA_EXPECTED  when we need to read more headers
+ *   ALL_DATA_READ       when all headers have been read.
+ */
+
+enum message_read_status
+evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer)
+{
+	char *line;
+	enum message_read_status status = ALL_DATA_READ;
+
+	size_t line_length;
+	/* XXX try */
+	line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
+	if (line == NULL) {
+		if (req->evcon != NULL &&
+		    evbuffer_get_length(buffer) > req->evcon->max_headers_size)
+			return (DATA_TOO_LONG);
+		else
+			return (MORE_DATA_EXPECTED);
+	}
+
+	if (req->evcon != NULL &&
+	    line_length > req->evcon->max_headers_size) {
+		mm_free(line);
+		return (DATA_TOO_LONG);
+	}
+
+	req->headers_size = line_length;
+
+	switch (req->kind) {
+	case EVHTTP_REQUEST:
+		if (evhttp_parse_request_line(req, line) == -1)
+			status = DATA_CORRUPTED;
+		break;
+	case EVHTTP_RESPONSE:
+		if (evhttp_parse_response_line(req, line) == -1)
+			status = DATA_CORRUPTED;
+		break;
+	default:
+		status = DATA_CORRUPTED;
+	}
+
+	mm_free(line);
+	return (status);
+}
+
+static int
+evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line)
+{
+	struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq);
+	char *newval;
+	size_t old_len, line_len;
+
+	if (header == NULL)
+		return (-1);
+
+	old_len = strlen(header->value);
+	line_len = strlen(line);
+
+	newval = mm_realloc(header->value, old_len + line_len + 1);
+	if (newval == NULL)
+		return (-1);
+
+	memcpy(newval + old_len, line, line_len + 1);
+	header->value = newval;
+
+	return (0);
+}
+
+enum message_read_status
+evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
+{
+	enum message_read_status errcode = DATA_CORRUPTED;
+	char *line;
+	enum message_read_status status = MORE_DATA_EXPECTED;
+
+	struct evkeyvalq* headers = req->input_headers;
+	size_t line_length;
+	while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
+	       != NULL) {
+		char *skey, *svalue;
+
+		req->headers_size += line_length;
+
+		if (req->evcon != NULL &&
+		    req->headers_size > req->evcon->max_headers_size) {
+			errcode = DATA_TOO_LONG;
+			goto error;
+		}
+
+		if (*line == '\0') { /* Last header - Done */
+			status = ALL_DATA_READ;
+			mm_free(line);
+			break;
+		}
+
+		/* Check if this is a continuation line */
+		if (*line == ' ' || *line == '\t') {
+			if (evhttp_append_to_last_header(headers, line) == -1)
+				goto error;
+			mm_free(line);
+			continue;
+		}
+
+		/* Processing of header lines */
+		svalue = line;
+		skey = strsep(&svalue, ":");
+		if (svalue == NULL)
+			goto error;
+
+		svalue += strspn(svalue, " ");
+
+		if (evhttp_add_header(headers, skey, svalue) == -1)
+			goto error;
+
+		mm_free(line);
+	}
+
+	if (status == MORE_DATA_EXPECTED) {
+		if (req->evcon != NULL &&
+		req->headers_size + evbuffer_get_length(buffer) > req->evcon->max_headers_size)
+			return (DATA_TOO_LONG);
+	}
+
+	return (status);
+
+ error:
+	mm_free(line);
+	return (errcode);
+}
+
+static int
+evhttp_get_body_length(struct evhttp_request *req)
+{
+	struct evkeyvalq *headers = req->input_headers;
+	const char *content_length;
+	const char *connection;
+
+	content_length = evhttp_find_header(headers, "Content-Length");
+	connection = evhttp_find_header(headers, "Connection");
+
+	if (content_length == NULL && connection == NULL)
+		req->ntoread = -1;
+	else if (content_length == NULL &&
+	    evutil_ascii_strcasecmp(connection, "Close") != 0) {
+		/* Bad combination, we don't know when it will end */
+		event_warnx("%s: we got no content length, but the "
+		    "server wants to keep the connection open: %s.",
+		    __func__, connection);
+		return (-1);
+	} else if (content_length == NULL) {
+		req->ntoread = -1;
+	} else {
+		char *endp;
+		ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10);
+		if (*content_length == '\0' || *endp != '\0' || ntoread < 0) {
+			event_debug(("%s: illegal content length: %s",
+				__func__, content_length));
+			return (-1);
+		}
+		req->ntoread = ntoread;
+	}
+
+	event_debug(("%s: bytes to read: "EV_I64_FMT" (in buffer "EV_SIZE_FMT")\n",
+		__func__, EV_I64_ARG(req->ntoread),
+		EV_SIZE_ARG(evbuffer_get_length(bufferevent_get_input(req->evcon->bufev)))));
+
+	return (0);
+}
+
+static int
+evhttp_method_may_have_body(enum evhttp_cmd_type type)
+{
+	switch (type) {
+	case EVHTTP_REQ_POST:
+	case EVHTTP_REQ_PUT:
+	case EVHTTP_REQ_PATCH:
+		return 1;
+	case EVHTTP_REQ_TRACE:
+		return 0;
+	/* XXX May any of the below methods have a body? */
+	case EVHTTP_REQ_GET:
+	case EVHTTP_REQ_HEAD:
+	case EVHTTP_REQ_DELETE:
+	case EVHTTP_REQ_OPTIONS:
+	case EVHTTP_REQ_CONNECT:
+		return 0;
+	default:
+		return 0;
+	}
+}
+
+static void
+evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+	const char *xfer_enc;
+
+	/* If this is a request without a body, then we are done */
+	if (req->kind == EVHTTP_REQUEST &&
+	    !evhttp_method_may_have_body(req->type)) {
+		evhttp_connection_done(evcon);
+		return;
+	}
+	evcon->state = EVCON_READING_BODY;
+	xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding");
+	if (xfer_enc != NULL && evutil_ascii_strcasecmp(xfer_enc, "chunked") == 0) {
+		req->chunked = 1;
+		req->ntoread = -1;
+	} else {
+		if (evhttp_get_body_length(req) == -1) {
+			evhttp_connection_fail(evcon,
+			    EVCON_HTTP_INVALID_HEADER);
+			return;
+		}
+		if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
+			/* An incoming request with no content-length and no
+			 * transfer-encoding has no body. */
+			evhttp_connection_done(evcon);
+			return;
+		}
+	}
+
+	/* Should we send a 100 Continue status line? */
+	if (req->kind == EVHTTP_REQUEST && REQ_VERSION_ATLEAST(req, 1, 1)) {
+		const char *expect;
+
+		expect = evhttp_find_header(req->input_headers, "Expect");
+		if (expect) {
+			if (!evutil_ascii_strcasecmp(expect, "100-continue")) {
+				/* XXX It would be nice to do some sanity
+				   checking here. Does the resource exist?
+				   Should the resource accept post requests? If
+				   no, we should respond with an error. For
+				   now, just optimistically tell the client to
+				   send their message body. */
+				if (req->ntoread > 0) {
+					/* ntoread is ev_int64_t, max_body_size is ev_uint64_t */ 
+					if ((req->evcon->max_body_size <= EV_INT64_MAX) && (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
+						evhttp_send_error(req, HTTP_ENTITYTOOLARGE, NULL);
+						return;
+					}
+				}
+				if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
+					evhttp_send_continue(evcon, req);
+			} else {
+				evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
+					NULL);
+				return;
+			}
+		}
+	}
+
+	evhttp_read_body(evcon, req);
+	/* note the request may have been freed in evhttp_read_body */
+}
+
+static void
+evhttp_read_firstline(struct evhttp_connection *evcon,
+		      struct evhttp_request *req)
+{
+	enum message_read_status res;
+
+	res = evhttp_parse_firstline(req, bufferevent_get_input(evcon->bufev));
+	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)));
+		evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+		return;
+	} else if (res == MORE_DATA_EXPECTED) {
+		/* Need more header lines */
+		return;
+	}
+
+	evcon->state = EVCON_READING_HEADERS;
+	evhttp_read_header(evcon, req);
+}
+
+static void
+evhttp_read_header(struct evhttp_connection *evcon,
+		   struct evhttp_request *req)
+{
+	enum message_read_status res;
+	evutil_socket_t fd = evcon->fd;
+
+	res = evhttp_parse_headers(req, bufferevent_get_input(evcon->bufev));
+	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(fd)));
+		evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+		return;
+	} else if (res == MORE_DATA_EXPECTED) {
+		/* Need more header lines */
+		return;
+	}
+
+	/* Disable reading for now */
+	bufferevent_disable(evcon->bufev, EV_READ);
+
+	/* Done reading headers, do the real work */
+	switch (req->kind) {
+	case EVHTTP_REQUEST:
+		event_debug(("%s: checking for post data on "EV_SOCK_FMT"\n",
+			__func__, EV_SOCK_ARG(fd)));
+		evhttp_get_body(evcon, req);
+		/* note the request may have been freed in evhttp_get_body */
+		break;
+
+	case EVHTTP_RESPONSE:
+		/* Start over if we got a 100 Continue response. */
+		if (req->response_code == 100) {
+			evhttp_start_read(evcon);
+			return;
+		}
+		if (!evhttp_response_needs_body(req)) {
+			event_debug(("%s: skipping body for code %d\n",
+					__func__, req->response_code));
+			evhttp_connection_done(evcon);
+		} else {
+			event_debug(("%s: start of read body for %s on "
+				EV_SOCK_FMT"\n",
+				__func__, req->remote_host, EV_SOCK_ARG(fd)));
+			evhttp_get_body(evcon, req);
+			/* note the request may have been freed in
+			 * evhttp_get_body */
+		}
+		break;
+
+	default:
+		event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
+		    EV_SOCK_ARG(fd));
+		evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+		break;
+	}
+	/* request may have been freed above */
+}
+
+/*
+ * Creates a TCP connection to the specified port and executes a callback
+ * when finished.  Failure or success is indicate by the passed connection
+ * object.
+ *
+ * Although this interface accepts a hostname, it is intended to take
+ * only numeric hostnames so that non-blocking DNS resolution can
+ * happen elsewhere.
+ */
+
+struct evhttp_connection *
+evhttp_connection_new(const char *address, unsigned short port)
+{
+	return (evhttp_connection_base_new(NULL, NULL, address, port));
+}
+
+struct evhttp_connection *
+evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
+    const char *address, unsigned short port)
+{
+	struct evhttp_connection *evcon = NULL;
+
+	event_debug(("Attempting connection to %s:%d\n", address, port));
+
+	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;
+
+	evcon->timeout = -1;
+	evcon->retry_cnt = evcon->retry_max = 0;
+
+	if ((evcon->address = mm_strdup(address)) == NULL) {
+		event_warn("%s: strdup failed", __func__);
+		goto error;
+	}
+
+	if ((evcon->bufev = bufferevent_new(-1,
+		    evhttp_read_cb,
+		    evhttp_write_cb,
+		    evhttp_error_cb, evcon)) == NULL) {
+		event_warn("%s: bufferevent_new failed", __func__);
+		goto error;
+	}
+
+	evcon->state = EVCON_DISCONNECTED;
+	TAILQ_INIT(&evcon->requests);
+
+	if (base != NULL) {
+		evcon->base = base;
+		bufferevent_base_set(base, evcon->bufev);
+	}
+
+
+	event_deferred_cb_init(&evcon->read_more_deferred_cb,
+	    evhttp_deferred_read_cb, evcon);
+
+	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;
+}
+
+void
+evhttp_connection_set_base(struct evhttp_connection *evcon,
+    struct event_base *base)
+{
+	EVUTIL_ASSERT(evcon->base == NULL);
+	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
+	evcon->base = base;
+	bufferevent_base_set(base, evcon->bufev);
+}
+
+void
+evhttp_connection_set_timeout(struct evhttp_connection *evcon,
+    int timeout_in_secs)
+{
+	evcon->timeout = timeout_in_secs;
+
+	if (evcon->timeout == -1)
+		bufferevent_settimeout(evcon->bufev,
+		    HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
+	else
+		bufferevent_settimeout(evcon->bufev,
+		    evcon->timeout, evcon->timeout);
+}
+
+void
+evhttp_connection_set_retries(struct evhttp_connection *evcon,
+    int retry_max)
+{
+	evcon->retry_max = retry_max;
+}
+
+void
+evhttp_connection_set_closecb(struct evhttp_connection *evcon,
+    void (*cb)(struct evhttp_connection *, void *), void *cbarg)
+{
+	evcon->closecb = cb;
+	evcon->closecb_arg = cbarg;
+}
+
+void
+evhttp_connection_get_peer(struct evhttp_connection *evcon,
+    char **address, ev_uint16_t *port)
+{
+	*address = evcon->address;
+	*port = evcon->port;
+}
+
+int
+evhttp_connection_connect(struct evhttp_connection *evcon)
+{
+	int old_state = evcon->state;
+
+	if (evcon->state == EVCON_CONNECTING)
+		return (0);
+
+	evhttp_connection_reset(evcon);
+
+	EVUTIL_ASSERT(!(evcon->flags & EVHTTP_CON_INCOMING));
+	evcon->flags |= EVHTTP_CON_OUTGOING;
+
+	evcon->fd = bind_socket(
+		evcon->bind_address, evcon->bind_port, 0 /*reuse*/);
+	if (evcon->fd == -1) {
+		event_debug(("%s: failed to bind to \"%s\"",
+			__func__, evcon->bind_address));
+		return (-1);
+	}
+
+	/* Set up a callback for successful connection setup */
+	bufferevent_setfd(evcon->bufev, evcon->fd);
+	bufferevent_setcb(evcon->bufev,
+	    NULL /* evhttp_read_cb */,
+	    NULL /* evhttp_write_cb */,
+	    evhttp_connection_cb,
+	    evcon);
+	bufferevent_settimeout(evcon->bufev, 0,
+	    evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
+	/* make sure that we get a write callback */
+	bufferevent_enable(evcon->bufev, EV_WRITE);
+
+	evcon->state = EVCON_CONNECTING;
+
+	if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base,
+		AF_UNSPEC, evcon->address, evcon->port) < 0) {
+		evcon->state = old_state;
+		event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
+		    __func__, evcon->address);
+		/* some operating systems return ECONNREFUSED immediately
+		 * when connecting to a local address.  the cleanup is going
+		 * to reschedule this function call.
+		 */
+		evhttp_connection_cb_cleanup(evcon);
+		return (0);
+	}
+
+	return (0);
+}
+
+/*
+ * Starts an HTTP request on the provided evhttp_connection object.
+ * If the connection object is not connected to the web server already,
+ * this will start the connection.
+ */
+
+int
+evhttp_make_request(struct evhttp_connection *evcon,
+    struct evhttp_request *req,
+    enum evhttp_cmd_type type, const char *uri)
+{
+	/* We are making a request */
+	req->kind = EVHTTP_REQUEST;
+	req->type = type;
+	if (req->uri != NULL)
+		mm_free(req->uri);
+	if ((req->uri = mm_strdup(uri)) == NULL) {
+		event_warn("%s: strdup", __func__);
+		evhttp_request_free(req);
+		return (-1);
+	}
+
+	/* Set the protocol version if it is not supplied */
+	if (!req->major && !req->minor) {
+		req->major = 1;
+		req->minor = 1;
+	}
+
+	EVUTIL_ASSERT(req->evcon == NULL);
+	req->evcon = evcon;
+	EVUTIL_ASSERT(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
+
+       TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+	/* If the connection object is not connected; make it so */
+	if (!evhttp_connected(evcon)) {
+		int res = evhttp_connection_connect(evcon);
+	       /* evhttp_connection_fail(), which is called through
+		* evhttp_connection_connect(), assumes that req lies in
+		* evcon->requests.  Thus, enqueue the request in advance and r
+		* it in the error case. */
+	       if (res != 0)
+		       TAILQ_REMOVE(&evcon->requests, req, next);
+
+		return res;
+	}
+
+	/*
+	 * If it's connected already and we are the first in the queue,
+	 * then we can dispatch this request immediately.  Otherwise, it
+	 * will be dispatched once the pending requests are completed.
+	 */
+	if (TAILQ_FIRST(&evcon->requests) == req)
+		evhttp_request_dispatch(evcon);
+
+	return (0);
+}
+
+void
+evhttp_cancel_request(struct evhttp_request *req)
+{
+	struct evhttp_connection *evcon = req->evcon;
+	if (evcon != NULL) {
+		/* We need to remove it from the connection */
+		if (TAILQ_FIRST(&evcon->requests) == req) {
+			/* it's currently being worked on, so reset
+			 * the connection.
+			 */
+			evhttp_connection_fail(evcon,
+			    EVCON_HTTP_REQUEST_CANCEL);
+
+			/* connection fail freed the request */
+			return;
+		} else {
+			/* otherwise, we can just remove it from the
+			 * queue
+			 */
+			TAILQ_REMOVE(&evcon->requests, req, next);
+		}
+	}
+
+	evhttp_request_free(req);
+}
+
+/*
+ * Reads data from file descriptor into request structure
+ * Request structure needs to be set up correctly.
+ */
+
+void
+evhttp_start_read(struct evhttp_connection *evcon)
+{
+	/* Set up an event to read the headers */
+	bufferevent_disable(evcon->bufev, EV_WRITE);
+	bufferevent_enable(evcon->bufev, EV_READ);
+	evcon->state = EVCON_READING_FIRSTLINE;
+	/* Reset the bufferevent callbacks */
+	bufferevent_setcb(evcon->bufev,
+	    evhttp_read_cb,
+	    evhttp_write_cb,
+	    evhttp_error_cb,
+	    evcon);
+
+	/* If there's still data pending, process it next time through the
+	 * loop.  Don't do it now; that could get recusive. */
+	if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
+		event_deferred_cb_schedule(get_deferred_queue(evcon),
+		    &evcon->read_more_deferred_cb);
+	}
+}
+
+static void
+evhttp_send_done(struct evhttp_connection *evcon, void *arg)
+{
+	int need_close;
+	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+	TAILQ_REMOVE(&evcon->requests, req, next);
+
+	need_close =
+	    (REQ_VERSION_BEFORE(req, 1, 1) &&
+		!evhttp_is_connection_keepalive(req->input_headers))||
+	    evhttp_is_connection_close(req->flags, req->input_headers) ||
+	    evhttp_is_connection_close(req->flags, req->output_headers);
+
+	EVUTIL_ASSERT(req->flags & EVHTTP_REQ_OWN_CONNECTION);
+	evhttp_request_free(req);
+
+	if (need_close) {
+		evhttp_connection_free(evcon);
+		return;
+	}
+
+	/* we have a persistent connection; try to accept another request. */
+	if (evhttp_associate_new_request_with_connection(evcon) == -1) {
+		evhttp_connection_free(evcon);
+	}
+}
+
+/*
+ * 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"
+
+	struct evbuffer *buf = evbuffer_new();
+	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);
+
+	evhttp_send_page(req, buf);
+
+	evbuffer_free(buf);
+#undef ERR_FORMAT
+}
+
+/* Requires that headers and response code are already set up */
+
+static inline void
+evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
+{
+	struct evhttp_connection *evcon = req->evcon;
+
+	if (evcon == NULL) {
+		evhttp_request_free(req);
+		return;
+	}
+
+	EVUTIL_ASSERT(TAILQ_FIRST(&evcon->requests) == req);
+
+	/* we expect no more calls form the user on this request */
+	req->userdone = 1;
+
+	/* xxx: not sure if we really should expose the data buffer this way */
+	if (databuf != NULL)
+		evbuffer_add_buffer(req->output_buffer, databuf);
+
+	/* Adds headers to the response */
+	evhttp_make_header(evcon, req);
+
+	evhttp_write_buffer(evcon, evhttp_send_done, NULL);
+}
+
+void
+evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
+    struct evbuffer *databuf)
+{
+	evhttp_response_code(req, code, reason);
+
+	evhttp_send(req, databuf);
+}
+
+void
+evhttp_send_reply_start(struct evhttp_request *req, int code,
+    const char *reason)
+{
+	evhttp_response_code(req, code, reason);
+	if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
+	    REQ_VERSION_ATLEAST(req, 1, 1) &&
+	    evhttp_response_needs_body(req)) {
+		/*
+		 * prefer HTTP/1.1 chunked encoding to closing the connection;
+		 * note RFC 2616 section 4.4 forbids it with Content-Length:
+		 * and it's not necessary then anyway.
+		 */
+		evhttp_add_header(req->output_headers, "Transfer-Encoding",
+		    "chunked");
+		req->chunked = 1;
+	} else {
+		req->chunked = 0;
+	}
+	evhttp_make_header(req->evcon, req);
+	evhttp_write_buffer(req->evcon, NULL, NULL);
+}
+
+void
+evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
+{
+	struct evhttp_connection *evcon = req->evcon;
+	struct evbuffer *output;
+
+	if (evcon == NULL)
+		return;
+
+	output = bufferevent_get_output(evcon->bufev);
+
+	if (evbuffer_get_length(databuf) == 0)
+		return;
+	if (!evhttp_response_needs_body(req))
+		return;
+	if (req->chunked) {
+		evbuffer_add_printf(output, "%x\r\n",
+				    (unsigned)evbuffer_get_length(databuf));
+	}
+	evbuffer_add_buffer(output, databuf);
+	if (req->chunked) {
+		evbuffer_add(output, "\r\n", 2);
+	}
+	evhttp_write_buffer(evcon, NULL, NULL);
+}
+
+void
+evhttp_send_reply_end(struct evhttp_request *req)
+{
+	struct evhttp_connection *evcon = req->evcon;
+	struct evbuffer *output;
+
+	if (evcon == NULL) {
+		evhttp_request_free(req);
+		return;
+	}
+
+	output = bufferevent_get_output(evcon->bufev);
+
+	/* we expect no more calls form the user on this request */
+	req->userdone = 1;
+
+	if (req->chunked) {
+		evbuffer_add(output, "0\r\n\r\n", 5);
+		evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
+		req->chunked = 0;
+	} else if (evbuffer_get_length(output) == 0) {
+		/* let the connection know that we are done with the request */
+		evhttp_send_done(evcon, NULL);
+	} else {
+		/* make the callback execute after all data has been written */
+		evcon->cb = evhttp_send_done;
+		evcon->cb_arg = NULL;
+	}
+}
+
+static const char *informational_phrases[] = {
+	/* 100 */ "Continue",
+	/* 101 */ "Switching Protocols"
+};
+
+static const char *success_phrases[] = {
+	/* 200 */ "OK",
+	/* 201 */ "Created",
+	/* 202 */ "Accepted",
+	/* 203 */ "Non-Authoritative Information",
+	/* 204 */ "No Content",
+	/* 205 */ "Reset Content",
+	/* 206 */ "Partial Content"
+};
+
+static const char *redirection_phrases[] = {
+	/* 300 */ "Multiple Choices",
+	/* 301 */ "Moved Permanently",
+	/* 302 */ "Found",
+	/* 303 */ "See Other",
+	/* 304 */ "Not Modified",
+	/* 305 */ "Use Proxy",
+	/* 307 */ "Temporary Redirect"
+};
+
+static const char *client_error_phrases[] = {
+	/* 400 */ "Bad Request",
+	/* 401 */ "Unauthorized",
+	/* 402 */ "Payment Required",
+	/* 403 */ "Forbidden",
+	/* 404 */ "Not Found",
+	/* 405 */ "Method Not Allowed",
+	/* 406 */ "Not Acceptable",
+	/* 407 */ "Proxy Authentication Required",
+	/* 408 */ "Request Time-out",
+	/* 409 */ "Conflict",
+	/* 410 */ "Gone",
+	/* 411 */ "Length Required",
+	/* 412 */ "Precondition Failed",
+	/* 413 */ "Request Entity Too Large",
+	/* 414 */ "Request-URI Too Large",
+	/* 415 */ "Unsupported Media Type",
+	/* 416 */ "Requested range not satisfiable",
+	/* 417 */ "Expectation Failed"
+};
+
+static const char *server_error_phrases[] = {
+	/* 500 */ "Internal Server Error",
+	/* 501 */ "Not Implemented",
+	/* 502 */ "Bad Gateway",
+	/* 503 */ "Service Unavailable",
+	/* 504 */ "Gateway Time-out",
+	/* 505 */ "HTTP Version not supported"
+};
+
+struct response_class {
+	const char *name;
+	size_t num_responses;
+	const char **responses;
+};
+
+#ifndef MEMBERSOF
+#define MEMBERSOF(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+static const struct response_class response_classes[] = {
+	/* 1xx */ { "Informational", MEMBERSOF(informational_phrases), informational_phrases },
+	/* 2xx */ { "Success", MEMBERSOF(success_phrases), success_phrases },
+	/* 3xx */ { "Redirection", MEMBERSOF(redirection_phrases), redirection_phrases },
+	/* 4xx */ { "Client Error", MEMBERSOF(client_error_phrases), client_error_phrases },
+	/* 5xx */ { "Server Error", MEMBERSOF(server_error_phrases), server_error_phrases }
+};
+
+static const char *
+evhttp_response_phrase_internal(int code)
+{
+	int klass = code / 100 - 1;
+	int subcode = code % 100;
+
+	/* Unknown class - can't do any better here */
+	if (klass < 0 || klass >= (int) MEMBERSOF(response_classes))
+		return "Unknown Status Class";
+
+	/* Unknown sub-code, return class name at least */
+	if (subcode >= (int) response_classes[klass].num_responses)
+		return response_classes[klass].name;
+
+	return response_classes[klass].responses[subcode];
+}
+
+void
+evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
+{
+	req->kind = EVHTTP_RESPONSE;
+	req->response_code = code;
+	if (req->response_code_line != NULL)
+		mm_free(req->response_code_line);
+	if (reason == NULL)
+		reason = evhttp_response_phrase_internal(code);
+	req->response_code_line = mm_strdup(reason);
+	if (req->response_code_line == NULL) {
+		event_warn("%s: strdup", __func__);
+		/* XXX what else can we do? */
+	}
+}
+
+void
+evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
+{
+	if (!req->major || !req->minor) {
+		req->major = 1;
+		req->minor = 1;
+	}
+
+	if (req->kind != EVHTTP_RESPONSE)
+		evhttp_response_code(req, 200, "OK");
+
+	evhttp_clear_headers(req->output_headers);
+	evhttp_add_header(req->output_headers, "Content-Type", "text/html");
+	evhttp_add_header(req->output_headers, "Connection", "close");
+
+	evhttp_send(req, databuf);
+}
+
+static const char uri_chars[256] = {
+	/* 0 */
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 1, 1, 0,
+	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 0, 0, 0, 0, 0, 0,
+	/* 64 */
+	0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 0, 1,
+	0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 1, 0,
+	/* 128 */
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	/* 192 */
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#define CHAR_IS_UNRESERVED(c)			\
+	(uri_chars[(unsigned char)(c)])
+
+/*
+ * Helper functions to encode/decode a string for inclusion in a URI.
+ * The returned string must be freed by the caller.
+ */
+char *
+evhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
+{
+	struct evbuffer *buf = evbuffer_new();
+	const char *p, *end;
+	char *result;
+
+	if (buf == NULL)
+		return (NULL);
+
+	if (len >= 0)
+		end = uri+len;
+	else
+		end = uri+strlen(uri);
+
+	for (p = uri; p < end; p++) {
+		if (CHAR_IS_UNRESERVED(*p)) {
+			evbuffer_add(buf, p, 1);
+		} else if (*p == ' ' && space_as_plus) {
+			evbuffer_add(buf, "+", 1);
+		} else {
+			evbuffer_add_printf(buf, "%%%02X", (unsigned char)(*p));
+		}
+	}
+	evbuffer_add(buf, "", 1); /* NUL-terminator. */
+	result = mm_malloc(evbuffer_get_length(buf));
+	if (result)
+		evbuffer_remove(buf, result, evbuffer_get_length(buf));
+	evbuffer_free(buf);
+
+	return (result);
+}
+
+char *
+evhttp_encode_uri(const char *str)
+{
+	return evhttp_uriencode(str, -1, 0);
+}
+
+/*
+ * @param decode_plus_ctl: if 1, we decode plus into space.  If 0, we don't.
+ *     If -1, when true we transform plus to space only after we've seen
+ *     a ?.  -1 is deprecated.
+ * @return the number of bytes written to 'ret'.
+ */
+static int
+evhttp_decode_uri_internal(
+	const char *uri, size_t length, char *ret, int decode_plus_ctl)
+{
+	char c;
+	int j;
+	int decode_plus = (decode_plus_ctl == 1) ? 1: 0;
+	unsigned i;
+
+	for (i = j = 0; i < length; i++) {
+		c = uri[i];
+		if (c == '?') {
+			if (decode_plus_ctl < 0)
+				decode_plus = 1;
+		} else if (c == '+' && decode_plus) {
+			c = ' ';
+		} else if (c == '%' && EVUTIL_ISXDIGIT(uri[i+1]) &&
+		    EVUTIL_ISXDIGIT(uri[i+2])) {
+			char tmp[3];
+			tmp[0] = uri[i+1];
+			tmp[1] = uri[i+2];
+			tmp[2] = '\0';
+			c = (char)strtol(tmp, NULL, 16);
+			i += 2;
+		}
+		ret[j++] = c;
+	}
+	ret[j] = '\0';
+
+	return (j);
+}
+
+/* deprecated */
+char *
+evhttp_decode_uri(const char *uri)
+{
+	char *ret;
+
+	if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
+		event_warn("%s: malloc(%lu)", __func__,
+			  (unsigned long)(strlen(uri) + 1));
+		return (NULL);
+	}
+
+	evhttp_decode_uri_internal(uri, strlen(uri),
+	    ret, -1 /*always_decode_plus*/);
+
+	return (ret);
+}
+
+char *
+evhttp_uridecode(const char *uri, int decode_plus, size_t *size_out)
+{
+	char *ret;
+	int n;
+
+	if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
+		event_warn("%s: malloc(%lu)", __func__,
+			  (unsigned long)(strlen(uri) + 1));
+		return (NULL);
+	}
+
+	n = evhttp_decode_uri_internal(uri, strlen(uri),
+	    ret, !!decode_plus/*always_decode_plus*/);
+
+	if (size_out) {
+		EVUTIL_ASSERT(n >= 0);
+		*size_out = (size_t)n;
+	}
+
+	return (ret);
+}
+
+/*
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ */
+
+static int
+evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
+    int is_whole_uri)
+{
+	char *line=NULL;
+	char *argument;
+	char *p;
+	const char *query_part;
+	int result = -1;
+	struct evhttp_uri *uri=NULL;
+
+	TAILQ_INIT(headers);
+
+	if (is_whole_uri) {
+		uri = evhttp_uri_parse(str);
+		if (!uri)
+			goto error;
+		query_part = evhttp_uri_get_query(uri);
+	} else {
+		query_part = str;
+	}
+
+	/* No arguments - we are done */
+	if (!query_part || !strlen(query_part)) {
+		result = 0;
+		goto done;
+	}
+
+	if ((line = mm_strdup(query_part)) == NULL) {
+		event_warn("%s: strdup", __func__);
+		goto error;
+	}
+
+	p = argument = line;
+	while (p != NULL && *p != '\0') {
+		char *key, *value, *decoded_value;
+		argument = strsep(&p, "&");
+
+		value = argument;
+		key = strsep(&value, "=");
+		if (value == NULL || *key == '\0') {
+			goto error;
+		}
+
+		if ((decoded_value = mm_malloc(strlen(value) + 1)) == NULL) {
+			event_warn("%s: mm_malloc", __func__);
+			goto error;
+		}
+		evhttp_decode_uri_internal(value, strlen(value),
+		    decoded_value, 1 /*always_decode_plus*/);
+		event_debug(("Query Param: %s -> %s\n", key, decoded_value));
+		evhttp_add_header_internal(headers, key, decoded_value);
+		mm_free(decoded_value);
+	}
+
+	result = 0;
+	goto done;
+error:
+	evhttp_clear_headers(headers);
+done:
+	if (line)
+		mm_free(line);
+	if (uri)
+		evhttp_uri_free(uri);
+	return result;
+}
+
+int
+evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
+{
+	return evhttp_parse_query_impl(uri, headers, 1);
+}
+int
+evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers)
+{
+	return evhttp_parse_query_impl(uri, headers, 0);
+}
+
+static struct evhttp_cb *
+evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req)
+{
+	struct evhttp_cb *cb;
+	size_t offset = 0;
+	char *translated;
+	const char *path;
+
+	/* Test for different URLs */
+	path = evhttp_uri_get_path(req->uri_elems);
+	offset = strlen(path);
+	if ((translated = mm_malloc(offset + 1)) == NULL)
+		return (NULL);
+	evhttp_decode_uri_internal(path, offset, translated,
+	    0 /* decode_plus */);
+
+	TAILQ_FOREACH(cb, callbacks, next) {
+		if (!strcmp(cb->what, translated)) {
+			mm_free(translated);
+			return (cb);
+		}
+	}
+
+	mm_free(translated);
+	return (NULL);
+}
+
+
+static int
+prefix_suffix_match(const char *pattern, const char *name, int ignorecase)
+{
+	char c;
+
+	while (1) {
+		switch (c = *pattern++) {
+		case '\0':
+			return *name == '\0';
+
+		case '*':
+			while (*name != '\0') {
+				if (prefix_suffix_match(pattern, name,
+					ignorecase))
+					return (1);
+				++name;
+			}
+			return (0);
+		default:
+			if (c != *name) {
+				if (!ignorecase ||
+				    EVUTIL_TOLOWER(c) != EVUTIL_TOLOWER(*name))
+					return (0);
+			}
+			++name;
+		}
+	}
+	/* NOTREACHED */
+}
+
+/*
+   Search the vhost hierarchy beginning with http for a server alias
+   matching hostname.  If a match is found, and outhttp is non-null,
+   outhttp is set to the matching http object and 1 is returned.
+*/
+
+static int
+evhttp_find_alias(struct evhttp *http, struct evhttp **outhttp,
+		  const char *hostname)
+{
+	struct evhttp_server_alias *alias;
+	struct evhttp *vhost;
+
+	TAILQ_FOREACH(alias, &http->aliases, next) {
+		/* XXX Do we need to handle IP addresses? */
+		if (!evutil_ascii_strcasecmp(alias->alias, hostname)) {
+			if (outhttp)
+				*outhttp = http;
+			return 1;
+		}
+	}
+
+	/* XXX It might be good to avoid recursion here, but I don't
+	   see a way to do that w/o a list. */
+	TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
+		if (evhttp_find_alias(vhost, outhttp, hostname))
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+   Attempts to find the best http object to handle a request for a hostname.
+   All aliases for the root http object and vhosts are searched for an exact
+   match. Then, the vhost hierarchy is traversed again for a matching
+   pattern.
+
+   If an alias or vhost is matched, 1 is returned, and outhttp, if non-null,
+   is set with the best matching http object. If there are no matches, the
+   root http object is stored in outhttp and 0 is returned.
+*/
+
+static int
+evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
+		  const char *hostname)
+{
+	struct evhttp *vhost;
+	struct evhttp *oldhttp;
+	int match_found = 0;
+
+	if (evhttp_find_alias(http, outhttp, hostname))
+		return 1;
+
+	do {
+		oldhttp = http;
+		TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
+			if (prefix_suffix_match(vhost->vhost_pattern,
+				hostname, 1 /* ignorecase */)) {
+				http = vhost;
+				match_found = 1;
+				break;
+			}
+		}
+	} while (oldhttp != http);
+
+	if (outhttp)
+		*outhttp = http;
+
+	return match_found;
+}
+
+static void
+evhttp_handle_request(struct evhttp_request *req, void *arg)
+{
+	struct evhttp *http = arg;
+	struct evhttp_cb *cb = NULL;
+	const char *hostname;
+
+	/* we have a new request on which the user needs to take action */
+	req->userdone = 0;
+
+	if (req->type == 0 || req->uri == NULL) {
+		evhttp_send_error(req, HTTP_BADREQUEST, NULL);
+		return;
+	}
+
+	if ((http->allowed_methods & req->type) == 0) {
+		event_debug(("Rejecting disallowed method %x (allowed: %x)\n",
+			(unsigned)req->type, (unsigned)http->allowed_methods));
+		evhttp_send_error(req, HTTP_NOTIMPLEMENTED, NULL);
+		return;
+	}
+
+	/* handle potential virtual hosts */
+	hostname = evhttp_request_get_host(req);
+	if (hostname != NULL) {
+		evhttp_find_vhost(http, &http, hostname);
+	}
+
+	if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) {
+		(*cb->cb)(req, cb->cbarg);
+		return;
+	}
+
+	/* Generic call back */
+	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
+	}
+}
+
+/* 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;
+
+	evhttp_get_request(http, nfd, peer_sa, peer_socklen);
+}
+
+int
+evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port)
+{
+	struct evhttp_bound_socket *bound =
+		evhttp_bind_socket_with_handle(http, address, port);
+	if (bound == NULL)
+		return (-1);
+	return (0);
+}
+
+struct evhttp_bound_socket *
+evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port)
+{
+	evutil_socket_t fd;
+	struct evhttp_bound_socket *bound;
+
+	if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1)
+		return (NULL);
+
+	if (listen(fd, 128) == -1) {
+		event_sock_warn(fd, "%s: listen", __func__);
+		evutil_closesocket(fd);
+		return (NULL);
+	}
+
+	bound = evhttp_accept_socket_with_handle(http, fd);
+
+	if (bound != NULL) {
+		event_debug(("Bound to port %d - Awaiting connections ... ",
+			port));
+		return (bound);
+	}
+
+	return (NULL);
+}
+
+int
+evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd)
+{
+	struct evhttp_bound_socket *bound =
+		evhttp_accept_socket_with_handle(http, fd);
+	if (bound == NULL)
+		return (-1);
+	return (0);
+}
+
+
+struct evhttp_bound_socket *
+evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd)
+{
+	struct evhttp_bound_socket *bound;
+	struct evconnlistener *listener;
+	const int flags =
+	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE;
+
+	listener = evconnlistener_new(http->base, NULL, NULL,
+	    flags,
+	    0, /* Backlog is '0' because we already said 'listen' */
+	    fd);
+	if (!listener)
+		return (NULL);
+
+	bound = evhttp_bind_listener(http, listener);
+	if (!bound) {
+		evconnlistener_free(listener);
+		return (NULL);
+	}
+	return (bound);
+}
+
+struct evhttp_bound_socket *
+evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener)
+{
+	struct evhttp_bound_socket *bound;
+
+	bound = mm_malloc(sizeof(struct evhttp_bound_socket));
+	if (bound == NULL)
+		return (NULL);
+
+	bound->listener = listener;
+	TAILQ_INSERT_TAIL(&http->sockets, bound, next);
+
+	evconnlistener_set_cb(listener, accept_socket_cb, http);
+	return bound;
+}
+
+evutil_socket_t
+evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound)
+{
+	return evconnlistener_get_fd(bound->listener);
+}
+
+struct evconnlistener *
+evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound)
+{
+	return bound->listener;
+}
+
+void
+evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound)
+{
+	TAILQ_REMOVE(&http->sockets, bound, next);
+	evconnlistener_free(bound->listener);
+	mm_free(bound);
+}
+
+static struct evhttp*
+evhttp_new_object(void)
+{
+	struct evhttp *http = NULL;
+
+	if ((http = mm_calloc(1, sizeof(struct evhttp))) == NULL) {
+		event_warn("%s: calloc", __func__);
+		return (NULL);
+	}
+
+	http->timeout = -1;
+	evhttp_set_max_headers_size(http, EV_SIZE_MAX);
+	evhttp_set_max_body_size(http, EV_SIZE_MAX);
+	evhttp_set_allowed_methods(http,
+	    EVHTTP_REQ_GET |
+	    EVHTTP_REQ_POST |
+	    EVHTTP_REQ_HEAD |
+	    EVHTTP_REQ_PUT |
+	    EVHTTP_REQ_DELETE);
+
+	TAILQ_INIT(&http->sockets);
+	TAILQ_INIT(&http->callbacks);
+	TAILQ_INIT(&http->connections);
+	TAILQ_INIT(&http->virtualhosts);
+	TAILQ_INIT(&http->aliases);
+
+	return (http);
+}
+
+struct evhttp *
+evhttp_new(struct event_base *base)
+{
+	struct evhttp *http = NULL;
+
+	http = evhttp_new_object();
+	if (http == NULL)
+		return (NULL);
+	http->base = base;
+
+	return (http);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, unsigned short port)
+{
+	struct evhttp *http = NULL;
+
+	http = evhttp_new_object();
+	if (http == NULL)
+		return (NULL);
+	if (evhttp_bind_socket(http, address, port) == -1) {
+		mm_free(http);
+		return (NULL);
+	}
+
+	return (http);
+}
+
+void
+evhttp_free(struct evhttp* http)
+{
+	struct evhttp_cb *http_cb;
+	struct evhttp_connection *evcon;
+	struct evhttp_bound_socket *bound;
+	struct evhttp* vhost;
+	struct evhttp_server_alias *alias;
+
+	/* Remove the accepting part */
+	while ((bound = TAILQ_FIRST(&http->sockets)) != NULL) {
+		TAILQ_REMOVE(&http->sockets, bound, next);
+
+		evconnlistener_free(bound->listener);
+
+		mm_free(bound);
+	}
+
+	while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
+		/* evhttp_connection_free removes the connection */
+		evhttp_connection_free(evcon);
+	}
+
+	while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
+		TAILQ_REMOVE(&http->callbacks, http_cb, next);
+		mm_free(http_cb->what);
+		mm_free(http_cb);
+	}
+
+	while ((vhost = TAILQ_FIRST(&http->virtualhosts)) != NULL) {
+		TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
+
+		evhttp_free(vhost);
+	}
+
+	if (http->vhost_pattern != NULL)
+		mm_free(http->vhost_pattern);
+
+	while ((alias = TAILQ_FIRST(&http->aliases)) != NULL) {
+		TAILQ_REMOVE(&http->aliases, alias, next);
+		mm_free(alias->alias);
+		mm_free(alias);
+	}
+
+	mm_free(http);
+}
+
+int
+evhttp_add_virtual_host(struct evhttp* http, const char *pattern,
+    struct evhttp* vhost)
+{
+	/* a vhost can only be a vhost once and should not have bound sockets */
+	if (vhost->vhost_pattern != NULL ||
+	    TAILQ_FIRST(&vhost->sockets) != NULL)
+		return (-1);
+
+	vhost->vhost_pattern = mm_strdup(pattern);
+	if (vhost->vhost_pattern == NULL)
+		return (-1);
+
+	TAILQ_INSERT_TAIL(&http->virtualhosts, vhost, next_vhost);
+
+	return (0);
+}
+
+int
+evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost)
+{
+	if (vhost->vhost_pattern == NULL)
+		return (-1);
+
+	TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
+
+	mm_free(vhost->vhost_pattern);
+	vhost->vhost_pattern = NULL;
+
+	return (0);
+}
+
+int
+evhttp_add_server_alias(struct evhttp *http, const char *alias)
+{
+	struct evhttp_server_alias *evalias;
+
+	evalias = mm_calloc(1, sizeof(*evalias));
+	if (!evalias)
+		return -1;
+
+	evalias->alias = mm_strdup(alias);
+	if (!evalias->alias) {
+		mm_free(evalias);
+		return -1;
+	}
+
+	TAILQ_INSERT_TAIL(&http->aliases, evalias, next);
+
+	return 0;
+}
+
+int
+evhttp_remove_server_alias(struct evhttp *http, const char *alias)
+{
+	struct evhttp_server_alias *evalias;
+
+	TAILQ_FOREACH(evalias, &http->aliases, next) {
+		if (evutil_ascii_strcasecmp(evalias->alias, alias) == 0) {
+			TAILQ_REMOVE(&http->aliases, evalias, next);
+			mm_free(evalias->alias);
+			mm_free(evalias);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void
+evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
+{
+	http->timeout = timeout_in_secs;
+}
+
+void
+evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size)
+{
+	if (max_headers_size < 0)
+		http->default_max_headers_size = EV_SIZE_MAX;
+	else
+		http->default_max_headers_size = max_headers_size;
+}
+
+void
+evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size)
+{
+	if (max_body_size < 0)
+		http->default_max_body_size = EV_UINT64_MAX;
+	else
+		http->default_max_body_size = max_body_size;
+}
+
+void
+evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods)
+{
+	http->allowed_methods = methods;
+}
+
+int
+evhttp_set_cb(struct evhttp *http, const char *uri,
+    void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+	struct evhttp_cb *http_cb;
+
+	TAILQ_FOREACH(http_cb, &http->callbacks, next) {
+		if (strcmp(http_cb->what, uri) == 0)
+			return (-1);
+	}
+
+	if ((http_cb = mm_calloc(1, sizeof(struct evhttp_cb))) == NULL) {
+		event_warn("%s: calloc", __func__);
+		return (-2);
+	}
+
+	http_cb->what = mm_strdup(uri);
+	if (http_cb->what == NULL) {
+		event_warn("%s: strdup", __func__);
+		mm_free(http_cb);
+		return (-3);
+	}
+	http_cb->cb = cb;
+	http_cb->cbarg = cbarg;
+
+	TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
+
+	return (0);
+}
+
+int
+evhttp_del_cb(struct evhttp *http, const char *uri)
+{
+	struct evhttp_cb *http_cb;
+
+	TAILQ_FOREACH(http_cb, &http->callbacks, next) {
+		if (strcmp(http_cb->what, uri) == 0)
+			break;
+	}
+	if (http_cb == NULL)
+		return (-1);
+
+	TAILQ_REMOVE(&http->callbacks, http_cb, next);
+	mm_free(http_cb->what);
+	mm_free(http_cb);
+
+	return (0);
+}
+
+void
+evhttp_set_gencb(struct evhttp *http,
+    void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+	http->gencb = cb;
+	http->gencbarg = cbarg;
+}
+
+/*
+ * Request related functions
+ */
+
+struct evhttp_request *
+evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+	struct evhttp_request *req = NULL;
+
+	/* Allocate request structure */
+	if ((req = mm_calloc(1, sizeof(struct evhttp_request))) == NULL) {
+		event_warn("%s: calloc", __func__);
+		goto error;
+	}
+
+	req->headers_size = 0;
+	req->body_size = 0;
+
+	req->kind = EVHTTP_RESPONSE;
+	req->input_headers = mm_calloc(1, sizeof(struct evkeyvalq));
+	if (req->input_headers == NULL) {
+		event_warn("%s: calloc", __func__);
+		goto error;
+	}
+	TAILQ_INIT(req->input_headers);
+
+	req->output_headers = mm_calloc(1, sizeof(struct evkeyvalq));
+	if (req->output_headers == NULL) {
+		event_warn("%s: calloc", __func__);
+		goto error;
+	}
+	TAILQ_INIT(req->output_headers);
+
+	if ((req->input_buffer = evbuffer_new()) == NULL) {
+		event_warn("%s: evbuffer_new", __func__);
+		goto error;
+	}
+
+	if ((req->output_buffer = evbuffer_new()) == NULL) {
+		event_warn("%s: evbuffer_new", __func__);
+		goto error;
+	}
+
+	req->cb = cb;
+	req->cb_arg = arg;
+
+	return (req);
+
+ error:
+	if (req != NULL)
+		evhttp_request_free(req);
+	return (NULL);
+}
+
+void
+evhttp_request_free(struct evhttp_request *req)
+{
+	if ((req->flags & EVHTTP_REQ_DEFER_FREE) != 0) {
+		req->flags |= EVHTTP_REQ_NEEDS_FREE;
+		return;
+	}
+
+	if (req->remote_host != NULL)
+		mm_free(req->remote_host);
+	if (req->uri != NULL)
+		mm_free(req->uri);
+	if (req->uri_elems != NULL)
+		evhttp_uri_free(req->uri_elems);
+	if (req->response_code_line != NULL)
+		mm_free(req->response_code_line);
+	if (req->host_cache != NULL)
+		mm_free(req->host_cache);
+
+	evhttp_clear_headers(req->input_headers);
+	mm_free(req->input_headers);
+
+	evhttp_clear_headers(req->output_headers);
+	mm_free(req->output_headers);
+
+	if (req->input_buffer != NULL)
+		evbuffer_free(req->input_buffer);
+
+	if (req->output_buffer != NULL)
+		evbuffer_free(req->output_buffer);
+
+	mm_free(req);
+}
+
+void
+evhttp_request_own(struct evhttp_request *req)
+{
+	req->flags |= EVHTTP_USER_OWNED;
+}
+
+int
+evhttp_request_is_owned(struct evhttp_request *req)
+{
+	return (req->flags & EVHTTP_USER_OWNED) != 0;
+}
+
+struct evhttp_connection *
+evhttp_request_get_connection(struct evhttp_request *req)
+{
+	return req->evcon;
+}
+
+struct event_base *
+evhttp_connection_get_base(struct evhttp_connection *conn)
+{
+	return conn->base;
+}
+
+void
+evhttp_request_set_chunked_cb(struct evhttp_request *req,
+    void (*cb)(struct evhttp_request *, void *))
+{
+	req->chunk_cb = cb;
+}
+
+/*
+ * Allows for inspection of the request URI
+ */
+
+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));
+	return (req->uri);
+}
+
+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));
+	return (req->uri_elems);
+}
+
+const char *
+evhttp_request_get_host(struct evhttp_request *req)
+{
+	const char *host = NULL;
+
+	if (req->host_cache)
+		return req->host_cache;
+
+	if (req->uri_elems)
+		host = evhttp_uri_get_host(req->uri_elems);
+	if (!host && req->input_headers) {
+		const char *p;
+		size_t len;
+
+		host = evhttp_find_header(req->input_headers, "Host");
+		/* The Host: header may include a port. Remove it here
+		   to be consistent with uri_elems case above. */
+		if (host) {
+			p = host + strlen(host) - 1;
+			while (p > host && EVUTIL_ISDIGIT(*p))
+				--p;
+			if (p > host && *p == ':') {
+				len = p - host;
+				req->host_cache = mm_malloc(len + 1);
+				if (!req->host_cache) {
+					event_warn("%s: malloc", __func__);
+					return NULL;
+				}
+				memcpy(req->host_cache, host, len);
+				req->host_cache[len] = '\0';
+				host = req->host_cache;
+			}
+		}
+	}
+
+	return host;
+}
+
+enum evhttp_cmd_type
+evhttp_request_get_command(const struct evhttp_request *req) {
+	return (req->type);
+}
+
+int
+evhttp_request_get_response_code(const struct evhttp_request *req)
+{
+	return req->response_code;
+}
+
+/** Returns the input headers */
+struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req)
+{
+	return (req->input_headers);
+}
+
+/** Returns the output headers */
+struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req)
+{
+	return (req->output_headers);
+}
+
+/** Returns the input buffer */
+struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req)
+{
+	return (req->input_buffer);
+}
+
+/** Returns the output buffer */
+struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req)
+{
+	return (req->output_buffer);
+}
+
+
+/*
+ * Takes a file descriptor to read a request from.
+ * The callback is executed once the whole request has been read.
+ */
+
+static struct evhttp_connection*
+evhttp_get_request_connection(
+	struct evhttp* http,
+	evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
+{
+	struct evhttp_connection *evcon;
+	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)));
+
+	/* we need a connection object to put the http request on */
+	evcon = evhttp_connection_base_new(
+		http->base, NULL, hostname, atoi(portname));
+	mm_free(hostname);
+	mm_free(portname);
+	if (evcon == NULL)
+		return (NULL);
+
+	evcon->max_headers_size = http->default_max_headers_size;
+	evcon->max_body_size = http->default_max_body_size;
+
+	evcon->flags |= EVHTTP_CON_INCOMING;
+	evcon->state = EVCON_READING_FIRSTLINE;
+
+	evcon->fd = fd;
+
+	bufferevent_setfd(evcon->bufev, fd);
+
+	return (evcon);
+}
+
+static int
+evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
+{
+	struct evhttp *http = evcon->http_server;
+	struct evhttp_request *req;
+	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);
+	}
+	req->remote_port = evcon->port;
+
+	req->evcon = evcon;	/* the request ends up owning the connection */
+	req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+
+	/* We did not present the request to the user user yet, so treat it as
+	 * if the user was done with the request.  This allows us to free the
+	 * request on a persistent connection if the client drops it without
+	 * sending a request.
+	 */
+	req->userdone = 1;
+
+	TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+	req->kind = EVHTTP_REQUEST;
+
+
+	evhttp_start_read(evcon);
+
+	return (0);
+}
+
+static void
+evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
+    struct sockaddr *sa, ev_socklen_t salen)
+{
+	struct evhttp_connection *evcon;
+
+	evcon = evhttp_get_request_connection(http, fd, sa, salen);
+	if (evcon == NULL) {
+		event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
+		    __func__, EV_SOCK_ARG(fd));
+		evutil_closesocket(fd);
+		return;
+	}
+
+	/* the timeout can be used by the server to close idle connections */
+	if (http->timeout != -1)
+		evhttp_connection_set_timeout(evcon, http->timeout);
+
+	/*
+	 * if we want to accept more than one request on a connection,
+	 * we need to know which http server it belongs to.
+	 */
+	evcon->http_server = http;
+	TAILQ_INSERT_TAIL(&http->connections, evcon, next);
+
+	if (evhttp_associate_new_request_with_connection(evcon) == -1)
+		evhttp_connection_free(evcon);
+}
+
+
+/*
+ * Network helper functions that we do not want to export to the rest of
+ * the world.
+ */
+
+static void
+name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
+    char **phost, char **pport)
+{
+	char ntop[NI_MAXHOST];
+	char strport[NI_MAXSERV];
+	int ni_result;
+
+#ifdef _EVENT_HAVE_GETNAMEINFO
+	ni_result = getnameinfo(sa, salen,
+		ntop, sizeof(ntop), strport, sizeof(strport),
+		NI_NUMERICHOST|NI_NUMERICSERV);
+
+	if (ni_result != 0) {
+#ifdef EAI_SYSTEM
+		/* Windows doesn't have an EAI_SYSTEM. */
+		if (ni_result == EAI_SYSTEM)
+			event_err(1, "getnameinfo failed");
+		else
+#endif
+			event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
+		return;
+	}
+#else
+	ni_result = fake_getnameinfo(sa, salen,
+		ntop, sizeof(ntop), strport, sizeof(strport),
+		NI_NUMERICHOST|NI_NUMERICSERV);
+	if (ni_result != 0)
+			return;
+#endif
+
+	*phost = mm_strdup(ntop);
+	*pport = mm_strdup(strport);
+}
+
+/* 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)
+{
+	evutil_socket_t fd;
+
+	int on = 1, r;
+	int serrno;
+
+	/* Create listen socket */
+	fd = socket(ai ? ai->ai_family : AF_INET, SOCK_STREAM, 0);
+	if (fd == -1) {
+			event_sock_warn(-1, "socket");
+			return (-1);
+	}
+
+	if (evutil_make_socket_nonblocking(fd) < 0)
+		goto out;
+	if (evutil_make_socket_closeonexec(fd) < 0)
+		goto out;
+
+	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on))<0)
+		goto out;
+	if (reuse) {
+		if (evutil_make_listen_socket_reuseable(fd) < 0)
+			goto out;
+	}
+
+	if (ai != NULL) {
+		r = bind(fd, ai->ai_addr, (ev_socklen_t)ai->ai_addrlen);
+		if (r == -1)
+			goto out;
+	}
+
+	return (fd);
+
+ out:
+	serrno = EVUTIL_SOCKET_ERROR();
+	evutil_closesocket(fd);
+	EVUTIL_SET_SOCKET_ERROR(serrno);
+	return (-1);
+}
+
+static struct evutil_addrinfo *
+make_addrinfo(const char *address, ev_uint16_t port)
+{
+	struct evutil_addrinfo *ai = NULL;
+
+	struct evutil_addrinfo hints;
+	char strport[NI_MAXSERV];
+	int ai_result;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	/* turn NULL hostname into INADDR_ANY, and skip looking up any address
+	 * types we don't have an interface to connect to. */
+	hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG;
+	evutil_snprintf(strport, sizeof(strport), "%d", port);
+	if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai))
+	    != 0) {
+		if (ai_result == EVUTIL_EAI_SYSTEM)
+			event_warn("getaddrinfo");
+		else
+			event_warnx("getaddrinfo: %s",
+			    evutil_gai_strerror(ai_result));
+		return (NULL);
+	}
+
+	return (ai);
+}
+
+static evutil_socket_t
+bind_socket(const char *address, ev_uint16_t port, int reuse)
+{
+	evutil_socket_t fd;
+	struct evutil_addrinfo *aitop = NULL;
+
+	/* just create an unbound socket */
+	if (address == NULL && port == 0)
+		return bind_socket_ai(NULL, 0);
+
+	aitop = make_addrinfo(address, port);
+
+	if (aitop == NULL)
+		return (-1);
+
+	fd = bind_socket_ai(aitop, reuse);
+
+	evutil_freeaddrinfo(aitop);
+
+	return (fd);
+}
+
+struct evhttp_uri {
+	unsigned flags;
+	char *scheme; /* scheme; e.g http, ftp etc */
+	char *userinfo; /* userinfo (typically username:pass), or NULL */
+	char *host; /* hostname, IP address, or NULL */
+	int port; /* port, or zero */
+	char *path; /* path, or "". */
+	char *query; /* query, or NULL */
+	char *fragment; /* fragment or NULL */
+};
+
+struct evhttp_uri *
+evhttp_uri_new(void)
+{
+	struct evhttp_uri *uri = mm_calloc(sizeof(struct evhttp_uri), 1);
+	if (uri)
+		uri->port = -1;
+	return uri;
+}
+
+void
+evhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags)
+{
+	uri->flags = flags;
+}
+
+/* Return true if the string starting at s and ending immediately before eos
+ * is a valid URI scheme according to RFC3986
+ */
+static int
+scheme_ok(const char *s, const char *eos)
+{
+	/* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+	EVUTIL_ASSERT(eos >= s);
+	if (s == eos)
+		return 0;
+	if (!EVUTIL_ISALPHA(*s))
+		return 0;
+	while (++s < eos) {
+		if (! EVUTIL_ISALNUM(*s) &&
+		    *s != '+' && *s != '-' && *s != '.')
+			return 0;
+	}
+	return 1;
+}
+
+#define SUBDELIMS "!$&'()*+,;="
+
+/* Return true iff [s..eos) is a valid userinfo */
+static int
+userinfo_ok(const char *s, const char *eos)
+{
+	while (s < eos) {
+		if (CHAR_IS_UNRESERVED(*s) ||
+		    strchr(SUBDELIMS, *s) ||
+		    *s == ':')
+			++s;
+		else if (*s == '%' && s+2 < eos &&
+		    EVUTIL_ISXDIGIT(s[1]) &&
+		    EVUTIL_ISXDIGIT(s[2]))
+			s += 3;
+		else
+			return 0;
+	}
+	return 1;
+}
+
+static int
+regname_ok(const char *s, const char *eos)
+{
+	while (s && s<eos) {
+		if (CHAR_IS_UNRESERVED(*s) ||
+		    strchr(SUBDELIMS, *s))
+			++s;
+		else if (*s == '%' &&
+		    EVUTIL_ISXDIGIT(s[1]) &&
+		    EVUTIL_ISXDIGIT(s[2]))
+			s += 3;
+		else
+			return 0;
+	}
+	return 1;
+}
+
+static int
+parse_port(const char *s, const char *eos)
+{
+	int portnum = 0;
+	while (s < eos) {
+		if (! EVUTIL_ISDIGIT(*s))
+			return -1;
+		portnum = (portnum * 10) + (*s - '0');
+		if (portnum < 0)
+			return -1;
+		++s;
+	}
+	return portnum;
+}
+
+/* returns 0 for bad, 1 for ipv6, 2 for IPvFuture */
+static int
+bracket_addr_ok(const char *s, const char *eos)
+{
+	if (s + 3 > eos || *s != '[' || *(eos-1) != ']')
+		return 0;
+	if (s[1] == 'v') {
+		/* IPvFuture, or junk.
+		   "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
+		 */
+		s += 2; /* skip [v */
+		--eos;
+		if (!EVUTIL_ISXDIGIT(*s)) /*require at least one*/
+			return 0;
+		while (s < eos && *s != '.') {
+			if (EVUTIL_ISXDIGIT(*s))
+				++s;
+			else
+				return 0;
+		}
+		if (*s != '.')
+			return 0;
+		++s;
+		while (s < eos) {
+			if (CHAR_IS_UNRESERVED(*s) ||
+			    strchr(SUBDELIMS, *s) ||
+			    *s == ':')
+				++s;
+			else
+				return 0;
+		}
+		return 2;
+	} else {
+		/* IPv6, or junk */
+		char buf[64];
+		ev_ssize_t n_chars = eos-s-2;
+		struct in6_addr in6;
+		if (n_chars >= 64) /* way too long */
+			return 0;
+		memcpy(buf, s+1, n_chars);
+		buf[n_chars]='\0';
+		return (evutil_inet_pton(AF_INET6,buf,&in6)==1) ? 1 : 0;
+	}
+}
+
+static int
+parse_authority(struct evhttp_uri *uri, char *s, char *eos)
+{
+	char *cp, *port;
+	EVUTIL_ASSERT(eos);
+	if (eos == s) {
+		uri->host = mm_strdup("");
+		if (uri->host == NULL) {
+			event_warn("%s: strdup", __func__);
+			return -1;
+		}
+		return 0;
+	}
+
+	/* Optionally, we start with "userinfo@" */
+
+	cp = strchr(s, '@');
+	if (cp && cp < eos) {
+		if (! userinfo_ok(s,cp))
+			return -1;
+		*cp++ = '\0';
+		uri->userinfo = mm_strdup(s);
+		if (uri->userinfo == NULL) {
+			event_warn("%s: strdup", __func__);
+			return -1;
+		}
+	} else {
+		cp = s;
+	}
+	/* Optionally, we end with ":port" */
+	for (port=eos-1; port >= cp && EVUTIL_ISDIGIT(*port); --port)
+		;
+	if (port >= cp && *port == ':') {
+		if (port+1 == eos) /* Leave port unspecified; the RFC allows a
+				    * nil port */
+			uri->port = -1;
+		else if ((uri->port = parse_port(port+1, eos))<0)
+			return -1;
+		eos = port;
+	}
+	/* Now, cp..eos holds the "host" port, which can be an IPv4Address,
+	 * an IP-Literal, or a reg-name */
+	EVUTIL_ASSERT(eos >= cp);
+	if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
+		/* IPv6address, IP-Literal, or junk. */
+		if (! bracket_addr_ok(cp, eos))
+			return -1;
+	} 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);
+	if (uri->host == NULL) {
+		event_warn("%s: malloc", __func__);
+		return -1;
+	}
+	memcpy(uri->host, cp, eos-cp);
+	uri->host[eos-cp] = '\0';
+	return 0;
+
+}
+
+static char *
+end_of_authority(char *cp)
+{
+	while (*cp) {
+		if (*cp == '?' || *cp == '#' || *cp == '/')
+			return cp;
+		++cp;
+	}
+	return cp;
+}
+
+enum uri_part {
+	PART_PATH,
+	PART_QUERY,
+	PART_FRAGMENT
+};
+
+/* Return the character after the longest prefix of 'cp' that matches...
+ *   *pchar / "/" if allow_qchars is false, or
+ *   *(pchar / "/" / "?") if allow_qchars is true.
+ */
+static char *
+end_of_path(char *cp, enum uri_part part, unsigned flags)
+{
+	if (flags & EVHTTP_URI_NONCONFORMANT) {
+		/* If NONCONFORMANT:
+		 *   Path is everything up to a # or ? or nul.
+		 *   Query is everything up a # or nul
+		 *   Fragment is everything up to a nul.
+		 */
+		switch (part) {
+		case PART_PATH:
+			while (*cp && *cp != '#' && *cp != '?')
+				++cp;
+			break;
+		case PART_QUERY:
+			while (*cp && *cp != '#')
+				++cp;
+			break;
+		case PART_FRAGMENT:
+			cp += strlen(cp);
+			break;
+		};
+		return cp;
+	}
+
+	while (*cp) {
+		if (CHAR_IS_UNRESERVED(*cp) ||
+		    strchr(SUBDELIMS, *cp) ||
+		    *cp == ':' || *cp == '@' || *cp == '/')
+			++cp;
+		else if (*cp == '%' && EVUTIL_ISXDIGIT(cp[1]) &&
+		    EVUTIL_ISXDIGIT(cp[2]))
+			cp += 3;
+		else if (*cp == '?' && part != PART_PATH)
+			++cp;
+		else
+			return cp;
+	}
+	return cp;
+}
+
+static int
+path_matches_noscheme(const char *cp)
+{
+	while (*cp) {
+		if (*cp == ':')
+			return 0;
+		else if (*cp == '/')
+			return 1;
+		++cp;
+	}
+	return 1;
+}
+
+struct evhttp_uri *
+evhttp_uri_parse(const char *source_uri)
+{
+	return evhttp_uri_parse_with_flags(source_uri, 0);
+}
+
+struct evhttp_uri *
+evhttp_uri_parse_with_flags(const char *source_uri, unsigned flags)
+{
+	char *readbuf = NULL, *readp = NULL, *token = NULL, *query = NULL;
+	char *path = NULL, *fragment = NULL;
+	int got_authority = 0;
+
+	struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
+	if (uri == NULL) {
+		event_warn("%s: calloc", __func__);
+		goto err;
+	}
+	uri->port = -1;
+	uri->flags = flags;
+
+	readbuf = mm_strdup(source_uri);
+	if (readbuf == NULL) {
+		event_warn("%s: strdup", __func__);
+		goto err;
+	}
+
+	readp = readbuf;
+	token = NULL;
+
+	/* We try to follow RFC3986 here as much as we can, and match
+	   the productions
+
+	      URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+
+	      relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
+	 */
+
+	/* 1. scheme: */
+	token = strchr(readp, ':');
+	if (token && scheme_ok(readp,token)) {
+		*token = '\0';
+		uri->scheme = mm_strdup(readp);
+		if (uri->scheme == NULL) {
+			event_warn("%s: strdup", __func__);
+			goto err;
+		}
+		readp = token+1; /* eat : */
+	}
+
+	/* 2. Optionally, "//" then an 'authority' part. */
+	if (readp[0]=='/' && readp[1] == '/') {
+		char *authority;
+		readp += 2;
+		authority = readp;
+		path = end_of_authority(readp);
+		if (parse_authority(uri, authority, path) < 0)
+			goto err;
+		readp = path;
+		got_authority = 1;
+	}
+
+	/* 3. Query: path-abempty, path-absolute, path-rootless, or path-empty
+	 */
+	path = readp;
+	readp = end_of_path(path, PART_PATH, flags);
+
+	/* Query */
+	if (*readp == '?') {
+		*readp = '\0';
+		++readp;
+		query = readp;
+		readp = end_of_path(readp, PART_QUERY, flags);
+	}
+	/* fragment */
+	if (*readp == '#') {
+		*readp = '\0';
+		++readp;
+		fragment = readp;
+		readp = end_of_path(readp, PART_FRAGMENT, flags);
+	}
+	if (*readp != '\0') {
+		goto err;
+	}
+
+	/* These next two cases may be unreachable; I'm leaving them
+	 * in to be defensive. */
+	/* If you didn't get an authority, the path can't begin with "//" */
+	if (!got_authority && path[0]=='/' && path[1]=='/')
+		goto err;
+	/* If you did get an authority, the path must begin with "/" or be
+	 * empty. */
+	if (got_authority && path[0] != '/' && path[0] != '\0')
+		goto err;
+	/* (End of maybe-unreachable cases) */
+
+	/* If there was no scheme, the first part of the path (if any) must
+	 * have no colon in it. */
+	if (! uri->scheme && !path_matches_noscheme(path))
+		goto err;
+
+	EVUTIL_ASSERT(path);
+	uri->path = mm_strdup(path);
+	if (uri->path == NULL) {
+		event_warn("%s: strdup", __func__);
+		goto err;
+	}
+
+	if (query) {
+		uri->query = mm_strdup(query);
+		if (uri->query == NULL) {
+			event_warn("%s: strdup", __func__);
+			goto err;
+		}
+	}
+	if (fragment) {
+		uri->fragment = mm_strdup(fragment);
+		if (uri->fragment == NULL) {
+			event_warn("%s: strdup", __func__);
+			goto err;
+		}
+	}
+
+	mm_free(readbuf);
+
+	return uri;
+err:
+	if (uri)
+		evhttp_uri_free(uri);
+	if (readbuf)
+		mm_free(readbuf);
+	return NULL;
+}
+
+void
+evhttp_uri_free(struct evhttp_uri *uri)
+{
+#define _URI_FREE_STR(f)		\
+	if (uri->f) {			\
+		mm_free(uri->f);		\
+	}
+
+	_URI_FREE_STR(scheme);
+	_URI_FREE_STR(userinfo);
+	_URI_FREE_STR(host);
+	_URI_FREE_STR(path);
+	_URI_FREE_STR(query);
+	_URI_FREE_STR(fragment);
+
+	mm_free(uri);
+#undef _URI_FREE_STR
+}
+
+char *
+evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
+{
+	struct evbuffer *tmp = 0;
+	size_t joined_size = 0;
+	char *output = NULL;
+
+#define _URI_ADD(f)	evbuffer_add(tmp, uri->f, strlen(uri->f))
+
+	if (!uri || !buf || !limit)
+		return NULL;
+
+	tmp = evbuffer_new();
+	if (!tmp)
+		return NULL;
+
+	if (uri->scheme) {
+		_URI_ADD(scheme);
+		evbuffer_add(tmp, ":", 1);
+	}
+	if (uri->host) {
+		evbuffer_add(tmp, "//", 2);
+		if (uri->userinfo)
+			evbuffer_add_printf(tmp,"%s@", uri->userinfo);
+		_URI_ADD(host);
+		if (uri->port >= 0)
+			evbuffer_add_printf(tmp,":%d", uri->port);
+
+		if (uri->path && uri->path[0] != '/' && uri->path[0] != '\0')
+			goto err;
+	}
+
+	if (uri->path)
+		_URI_ADD(path);
+
+	if (uri->query) {
+		evbuffer_add(tmp, "?", 1);
+		_URI_ADD(query);
+	}
+
+	if (uri->fragment) {
+		evbuffer_add(tmp, "#", 1);
+		_URI_ADD(fragment);
+	}
+
+	evbuffer_add(tmp, "\0", 1); /* NUL */
+
+	joined_size = evbuffer_get_length(tmp);
+
+	if (joined_size > limit) {
+		/* It doesn't fit. */
+		evbuffer_free(tmp);
+		return NULL;
+	}
+       	evbuffer_remove(tmp, buf, joined_size);
+
+	output = buf;
+err:
+	evbuffer_free(tmp);
+
+	return output;
+#undef _URI_ADD
+}
+
+const char *
+evhttp_uri_get_scheme(const struct evhttp_uri *uri)
+{
+	return uri->scheme;
+}
+const char *
+evhttp_uri_get_userinfo(const struct evhttp_uri *uri)
+{
+	return uri->userinfo;
+}
+const char *
+evhttp_uri_get_host(const struct evhttp_uri *uri)
+{
+	return uri->host;
+}
+int
+evhttp_uri_get_port(const struct evhttp_uri *uri)
+{
+	return uri->port;
+}
+const char *
+evhttp_uri_get_path(const struct evhttp_uri *uri)
+{
+	return uri->path;
+}
+const char *
+evhttp_uri_get_query(const struct evhttp_uri *uri)
+{
+	return uri->query;
+}
+const char *
+evhttp_uri_get_fragment(const struct evhttp_uri *uri)
+{
+	return uri->fragment;
+}
+
+#define _URI_SET_STR(f) do {					\
+	if (uri->f)						\
+		mm_free(uri->f);				\
+	if (f) {						\
+		if ((uri->f = mm_strdup(f)) == NULL) {		\
+			event_warn("%s: strdup()", __func__);	\
+			return -1;				\
+		}						\
+	} else {						\
+		uri->f = NULL;					\
+	}							\
+	} while(0)
+
+int
+evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme)
+{
+	if (scheme && !scheme_ok(scheme, scheme+strlen(scheme)))
+		return -1;
+
+	_URI_SET_STR(scheme);
+	return 0;
+}
+int
+evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo)
+{
+	if (userinfo && !userinfo_ok(userinfo, userinfo+strlen(userinfo)))
+		return -1;
+	_URI_SET_STR(userinfo);
+	return 0;
+}
+int
+evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
+{
+	if (host) {
+		if (host[0] == '[') {
+			if (! bracket_addr_ok(host, host+strlen(host)))
+				return -1;
+		} else {
+			if (! regname_ok(host, host+strlen(host)))
+				return -1;
+		}
+	}
+
+	_URI_SET_STR(host);
+	return 0;
+}
+int
+evhttp_uri_set_port(struct evhttp_uri *uri, int port)
+{
+	if (port < -1)
+		return -1;
+	uri->port = port;
+	return 0;
+}
+#define end_of_cpath(cp,p,f) \
+	((const char*)(end_of_path(((char*)(cp)), (p), (f))))
+
+int
+evhttp_uri_set_path(struct evhttp_uri *uri, const char *path)
+{
+	if (path && end_of_cpath(path, PART_PATH, uri->flags) != path+strlen(path))
+		return -1;
+
+	_URI_SET_STR(path);
+	return 0;
+}
+int
+evhttp_uri_set_query(struct evhttp_uri *uri, const char *query)
+{
+	if (query && end_of_cpath(query, PART_QUERY, uri->flags) != query+strlen(query))
+		return -1;
+	_URI_SET_STR(query);
+	return 0;
+}
+int
+evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment)
+{
+	if (fragment && end_of_cpath(fragment, PART_FRAGMENT, uri->flags) != fragment+strlen(fragment))
+		return -1;
+	_URI_SET_STR(fragment);
+	return 0;
+}