Projects
Mega:23.03
libevent
_service:tar_scm:http-add-callback-to-allow-ser...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:http-add-callback-to-allow-server-to-decline-and-the.patch of Package libevent
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); + + for (n = 0; n < sizeof(evcons)/sizeof(evcons[0]); ++n) { + struct evhttp_connection* evcon = NULL; + struct evhttp_request *req = NULL; + evcons[n] = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + evcon = evcons[n]; + evhttp_connection_set_retries(evcon, 0); + + tt_assert(evcon); + + req = evhttp_request_new(http_request_done_newreqcb, &newreqcb_test_state); + evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); + evhttp_request_set_error_cb(req, http_request_error_newreqcb); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + ++newreqcb_test_state.connections_started; + http_newreqcb_test_state_check(&newreqcb_test_state); + } + + event_base_dispatch(data->base); + + http_newreqcb_test_state_check(&newreqcb_test_state); + tt_int_op(newreqcb_test_state.connections_throttled, >, 0); + + end: + evhttp_free(http); + + for (n = 0; n < sizeof(evcons)/sizeof(evcons[0]); ++n) { + if (evcons[n]) + evhttp_connection_free(evcons[n]); + + } + +} + + #define HTTP_LEGACY(name) \ { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ http_##name##_test } @@ -4725,6 +4848,8 @@ struct testcase_t http_testcases[] = { HTTP(request_extra_body), + HTTP(newreqcb), + #ifdef EVENT__HAVE_OPENSSL HTTPS(basic), HTTPS(filter_basic), -- 1.8.3.1
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2