}
}
+void
+evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon,
+ ev_ssize_t new_max_headers_size)
+{
+ if (new_max_headers_size<0)
+ evcon->max_headers_size = EV_SIZE_MAX;
+ else
+ evcon->max_headers_size = new_max_headers_size;
+}
+void
+evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
+ ev_ssize_t new_max_body_size)
+{
+ if (new_max_body_size<0)
+ evcon->max_body_size = EV_UINT64_MAX;
+ else
+ evcon->max_body_size = new_max_body_size;
+}
+
static int
evhttp_connection_incoming_fail(struct evhttp_request *req,
enum evhttp_connection_error error)
* data is corrupted
* return REQUEST_CANCELED:
* request was canceled by the user calling evhttp_cancel_request
+ * return DATA_TOO_LONG:
+ * ran over the maximum limit
*/
static enum message_read_status
/* could not get chunk size */
return (DATA_CORRUPTED);
}
+ if (req->body_size + ntoread > req->evcon->max_body_size) {
+ /* failed body length test */
+ event_debug(("Request body is too long"));
+ return (DATA_TOO_LONG);
+ }
+ req->body_size += ntoread;
req->ntoread = ntoread;
if (req->ntoread == 0) {
/* Last chunk */
switch (evhttp_parse_headers(req, buf)) {
case DATA_CORRUPTED:
+ case DATA_TOO_LONG:
evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
break;
case ALL_DATA_READ:
evhttp_read_trailer(evcon, req);
return;
case DATA_CORRUPTED:
+ case DATA_TOO_LONG:/*separate error for this? XXX */
/* corrupted data */
evhttp_connection_fail(evcon,
EVCON_HTTP_INVALID_HEADER);
} else if (req->ntoread < 0) {
/* Read until connection close. */
evbuffer_add_buffer(req->input_buffer, buf);
+ req->body_size += evbuffer_get_length(buf);
} else if (req->chunk_cb != NULL ||
evbuffer_get_length(buf) >= req->ntoread) {
/* We've postponed moving the data until now, but we're
* about to use it. */
req->ntoread -= evbuffer_get_length(buf);
+ req->body_size += evbuffer_get_length(buf);
evbuffer_add_buffer(req->input_buffer, buf);
}
+ if (req->body_size > req->evcon->max_body_size) {
+ /* failed body length test */
+ event_debug(("Request body is too long"));
+ evhttp_connection_fail(evcon,
+ EVCON_HTTP_INVALID_HEADER);
+ return;
+ }
+
if (evbuffer_get_length(req->input_buffer) > 0 && req->chunk_cb != NULL) {
req->flags |= EVHTTP_REQ_DEFER_FREE;
(*req->chunk_cb)(req, req->cb_arg);
char *line;
enum message_read_status status = ALL_DATA_READ;
- line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF);
- if (line == NULL)
- return (MORE_DATA_EXPECTED);
+ size_t line_length;
+ /* XXX try */
+ line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
+ if (line == NULL) {
+ if (req->evcon != NULL &&
+ evbuffer_get_length(buffer) > req->evcon->max_headers_size)
+ return (DATA_TOO_LONG);
+ else
+ return (MORE_DATA_EXPECTED);
+ }
+
+ if (req->evcon != NULL &&
+ line_length > req->evcon->max_headers_size) {
+ mm_free(line);
+ return (DATA_TOO_LONG);
+ }
+
+ req->headers_size = line_length;
switch (req->kind) {
case EVHTTP_REQUEST:
enum message_read_status
evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
{
+ enum message_read_status errcode = DATA_CORRUPTED;
char *line;
enum message_read_status status = MORE_DATA_EXPECTED;
struct evkeyvalq* headers = req->input_headers;
- while ((line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF))
+ size_t line_length;
+ while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
!= NULL) {
char *skey, *svalue;
+ req->headers_size += line_length;
+
+ if (req->evcon != NULL &&
+ req->headers_size > req->evcon->max_headers_size) {
+ errcode = DATA_TOO_LONG;
+ goto error;
+ }
+
if (*line == '\0') { /* Last header - Done */
status = ALL_DATA_READ;
mm_free(line);
mm_free(line);
}
+ if (status == MORE_DATA_EXPECTED) {
+ if (req->headers_size + evbuffer_get_length(buffer) > req->evcon->max_headers_size)
+ return (DATA_TOO_LONG);
+ }
+
return (status);
error:
mm_free(line);
- return (DATA_CORRUPTED);
+ return (errcode);
}
static int
enum message_read_status res;
res = evhttp_parse_firstline(req, bufferevent_get_input(evcon->bufev));
- if (res == DATA_CORRUPTED) {
+ if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
/* Error while reading, terminate */
event_debug(("%s: bad header lines on %d\n",
__func__, evcon->fd));
int fd = evcon->fd;
res = evhttp_parse_headers(req, bufferevent_get_input(evcon->bufev));
- if (res == DATA_CORRUPTED) {
+ if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
/* Error while reading, terminate */
event_debug(("%s: bad header lines on %d\n", __func__, fd));
evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
evcon->fd = -1;
evcon->port = port;
+ evcon->max_headers_size = EV_SIZE_MAX;
+ evcon->max_body_size = EV_SIZE_MAX;
+
evcon->timeout = -1;
evcon->retry_cnt = evcon->retry_max = 0;
}
http->timeout = -1;
+ evhttp_set_max_headers_size(http, EV_SIZE_MAX);
+ evhttp_set_max_body_size(http, EV_SIZE_MAX);
TAILQ_INIT(&http->sockets);
TAILQ_INIT(&http->callbacks);
http->timeout = timeout_in_secs;
}
+void evhttp_set_max_headers_size(struct evhttp* http, ssize_t max_headers_size) {
+ http->default_max_headers_size = max_headers_size;
+}
+
+void evhttp_set_max_body_size(struct evhttp* http, ssize_t max_body_size) {
+ http->default_max_body_size = max_body_size;
+}
+
int
evhttp_set_cb(struct evhttp *http, const char *uri,
void (*cb)(struct evhttp_request *, void *), void *cbarg)
goto error;
}
+ req->headers_size = 0;
+ req->body_size = 0;
+
req->kind = EVHTTP_RESPONSE;
req->input_headers = mm_calloc(1, sizeof(struct evkeyvalq));
if (req->input_headers == NULL) {
if (evcon == NULL)
return (NULL);
+ evhttp_connection_set_max_headers_size(evcon, http->default_max_headers_size);
+ evhttp_connection_set_max_body_size(evcon, http->default_max_body_size);
+
evcon->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_READING_FIRSTLINE;
evhttp_free(http);
}
+
+static void
+http_data_length_constraints_test_done(struct evhttp_request *req, void *arg)
+{
+ tt_assert(req);
+ tt_int_op(req->response_code, ==, HTTP_BADREQUEST);
+end:
+ event_loopexit(NULL);
+}
+
+static void
+http_data_length_constraints_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+ char long_str[8192];
+
+ test_ok = 0;
+
+ http = http_setup(&port, NULL);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ tt_assert(evcon);
+
+ /* also bind to local host */
+ evhttp_connection_set_local_address(evcon, "127.0.0.1");
+
+ /*
+ * At this point, we want to schedule an HTTP GET request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ tt_assert(req);
+
+ memset(long_str, 'a', 8192);
+ long_str[8191] = '\0';
+ /* Add the information that we care about */
+ evhttp_set_max_headers_size(http, 8191);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evhttp_add_header(req->output_headers, "Longheader", long_str);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ tt_assert(req);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ /* GET /?arg=verylongvalue HTTP/1.1 */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ evhttp_set_max_body_size(http, 8190);
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evbuffer_add_printf(req->output_buffer, long_str);
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ test_ok = 1;
+ end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+ if (http)
+ evhttp_free(http);
+}
+
#define HTTP_LEGACY(name) \
{ #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
http_##name##_test }
HTTP_LEGACY(stream_in_cancel),
HTTP_LEGACY(connection_retry),
+ HTTP_LEGACY(data_length_constraints),
END_OF_TESTCASES
};