Skip to content

Commit

Permalink
http: lingering close (like nginx have) for entity-too-large
Browse files Browse the repository at this point in the history
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
  • Loading branch information
azat committed Mar 9, 2016
1 parent 680742e commit 9fde518
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
1 change: 1 addition & 0 deletions http-internal.h
Expand Up @@ -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
Expand Down
46 changes: 42 additions & 4 deletions http.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions include/event2/http.h
Expand Up @@ -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 */

/**
Expand Down Expand Up @@ -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
/**
Expand Down

0 comments on commit 9fde518

Please sign in to comment.