summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2014-11-30 19:26:20 -0500
committerNick Mathewson <nickm@torproject.org>2014-11-30 19:26:20 -0500
commit7fd4941465e42ec46721150e7ccf1aa2d2bf670b (patch)
tree884addab377c375af6a9f3a7f41ef25c947c5508
parent23133cacc841e3e5b226de8b4ab39ad2eab68317 (diff)
parent10fe4ef300df9475ec37e817aa5777044f7d03b4 (diff)
downloadlibevent-7fd4941465e42ec46721150e7ccf1aa2d2bf670b.tar.gz
Merge remote-tracking branch 'origin/pr/182'
-rw-r--r--http-internal.h1
-rw-r--r--http.c34
-rw-r--r--include/event2/http.h8
-rw-r--r--test/regress_http.c58
4 files changed, 101 insertions, 0 deletions
diff --git a/http-internal.h b/http-internal.h
index 6f2f5b85..a83160c8 100644
--- a/http-internal.h
+++ b/http-internal.h
@@ -74,6 +74,7 @@ struct evhttp_connection {
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
+#define EVHTTP_CON_AUTOFREE 0x0008 /* set when we want to auto free the connection */
struct timeval timeout; /* timeout for events */
int retry_cnt; /* retry count */
diff --git a/http.c b/http.c
index 2049e4d7..dd8c9cff 100644
--- a/http.c
+++ b/http.c
@@ -769,6 +769,7 @@ evhttp_connection_done(struct evhttp_connection *evcon)
{
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
+ int free_evcon = 0;
if (con_outgoing) {
/* idle or close the connection */
@@ -801,6 +802,12 @@ evhttp_connection_done(struct evhttp_connection *evcon)
* need to detect if the other side closes it.
*/
evhttp_connection_start_detectclose(evcon);
+ } else if ((evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ /*
+ * If we have no more requests that need completion
+ * and we're not waiting for the connection to close
+ */
+ free_evcon = 1;
}
} else {
/*
@@ -819,6 +826,16 @@ evhttp_connection_done(struct evhttp_connection *evcon)
if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) {
evhttp_request_free(req);
}
+
+ /* If this was the last request of an outgoing connection and we're
+ * not waiting to receive a connection close event and we want to
+ * automatically free the connection. We check to ensure our request
+ * list is empty one last time just in case our callback added a
+ * new request.
+ */
+ if (free_evcon && TAILQ_FIRST(&evcon->requests) == NULL) {
+ evhttp_connection_free(evcon);
+ }
}
/*
@@ -1175,6 +1192,11 @@ evhttp_connection_free(struct evhttp_connection *evcon)
}
void
+evhttp_connection_free_on_completion(struct evhttp_connection *evcon) {
+ evcon->flags |= EVHTTP_CON_AUTOFREE;
+}
+
+void
evhttp_connection_set_local_address(struct evhttp_connection *evcon,
const char *address)
{
@@ -1243,6 +1265,7 @@ evhttp_connection_reset_(struct evhttp_connection *evcon)
shutdown(evcon->fd, EVUTIL_SHUT_WR);
evutil_closesocket(evcon->fd);
+ bufferevent_setfd(evcon->bufev, -1);
evcon->fd = -1;
}
@@ -1385,6 +1408,17 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
*/
EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
evhttp_connection_reset_(evcon);
+
+ /*
+ * If we have no more requests that need completion
+ * and we want to auto-free the connection when all
+ * requests have been completed.
+ */
+ if (TAILQ_FIRST(&evcon->requests) == NULL
+ && (evcon->flags & EVHTTP_CON_OUTGOING)
+ && (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ evhttp_connection_free(evcon);
+ }
return;
}
diff --git a/include/event2/http.h b/include/event2/http.h
index 7cade877..4284d5fc 100644
--- a/include/event2/http.h
+++ b/include/event2/http.h
@@ -675,6 +675,14 @@ void evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
EVENT2_EXPORT_SYMBOL
void evhttp_connection_free(struct evhttp_connection *evcon);
+/** Disowns a given connection object
+ *
+ * Can be used to tell libevent to free the connection object after
+ * the last request has completed or failed.
+ */
+EVENT2_EXPORT_SYMBOL
+void evhttp_connection_free_on_completion(struct evhttp_connection *evcon);
+
/** sets the ip address from which http connections are made */
EVENT2_EXPORT_SYMBOL
void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
diff --git a/test/regress_http.c b/test/regress_http.c
index d62dfbbf..35f6dd76 100644
--- a/test/regress_http.c
+++ b/test/regress_http.c
@@ -1160,6 +1160,63 @@ http_connection_async_test(void *arg)
}
static void
+http_autofree_connection_test(void *arg)
+{
+ struct basic_test_data *data = arg;
+ ev_uint16_t port = 0;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req[2] = { NULL };
+
+ test_ok = 0;
+ http = http_setup(&port, data->base, 0);
+
+ evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
+ tt_assert(evcon);
+
+ /*
+ * At this point, we want to schedule two request to the HTTP
+ * server using our make request method.
+ */
+ req[0] = evhttp_request_new(http_request_empty_done, data->base);
+ req[1] = evhttp_request_new(http_request_empty_done, data->base);
+
+ /* Add the information that we care about */
+ evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost");
+ evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close");
+ evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis");
+ evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost");
+ evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close");
+ evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis");
+
+ /* We give ownership of the request to the connection */
+ if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) {
+ tt_abort_msg("couldn't make request");
+ }
+ if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) {
+ tt_abort_msg("couldn't make request");
+ }
+
+ /*
+ * Tell libevent to free the connection when the request completes
+ * We then set the evcon pointer to NULL since we don't want to free it
+ * when this function ends.
+ */
+ evhttp_connection_free_on_completion(evcon);
+ evcon = NULL;
+
+ event_base_dispatch(data->base);
+
+ /* at this point, the http server should have no connection */
+ tt_assert(TAILQ_FIRST(&http->connections) == NULL);
+
+ end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+ if (http)
+ evhttp_free(http);
+}
+
+static void
http_request_never_call(struct evhttp_request *req, void *arg)
{
fprintf(stdout, "FAILED\n");
@@ -3897,6 +3954,7 @@ struct testcase_t http_testcases[] = {
HTTP(failure),
HTTP(connection),
HTTP(persist_connection),
+ HTTP(autofree_connection),
HTTP(connection_async),
HTTP(close_detection),
HTTP(close_detection_delay),