From 9fde5189df2722bf58041f03a9d0b69c87dffa8b Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Mon, 15 Feb 2016 00:12:54 +0300 Subject: [PATCH] http: lingering close (like nginx have) for entity-too-large By lingering close I mean something what nginx have for this name, by this term I mean that we need to read all the body even if it's size greater then `max_body_size`, otherwise browsers on win32 (including chrome) failed read the http status - entity-too-large (while on linux chrome for instance are good), and also this includes badly written http clients. Refs: #321 v2: do this only under EVHTTP_SERVER_LINGERING_CLOSE --- http-internal.h | 1 + http.c | 46 +++++++++++++++++++++++++++++++++++++++---- include/event2/http.h | 15 ++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/http-internal.h b/http-internal.h index ba6e49ef9b..31002e0d2a 100644 --- a/http-internal.h +++ b/http-internal.h @@ -154,6 +154,7 @@ struct evhttp { size_t default_max_headers_size; ev_uint64_t default_max_body_size; + int flags; const char *default_content_type; /* Bitmask of all HTTP methods that we accept and pass to user diff --git a/http.c b/http.c index a2d9f87885..5df8791d5d 100644 --- a/http.c +++ b/http.c @@ -984,6 +984,25 @@ evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req) } } +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_SIZE_FMT, + EV_SIZE_ARG(req->ntoread))); + + evbuffer_drain(buf, n); + if (!req->ntoread) + evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG); +} + static void evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) { @@ -1037,9 +1056,12 @@ 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, - EVREQ_HTTP_DATA_TOO_LONG); + + if (evcon->flags & EVHTTP_CON_LINGERING_CLOSE) + evhttp_lingering_close(evcon, req); + else + evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG); + return; } @@ -1055,7 +1077,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) } } - if (req->ntoread == 0) { + if (!req->ntoread) { bufferevent_disable(evcon->bufev, EV_READ); /* Completed content length */ evhttp_connection_done(evcon); @@ -3719,6 +3741,20 @@ evhttp_set_timeout_tv(struct evhttp* http, const struct timeval* tv) } } +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 evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size) { @@ -4091,6 +4127,8 @@ 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; diff --git a/include/event2/http.h b/include/event2/http.h index c7ed4ccf6c..10a7e975f2 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -373,6 +373,19 @@ void evhttp_set_timeout(struct evhttp *http, int timeout_in_secs); EVENT2_EXPORT_SYMBOL void evhttp_set_timeout_tv(struct evhttp *http, const struct timeval* tv); +/* Read all the clients body, and only after this respond with an error if the + * clients body exceed max_body_size */ +#define EVHTTP_SERVER_LINGERING_CLOSE 0x0001 +/** + * Set connection flags for HTTP server. + * + * @see EVHTTP_SERVER_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_set_flags(struct evhttp *http, int flags); + /* Request/Response functionality */ /** @@ -643,6 +656,8 @@ void evhttp_connection_set_family(struct evhttp_connection *evcon, * connection, but if at that time we have some data to send then we * can send get EPIPE and fail, while we can read that HTTP error. */ #define EVHTTP_CON_READ_ON_WRITE_ERROR 0x0010 +/* @see EVHTTP_SERVER_LINGERING_CLOSE */ +#define EVHTTP_CON_LINGERING_CLOSE 0x0020 /* Padding for public flags, @see EVHTTP_CON_* in http-internal.h */ #define EVHTTP_CON_PUBLIC_FLAGS_END 0x100000 /**