From 2b9ec4c13c3483c22fdca8e9fccdc017b6135298 Mon Sep 17 00:00:00 2001
From: John Ohl <john@collabriasoftware.com>
Date: Sun, 26 Oct 2014 01:18:10 -0400
Subject: [PATCH] Implement interface that provides the ability to have an
 outbound evhttp_connection free itself once all requests have completed

---
 http-internal.h       |  1 +
 http.c                | 33 +++++++++++++++++++++++++++++++++
 include/event2/http.h |  8 ++++++++
 3 files changed, 42 insertions(+)

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 3941d179..e02297ac 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);
+	}
 }
 
 /*
@@ -1174,6 +1191,11 @@ evhttp_connection_free(struct evhttp_connection *evcon)
 	mm_free(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)
@@ -1385,6 +1407,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,
-- 
2.40.0