]> granicus.if.org Git - libevent/commitdiff
http: allow setting bevcb per socket
authorLeon M. George <leon@georgemail.eu>
Mon, 1 Aug 2022 08:16:18 +0000 (10:16 +0200)
committerAzat Khuzhin <azat@libevent.org>
Sat, 13 Aug 2022 18:12:18 +0000 (20:12 +0200)
Co-authored-by: Azat Khuzhin <azat@libevent.org>
v2: remove handling of HTTP_BIND_IPV6

http-internal.h
http.c
include/event2/http.h
test/regress_http.c

index a5844e1de1c32c8515e4d755569176be2944f725..705daba29f9548ad821e36f3b59948d9f7f92ff6 100644 (file)
@@ -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 d578735dd815d5961e5d345161127c57064d669a..9072fcec16a8defdf3b60f0ffb7bb5102f3c5e23 100644 (file)
--- 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));
index 27cbd35edeb958dda4399d711125249c271dad7c..50c0a27ba445419e4aa3923df8de54bedd8b4425 100644 (file)
@@ -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
index d950e644c4000e99e76230c7d8402356db1c860b..3f6b71b1a841a141e24e07b79fe50b339712f5f7 100644 (file)
@@ -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