From 46ee061ca011346ec3d99c896cae8571847bdf70 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 25 Oct 2010 11:47:05 -0400 Subject: [PATCH 1/2] Add a function to change a listener's callback. You can also now initialize listeners with no callbacks set; if so, they won't get enabled until the callback is set to non-NULL. --- include/event2/listener.h | 13 +++++++++++-- listener.c | 41 +++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/include/event2/listener.h b/include/event2/listener.h index dededc50..0cb8583c 100644 --- a/include/event2/listener.h +++ b/include/event2/listener.h @@ -75,7 +75,9 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *); on a given file descriptor. @param base The event base to associate the listener with. - @param cb A callback to be invoked when a new connection arrives. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. @param ptr A user-supplied pointer to give to the callback. @param flags Any number of LEV_OPT_* flags @param backlog Passed to the listen() call to determine the length of the @@ -93,7 +95,9 @@ struct evconnlistener *evconnlistener_new(struct event_base *base, on a given address. @param base The event base to associate the listener with. - @param cb A callback to be invoked when a new connection arrives. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. @param ptr A user-supplied pointer to give to the callback. @param flags Any number of LEV_OPT_* flags @param backlog Passed to the listen() call to determine the length of the @@ -123,6 +127,11 @@ struct event_base *evconnlistener_get_base(struct evconnlistener *lev); /** Return the socket that an evconnlistner is listening on. */ evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev); +/** Change the callback on the listener to cb and its user_data to arg. + */ +void evconnlistener_set_cb(struct evconnlistener *lev, + evconnlistener_cb cb, void *arg); + /** Set an evconnlistener's error callback. */ void evconnlistener_set_error_cb(struct evconnlistener *lev, evconnlistener_errorcb errorcb); diff --git a/listener.c b/listener.c index 1ff0f491..3052169d 100644 --- a/listener.c +++ b/listener.c @@ -73,7 +73,8 @@ struct evconnlistener { evconnlistener_errorcb errorcb; void *user_data; unsigned flags; - int refcnt; + short refcnt; + unsigned enabled : 1; }; struct evconnlistener_event { @@ -89,7 +90,6 @@ struct evconnlistener_iocp { struct event_iocp_port *port; short n_accepting; unsigned shutting_down : 1; - unsigned enabled : 1; struct accepting_socket **accepting; }; #endif @@ -185,6 +185,7 @@ evconnlistener_new(struct event_base *base, event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST, listener_read_cb, lev); + evconnlistener_enable(&lev->base); return &lev->base; @@ -268,7 +269,11 @@ evconnlistener_enable(struct evconnlistener *lev) { int r; LOCK(lev); - r = lev->ops->enable(lev); + lev->enabled = 1; + if (lev->cb) + r = lev->ops->enable(lev); + else + r = 0; UNLOCK(lev); return r; } @@ -278,6 +283,7 @@ evconnlistener_disable(struct evconnlistener *lev) { int r; LOCK(lev); + lev->enabled = 0; r = lev->ops->disable(lev); UNLOCK(lev); return r; @@ -335,7 +341,23 @@ event_listener_getbase(struct evconnlistener *lev) return event_get_base(&lev_e->listener); } -void evconnlistener_set_error_cb(struct evconnlistener *lev, +void +evconnlistener_set_cb(struct evconnlistener *lev, + evconnlistener_cb cb, void *arg) +{ + int enable = 0; + LOCK(lev); + if (lev->enabled && !lev->cb) + enable = 1; + lev->cb = cb; + lev->user_data = arg; + if (enable) + evconnlistener_enable(lev); + UNLOCK(lev); +} + +void +evconnlistener_set_error_cb(struct evconnlistener *lev, evconnlistener_errorcb errorcb) { LOCK(lev); @@ -474,7 +496,7 @@ start_accepting(struct accepting_socket *as) SOCKET s = socket(as->family, SOCK_STREAM, 0); int error = 0; - if (!as->lev->enabled) + if (!as->lev->base.enabled) return 0; if (s == INVALID_SOCKET) { @@ -574,7 +596,7 @@ accepted_socket_invoke_user_cb(struct deferred_cb *dcb, void *arg) if (errorcb) { WSASetLastError(error); errorcb(lev, data); - } else { + } else if (cb) { cb(lev, sock, sa_remote, socklen_remote, data); } @@ -638,7 +660,6 @@ iocp_listener_enable(struct evconnlistener *lev) EVUTIL_UPCAST(lev, struct evconnlistener_iocp, base); LOCK(lev); - lev_iocp->enabled = 1; for (i = 0; i < lev_iocp->n_accepting; ++i) { struct accepting_socket *as = lev_iocp->accepting[i]; if (!as) @@ -660,7 +681,6 @@ iocp_listener_disable_impl(struct evconnlistener *lev, int shutdown) EVUTIL_UPCAST(lev, struct evconnlistener_iocp, base); LOCK(lev); - lev_iocp->enabled = 0; for (i = 0; i < lev_iocp->n_accepting; ++i) { struct accepting_socket *as = lev_iocp->accepting[i]; if (!as) @@ -760,11 +780,12 @@ evconnlistener_new_async(struct event_base *base, lev->base.user_data = ptr; lev->base.flags = flags; lev->base.refcnt = 1; + lev->base.enabled = 1; lev->port = event_base_get_iocp(base); lev->fd = fd; lev->event_base = base; - lev->enabled = 1; + if (event_iocp_port_associate(lev->port, fd, 1) < 0) goto err_free_lev; @@ -784,7 +805,7 @@ evconnlistener_new_async(struct event_base *base, event_warnx("Couldn't create accepting socket"); goto err_free_accepting; } - if (start_accepting(lev->accepting[i]) < 0) { + if (cb && start_accepting(lev->accepting[i]) < 0) { event_warnx("Couldn't start accepting on socket"); EnterCriticalSection(&lev->accepting[i]->lock); free_and_unlock_accepting_socket(lev->accepting[i]); From 006efa7dbbcaec4d412670107b893f04d38f0d83 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 25 Oct 2010 11:50:51 -0400 Subject: [PATCH 2/2] Functions to actually use evhttp_bound_socket with/as evconnlistener. --- http.c | 38 ++++++++++++++++++++++++++++---------- include/event2/http.h | 13 +++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/http.c b/http.c index c88522db..162f955a 100644 --- a/http.c +++ b/http.c @@ -2764,23 +2764,35 @@ evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd) const int flags = LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE; + listener = evconnlistener_new(http->base, NULL, NULL, + flags, + 0, /* Backlog is '0' because we already said 'listen' */ + fd); + if (!listener) + return (NULL); + + bound = evhttp_bind_listener(http, listener); + if (!bound) { + evconnlistener_free(listener); + return (NULL); + } + return (bound); +} + +struct evhttp_bound_socket * +evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener) +{ + struct evhttp_bound_socket *bound; + bound = mm_malloc(sizeof(struct evhttp_bound_socket)); if (bound == NULL) return (NULL); - listener = evconnlistener_new(http->base, accept_socket_cb, http, - flags, - 0, /* Backlog is '0' because we already said 'listen' */ - fd); - if (!listener) { - mm_free(bound); - return (NULL); - } bound->listener = listener; - TAILQ_INSERT_TAIL(&http->sockets, bound, next); - return (bound); + evconnlistener_set_cb(listener, accept_socket_cb, http); + return bound; } evutil_socket_t evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound) @@ -2788,6 +2800,12 @@ evutil_socket_t evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound) return evconnlistener_get_fd(bound->listener); } +struct evconnlistener * +evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound) +{ + return bound->listener; +} + void evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound) { diff --git a/include/event2/http.h b/include/event2/http.h index 5e96934d..66246f84 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -63,6 +63,7 @@ struct evhttp; struct evhttp_request; struct evkeyvalq; struct evhttp_bound_socket; +struct evconnlistener; /** * Create a new HTTP server. @@ -130,6 +131,18 @@ int evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd); */ struct evhttp_bound_socket *evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd); +/** + * The most low-level evhttp_bind/accept method: takes an evconnlistener, and + * returns an evhttp_bound_socket. The listener will be freed when the bound + * socket is freed. + */ +struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener); + +/** + * Return the listener used to implement a bound socket. + */ +struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound); + /** * Makes an HTTP server stop accepting connections on the specified socket *