diff options
author | Elliott Hughes <enh@google.com> | 2017-08-07 14:18:18 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2017-08-07 16:58:18 -0700 |
commit | 2a572d125a91a4aafd3ad8ce87259fc640fa0763 (patch) | |
tree | bd05e575a4a2629cc420c5e2bc54f176fee611ab /http.c | |
parent | 596447c7ff2881a67e7082c905112584c3e61a17 (diff) | |
download | libevent-2a572d125a91a4aafd3ad8ce87259fc640fa0763.tar.gz |
Upgrade to 2.1.8-stable (2017-01-22).
Bug: N/A
Test: builds
Change-Id: Idbbdc1db3d01984a4f4b60f8fdf455140b6b7ca6
Diffstat (limited to 'http.c')
-rw-r--r-- | http.c | 1107 |
1 files changed, 801 insertions, 306 deletions
@@ -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; } |