[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: [Libevent-users] [PATCH] client HTTPS with evhttp
On 12/12/11 12:21, Mark Ellzey wrote:
Would you mind submitting the patch branched against patches-2.0 on github?
Easier to pull in / comment / test.
Actually, if it's ok, let me retarget this at trunk.
diff --git a/include/event2/http.h b/include/event2/http.h
index 5c9f0e5..5372208 100644
--- a/include/event2/http.h
+++ b/include/event2/http.h
@@ -430,15 +430,17 @@ enum evhttp_cmd_type {
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
/**
- * Create and return a connection object that can be used to for making HTTP
+ * Create and return a connection object that can be used for making HTTP
* requests. The connection object tries to resolve address and establish the
- * connection when it is given an http request object.
+ * connection when it is given an http request object. The specified
+ * bufferevent will be reused even if the underlying persistent HTTP connection
+ * goes down and needs to be reestablished.
*
* @param base the event_base to use for handling the connection
* @param dnsbase the dns_base to use for resolving host names; if not
* specified host name resolution will block.
* @param bev a bufferevent to use for connecting to the server; if NULL, a
- * socket-based bufferevent will be created. This buffrevent will be freed
+ * socket-based bufferevent will be created. This bufferevent will be freed
* when the connection closes. It must have no fd set on it.
* @param address the address to which to connect
* @param port the port to connect to
@@ -448,6 +450,38 @@ struct evhttp_connection *evhttp_connection_base_bufferevent_new(
struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev, const char *address, unsigned short port);
/**
+ * Creates and returns a new bufferevent object.
+ */
+typedef struct bufferevent* (*bev_factory_cb)(void *);
+
+/**
+ * Create and return a connection object that can be used for making HTTP
+ * requests. The connection object tries to resolve address and establish the
+ * connection when it is given an http request object. The specified factory
+ * function is called with the user-supplied argument to retrieve a new
+ * bufferevent whenever the underlying HTTP connection needs to be
+ * reestablished. This is what you want if, for example, you have a bufferevent
+ * that needs to perform some setup for new connections, such as an SSL
+ * bufferevent.
+ *
+ * @param base the event_base to use for handling the connection
+ * @param dnsbase the dns_base to use for resolving host names; if not
+ * specified host name resolution will block.
+ * @param cb a callback that returns a new bufferevent to use for connecting to
+ * the server; if NULL, behavior is the same as in calling
+ * evhttp_connection_base_bufferevent_new with a NULL bufferevent. The
+ * returned bufferevents will be freed as necessary. The returned
+ * bufferevents must have no fd set on them.
+ * @param arg the argument to supply to the callback
+ * @param address the address to which to connect
+ * @param port the port to connect to
+ * @return an evhttp_connection object that can be used for making requests
+ */
+struct evhttp_connection *evhttp_connection_base_bufferevent_factory_new(
+ struct event_base *base, struct evdns_base *dnsbase,
+ bev_factory_cb cb, void * arg, const char *address, unsigned short port);
+
+/**
* Return the bufferevent that an evhttp_connection is using.
*/
struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon);
@@ -474,9 +508,11 @@ void evhttp_request_set_chunked_cb(struct evhttp_request *,
void evhttp_request_free(struct evhttp_request *req);
/**
- * Create and return a connection object that can be used to for making HTTP
+ * Create and return a connection object that can be used for making HTTP
* requests. The connection object tries to resolve address and establish the
- * connection when it is given an http request object.
+ * connection when it is given an http request object. This function is
+ * equivalent to calling evhttp_connection_base_bufferevent_new with a NULL
+ * bufferevent.
*
* @param base the event_base to use for handling the connection
* @param dnsbase the dns_base to use for resolving host names; if not
diff --git a/http-internal.h b/http-internal.h
index 7fab641..12ff349 100644
--- a/http-internal.h
+++ b/http-internal.h
@@ -13,6 +13,7 @@
#include "event2/event_struct.h"
#include "util-internal.h"
#include "defer-internal.h"
+#include "event2/http.h"
#define HTTP_CONNECT_TIMEOUT 45
#define HTTP_WRITE_TIMEOUT 50
@@ -66,6 +67,8 @@ struct evhttp_connection {
evutil_socket_t fd;
struct bufferevent *bufev;
+ bev_factory_cb bufcb;
+ void *bufcb_arg;
struct event retry_ev; /* for retrying connects */
@@ -82,6 +85,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_BEV_USED 0x0008 /* mark whether we need to allocate a new bufferevent */
struct timeval timeout; /* timeout for events */
int retry_cnt; /* retry count */
diff --git a/http.c b/http.c
index 1fccc2c..8501437 100644
--- a/http.c
+++ b/http.c
@@ -1152,8 +1152,11 @@ evhttp_connection_free(struct evhttp_connection *evcon)
event_debug_unassign(&evcon->retry_ev);
}
- if (evcon->bufev != NULL)
+ if (evcon->bufev != NULL) {
+ /* ensure freeing doesn't prematurely close the file descriptor */
+ bufferevent_setfd(evcon->bufev, -1);
bufferevent_free(evcon->bufev);
+ }
event_deferred_cb_cancel(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
@@ -1207,6 +1210,7 @@ evhttp_request_dispatch(struct evhttp_connection* evcon)
EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
evcon->state = EVCON_WRITING;
+ evcon->flags |= EVHTTP_BEV_USED;
/* Create the header from the store arguments */
evhttp_make_header(evcon, req);
@@ -1239,6 +1243,7 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
if (evhttp_connected(evcon) && evcon->closecb != NULL)
(*evcon->closecb)(evcon, evcon->closecb_arg);
+ bufferevent_setfd(evcon->bufev, -1);
shutdown(evcon->fd, EVUTIL_SHUT_WR);
evutil_closesocket(evcon->fd);
evcon->fd = -1;
@@ -2212,6 +2217,30 @@ evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_bas
return (NULL);
}
+struct evhttp_connection *evhttp_connection_base_bufferevent_factory_new(
+ struct event_base *base, struct evdns_base *dnsbase,
+ bev_factory_cb cb, void * arg, const char *address, unsigned short port)
+{
+ struct bufferevent *bev = NULL;
+
+ if (NULL != cb) {
+ if (NULL == (bev = (*cb)(arg))) {
+ event_warn("%s: bufferevent factory callback failed", __func__);
+ return (NULL);
+ }
+ }
+
+ struct evhttp_connection *ret =
+ evhttp_connection_base_bufferevent_new(base, dnsbase, bev, address, port);
+
+ if (NULL != ret) {
+ ret->bufcb = cb;
+ ret->bufcb_arg = arg;
+ }
+
+ return (ret);
+}
+
struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
{
return evcon->bufev;
@@ -2305,6 +2334,30 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
return (-1);
}
+ /* if we have a bufferevent factory callback set, get a new bufferevent */
+ if (NULL != evcon->bufcb && 0 != (evcon->flags & EVHTTP_BEV_USED)) {
+ struct bufferevent *bev = (*evcon->bufcb)(evcon->bufcb_arg);
+
+ if (NULL == bev) {
+ event_warn("%s: bufferevent factory callback failed", __func__);
+ shutdown(evcon->fd, EVUTIL_SHUT_WR);
+ evutil_closesocket(evcon->fd);
+ evcon->fd = -1;
+ return (-1);
+ }
+
+ if (bufferevent_get_base(bev) != evcon->base) {
+ bufferevent_base_set(evcon->base, bev);
+ }
+
+ /* ensure freeing doesn't close the newly-allocated file descriptor
+ * (which might just have the same fd number) */
+ bufferevent_setfd(evcon->bufev, -1);
+ bufferevent_free(evcon->bufev);
+ evcon->bufev = bev;
+ evcon->flags ^= EVHTTP_BEV_USED;
+ }
+
/* Set up a callback for successful connection setup */
bufferevent_setfd(evcon->bufev, evcon->fd);
bufferevent_setcb(evcon->bufev,