Skip to content

Commit

Permalink
Merge pull request libevent#1315 from yogo1212/http_per_socket_bebcb
Browse files Browse the repository at this point in the history
In it's current form, libevent requires multiple struct evhttp objects to be created in order to enable listening on sockets with more than one type of encryption.

This change allows specifying per-socket how the associated bufferevents should be created.
Thus, it becomes possible to have multiple listening sockets with different encryption parameters using only one evttp.
  • Loading branch information
azat authored Aug 13, 2022
2 parents c198b0c + 1bdc913 commit 77a9b60
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 10 deletions.
4 changes: 4 additions & 0 deletions http-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
37 changes: 27 additions & 10 deletions http.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand All @@ -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)
{
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}

Expand All @@ -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(
Expand Down Expand Up @@ -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));
Expand Down
10 changes: 10 additions & 0 deletions include/event2/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
93 changes: 93 additions & 0 deletions test/regress_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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[] = {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions test/regress_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ get_ssl_ctx(void)
void
init_ssl(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;

#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
Expand Down

0 comments on commit 77a9b60

Please sign in to comment.