aboutsummaryrefslogtreecommitdiff
path: root/http.c
diff options
context:
space:
mode:
authorNarayan Kamath <narayan@google.com>2017-09-13 12:53:52 +0100
committerNarayan Kamath <narayan@google.com>2017-09-13 14:18:03 +0100
commitfc74cb45eafe51162b10a850016c6d2e1f8fd23c (patch)
tree203fb0f2feed47099e5bf999bcaff954f5c0e49d /http.c
parent9dfd4017adef7eaf179f743bf746254917a4fb74 (diff)
downloadlibevent-fc74cb45eafe51162b10a850016c6d2e1f8fd23c.tar.gz
Revert "Revert "Upgrade to 2.1.8-stable (2017-01-22)." and "Probably Mac build fix?""
This reverts commit 83a0c9c65a60a92d3ea5542596b3ba56db492c37. Bug: 64543673 Test: make checkbuild Test: Manual tombstoned test Change-Id: I84bb128d1dec433195f2cbdbf70236ba17fa9955
Diffstat (limited to 'http.c')
-rw-r--r--http.c1107
1 files changed, 801 insertions, 306 deletions
diff --git a/http.c b/http.c
index 377597e..f5a2ef9 100644
--- a/http.c
+++ b/http.c
@@ -26,26 +26,31 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef _EVENT_HAVE_SYS_PARAM_H
+#ifdef EVENT__HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
-#ifdef _EVENT_HAVE_SYS_TYPES_H
+#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
+#ifdef EVENT__HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
+#endif
+#ifdef EVENT__HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef EVENT__HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/stat.h>
-#include <sys/wait.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -53,17 +58,17 @@
#include <sys/queue.h>
-#ifdef _EVENT_HAVE_NETINET_IN_H
+#ifdef EVENT__HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
-#ifdef _EVENT_HAVE_ARPA_INET_H
+#ifdef EVENT__HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
-#ifdef _EVENT_HAVE_NETDB_H
+#ifdef EVENT__HAVE_NETDB_H
#include <netdb.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
@@ -71,15 +76,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <syslog.h>
#endif
#include <signal.h>
-#include <time.h>
-#ifdef _EVENT_HAVE_UNISTD_H
+#ifdef EVENT__HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef _EVENT_HAVE_FCNTL_H
+#ifdef EVENT__HAVE_FCNTL_H
#include <fcntl.h>
#endif
@@ -91,7 +95,6 @@
#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"
@@ -102,7 +105,7 @@
#include "mm-internal.h"
#include "bufferevent-internal.h"
-#ifndef _EVENT_HAVE_GETNAMEINFO
+#ifndef EVENT__HAVE_GETNAMEINFO
#define NI_MAXSERV 32
#define NI_MAXHOST 1025
@@ -193,12 +196,10 @@ static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request
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
+#ifndef EVENT__HAVE_STRSEP
/* strsep replacement for platforms that lack it. Only works if
* del is one character long. */
static char *
@@ -352,23 +353,6 @@ evhttp_response_needs_body(struct evhttp_request *req)
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.
@@ -450,7 +434,10 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
evhttp_remove_header(req->output_headers, "Proxy-Connection");
/* Generate request line */
- method = evhttp_method(req->type);
+ if (!(method = evhttp_method(req->type))) {
+ method = "NULL";
+ }
+
evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
"%s %s HTTP/%d.%d\r\n",
method, req->uri, req->major, req->minor);
@@ -480,6 +467,13 @@ evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0);
}
}
+static int
+evhttp_is_request_connection_close(struct evhttp_request *req)
+{
+ return
+ evhttp_is_connection_close(req->flags, req->input_headers) ||
+ evhttp_is_connection_close(req->flags, req->output_headers);
+}
/* Return true iff 'headers' contains 'Connection: keep-alive' */
static int
@@ -496,19 +490,7 @@ 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) {
+ if (sizeof(date) - evutil_date_rfc1123(date, sizeof(date), NULL) > 0) {
evhttp_add_header(headers, "Date", date);
}
}
@@ -571,9 +553,11 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
/* Potentially add headers for unidentified content. */
if (evhttp_response_needs_body(req)) {
if (evhttp_find_header(req->output_headers,
- "Content-Type") == NULL) {
+ "Content-Type") == NULL
+ && evcon->http_server->default_content_type) {
evhttp_add_header(req->output_headers,
- "Content-Type", "text/html; charset=ISO-8859-1");
+ "Content-Type",
+ evcon->http_server->default_content_type);
}
}
@@ -586,6 +570,23 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
}
}
+enum expect { NO, CONTINUE, OTHER };
+static enum expect evhttp_have_expect(struct evhttp_request *req, int input)
+{
+ const char *expect;
+ struct evkeyvalq *h = input ? req->input_headers : req->output_headers;
+
+ if (!(req->kind == EVHTTP_REQUEST) || !REQ_VERSION_ATLEAST(req, 1, 1))
+ return NO;
+
+ expect = evhttp_find_header(h, "Expect");
+ if (!expect)
+ return NO;
+
+ return !evutil_ascii_strcasecmp(expect, "100-continue") ? CONTINUE : OTHER;
+}
+
+
/** 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 */
@@ -611,14 +612,12 @@ evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
}
evbuffer_add(output, "\r\n", 2);
- if (evbuffer_get_length(req->output_buffer) > 0) {
+ if (evhttp_have_expect(req, 0) != CONTINUE &&
+ evbuffer_get_length(req->output_buffer)) {
/*
* 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);
}
}
@@ -644,11 +643,19 @@ evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
static int
evhttp_connection_incoming_fail(struct evhttp_request *req,
- enum evhttp_connection_error error)
+ enum evhttp_request_error error)
{
switch (error) {
- case EVCON_HTTP_TIMEOUT:
- case EVCON_HTTP_EOF:
+ case EVREQ_HTTP_DATA_TOO_LONG:
+ req->response_code = HTTP_ENTITYTOOLARGE;
+ break;
+ default:
+ req->response_code = HTTP_BADREQUEST;
+ }
+
+ switch (error) {
+ case EVREQ_HTTP_TIMEOUT:
+ case EVREQ_HTTP_EOF:
/*
* these are cases in which we probably should just
* close the connection and not send a reply. this
@@ -666,9 +673,10 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
req->evcon = NULL;
}
return (-1);
- case EVCON_HTTP_INVALID_HEADER:
- case EVCON_HTTP_BUFFER_ERROR:
- case EVCON_HTTP_REQUEST_CANCEL:
+ case EVREQ_HTTP_INVALID_HEADER:
+ case EVREQ_HTTP_BUFFER_ERROR:
+ case EVREQ_HTTP_REQUEST_CANCEL:
+ case EVREQ_HTTP_DATA_TOO_LONG:
default: /* xxx: probably should just error on default */
/* the callback looks at the uri to determine errors */
if (req->uri) {
@@ -690,17 +698,36 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
return (0);
}
+/* Free connection ownership of which can be acquired by user using
+ * evhttp_request_own(). */
+static inline void
+evhttp_request_free_auto(struct evhttp_request *req)
+{
+ if (!(req->flags & EVHTTP_USER_OWNED))
+ evhttp_request_free(req);
+}
+
+static void
+evhttp_request_free_(struct evhttp_connection *evcon, struct evhttp_request *req)
+{
+ TAILQ_REMOVE(&evcon->requests, req, next);
+ evhttp_request_free_auto(req);
+}
+
/* 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)
+evhttp_connection_fail_(struct evhttp_connection *evcon,
+ enum evhttp_request_error error)
{
+ const int errsave = EVUTIL_SOCKET_ERROR();
struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
void (*cb)(struct evhttp_request *, void *);
void *cb_arg;
+ void (*error_cb)(enum evhttp_request_error, void *);
+ void *error_cb_arg;
EVUTIL_ASSERT(req != NULL);
bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
@@ -719,8 +746,10 @@ evhttp_connection_fail(struct evhttp_connection *evcon,
return;
}
+ error_cb = req->error_cb;
+ error_cb_arg = req->cb_arg;
/* when the request was canceled, the callback is not executed */
- if (error != EVCON_HTTP_REQUEST_CANCEL) {
+ if (error != EVREQ_HTTP_REQUEST_CANCEL) {
/* save the callback for later; the cb might free our object */
cb = req->cb;
cb_arg = req->cb_arg;
@@ -733,17 +762,24 @@ evhttp_connection_fail(struct evhttp_connection *evcon,
* 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);
+ evhttp_request_free_(evcon, req);
/* reset the connection */
- evhttp_connection_reset(evcon);
+ 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);
+ evhttp_connection_connect_(evcon);
+
+ /* The call to evhttp_connection_reset_ overwrote errno.
+ * Let's restore the original errno, so that the user's
+ * callback can have a better idea of what the error was.
+ */
+ EVUTIL_SET_SOCKET_ERROR(errsave);
/* inform the user */
+ if (error_cb != NULL)
+ error_cb(error, error_cb_arg);
if (cb != NULL)
(*cb)(NULL, cb_arg);
}
@@ -772,22 +808,19 @@ evhttp_connection_done(struct evhttp_connection *evcon)
{
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
+ int free_evcon = 0;
if (con_outgoing) {
/* idle or close the connection */
- int need_close;
+ int need_close = evhttp_is_request_connection_close(req);
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);
+ evhttp_connection_reset_(evcon);
if (TAILQ_FIRST(&evcon->requests) != NULL) {
/*
@@ -795,7 +828,7 @@ evhttp_connection_done(struct evhttp_connection *evcon)
* and deal with the next request.
*/
if (!evhttp_connected(evcon))
- evhttp_connection_connect(evcon);
+ evhttp_connection_connect_(evcon);
else
evhttp_request_dispatch(evcon);
} else if (!need_close) {
@@ -804,6 +837,12 @@ evhttp_connection_done(struct evhttp_connection *evcon)
* need to detect if the other side closes it.
*/
evhttp_connection_start_detectclose(evcon);
+ } else if ((evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ /*
+ * If we have no more requests that need completion
+ * and we're not waiting for the connection to close
+ */
+ free_evcon = 1;
}
} else {
/*
@@ -816,11 +855,19 @@ evhttp_connection_done(struct evhttp_connection *evcon)
/* 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 this was an outgoing request, we own and it's done. so free it. */
+ if (con_outgoing) {
+ evhttp_request_free_auto(req);
+ }
+
+ /* If this was the last request of an outgoing connection and we're
+ * not waiting to receive a connection close event and we want to
+ * automatically free the connection. We check to ensure our request
+ * list is empty one last time just in case our callback added a
+ * new request.
*/
- if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) {
- evhttp_request_free(req);
+ if (free_evcon && TAILQ_FIRST(&evcon->requests) == NULL) {
+ evhttp_connection_free(evcon);
}
}
@@ -934,10 +981,10 @@ 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)) {
+ switch (evhttp_parse_headers_(req, buf)) {
case DATA_CORRUPTED:
case DATA_TOO_LONG:
- evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
break;
case ALL_DATA_READ:
bufferevent_disable(evcon->bufev, EV_READ);
@@ -946,12 +993,40 @@ evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
case MORE_DATA_EXPECTED:
case REQUEST_CANCELED: /* ??? */
default:
- bufferevent_enable(evcon->bufev, EV_READ);
break;
}
}
static void
+evhttp_lingering_close(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
+
+ 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;
+
+ event_debug(("Request body is too long, left " EV_I64_FMT,
+ EV_I64_ARG(req->ntoread)));
+
+ evbuffer_drain(buf, n);
+ if (!req->ntoread)
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
+}
+static void
+evhttp_lingering_fail(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ if (evcon->flags & EVHTTP_CON_LINGERING_CLOSE)
+ evhttp_lingering_close(evcon, req);
+ else
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
+}
+
+static void
evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
{
struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
@@ -964,14 +1039,14 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
evhttp_read_trailer(evcon, req);
return;
case DATA_CORRUPTED:
- case DATA_TOO_LONG:/*separate error for this? XXX */
+ case DATA_TOO_LONG:
/* corrupted data */
- evhttp_connection_fail(evcon,
- EVCON_HTTP_INVALID_HEADER);
+ evhttp_connection_fail_(evcon,
+ EVREQ_HTTP_DATA_TOO_LONG);
return;
case REQUEST_CANCELED:
/* request canceled */
- evhttp_request_free(req);
+ evhttp_request_free_auto(req);
return;
case MORE_DATA_EXPECTED:
default:
@@ -980,7 +1055,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
} 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);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
}
@@ -1004,9 +1079,8 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
(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);
+
+ evhttp_lingering_fail(evcon, req);
return;
}
@@ -1017,24 +1091,21 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
evbuffer_drain(req->input_buffer,
evbuffer_get_length(req->input_buffer));
if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
- evhttp_request_free(req);
+ evhttp_request_free_auto(req);
return;
}
}
- if (req->ntoread == 0) {
+ if (!req->ntoread) {
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))
+ ((evcon)->base)
/*
* Gets called when more data becomes available
@@ -1047,7 +1118,7 @@ evhttp_read_cb(struct bufferevent *bufev, void *arg)
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
/* Cancel if it's pending. */
- event_deferred_cb_cancel(get_deferred_queue(evcon),
+ event_deferred_cb_cancel_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
switch (evcon->state) {
@@ -1083,7 +1154,7 @@ evhttp_read_cb(struct bufferevent *bufev, void *arg)
__func__, EV_SIZE_ARG(total_len)));
#endif
- evhttp_connection_reset(evcon);
+ evhttp_connection_reset_(evcon);
}
break;
case EVCON_DISCONNECTED:
@@ -1096,7 +1167,7 @@ evhttp_read_cb(struct bufferevent *bufev, void *arg)
}
static void
-evhttp_deferred_read_cb(struct deferred_cb *cb, void *data)
+evhttp_deferred_read_cb(struct event_callback *cb, void *data)
{
struct evhttp_connection *evcon = data;
evhttp_read_cb(evcon->bufev, evcon);
@@ -1107,14 +1178,20 @@ 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);
+ struct evbuffer *output = bufferevent_get_output(evcon->bufev);
EVUTIL_ASSERT(req != NULL);
EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
+ /* We need to wait until we've written all of our output data before we can
+ * continue */
+ if (evbuffer_get_length(output) > 0)
+ return;
+
/* We are done writing our header and are now expecting the response */
req->kind = EVHTTP_RESPONSE;
- evhttp_start_read(evcon);
+ evhttp_start_read_(evcon);
}
/*
@@ -1135,11 +1212,10 @@ evhttp_connection_free(struct evhttp_connection *evcon)
/* 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.
+ * evhttp_connection_fail_.
*/
while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
- TAILQ_REMOVE(&evcon->requests, req, next);
- evhttp_request_free(req);
+ evhttp_request_free_(evcon, req);
}
if (evcon->http_server != NULL) {
@@ -1155,12 +1231,18 @@ evhttp_connection_free(struct evhttp_connection *evcon)
if (evcon->bufev != NULL)
bufferevent_free(evcon->bufev);
- event_deferred_cb_cancel(get_deferred_queue(evcon),
+ event_deferred_cb_cancel_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
+ if (evcon->fd == -1)
+ evcon->fd = bufferevent_getfd(evcon->bufev);
+
if (evcon->fd != -1) {
+ bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
shutdown(evcon->fd, EVUTIL_SHUT_WR);
- evutil_closesocket(evcon->fd);
+ if (!(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE)) {
+ evutil_closesocket(evcon->fd);
+ }
}
if (evcon->bind_address != NULL)
@@ -1173,6 +1255,11 @@ evhttp_connection_free(struct evhttp_connection *evcon)
}
void
+evhttp_connection_free_on_completion(struct evhttp_connection *evcon) {
+ evcon->flags |= EVHTTP_CON_AUTOFREE;
+}
+
+void
evhttp_connection_set_local_address(struct evhttp_connection *evcon,
const char *address)
{
@@ -1217,9 +1304,10 @@ evhttp_request_dispatch(struct evhttp_connection* evcon)
/* 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)
+evhttp_connection_reset_(struct evhttp_connection *evcon)
{
struct evbuffer *tmp;
+ int err;
/* 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
@@ -1230,9 +1318,12 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
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.
+ might. That caused an assertion failure in evhttp_connection_fail_.
*/
- bufferevent_disable_hard(evcon->bufev, EV_READ|EV_WRITE);
+ bufferevent_disable_hard_(evcon->bufev, EV_READ|EV_WRITE);
+
+ if (evcon->fd == -1)
+ evcon->fd = bufferevent_getfd(evcon->bufev);
if (evcon->fd != -1) {
/* inform interested parties about connection close */
@@ -1243,12 +1334,17 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
evutil_closesocket(evcon->fd);
evcon->fd = -1;
}
+ bufferevent_setfd(evcon->bufev, -1);
/* we need to clean up any buffered data */
tmp = bufferevent_get_output(evcon->bufev);
- evbuffer_drain(tmp, evbuffer_get_length(tmp));
+ err = evbuffer_drain(tmp, -1);
+ EVUTIL_ASSERT(!err && "drain output");
tmp = bufferevent_get_input(evcon->bufev);
- evbuffer_drain(tmp, evbuffer_get_length(tmp));
+ err = evbuffer_drain(tmp, -1);
+ EVUTIL_ASSERT(!err && "drain input");
+
+ evcon->flags &= ~EVHTTP_CON_READING_ERROR;
evcon->state = EVCON_DISCONNECTED;
}
@@ -1275,7 +1371,7 @@ evhttp_connection_retry(evutil_socket_t fd, short what, void *arg)
struct evhttp_connection *evcon = arg;
evcon->state = EVCON_DISCONNECTED;
- evhttp_connection_connect(evcon);
+ evhttp_connection_connect_(evcon);
}
static void
@@ -1283,16 +1379,28 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
{
struct evcon_requestq requests;
+ evhttp_connection_reset_(evcon);
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
+ struct timeval tv_retry = evcon->initial_retry_timeout;
+ int i;
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);
+ for (i=0; i < evcon->retry_cnt; ++i) {
+ tv_retry.tv_usec *= 2;
+ if (tv_retry.tv_usec > 1000000) {
+ tv_retry.tv_usec -= 1000000;
+ tv_retry.tv_sec += 1;
+ }
+ tv_retry.tv_sec *= 2;
+ if (tv_retry.tv_sec > 3600) {
+ tv_retry.tv_sec = 3600;
+ tv_retry.tv_usec = 0;
+ }
+ }
+ event_add(&evcon->retry_ev, &tv_retry);
evcon->retry_cnt++;
return;
}
- evhttp_connection_reset(evcon);
/*
* User callback can do evhttp_make_request() on the same
@@ -1315,16 +1423,43 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
/* we might want to set an error here */
request->cb(request, request->cb_arg);
- evhttp_request_free(request);
+ evhttp_request_free_auto(request);
}
}
static void
+evhttp_connection_read_on_write_error(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ struct evbuffer *buf;
+
+ /** Second time, we can't read anything */
+ if (evcon->flags & EVHTTP_CON_READING_ERROR) {
+ evcon->flags &= ~EVHTTP_CON_READING_ERROR;
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
+ return;
+ }
+
+ req->kind = EVHTTP_RESPONSE;
+
+ buf = bufferevent_get_output(evcon->bufev);
+ evbuffer_unfreeze(buf, 1);
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+ evbuffer_freeze(buf, 1);
+
+ evhttp_start_read_(evcon);
+ evcon->flags |= EVHTTP_CON_READING_ERROR;
+}
+
+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);
+ if (evcon->fd == -1)
+ evcon->fd = bufferevent_getfd(bufev);
+
switch (evcon->state) {
case EVCON_CONNECTING:
if (what & BEV_EVENT_TIMEOUT) {
@@ -1367,16 +1502,34 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
* disconnected.
*/
EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
- evhttp_connection_reset(evcon);
+ evhttp_connection_reset_(evcon);
+
+ /*
+ * If we have no more requests that need completion
+ * and we want to auto-free the connection when all
+ * requests have been completed.
+ */
+ if (TAILQ_FIRST(&evcon->requests) == NULL
+ && (evcon->flags & EVHTTP_CON_OUTGOING)
+ && (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ evhttp_connection_free(evcon);
+ }
return;
}
if (what & BEV_EVENT_TIMEOUT) {
- evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
- evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ if (what & BEV_EVENT_WRITING &&
+ evcon->flags & EVHTTP_CON_READ_ON_WRITE_ERROR) {
+ evhttp_connection_read_on_write_error(evcon, req);
+ return;
+ }
+
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
+ } else if (what == BEV_EVENT_CONNECTED) {
} else {
- evhttp_connection_fail(evcon, EVCON_HTTP_BUFFER_ERROR);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_BUFFER_ERROR);
}
}
@@ -1390,12 +1543,15 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
int error;
ev_socklen_t errsz = sizeof(error);
+ if (evcon->fd == -1)
+ evcon->fd = bufferevent_getfd(bufev);
+
if (!(what & BEV_EVENT_CONNECTED)) {
/* some operating systems return ECONNREFUSED immediately
* when connecting to a local address. the cleanup is going
* to reschedule this function call.
*/
-#ifndef WIN32
+#ifndef _WIN32
if (errno == ECONNREFUSED)
goto cleanup;
#endif
@@ -1403,6 +1559,12 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
return;
}
+ if (evcon->fd == -1) {
+ event_debug(("%s: bufferevent_getfd returned -1",
+ __func__));
+ goto cleanup;
+ }
+
/* Check if the connection completed */
if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&errsz) == -1) {
@@ -1437,14 +1599,12 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
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);
+ if (!evutil_timerisset(&evcon->timeout)) {
+ const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
+ const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
+ bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
+ } else {
+ bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
}
/* try to start requests that have queued up on this connection */
@@ -1510,6 +1670,8 @@ evhttp_parse_response_line(struct evhttp_request *req, char *line)
return (-1);
}
+ if (req->response_code_line != NULL)
+ mm_free(req->response_code_line);
if ((req->response_code_line = mm_strdup(readable)) == NULL) {
event_warn("%s: strdup", __func__);
return (-1);
@@ -1528,6 +1690,8 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
char *version;
const char *hostname;
const char *scheme;
+ size_t method_len;
+ enum evhttp_cmd_type type;
/* Parse the request line */
method = strsep(&line, " ");
@@ -1540,30 +1704,122 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
if (line != NULL)
return (-1);
+ method_len = (uri - method) - 1;
+ type = EVHTTP_REQ_UNKNOWN_;
+
/* 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",
+ switch (method_len) {
+ case 3:
+ /* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
+
+ /* Since both GET and PUT share the same character 'T' at the end,
+ * if the string doesn't have 'T', we can immediately determine this
+ * is an invalid HTTP method */
+
+ if (method[2] != 'T') {
+ break;
+ }
+
+ switch (*method) {
+ case 'G':
+ /* This first byte is 'G', so make sure the next byte is
+ * 'E', if it isn't then this isn't a valid method */
+
+ if (method[1] == 'E') {
+ type = EVHTTP_REQ_GET;
+ }
+
+ break;
+ case 'P':
+ /* First byte is P, check second byte for 'U', if not,
+ * we know it's an invalid method */
+ if (method[1] == 'U') {
+ type = EVHTTP_REQ_PUT;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 4:
+ /* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
+ switch (*method) {
+ case 'P':
+ if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
+ type = EVHTTP_REQ_POST;
+ }
+ break;
+ case 'H':
+ if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
+ type = EVHTTP_REQ_HEAD;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 5:
+ /* Method length is 5 bytes, which can only encompass PATCH and TRACE */
+ switch (*method) {
+ case 'P':
+ if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
+ type = EVHTTP_REQ_PATCH;
+ }
+ break;
+ case 'T':
+ if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
+ type = EVHTTP_REQ_TRACE;
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ case 6:
+ /* Method length is 6, only valid method 6 bytes in length is DELEte */
+
+ /* If the first byte isn't 'D' then it's invalid */
+ if (*method != 'D') {
+ break;
+ }
+
+ if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
+ type = EVHTTP_REQ_DELETE;
+ }
+
+ break;
+ case 7:
+ /* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
+ switch (*method) {
+ case 'O':
+ if (method[6] == 'S' && method[5] == 'N' && method[4] == 'O' &&
+ method[3] == 'I' && method[2] == 'T' && method[1] == 'P') {
+ type = EVHTTP_REQ_OPTIONS;
+ }
+
+ break;
+ case 'C':
+ if (method[6] == 'T' && method[5] == 'C' && method[4] == 'E' &&
+ method[3] == 'N' && method[2] == 'N' && method[1] == 'O') {
+ type = EVHTTP_REQ_CONNECT;
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ } /* switch */
+
+ if ((int)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. */
+ /* No error yet; we'll give a better error later when
+ * we see that req->type is unsupported. */
}
+
+ req->type = type;
if (evhttp_parse_http_version(version, req) < 0)
return (-1);
@@ -1719,7 +1975,7 @@ evhttp_add_header_internal(struct evkeyvalq *headers,
*/
enum message_read_status
-evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer)
+evhttp_parse_firstline_(struct evhttp_request *req, struct evbuffer *buffer)
{
char *line;
enum message_read_status status = ALL_DATA_READ;
@@ -1761,7 +2017,7 @@ evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer)
}
static int
-evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line)
+evhttp_append_to_last_header(struct evkeyvalq *headers, char *line)
{
struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq);
char *newval;
@@ -1771,20 +2027,27 @@ evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line)
return (-1);
old_len = strlen(header->value);
+
+ /* Strip space from start and end of line. */
+ while (*line == ' ' || *line == '\t')
+ ++line;
+ evutil_rtrim_lws_(line);
+
line_len = strlen(line);
- newval = mm_realloc(header->value, old_len + line_len + 1);
+ newval = mm_realloc(header->value, old_len + line_len + 2);
if (newval == NULL)
return (-1);
- memcpy(newval + old_len, line, line_len + 1);
+ newval[old_len] = ' ';
+ memcpy(newval + old_len + 1, line, line_len + 1);
header->value = newval;
return (0);
}
enum message_read_status
-evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
+evhttp_parse_headers_(struct evhttp_request *req, struct evbuffer* buffer)
{
enum message_read_status errcode = DATA_CORRUPTED;
char *line;
@@ -1825,6 +2088,7 @@ evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
goto error;
svalue += strspn(svalue, " ");
+ evutil_rtrim_lws_(svalue);
if (evhttp_add_header(headers, skey, svalue) == -1)
goto error;
@@ -1924,8 +2188,7 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
req->ntoread = -1;
} else {
if (evhttp_get_body_length(req) == -1) {
- evhttp_connection_fail(evcon,
- EVCON_HTTP_INVALID_HEADER);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
}
if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
@@ -1937,12 +2200,8 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
}
/* 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")) {
+ switch (evhttp_have_expect(req, 1)) {
+ case CONTINUE:
/* XXX It would be nice to do some sanity
checking here. Does the resource exist?
Should the resource accept post requests? If
@@ -1951,19 +2210,19 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
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);
+ if ((req->evcon->max_body_size <= EV_INT64_MAX) &&
+ (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
+ evhttp_lingering_fail(evcon, req);
return;
}
}
if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
evhttp_send_continue(evcon, req);
- } else {
- evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
- NULL);
- return;
- }
- }
+ break;
+ case OTHER:
+ evhttp_send_error(req, HTTP_EXPECTATIONFAILED, NULL);
+ return;
+ case NO: break;
}
evhttp_read_body(evcon, req);
@@ -1976,12 +2235,12 @@ evhttp_read_firstline(struct evhttp_connection *evcon,
{
enum message_read_status res;
- res = evhttp_parse_firstline(req, bufferevent_get_input(evcon->bufev));
+ 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);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
} else if (res == MORE_DATA_EXPECTED) {
/* Need more header lines */
@@ -1999,20 +2258,25 @@ evhttp_read_header(struct evhttp_connection *evcon,
enum message_read_status res;
evutil_socket_t fd = evcon->fd;
- res = evhttp_parse_headers(req, bufferevent_get_input(evcon->bufev));
+ 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);
+ evhttp_connection_fail_(evcon, EVREQ_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);
+ /* Callback can shut down connection with negative return value */
+ if (req->header_cb != NULL) {
+ if ((*req->header_cb)(req, req->cb_arg) < 0) {
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
+ return;
+ }
+ }
/* Done reading headers, do the real work */
switch (req->kind) {
@@ -2026,7 +2290,9 @@ evhttp_read_header(struct evhttp_connection *evcon,
case EVHTTP_RESPONSE:
/* Start over if we got a 100 Continue response. */
if (req->response_code == 100) {
- evhttp_start_read(evcon);
+ struct evbuffer *output = bufferevent_get_output(evcon->bufev);
+ evbuffer_add_buffer(output, req->output_buffer);
+ evhttp_start_write_(evcon);
return;
}
if (!evhttp_response_needs_body(req)) {
@@ -2046,7 +2312,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
default:
event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
EV_SOCK_ARG(fd));
- evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
break;
}
/* request may have been freed above */
@@ -2063,14 +2329,14 @@ evhttp_read_header(struct evhttp_connection *evcon,
*/
struct evhttp_connection *
-evhttp_connection_new(const char *address, unsigned short port)
+evhttp_connection_new(const char *address, ev_uint16_t port)
{
return (evhttp_connection_base_new(NULL, NULL, address, port));
}
struct evhttp_connection *
-evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
- const char *address, unsigned short port)
+evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
+ const char *address, ev_uint16_t port)
{
struct evhttp_connection *evcon = NULL;
@@ -2087,7 +2353,7 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
evcon->max_headers_size = EV_SIZE_MAX;
evcon->max_body_size = EV_SIZE_MAX;
- evcon->timeout = -1;
+ evutil_timerclear(&evcon->timeout);
evcon->retry_cnt = evcon->retry_max = 0;
if ((evcon->address = mm_strdup(address)) == NULL) {
@@ -2095,27 +2361,35 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
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;
+ if (bev == NULL) {
+ if (!(bev = bufferevent_socket_new(base, -1, 0))) {
+ event_warn("%s: bufferevent_socket_new failed", __func__);
+ goto error;
+ }
}
+ bufferevent_setcb(bev, evhttp_read_cb, evhttp_write_cb, evhttp_error_cb, evcon);
+ evcon->bufev = bev;
+
evcon->state = EVCON_DISCONNECTED;
TAILQ_INIT(&evcon->requests);
+ evcon->initial_retry_timeout.tv_sec = 2;
+ evcon->initial_retry_timeout.tv_usec = 0;
+
if (base != NULL) {
evcon->base = base;
- bufferevent_base_set(base, evcon->bufev);
+ if (bufferevent_get_base(bev) != base)
+ bufferevent_base_set(base, evcon->bufev);
}
-
- event_deferred_cb_init(&evcon->read_more_deferred_cb,
+ event_deferred_cb_init_(
+ &evcon->read_more_deferred_cb,
+ bufferevent_get_priority(bev),
evhttp_deferred_read_cb, evcon);
evcon->dns_base = dnsbase;
+ evcon->ai_family = AF_UNSPEC;
return (evcon);
@@ -2125,12 +2399,46 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
return (NULL);
}
-struct bufferevent *
-evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
+struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
{
return evcon->bufev;
}
+struct evhttp *
+evhttp_connection_get_server(struct evhttp_connection *evcon)
+{
+ return evcon->http_server;
+}
+
+struct evhttp_connection *
+evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
+ const char *address, ev_uint16_t port)
+{
+ return evhttp_connection_base_bufferevent_new(base, dnsbase, NULL, address, port);
+}
+
+void evhttp_connection_set_family(struct evhttp_connection *evcon,
+ int family)
+{
+ evcon->ai_family = family;
+}
+
+int evhttp_connection_set_flags(struct evhttp_connection *evcon,
+ int flags)
+{
+ int avail_flags = 0;
+ avail_flags |= EVHTTP_CON_REUSE_CONNECTED_ADDR;
+ avail_flags |= EVHTTP_CON_READ_ON_WRITE_ERROR;
+
+ if (flags & ~avail_flags || flags > EVHTTP_CON_PUBLIC_FLAGS_END)
+ return 1;
+ evcon->flags &= ~avail_flags;
+
+ evcon->flags |= flags;
+
+ return 0;
+}
+
void
evhttp_connection_set_base(struct evhttp_connection *evcon,
struct event_base *base)
@@ -2145,14 +2453,41 @@ void
evhttp_connection_set_timeout(struct evhttp_connection *evcon,
int timeout_in_secs)
{
- evcon->timeout = timeout_in_secs;
+ if (timeout_in_secs == -1)
+ evhttp_connection_set_timeout_tv(evcon, NULL);
+ else {
+ struct timeval tv;
+ tv.tv_sec = timeout_in_secs;
+ tv.tv_usec = 0;
+ evhttp_connection_set_timeout_tv(evcon, &tv);
+ }
+}
+
+void
+evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval* tv)
+{
+ if (tv) {
+ evcon->timeout = *tv;
+ bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
+ } else {
+ const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
+ const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
+ evutil_timerclear(&evcon->timeout);
+ bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
+ }
+}
- 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_initial_retry_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv)
+{
+ if (tv) {
+ evcon->initial_retry_timeout = *tv;
+ } else {
+ evutil_timerclear(&evcon->initial_retry_timeout);
+ evcon->initial_retry_timeout.tv_sec = 2;
+ }
}
void
@@ -2178,43 +2513,73 @@ evhttp_connection_get_peer(struct evhttp_connection *evcon,
*port = evcon->port;
}
+const struct sockaddr*
+evhttp_connection_get_addr(struct evhttp_connection *evcon)
+{
+ return bufferevent_socket_get_conn_address_(evcon->bufev);
+}
+
int
-evhttp_connection_connect(struct evhttp_connection *evcon)
+evhttp_connection_connect_(struct evhttp_connection *evcon)
{
int old_state = evcon->state;
+ const char *address = evcon->address;
+ const struct sockaddr *sa = evhttp_connection_get_addr(evcon);
+ int ret;
if (evcon->state == EVCON_CONNECTING)
return (0);
- evhttp_connection_reset(evcon);
+ 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);
+ if (evcon->bind_address || evcon->bind_port) {
+ 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);
+ }
+
+ bufferevent_setfd(evcon->bufev, evcon->fd);
+ } else {
+ bufferevent_setfd(evcon->bufev, -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);
+ if (!evutil_timerisset(&evcon->timeout)) {
+ const struct timeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 };
+ bufferevent_set_timeouts(evcon->bufev, &conn_tv, &conn_tv);
+ } else {
+ bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->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) {
+ if (evcon->flags & EVHTTP_CON_REUSE_CONNECTED_ADDR &&
+ sa &&
+ (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)) {
+ int socklen = sizeof(struct sockaddr_in);
+ if (sa->sa_family == AF_INET6) {
+ socklen = sizeof(struct sockaddr_in6);
+ }
+ ret = bufferevent_socket_connect(evcon->bufev, sa, socklen);
+ } else {
+ ret = bufferevent_socket_connect_hostname(evcon->bufev,
+ evcon->dns_base, evcon->ai_family, address, evcon->port);
+ }
+
+ if (ret < 0) {
evcon->state = old_state;
event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
__func__, evcon->address);
@@ -2247,7 +2612,7 @@ evhttp_make_request(struct evhttp_connection *evcon,
mm_free(req->uri);
if ((req->uri = mm_strdup(uri)) == NULL) {
event_warn("%s: strdup", __func__);
- evhttp_request_free(req);
+ evhttp_request_free_auto(req);
return (-1);
}
@@ -2261,17 +2626,17 @@ evhttp_make_request(struct evhttp_connection *evcon,
req->evcon = evcon;
EVUTIL_ASSERT(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
- TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+ 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);
+ 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
+ * remove it in the error case. */
+ if (res != 0)
+ TAILQ_REMOVE(&evcon->requests, req, next);
return res;
}
@@ -2297,8 +2662,8 @@ evhttp_cancel_request(struct evhttp_request *req)
/* it's currently being worked on, so reset
* the connection.
*/
- evhttp_connection_fail(evcon,
- EVCON_HTTP_REQUEST_CANCEL);
+ evhttp_connection_fail_(evcon,
+ EVREQ_HTTP_REQUEST_CANCEL);
/* connection fail freed the request */
return;
@@ -2310,7 +2675,7 @@ evhttp_cancel_request(struct evhttp_request *req)
}
}
- evhttp_request_free(req);
+ evhttp_request_free_auto(req);
}
/*
@@ -2319,11 +2684,11 @@ evhttp_cancel_request(struct evhttp_request *req)
*/
void
-evhttp_start_read(struct evhttp_connection *evcon)
+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,
@@ -2335,11 +2700,21 @@ evhttp_start_read(struct evhttp_connection *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),
+ event_deferred_cb_schedule_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
}
}
+void
+evhttp_start_write_(struct evhttp_connection *evcon)
+{
+ bufferevent_disable(evcon->bufev, EV_WRITE);
+ bufferevent_enable(evcon->bufev, EV_READ);
+
+ evcon->state = EVCON_WRITING;
+ evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
+}
+
static void
evhttp_send_done(struct evhttp_connection *evcon, void *arg)
{
@@ -2347,11 +2722,14 @@ evhttp_send_done(struct evhttp_connection *evcon, void *arg)
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
TAILQ_REMOVE(&evcon->requests, req, next);
+ if (req->on_complete_cb != NULL) {
+ req->on_complete_cb(req, req->on_complete_cb_arg);
+ }
+
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);
+ !evhttp_is_connection_keepalive(req->input_headers)) ||
+ evhttp_is_request_connection_close(req);
EVUTIL_ASSERT(req->flags & EVHTTP_REQ_OWN_CONNECTION);
evhttp_request_free(req);
@@ -2391,11 +2769,11 @@ evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
reason = evhttp_response_phrase_internal(error);
}
- evhttp_response_code(req, error, reason);
+ evhttp_response_code_(req, error, reason);
evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
- evhttp_send_page(req, buf);
+ evhttp_send_page_(req, buf);
evbuffer_free(buf);
#undef ERR_FORMAT
@@ -2432,7 +2810,7 @@ void
evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
struct evbuffer *databuf)
{
- evhttp_response_code(req, code, reason);
+ evhttp_response_code_(req, code, reason);
evhttp_send(req, databuf);
}
@@ -2441,7 +2819,7 @@ void
evhttp_send_reply_start(struct evhttp_request *req, int code,
const char *reason)
{
- evhttp_response_code(req, code, 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)) {
@@ -2461,7 +2839,8 @@ evhttp_send_reply_start(struct evhttp_request *req, int code,
}
void
-evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
+evhttp_send_reply_chunk_with_cb(struct evhttp_request *req, struct evbuffer *databuf,
+ void (*cb)(struct evhttp_connection *, void *), void *arg)
{
struct evhttp_connection *evcon = req->evcon;
struct evbuffer *output;
@@ -2483,10 +2862,15 @@ evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
if (req->chunked) {
evbuffer_add(output, "\r\n", 2);
}
- evhttp_write_buffer(evcon, NULL, NULL);
+ evhttp_write_buffer(evcon, cb, arg);
}
void
+evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ evhttp_send_reply_chunk_with_cb(req, databuf, NULL, NULL);
+}
+void
evhttp_send_reply_end(struct evhttp_request *req)
{
struct evhttp_connection *evcon = req->evcon;
@@ -2607,7 +2991,7 @@ evhttp_response_phrase_internal(int code)
}
void
-evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
+evhttp_response_code_(struct evhttp_request *req, int code, const char *reason)
{
req->kind = EVHTTP_RESPONSE;
req->response_code = code;
@@ -2623,7 +3007,7 @@ evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
}
void
-evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
+evhttp_send_page_(struct evhttp_request *req, struct evbuffer *databuf)
{
if (!req->major || !req->minor) {
req->major = 1;
@@ -2631,7 +3015,7 @@ evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
}
if (req->kind != EVHTTP_RESPONSE)
- evhttp_response_code(req, 200, "OK");
+ evhttp_response_code_(req, 200, "OK");
evhttp_clear_headers(req->output_headers);
evhttp_add_header(req->output_headers, "Content-Type", "text/html");
@@ -2677,13 +3061,31 @@ evhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
const char *p, *end;
char *result;
- if (buf == NULL)
+ if (buf == NULL) {
return (NULL);
+ }
- if (len >= 0)
- end = uri+len;
- else
- end = uri+strlen(uri);
+
+ if (len >= 0) {
+ if (uri + len < uri) {
+ return (NULL);
+ }
+
+ end = uri + len;
+ } else {
+ size_t slen = strlen(uri);
+
+ if (slen >= EV_SSIZE_MAX) {
+ /* we don't want to mix signed and unsigned */
+ return (NULL);
+ }
+
+ if (uri + slen < uri) {
+ return (NULL);
+ }
+
+ end = uri + slen;
+ }
for (p = uri; p < end; p++) {
if (CHAR_IS_UNRESERVED(*p)) {
@@ -2694,10 +3096,13 @@ evhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
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);
@@ -2715,7 +3120,7 @@ evhttp_encode_uri(const char *str)
* a ?. -1 is deprecated.
* @return the number of bytes written to 'ret'.
*/
-static int
+int
evhttp_decode_uri_internal(
const char *uri, size_t length, char *ret, int decode_plus_ctl)
{
@@ -2731,8 +3136,8 @@ evhttp_decode_uri_internal(
decode_plus = 1;
} else if (c == '+' && decode_plus) {
c = ' ';
- } else if (c == '%' && EVUTIL_ISXDIGIT(uri[i+1]) &&
- EVUTIL_ISXDIGIT(uri[i+2])) {
+ } else if ((i + 2) < length && c == '%' &&
+ EVUTIL_ISXDIGIT_(uri[i+1]) && EVUTIL_ISXDIGIT_(uri[i+2])) {
char tmp[3];
tmp[0] = uri[i+1];
tmp[1] = uri[i+2];
@@ -2920,7 +3325,7 @@ prefix_suffix_match(const char *pattern, const char *name, int ignorecase)
default:
if (c != *name) {
if (!ignorecase ||
- EVUTIL_TOLOWER(c) != EVUTIL_TOLOWER(*name))
+ EVUTIL_TOLOWER_(c) != EVUTIL_TOLOWER_(*name))
return (0);
}
++name;
@@ -3012,7 +3417,7 @@ evhttp_handle_request(struct evhttp_request *req, void *arg)
req->userdone = 0;
if (req->type == 0 || req->uri == NULL) {
- evhttp_send_error(req, HTTP_BADREQUEST, NULL);
+ evhttp_send_error(req, req->response_code, NULL);
return;
}
@@ -3061,13 +3466,13 @@ evhttp_handle_request(struct evhttp_request *req, void *arg)
return;
}
- evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
+ 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);
+ evhttp_send_page_(req, buf);
evbuffer_free(buf);
#undef ERR_FORMAT
@@ -3129,6 +3534,16 @@ evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd)
return (0);
}
+void
+evhttp_foreach_bound_socket(struct evhttp *http,
+ evhttp_bound_socket_foreach_fn *function,
+ void *argument)
+{
+ struct evhttp_bound_socket *bound;
+
+ TAILQ_FOREACH(bound, &http->sockets, next)
+ function(bound, argument);
+}
struct evhttp_bound_socket *
evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd)
@@ -3199,9 +3614,10 @@ evhttp_new_object(void)
return (NULL);
}
- http->timeout = -1;
+ evutil_timerclear(&http->timeout);
evhttp_set_max_headers_size(http, EV_SIZE_MAX);
evhttp_set_max_body_size(http, EV_SIZE_MAX);
+ evhttp_set_default_content_type(http, "text/html; charset=ISO-8859-1");
evhttp_set_allowed_methods(http,
EVHTTP_REQ_GET |
EVHTTP_REQ_POST |
@@ -3236,7 +3652,7 @@ evhttp_new(struct event_base *base)
*/
struct evhttp *
-evhttp_start(const char *address, unsigned short port)
+evhttp_start(const char *address, ev_uint16_t port)
{
struct evhttp *http = NULL;
@@ -3370,7 +3786,38 @@ evhttp_remove_server_alias(struct evhttp *http, const char *alias)
void
evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
{
- http->timeout = timeout_in_secs;
+ if (timeout_in_secs == -1) {
+ evhttp_set_timeout_tv(http, NULL);
+ } else {
+ struct timeval tv;
+ tv.tv_sec = timeout_in_secs;
+ tv.tv_usec = 0;
+ evhttp_set_timeout_tv(http, &tv);
+ }
+}
+
+void
+evhttp_set_timeout_tv(struct evhttp* http, const struct timeval* tv)
+{
+ if (tv) {
+ http->timeout = *tv;
+ } else {
+ evutil_timerclear(&http->timeout);
+ }
+}
+
+int evhttp_set_flags(struct evhttp *http, int flags)
+{
+ int avail_flags = 0;
+ avail_flags |= EVHTTP_SERVER_LINGERING_CLOSE;
+
+ if (flags & ~avail_flags)
+ return 1;
+ http->flags &= ~avail_flags;
+
+ http->flags |= flags;
+
+ return 0;
}
void
@@ -3392,6 +3839,12 @@ evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size)
}
void
+evhttp_set_default_content_type(struct evhttp *http,
+ const char *content_type) {
+ http->default_content_type = content_type;
+}
+
+void
evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods)
{
http->allowed_methods = methods;
@@ -3454,6 +3907,14 @@ evhttp_set_gencb(struct evhttp *http,
http->gencbarg = cbarg;
}
+void
+evhttp_set_bevcb(struct evhttp *http,
+ struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg)
+{
+ http->bevcb = cb;
+ http->bevcbarg = cbarg;
+}
+
/*
* Request related functions
*/
@@ -3573,6 +4034,28 @@ evhttp_request_set_chunked_cb(struct evhttp_request *req,
req->chunk_cb = cb;
}
+void
+evhttp_request_set_header_cb(struct evhttp_request *req,
+ int (*cb)(struct evhttp_request *, void *))
+{
+ req->header_cb = cb;
+}
+
+void
+evhttp_request_set_error_cb(struct evhttp_request *req,
+ void (*cb)(enum evhttp_request_error, void *))
+{
+ req->error_cb = cb;
+}
+
+void
+evhttp_request_set_on_complete_cb(struct evhttp_request *req,
+ void (*cb)(struct evhttp_request *, void *), void *cb_arg)
+{
+ req->on_complete_cb = cb;
+ req->on_complete_cb_arg = cb_arg;
+}
+
/*
* Allows for inspection of the request URI
*/
@@ -3611,7 +4094,7 @@ evhttp_request_get_host(struct evhttp_request *req)
to be consistent with uri_elems case above. */
if (host) {
p = host + strlen(host) - 1;
- while (p > host && EVUTIL_ISDIGIT(*p))
+ while (p > host && EVUTIL_ISDIGIT_(*p))
--p;
if (p > host && *p == ':') {
len = p - host;
@@ -3641,6 +4124,12 @@ evhttp_request_get_response_code(const struct evhttp_request *req)
return req->response_code;
}
+const char *
+evhttp_request_get_response_code_line(const struct evhttp_request *req)
+{
+ return req->response_code_line;
+}
+
/** Returns the input headers */
struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req)
{
@@ -3678,6 +4167,7 @@ evhttp_get_request_connection(
{
struct evhttp_connection *evcon;
char *hostname = NULL, *portname = NULL;
+ struct bufferevent* bev = NULL;
name_from_addr(sa, salen, &hostname, &portname);
if (hostname == NULL || portname == NULL) {
@@ -3690,8 +4180,11 @@ evhttp_get_request_connection(
__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));
+ if (http->bevcb != NULL) {
+ bev = (*http->bevcb)(http->base, http->bevcbarg);
+ }
+ evcon = evhttp_connection_base_bufferevent_new(
+ http->base, NULL, bev, hostname, atoi(portname));
mm_free(hostname);
mm_free(portname);
if (evcon == NULL)
@@ -3699,12 +4192,16 @@ evhttp_get_request_connection(
evcon->max_headers_size = http->default_max_headers_size;
evcon->max_body_size = http->default_max_body_size;
+ if (http->flags & EVHTTP_SERVER_LINGERING_CLOSE)
+ evcon->flags |= EVHTTP_CON_LINGERING_CLOSE;
evcon->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_READING_FIRSTLINE;
evcon->fd = fd;
+ bufferevent_enable(evcon->bufev, EV_READ);
+ bufferevent_disable(evcon->bufev, EV_WRITE);
bufferevent_setfd(evcon->bufev, fd);
return (evcon);
@@ -3740,7 +4237,7 @@ evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
req->kind = EVHTTP_REQUEST;
- evhttp_start_read(evcon);
+ evhttp_start_read_(evcon);
return (0);
}
@@ -3760,8 +4257,8 @@ evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
}
/* the timeout can be used by the server to close idle connections */
- if (http->timeout != -1)
- evhttp_connection_set_timeout(evcon, http->timeout);
+ if (evutil_timerisset(&http->timeout))
+ evhttp_connection_set_timeout_tv(evcon, &http->timeout);
/*
* if we want to accept more than one request on a connection,
@@ -3788,7 +4285,7 @@ name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
char strport[NI_MAXSERV];
int ni_result;
-#ifdef _EVENT_HAVE_GETNAMEINFO
+#ifdef EVENT__HAVE_GETNAMEINFO
ni_result = getnameinfo(sa, salen,
ntop, sizeof(ntop), strport, sizeof(strport),
NI_NUMERICHOST|NI_NUMERICSERV);
@@ -3826,17 +4323,13 @@ bind_socket_ai(struct evutil_addrinfo *ai, int reuse)
int serrno;
/* Create listen socket */
- fd = socket(ai ? ai->ai_family : AF_INET, SOCK_STREAM, 0);
+ fd = evutil_socket_(ai ? ai->ai_family : AF_INET,
+ SOCK_STREAM|EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC, 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) {
@@ -3946,10 +4439,10 @@ scheme_ok(const char *s, const char *eos)
EVUTIL_ASSERT(eos >= s);
if (s == eos)
return 0;
- if (!EVUTIL_ISALPHA(*s))
+ if (!EVUTIL_ISALPHA_(*s))
return 0;
while (++s < eos) {
- if (! EVUTIL_ISALNUM(*s) &&
+ if (! EVUTIL_ISALNUM_(*s) &&
*s != '+' && *s != '-' && *s != '.')
return 0;
}
@@ -3968,8 +4461,8 @@ userinfo_ok(const char *s, const char *eos)
*s == ':')
++s;
else if (*s == '%' && s+2 < eos &&
- EVUTIL_ISXDIGIT(s[1]) &&
- EVUTIL_ISXDIGIT(s[2]))
+ EVUTIL_ISXDIGIT_(s[1]) &&
+ EVUTIL_ISXDIGIT_(s[2]))
s += 3;
else
return 0;
@@ -3985,8 +4478,8 @@ regname_ok(const char *s, const char *eos)
strchr(SUBDELIMS, *s))
++s;
else if (*s == '%' &&
- EVUTIL_ISXDIGIT(s[1]) &&
- EVUTIL_ISXDIGIT(s[2]))
+ EVUTIL_ISXDIGIT_(s[1]) &&
+ EVUTIL_ISXDIGIT_(s[2]))
s += 3;
else
return 0;
@@ -3999,11 +4492,13 @@ parse_port(const char *s, const char *eos)
{
int portnum = 0;
while (s < eos) {
- if (! EVUTIL_ISDIGIT(*s))
+ if (! EVUTIL_ISDIGIT_(*s))
return -1;
portnum = (portnum * 10) + (*s - '0');
if (portnum < 0)
return -1;
+ if (portnum > 65535)
+ return -1;
++s;
}
return portnum;
@@ -4021,10 +4516,10 @@ bracket_addr_ok(const char *s, const char *eos)
*/
s += 2; /* skip [v */
--eos;
- if (!EVUTIL_ISXDIGIT(*s)) /*require at least one*/
+ if (!EVUTIL_ISXDIGIT_(*s)) /*require at least one*/
return 0;
while (s < eos && *s != '.') {
- if (EVUTIL_ISXDIGIT(*s))
+ if (EVUTIL_ISXDIGIT_(*s))
++s;
else
return 0;
@@ -4084,7 +4579,7 @@ parse_authority(struct evhttp_uri *uri, char *s, char *eos)
cp = s;
}
/* Optionally, we end with ":port" */
- for (port=eos-1; port >= cp && EVUTIL_ISDIGIT(*port); --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
@@ -4168,8 +4663,8 @@ end_of_path(char *cp, enum uri_part part, unsigned flags)
strchr(SUBDELIMS, *cp) ||
*cp == ':' || *cp == '@' || *cp == '/')
++cp;
- else if (*cp == '%' && EVUTIL_ISXDIGIT(cp[1]) &&
- EVUTIL_ISXDIGIT(cp[2]))
+ else if (*cp == '%' && EVUTIL_ISXDIGIT_(cp[1]) &&
+ EVUTIL_ISXDIGIT_(cp[2]))
cp += 3;
else if (*cp == '?' && part != PART_PATH)
++cp;
@@ -4329,20 +4824,20 @@ err:
void
evhttp_uri_free(struct evhttp_uri *uri)
{
-#define _URI_FREE_STR(f) \
+#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);
+ 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
+#undef URI_FREE_STR_
}
char *
@@ -4352,7 +4847,7 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
size_t joined_size = 0;
char *output = NULL;
-#define _URI_ADD(f) evbuffer_add(tmp, uri->f, strlen(uri->f))
+#define URI_ADD_(f) evbuffer_add(tmp, uri->f, strlen(uri->f))
if (!uri || !buf || !limit)
return NULL;
@@ -4362,14 +4857,14 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
return NULL;
if (uri->scheme) {
- _URI_ADD(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);
+ URI_ADD_(host);
if (uri->port >= 0)
evbuffer_add_printf(tmp,":%d", uri->port);
@@ -4378,16 +4873,16 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
}
if (uri->path)
- _URI_ADD(path);
+ URI_ADD_(path);
if (uri->query) {
evbuffer_add(tmp, "?", 1);
- _URI_ADD(query);
+ URI_ADD_(query);
}
if (uri->fragment) {
evbuffer_add(tmp, "#", 1);
- _URI_ADD(fragment);
+ URI_ADD_(fragment);
}
evbuffer_add(tmp, "\0", 1); /* NUL */
@@ -4406,7 +4901,7 @@ err:
evbuffer_free(tmp);
return output;
-#undef _URI_ADD
+#undef URI_ADD_
}
const char *
@@ -4445,7 +4940,7 @@ evhttp_uri_get_fragment(const struct evhttp_uri *uri)
return uri->fragment;
}
-#define _URI_SET_STR(f) do { \
+#define URI_SET_STR_(f) do { \
if (uri->f) \
mm_free(uri->f); \
if (f) { \
@@ -4464,7 +4959,7 @@ 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);
+ URI_SET_STR_(scheme);
return 0;
}
int
@@ -4472,7 +4967,7 @@ 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);
+ URI_SET_STR_(userinfo);
return 0;
}
int
@@ -4488,7 +4983,7 @@ evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
}
}
- _URI_SET_STR(host);
+ URI_SET_STR_(host);
return 0;
}
int
@@ -4508,7 +5003,7 @@ 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);
+ URI_SET_STR_(path);
return 0;
}
int
@@ -4516,7 +5011,7 @@ 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);
+ URI_SET_STR_(query);
return 0;
}
int
@@ -4524,6 +5019,6 @@ 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);
+ URI_SET_STR_(fragment);
return 0;
}