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,