diff options
-rw-r--r-- | buffer.c | 5 | ||||
-rw-r--r-- | bufferevent_async.c | 6 | ||||
-rw-r--r-- | cmake/CheckPrototypeDefinition.cmake | 3 | ||||
-rw-r--r-- | evdns.c | 13 | ||||
-rw-r--r-- | evutil.c | 14 | ||||
-rw-r--r-- | http.c | 2 | ||||
-rw-r--r-- | include/event2/listener.h | 9 | ||||
-rw-r--r-- | include/event2/util.h | 13 | ||||
-rw-r--r-- | listener.c | 5 | ||||
-rw-r--r-- | test/regress_buffer.c | 37 |
10 files changed, 96 insertions, 11 deletions
@@ -2742,7 +2742,10 @@ evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, if (n_vec == 0 && len < 0) { /* If no vectors are provided and they asked for "everything", * pretend they asked for the actual available amount. */ - len = buffer->total_len - len_so_far; + len = buffer->total_len; + if (start_at) { + len -= start_at->pos; + } } while (chain) { diff --git a/bufferevent_async.c b/bufferevent_async.c index f32573e4..6395e57a 100644 --- a/bufferevent_async.c +++ b/bufferevent_async.c @@ -381,9 +381,10 @@ be_async_destruct(struct bufferevent *bev) bev_async_del_write(bev_async); fd = evbuffer_overlapped_get_fd_(bev->input); - if (bev_p->options & BEV_OPT_CLOSE_ON_FREE) { - /* XXXX possible double-close */ + if (fd != (evutil_socket_t)INVALID_SOCKET && + (bev_p->options & BEV_OPT_CLOSE_ON_FREE)) { evutil_closesocket(fd); + evbuffer_overlapped_set_fd_(bev->input, INVALID_SOCKET); } } @@ -671,6 +672,7 @@ be_async_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op, if (fd != (evutil_socket_t)INVALID_SOCKET && (bev_a->bev.options & BEV_OPT_CLOSE_ON_FREE)) { closesocket(fd); + evbuffer_overlapped_set_fd_(bev->input, INVALID_SOCKET); } bev_a->ok = 0; return 0; diff --git a/cmake/CheckPrototypeDefinition.cmake b/cmake/CheckPrototypeDefinition.cmake index 5e6ba4bb..e0c6a572 100644 --- a/cmake/CheckPrototypeDefinition.cmake +++ b/cmake/CheckPrototypeDefinition.cmake @@ -52,8 +52,7 @@ function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIAB set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE}) set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN}) - # TODO: Fix this. If the Module path has more than one entry, the below will fail. - configure_file("${CMAKE_MODULE_PATH}/CheckPrototypeDefinition.c.in" + configure_file("${PROJECT_SOURCE_DIR}/cmake/CheckPrototypeDefinition.c.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY) file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE) @@ -2175,7 +2175,10 @@ evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) { log(EVDNS_LOG_DEBUG, "Giving up on request %p; tx_count==%d", arg, req->tx_count); reply_schedule_callback(req, 0, DNS_ERR_TIMEOUT, NULL); + + struct nameserver *ns = req->ns; request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1); + nameserver_failed(ns, "request timed out."); } else { /* retransmit it */ log(EVDNS_LOG_DEBUG, "Retransmitting request %p; tx_count==%d", @@ -2183,12 +2186,12 @@ evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) { (void) evtimer_del(&req->timeout_event); request_swap_ns(req, nameserver_pick(base)); evdns_request_transmit(req); - } - req->ns->timedout++; - if (req->ns->timedout > req->base->global_max_nameserver_timeout) { - req->ns->timedout = 0; - nameserver_failed(req->ns, "request timed out."); + req->ns->timedout++; + if (req->ns->timedout > req->base->global_max_nameserver_timeout) { + req->ns->timedout = 0; + nameserver_failed(req->ns, "request timed out."); + } } EVDNS_UNLOCK(base); @@ -368,6 +368,20 @@ evutil_make_listen_socket_reuseable(evutil_socket_t sock) } int +evutil_make_listen_socket_reuseable_port(evutil_socket_t sock) +{ +#if defined __linux__ && defined(SO_REUSEPORT) + int one = 1; + /* REUSEPORT on Linux 3.9+ means, "Multiple servers (processes or + * threads) can bind to the same port if they each set the option. */ + return setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void*) &one, + (ev_socklen_t)sizeof(one)); +#else + return 0; +#endif +} + +int evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock) { #if defined(EVENT__HAVE_NETINET_TCP_H) && defined(TCP_DEFER_ACCEPT) @@ -1285,6 +1285,7 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon) { struct evcon_requestq requests; + evhttp_connection_reset_(evcon); if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) { struct timeval tv_retry = evcon->initial_retry_timeout; int i; @@ -1306,7 +1307,6 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon) evcon->retry_cnt++; return; } - evhttp_connection_reset_(evcon); /* * User callback can do evhttp_make_request() on the same diff --git a/include/event2/listener.h b/include/event2/listener.h index 8c77803d..84b4da05 100644 --- a/include/event2/listener.h +++ b/include/event2/listener.h @@ -88,6 +88,15 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *); * to use the option before it is actually bound. */ #define LEV_OPT_DEFERRED_ACCEPT (1u<<6) +/** Flag: Indicates that we ask to allow multiple servers (processes or + * threads) to bind to the same port if they each set the option. + * + * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however + * SO_REUSEPORT does not imply SO_REUSEADDR. + * + * This is only available on Linux and kernel 3.9+ + */ +#define LEV_OPT_REUSEABLE_PORT (1u<<7) /** Allocate a new evconnlistener object to listen for incoming TCP connections diff --git a/include/event2/util.h b/include/event2/util.h index 14c6a25d..62b94773 100644 --- a/include/event2/util.h +++ b/include/event2/util.h @@ -327,6 +327,19 @@ int evutil_make_socket_nonblocking(evutil_socket_t sock); EVENT2_EXPORT_SYMBOL int evutil_make_listen_socket_reuseable(evutil_socket_t sock); +/** Do platform-specific operations to make a listener port reusable. + + Specifically, we want to make sure that multiple programs which also + set the same socket option will be able to bind, listen at the same time. + + This is a feature available only to Linux 3.9+ + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock); + /** Do platform-specific operations as needed to close a socket upon a successful execution of one of the exec*() functions. @@ -235,6 +235,11 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, goto err; } + if (flags & LEV_OPT_REUSEABLE_PORT) { + if (evutil_make_listen_socket_reuseable_port(fd) < 0) + goto err; + } + if (flags & LEV_OPT_DEFERRED_ACCEPT) { if (evutil_make_tcp_listen_socket_deferred(fd) < 0) goto err; diff --git a/test/regress_buffer.c b/test/regress_buffer.c index aa1cd2a7..4d6a5de2 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -1844,6 +1844,42 @@ end: } static void +test_evbuffer_peek_first_gt(void *info) +{ + struct evbuffer *buf = NULL, *tmp_buf = NULL; + struct evbuffer_ptr ptr; + struct evbuffer_iovec v[2]; + + buf = evbuffer_new(); + tmp_buf = evbuffer_new(); + evbuffer_add_printf(tmp_buf, "Contents of chunk 100\n"); + evbuffer_add_buffer(buf, tmp_buf); + evbuffer_add_printf(tmp_buf, "Contents of chunk 1\n"); + evbuffer_add_buffer(buf, tmp_buf); + + evbuffer_ptr_set(buf, &ptr, 0, EVBUFFER_PTR_SET); + + /** The only case that matters*/ + tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2); + /** Just in case */ + tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2); + + evbuffer_ptr_set(buf, &ptr, 20, EVBUFFER_PTR_ADD); + tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2); + tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2); + tt_int_op(evbuffer_peek(buf, 2, &ptr, NULL, 0), ==, 1); + tt_int_op(evbuffer_peek(buf, 2, &ptr, v, 2), ==, 1); + tt_int_op(evbuffer_peek(buf, 3, &ptr, NULL, 0), ==, 2); + tt_int_op(evbuffer_peek(buf, 3, &ptr, v, 2), ==, 2); + +end: + if (buf) + evbuffer_free(buf); + if (tmp_buf) + evbuffer_free(tmp_buf); +} + +static void test_evbuffer_peek(void *info) { struct evbuffer *buf = NULL, *tmp_buf = NULL; @@ -2210,6 +2246,7 @@ struct testcase_t evbuffer_testcases[] = { { "multicast_drain", test_evbuffer_multicast_drain, 0, NULL, NULL }, { "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL }, { "peek", test_evbuffer_peek, 0, NULL, NULL }, + { "peek_first_gt", test_evbuffer_peek_first_gt, 0, NULL, NULL }, { "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" }, { "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" }, { "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL}, |