From 1bdc91350ee30d3aa51fe809fd719c2c3183b445 Mon Sep 17 00:00:00 2001 From: "Leon M. George" Date: Mon, 1 Aug 2022 10:16:18 +0200 Subject: [PATCH] http: allow setting bevcb per socket Co-authored-by: Azat Khuzhin v2: remove handling of HTTP_BIND_IPV6 --- http-internal.h | 4 ++ http.c | 37 ++++++++++++----- include/event2/http.h | 10 +++++ test/regress_http.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 10 deletions(-) diff --git a/http-internal.h b/http-internal.h index a5844e1d..705daba2 100644 --- a/http-internal.h +++ b/http-internal.h @@ -128,6 +128,10 @@ TAILQ_HEAD(evconq, evhttp_connection); struct evhttp_bound_socket { TAILQ_ENTRY(evhttp_bound_socket) next; + struct evhttp *http; + struct bufferevent* (*bevcb)(struct event_base *, void *); + void *bevcbarg; + struct evconnlistener *listener; }; diff --git a/http.c b/http.c index d578735d..9072fcec 100644 --- a/http.c +++ b/http.c @@ -197,7 +197,7 @@ static void evhttp_read_header(struct evhttp_connection *evcon, static int evhttp_add_header_internal(struct evkeyvalq *headers, const char *key, const char *value); static const char *evhttp_response_phrase_internal(int code); -static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t); +static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t, struct bufferevent *bev); static void evhttp_write_buffer(struct evhttp_connection *, void (*)(struct evhttp_connection *, void *), void *); static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *); @@ -3795,9 +3795,15 @@ evhttp_handle_request(struct evhttp_request *req, void *arg) static void accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg) { - struct evhttp *http = arg; + struct evhttp_bound_socket *bound = arg; + + struct evhttp *http = bound->http; - evhttp_get_request(http, nfd, peer_sa, peer_socklen); + struct bufferevent *bev = NULL; + if (bound->bevcb) + bev = bound->bevcb(http->base, bound->bevcbarg); + + evhttp_get_request(http, nfd, peer_sa, peer_socklen, bev); } int @@ -3893,9 +3899,11 @@ evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener) return (NULL); bound->listener = listener; + bound->bevcb = NULL; + bound->http = http; TAILQ_INSERT_TAIL(&http->sockets, bound, next); - evconnlistener_set_cb(listener, accept_socket_cb, http); + evconnlistener_set_cb(listener, accept_socket_cb, bound); return bound; } @@ -3911,6 +3919,14 @@ evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound) return bound->listener; } +void +evhttp_bound_set_bevcb(struct evhttp_bound_socket *bound, + struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg) +{ + bound->bevcb = cb; + bound->bevcbarg = cbarg; +} + void evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound) { @@ -4517,10 +4533,10 @@ struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req) static struct evhttp_connection* evhttp_get_request_connection( struct evhttp* http, - evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen) + evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen, + struct bufferevent* bev) { struct evhttp_connection *evcon; - struct bufferevent* bev = NULL; #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN if (sa->sa_family == AF_UNIX) { @@ -4537,7 +4553,7 @@ evhttp_get_request_connection( EV_SOCK_FMT"\n", __func__, EV_SOCK_ARG(fd))); /* we need a connection object to put the http request on */ - if (http->bevcb != NULL) { + if (!bev && http->bevcb != NULL) { bev = (*http->bevcb)(http->base, http->bevcbarg); } @@ -4560,7 +4576,7 @@ evhttp_get_request_connection( __func__, hostname, portname, EV_SOCK_ARG(fd))); /* we need a connection object to put the http request on */ - if (http->bevcb != NULL) { + if (!bev && http->bevcb != NULL) { bev = (*http->bevcb)(http->base, http->bevcbarg); } evcon = evhttp_connection_base_bufferevent_new( @@ -4636,11 +4652,12 @@ evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon) static void evhttp_get_request(struct evhttp *http, evutil_socket_t fd, - struct sockaddr *sa, ev_socklen_t salen) + struct sockaddr *sa, ev_socklen_t salen, + struct bufferevent *bev) { struct evhttp_connection *evcon; - evcon = evhttp_get_request_connection(http, fd, sa, salen); + evcon = evhttp_get_request_connection(http, fd, sa, salen, bev); if (evcon == NULL) { event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT, __func__, EV_SOCK_ARG(fd)); diff --git a/include/event2/http.h b/include/event2/http.h index 27cbd35e..50c0a27b 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -172,6 +172,14 @@ struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evc EVENT2_EXPORT_SYMBOL struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound); +/* + * Like evhttp_set_bevcb. + * If cb returns a non-NULL bufferevent, * the callback supplied through + * evhttp_set_bevcb isn't used. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_bound_set_bevcb(struct evhttp_bound_socket *bound, struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg); + typedef void evhttp_bound_socket_foreach_fn(struct evhttp_bound_socket *, void *); /** * Applies the function specified in the first argument to all @@ -333,6 +341,8 @@ void evhttp_set_gencb(struct evhttp *http, /** Set a callback used to create new bufferevents for connections to a given evhttp object. + cb is not called if a non-NULL bufferevent was supplied by + evhttp_bound_set_bevcb. You can use this to override the default bufferevent type -- for example, to make this evhttp object use SSL bufferevents rather than unencrypted diff --git a/test/regress_http.c b/test/regress_http.c index d950e644..3f6b71b1 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -4309,6 +4309,93 @@ static void http_simple_test(void *arg) static void http_simple_nonconformant_test(void *arg) { http_simple_test_impl(arg, 0, 0, "/test nonconformant"); } +static int +https_bind_ssl_bevcb(struct evhttp *http, ev_uint16_t port, ev_uint16_t *pport, int mask) +{ + int _port; + struct evhttp_bound_socket *sock = NULL; + sock = evhttp_bind_socket_with_handle(http, "127.0.0.1", port); + +#ifdef EVENT__HAVE_OPENSSL + if (mask & HTTP_OPENSSL) { + init_ssl(); + evhttp_bound_set_bevcb(sock, https_bev, NULL); + } +#endif +#ifdef EVENT__HAVE_MBEDTLS + if (mask & HTTP_MBEDTLS) { + evhttp_bound_set_bevcb(sock, https_mbedtls_bev, NULL); + } +#endif + + _port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); + if (_port < 0) + return -1; + + if (pport) + *pport = (ev_uint16_t)_port; + + return 0; +} +static void +https_per_socket_bevcb_impl(void *arg, ev_uint16_t http_port, ev_uint16_t https_port, int mask) +{ + struct bufferevent *bev; + struct basic_test_data *data = arg; + struct evhttp_connection *evcon = NULL; + struct evhttp *http = NULL; + ev_uint16_t new_https_port = 0; + struct evhttp_request *req = NULL; + + http = evhttp_new(data->base); + tt_assert(http); + + evhttp_bind_socket_with_handle(http, "127.0.0.1", http_port); + + tt_assert(https_bind_ssl_bevcb(http, https_port, &new_https_port, mask) == 0); + + evhttp_set_gencb(http, http_basic_cb, http); + + bev = create_bev(data->base, -1, mask, 0); + +#ifdef EVENT__HAVE_OPENSSL + bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); +#endif +#ifdef EVENT__HAVE_MBEDTLS + bufferevent_mbedtls_set_allow_dirty_shutdown(bev, 1); +#endif + + evcon = evhttp_connection_base_bufferevent_new(data->base, NULL, bev, "127.0.0.1", new_https_port); + tt_assert(evcon); + + evhttp_connection_set_timeout(evcon, 1); + /* make sure to use the same address that is used by http */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + req = evhttp_request_new(http_request_done, (void *) BASIC_REQUEST_BODY); + tt_assert(req); + + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + evhttp_request_free(req); + TT_GRIPE(("make_request_failed")); + goto end; + } + + exit_base = data->base; + event_base_dispatch(data->base); + +end: + if (evcon) + evhttp_connection_free(evcon); + + if (http) + evhttp_free(http); +} +static void https_per_socket_bevcb(void *arg, int ssl) +{ https_per_socket_bevcb_impl(arg, 0, 0, ssl); } + static void http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base *dns_base, int ssl) { @@ -5758,6 +5845,8 @@ static void https_connection_test(void *arg) { http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, HTTP_OPENSSL); } static void https_persist_connection_test(void *arg) { http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, HTTP_OPENSSL); } +static void https_per_socket_bevcb_test(void *arg) +{ https_per_socket_bevcb_impl(arg, 0, 0, HTTP_OPENSSL); } #endif #ifdef EVENT__HAVE_MBEDTLS @@ -5791,6 +5880,8 @@ static void https_mbedtls_connection_test(void *arg) { http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, HTTP_MBEDTLS); } static void https_mbedtls_persist_connection_test(void *arg) { http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, HTTP_MBEDTLS); } +static void https_mbedtls_per_socket_bevcb_test(void *arg) +{ https_per_socket_bevcb_impl(arg, 0, 0, HTTP_MBEDTLS); } #endif struct testcase_t http_testcases[] = { @@ -5910,6 +6001,7 @@ struct testcase_t http_testcases[] = { HTTPS(write_during_read), HTTPS(connection), HTTPS(persist_connection), + HTTPS(per_socket_bevcb), #endif #ifdef EVENT__HAVE_MBEDTLS @@ -5929,6 +6021,7 @@ struct testcase_t http_testcases[] = { HTTPS_MBEDTLS(write_during_read), HTTPS_MBEDTLS(connection), HTTPS_MBEDTLS(persist_connection), + HTTPS_MBEDTLS(per_socket_bevcb), #endif END_OF_TESTCASES -- 2.40.0