| Index: third_party/libevent/http.c
|
| diff --git a/third_party/libevent/http.c b/third_party/libevent/http.c
|
| index 8fce37ab1bf573fa31897477d8a6947ad505dd1a..b04ad54bab023fec84fc7ff2c32657a353f7eb91 100644
|
| --- a/third_party/libevent/http.c
|
| +++ b/third_party/libevent/http.c
|
| @@ -88,7 +88,6 @@
|
| #include "evutil.h"
|
| #include "log.h"
|
| #include "http-internal.h"
|
| -#include "event-internal.h"
|
|
|
| #ifdef WIN32
|
| #define strcasecmp _stricmp
|
| @@ -103,7 +102,7 @@
|
| #define NI_NUMERICHOST 1
|
| #define NI_NUMERICSERV 2
|
|
|
| -int
|
| +static int
|
| fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
|
| size_t hostlen, char *serv, size_t servlen, int flags)
|
| {
|
| @@ -212,6 +211,10 @@ static void evhttp_read_firstline(struct evhttp_connection *evcon,
|
| struct evhttp_request *req);
|
| static void evhttp_read_header(struct evhttp_connection *evcon,
|
| struct evhttp_request *req);
|
| +static int evhttp_add_header_internal(struct evkeyvalq *headers,
|
| + const char *key, const char *value);
|
| +static int evhttp_decode_uri_internal(const char *uri, size_t length,
|
| + char *ret, int always_decode_plus);
|
|
|
| void evhttp_read(int, short, void *);
|
| void evhttp_write(int, short, void *);
|
| @@ -369,23 +372,20 @@ evhttp_connected(struct evhttp_connection *evcon)
|
| }
|
|
|
| /*
|
| - * Create the headers need for an HTTP request
|
| + * Create the headers needed for an HTTP request
|
| */
|
| static void
|
| evhttp_make_header_request(struct evhttp_connection *evcon,
|
| struct evhttp_request *req)
|
| {
|
| - char line[1024];
|
| const char *method;
|
|
|
| - evhttp_remove_header(req->output_headers, "Accept-Encoding");
|
| evhttp_remove_header(req->output_headers, "Proxy-Connection");
|
|
|
| /* Generate request line */
|
| method = evhttp_method(req->type);
|
| - evutil_snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
|
| + evbuffer_add_printf(evcon->output_buffer, "%s %s HTTP/%d.%d\r\n",
|
| method, req->uri, req->major, req->minor);
|
| - evbuffer_add(evcon->output_buffer, line, strlen(line));
|
|
|
| /* Add the content length on a post request if missing */
|
| if (req->type == EVHTTP_REQ_POST &&
|
| @@ -462,11 +462,9 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
|
| struct evhttp_request *req)
|
| {
|
| int is_keepalive = evhttp_is_connection_keepalive(req->input_headers);
|
| - char line[1024];
|
| - evutil_snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
|
| + evbuffer_add_printf(evcon->output_buffer, "HTTP/%d.%d %d %s\r\n",
|
| req->major, req->minor, req->response_code,
|
| req->response_code_line);
|
| - evbuffer_add(evcon->output_buffer, line, strlen(line));
|
|
|
| if (req->major == 1) {
|
| if (req->minor == 1)
|
| @@ -513,7 +511,6 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
|
| void
|
| evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
| {
|
| - char line[1024];
|
| struct evkeyval *header;
|
|
|
| /*
|
| @@ -527,9 +524,8 @@ evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
| }
|
|
|
| TAILQ_FOREACH(header, req->output_headers, next) {
|
| - evutil_snprintf(line, sizeof(line), "%s: %s\r\n",
|
| + evbuffer_add_printf(evcon->output_buffer, "%s: %s\r\n",
|
| header->key, header->value);
|
| - evbuffer_add(evcon->output_buffer, line, strlen(line));
|
| }
|
| evbuffer_add(evcon->output_buffer, "\r\n", 2);
|
|
|
| @@ -826,8 +822,8 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
|
|
|
| /* Completed chunk */
|
| evbuffer_add(req->input_buffer,
|
| - EVBUFFER_DATA(buf), req->ntoread);
|
| - evbuffer_drain(buf, req->ntoread);
|
| + EVBUFFER_DATA(buf), (size_t)req->ntoread);
|
| + evbuffer_drain(buf, (size_t)req->ntoread);
|
| req->ntoread = -1;
|
| if (req->chunk_cb != NULL) {
|
| (*req->chunk_cb)(req, req->cb_arg);
|
| @@ -891,8 +887,8 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
| } else if (EVBUFFER_LENGTH(buf) >= req->ntoread) {
|
| /* Completed content length */
|
| evbuffer_add(req->input_buffer, EVBUFFER_DATA(buf),
|
| - req->ntoread);
|
| - evbuffer_drain(buf, req->ntoread);
|
| + (size_t)req->ntoread);
|
| + evbuffer_drain(buf, (size_t)req->ntoread);
|
| req->ntoread = 0;
|
| evhttp_connection_done(evcon);
|
| return;
|
| @@ -1040,6 +1036,13 @@ evhttp_connection_set_local_address(struct evhttp_connection *evcon,
|
| event_err(1, "%s: strdup", __func__);
|
| }
|
|
|
| +void
|
| +evhttp_connection_set_local_port(struct evhttp_connection *evcon,
|
| + unsigned short port)
|
| +{
|
| + assert(evcon->state == EVCON_DISCONNECTED);
|
| + evcon->bind_port = port;
|
| +}
|
|
|
| static void
|
| evhttp_request_dispatch(struct evhttp_connection* evcon)
|
| @@ -1080,6 +1083,11 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
|
| evcon->fd = -1;
|
| }
|
| evcon->state = EVCON_DISCONNECTED;
|
| +
|
| + evbuffer_drain(evcon->input_buffer,
|
| + EVBUFFER_LENGTH(evcon->input_buffer));
|
| + evbuffer_drain(evcon->output_buffer,
|
| + EVBUFFER_LENGTH(evcon->output_buffer));
|
| }
|
|
|
| static void
|
| @@ -1352,22 +1360,46 @@ evhttp_remove_header(struct evkeyvalq *headers, const char *key)
|
| return (0);
|
| }
|
|
|
| +static int
|
| +evhttp_header_is_valid_value(const char *value)
|
| +{
|
| + const char *p = value;
|
| +
|
| + while ((p = strpbrk(p, "\r\n")) != NULL) {
|
| + /* we really expect only one new line */
|
| + p += strspn(p, "\r\n");
|
| + /* we expect a space or tab for continuation */
|
| + if (*p != ' ' && *p != '\t')
|
| + return (0);
|
| + }
|
| + return (1);
|
| +}
|
| +
|
| int
|
| evhttp_add_header(struct evkeyvalq *headers,
|
| const char *key, const char *value)
|
| {
|
| - struct evkeyval *header = NULL;
|
| -
|
| event_debug(("%s: key: %s val: %s\n", __func__, key, value));
|
|
|
| - if (strchr(value, '\r') != NULL || strchr(value, '\n') != NULL ||
|
| - strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
|
| + if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
|
| /* drop illegal headers */
|
| - event_debug(("%s: dropping illegal header\n", __func__));
|
| + event_debug(("%s: dropping illegal header key\n", __func__));
|
| + return (-1);
|
| + }
|
| +
|
| + if (!evhttp_header_is_valid_value(value)) {
|
| + event_debug(("%s: dropping illegal header value\n", __func__));
|
| return (-1);
|
| }
|
|
|
| - header = calloc(1, sizeof(struct evkeyval));
|
| + return (evhttp_add_header_internal(headers, key, value));
|
| +}
|
| +
|
| +static int
|
| +evhttp_add_header_internal(struct evkeyvalq *headers,
|
| + const char *key, const char *value)
|
| +{
|
| + struct evkeyval *header = calloc(1, sizeof(struct evkeyval));
|
| if (header == NULL) {
|
| event_warn("%s: calloc", __func__);
|
| return (-1);
|
| @@ -1470,6 +1502,7 @@ evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
|
| if (*line == ' ' || *line == '\t') {
|
| if (evhttp_append_to_last_header(headers, line) == -1)
|
| goto error;
|
| + free(line);
|
| continue;
|
| }
|
|
|
| @@ -1733,7 +1766,8 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
|
| assert(!(evcon->flags & EVHTTP_CON_INCOMING));
|
| evcon->flags |= EVHTTP_CON_OUTGOING;
|
|
|
| - evcon->fd = bind_socket(evcon->bind_address, 0 /*port*/, 0 /*reuse*/);
|
| + 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));
|
| @@ -1900,8 +1934,6 @@ void
|
| evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
|
| struct evbuffer *databuf)
|
| {
|
| - /* set up to watch for client close */
|
| - evhttp_connection_start_detectclose(req->evcon);
|
| evhttp_response_code(req, code, reason);
|
|
|
| evhttp_send(req, databuf);
|
| @@ -1911,8 +1943,6 @@ void
|
| evhttp_send_reply_start(struct evhttp_request *req, int code,
|
| const char *reason)
|
| {
|
| - /* set up to watch for client close */
|
| - evhttp_connection_start_detectclose(req->evcon);
|
| evhttp_response_code(req, code, reason);
|
| if (req->major == 1 && req->minor == 1) {
|
| /* use chunked encoding for HTTP/1.1 */
|
| @@ -2031,16 +2061,16 @@ evhttp_encode_uri(const char *uri)
|
| return (p);
|
| }
|
|
|
| -char *
|
| -evhttp_decode_uri(const char *uri)
|
| +/*
|
| + * @param always_decode_plus: when true we transform plus to space even
|
| + * if we have not seen a ?.
|
| + */
|
| +static int
|
| +evhttp_decode_uri_internal(
|
| + const char *uri, size_t length, char *ret, int always_decode_plus)
|
| {
|
| - char c, *ret;
|
| - int i, j, in_query = 0;
|
| -
|
| - ret = malloc(strlen(uri) + 1);
|
| - if (ret == NULL)
|
| - event_err(1, "%s: malloc(%lu)", __func__,
|
| - (unsigned long)(strlen(uri) + 1));
|
| + char c;
|
| + int i, j, in_query = always_decode_plus;
|
|
|
| for (i = j = 0; uri[i] != '\0'; i++) {
|
| c = uri[i];
|
| @@ -2057,14 +2087,28 @@ evhttp_decode_uri(const char *uri)
|
| ret[j++] = c;
|
| }
|
| ret[j] = '\0';
|
| -
|
| +
|
| + return (j);
|
| +}
|
| +
|
| +char *
|
| +evhttp_decode_uri(const char *uri)
|
| +{
|
| + char *ret;
|
| +
|
| + if ((ret = malloc(strlen(uri) + 1)) == NULL)
|
| + event_err(1, "%s: malloc(%lu)", __func__,
|
| + (unsigned long)(strlen(uri) + 1));
|
| +
|
| + evhttp_decode_uri_internal(uri, strlen(uri),
|
| + ret, 0 /*always_decode_plus*/);
|
| +
|
| return (ret);
|
| }
|
|
|
| /*
|
| * Helper function to parse out arguments in a query.
|
| * The arguments are separated by key and value.
|
| - * URI should already be decoded.
|
| */
|
|
|
| void
|
| @@ -2091,7 +2135,7 @@ evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
|
|
|
| p = argument;
|
| while (p != NULL && *p != '\0') {
|
| - char *key, *value;
|
| + char *key, *value, *decoded_value;
|
| argument = strsep(&p, "&");
|
|
|
| value = argument;
|
| @@ -2099,10 +2143,14 @@ evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
|
| if (value == NULL)
|
| goto error;
|
|
|
| - value = evhttp_decode_uri(value);
|
| - event_debug(("Query Param: %s -> %s\n", key, value));
|
| - evhttp_add_header(headers, key, value);
|
| - free(value);
|
| + if ((decoded_value = malloc(strlen(value) + 1)) == NULL)
|
| + event_err(1, "%s: malloc", __func__);
|
| +
|
| + evhttp_decode_uri_internal(value, strlen(value),
|
| + decoded_value, 1 /*always_decode_plus*/);
|
| + event_debug(("Query Param: %s -> %s\n", key, decoded_value));
|
| + evhttp_add_header_internal(headers, key, decoded_value);
|
| + free(decoded_value);
|
| }
|
|
|
| error:
|
| @@ -2489,11 +2537,20 @@ evhttp_get_request_connection(
|
| char *hostname = NULL, *portname = NULL;
|
|
|
| name_from_addr(sa, salen, &hostname, &portname);
|
| + if (hostname == NULL || portname == NULL) {
|
| + if (hostname) free(hostname);
|
| + if (portname) free(portname);
|
| + return (NULL);
|
| + }
|
| +
|
| event_debug(("%s: new request from %s:%s on %d\n",
|
| __func__, hostname, portname, fd));
|
|
|
| /* we need a connection object to put the http request on */
|
| - if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
|
| + evcon = evhttp_connection_new(hostname, atoi(portname));
|
| + free(hostname);
|
| + free(portname);
|
| + if (evcon == NULL)
|
| return (NULL);
|
|
|
| /* associate the base if we have one*/
|
| @@ -2615,12 +2672,12 @@ name_from_addr(struct sockaddr *sa, socklen_t salen,
|
| if (ni_result != 0)
|
| return;
|
| #endif
|
| - *phost = ntop;
|
| - *pport = strport;
|
| + *phost = strdup(ntop);
|
| + *pport = strdup(strport);
|
| }
|
|
|
| -/* Either connect or bind */
|
| -
|
| +/* Create a non-blocking socket and bind it */
|
| +/* todo: rename this function */
|
| static int
|
| bind_socket_ai(struct addrinfo *ai, int reuse)
|
| {
|
| @@ -2650,9 +2707,11 @@ bind_socket_ai(struct addrinfo *ai, int reuse)
|
| (void *)&on, sizeof(on));
|
| }
|
|
|
| - r = bind(fd, ai->ai_addr, ai->ai_addrlen);
|
| - if (r == -1)
|
| - goto out;
|
| + if (ai != NULL) {
|
| + r = bind(fd, ai->ai_addr, ai->ai_addrlen);
|
| + if (r == -1)
|
| + goto out;
|
| + }
|
|
|
| return (fd);
|
|
|
| @@ -2705,7 +2764,13 @@ static int
|
| bind_socket(const char *address, u_short port, int reuse)
|
| {
|
| int fd;
|
| - struct addrinfo *aitop = make_addrinfo(address, port);
|
| + struct addrinfo *aitop = NULL;
|
| +
|
| + /* just create an unbound socket */
|
| + if (address == NULL && port == 0)
|
| + return bind_socket_ai(NULL, 0);
|
| +
|
| + aitop = make_addrinfo(address, port);
|
|
|
| if (aitop == NULL)
|
| return (-1);
|
|
|