]> granicus.if.org Git - libevent/commitdiff
http: lingering close (like nginx have) for entity-too-large
authorAzat Khuzhin <a3at.mail@gmail.com>
Sun, 14 Feb 2016 21:12:54 +0000 (00:12 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Wed, 9 Mar 2016 15:52:07 +0000 (18:52 +0300)
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
http.c
include/event2/http.h

index ba6e49ef9b9b379ec5e1f1508cee4546f671813c..31002e0d2afca8ced15e581f3d0196ab94eacd72 100644 (file)
@@ -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 a2d9f878854d522a07a773fb08a93071c13d0baa..5df8791d5d8fe889c539d65d907c96b35ca3a6d1 100644 (file)
--- 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;
index c7ed4ccf6cd21a8d4a80f03e239336965657bb19..10a7e975f2238df6daa6412307963651900e6f9e 100644 (file)
@@ -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
 /**