@@ -0,0 +1,255 @@
+From 727bcea130eb4beea9b1ea53604b9807f6819a9a Mon Sep 17 00:00:00 2001
+From: John Fremlin <john@fremlin.org>
+Date: Fri, 1 Dec 2017 01:29:32 +0000
+Subject: [PATCH 103/319] http: add callback to allow server to decline (and
+ thereby close) incoming connections.
+
+This is important, as otherwise clients can easily exhaust the file
+descriptors available on a libevent HTTP server, which can cause
+problems in other code which does not handle EMFILE well: for example,
+see https://github.com/bitcoin/bitcoin/issues/11368
+
+Closes: #578 (patch cherry picked)
+---
+ http-internal.h | 2 +
+ http.c | 25 +++++++---
+ include/event2/http.h | 18 ++++++++
+ test/regress_http.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 163 insertions(+), 7 deletions(-)
+
+diff --git a/http-internal.h b/http-internal.h
+index b7d21ef..9e5b0f9 100644
+--- a/http-internal.h
++++ b/http-internal.h
+@@ -170,6 +170,8 @@ struct evhttp {
+ void *gencbarg;
+ struct bufferevent* (*bevcb)(struct event_base *, void *);
+ void *bevcbarg;
++ int (*newreqcb)(struct evhttp_request *req, void *);
++ void *newreqcbarg;
+
+ struct event_base *base;
+ };
+diff --git a/http.c b/http.c
+index b3087b5..f2e4971 100644
+--- a/http.c
++++ b/http.c
+@@ -3929,6 +3929,14 @@ evhttp_set_bevcb(struct evhttp *http,
+ http->bevcbarg = cbarg;
+ }
+
++void
++evhttp_set_newreqcb(struct evhttp *http,
++ int (*cb)(struct evhttp_request *, void *), void *cbarg)
++{
++ http->newreqcb = cb;
++ http->newreqcbarg = cbarg;
++}
++
+ /*
+ * Request related functions
+ */
+@@ -4239,17 +4247,20 @@ evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
+ req->evcon = evcon; /* the request ends up owning the connection */
+ req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+
+- /* We did not present the request to the user user yet, so treat it as
+- * if the user was done with the request. This allows us to free the
+- * request on a persistent connection if the client drops it without
+- * sending a request.
++ /* We did not present the request to the user yet, so treat it
++ * as if the user was done with the request. This allows us
++ * to free the request on a persistent connection if the
++ * client drops it without sending a request.
+ */
+ req->userdone = 1;
+-
+- TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+-
+ req->kind = EVHTTP_REQUEST;
+
++ if (http->newreqcb && http->newreqcb(req, http->newreqcbarg) == -1) {
++ evhttp_request_free(req);
++ return (-1);
++ }
++
++ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ evhttp_start_read_(evcon);
+
+diff --git a/include/event2/http.h b/include/event2/http.h
+index 2a41303..ed9acf4 100644
+--- a/include/event2/http.h
++++ b/include/event2/http.h
+@@ -298,6 +298,24 @@ EVENT2_EXPORT_SYMBOL
+ void evhttp_set_bevcb(struct evhttp *http,
+ struct bufferevent *(*cb)(struct event_base *, void *), void *arg);
+
++
++/**
++ Set a callback which allows the user to note or throttle incoming requests.
++
++ The requests are not populated with HTTP level information. They
++ are just associated to a connection.
++
++ If the callback returns -1, the associated connection is terminated
++ and the request is closed.
++
++ @param http the evhttp server object for which to set the callback
++ @param cb the callback to invoke for incoming connections
++ @param arg an context argument for the callback
++ */
++EVENT2_EXPORT_SYMBOL
++void evhttp_set_newreqcb(struct evhttp *http,
++ int (*cb)(struct evhttp_request*, void *), void *arg);
++
+ /**
+ Adds a virtual host to the http server.
+
+diff --git a/test/regress_http.c b/test/regress_http.c
+index b761df0..c459910 100644
+--- a/test/regress_http.c
++++ b/test/regress_http.c
+@@ -4604,6 +4604,129 @@ http_request_extra_body_test(void *arg)
+ evbuffer_free(body);
+ }
+
++struct http_newreqcb_test_state
++{
++ int connections_started;
++ int connections_noticed;
++ int connections_throttled;
++ int connections_good;
++ int connections_error;
++ int connections_done;
++};
++
++static void
++http_newreqcb_test_state_check(struct http_newreqcb_test_state* state)
++{
++ tt_int_op(state->connections_started, >=, 0);
++ tt_int_op(state->connections_started, >=, state->connections_noticed);
++ tt_int_op(state->connections_throttled, >=, state->connections_error);
++
++ tt_int_op(state->connections_done, <=, state->connections_started);
++ if (state->connections_good + state->connections_error == state->connections_started) {
++ tt_int_op(state->connections_throttled, ==, state->connections_error);
++ tt_int_op(state->connections_good + state->connections_error, ==, state->connections_done);
++ event_base_loopexit(exit_base, NULL);
++ }
++
++ return;
++end:
++ tt_fail();
++ exit(17);
++}
++
++static void
++http_request_done_newreqcb(struct evhttp_request *req, void *arg)
++{
++ struct http_newreqcb_test_state* state = arg;
++ if (req && evhttp_request_get_response_code(req) == HTTP_OK) {
++ ++state->connections_good;
++ evhttp_request_set_error_cb(req, NULL);
++ }
++ ++state->connections_done;
++
++ http_newreqcb_test_state_check(state);
++}
++
++static void
++http_request_error_newreqcb(enum evhttp_request_error err, void *arg)
++{
++ struct http_newreqcb_test_state* state = arg;
++ ++state->connections_error;
++
++ http_newreqcb_test_state_check(state);
++}
++
++static int
++http_newreqcb(struct evhttp_request* req, void *arg)
++{
++ struct http_newreqcb_test_state* state = arg;
++ ++state->connections_noticed;
++ http_newreqcb_test_state_check(state);
++ if (1 == state->connections_noticed % 7) {
++ state->connections_throttled++;
++ return -1;
++ }
++ return 0;
++}
++
++
++static void
++http_newreqcb_test(void *arg)
++{
++ struct basic_test_data *data = arg;
++ ev_uint16_t port = 0;
++ struct evhttp *http = http_setup(&port, data->base, 0);
++ struct evhttp_connection *evcons[100];
++ struct http_newreqcb_test_state newreqcb_test_state;
++ unsigned n;
++
++ exit_base = data->base;
++ test_ok = 0;
++
++ memset(&newreqcb_test_state, 0, sizeof(newreqcb_test_state));
++ memset(evcons, 0, sizeof(evcons));
++
++ evhttp_set_newreqcb(http, http_newreqcb, &newreqcb_test_state);
|
|
Deleted |
_service:tar_git:libevent-2.1.8+main.20221222221948.14be4a4.tar.bz2
^
|
|
Added |
_service:tar_git:libevent-2.1.8+main.20221222223105.cef13df.tar.bz2
^
|