Make the http connection close detection work properly with bufferevents and fix a potential memory leak associated with it

svn:r963
This commit is contained in:
Niels Provos 2008-12-19 21:31:43 +00:00
parent 8c59416878
commit f700566cb9
3 changed files with 32 additions and 26 deletions

View File

@ -131,7 +131,7 @@ Changes in current version:
o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn
o Fix a typo in setting the global event base; reported by lance.
o Set the 0x20 bit on outgoing alphabetic characters in DNS requests randomly, and insist on a match in replies. This helps resist DNS poisoning attacks.
o Make the http connection close detection work properly with bufferevents and fix a potential memory leak associated with it.
Changes in 1.4.0:
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.

View File

@ -62,7 +62,6 @@ struct evhttp_connection {
struct bufferevent *bufev;
struct event retry_ev; /* for retrying connects */
struct event close_ev;
char *bind_address; /* address to use for binding the src */
u_short bind_port; /* local port for binding the src */

55
http.c
View File

@ -950,9 +950,6 @@ evhttp_connection_free(struct evhttp_connection *evcon)
TAILQ_REMOVE(&http->connections, evcon, next);
}
if (event_initialized(&evcon->close_ev))
event_del(&evcon->close_ev);
if (event_initialized(&evcon->retry_ev))
event_del(&evcon->retry_ev);
@ -1030,32 +1027,18 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
evcon->state = EVCON_DISCONNECTED;
}
static void
evhttp_detect_close_cb(evutil_socket_t fd, short what, void *arg)
{
struct evhttp_connection *evcon = arg;
evhttp_connection_reset(evcon);
}
static void
evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
{
evcon->flags |= EVHTTP_CON_CLOSEDETECT;
if (event_initialized(&evcon->close_ev))
event_del(&evcon->close_ev);
event_assign(&evcon->close_ev, evcon->base, evcon->fd, EV_READ,
evhttp_detect_close_cb, evcon);
event_add(&evcon->close_ev, NULL);
bufferevent_enable(evcon->bufev, EV_READ);
}
static void
evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
{
evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
if (event_initialized(&evcon->close_ev))
event_del(&evcon->close_ev);
bufferevent_disable(evcon->bufev, EV_READ);
}
static void
@ -1128,6 +1111,29 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
break;
}
/* when we are in close detect mode, a read error means that
* the other side closed their connection.
*/
if (evcon->flags & EVHTTP_CON_CLOSEDETECT) {
evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
if (evcon->http_server == NULL) {
/* For connections from the client, we just
* reset the connection so that it becomes
* disconnected.
*/
assert(evcon->state == EVCON_IDLE);
evhttp_connection_reset(evcon);
} else {
/* For connections from the server, we free
* them if there is no request working on
* them.
*/
if (evcon->state == EVCON_READING_FIRSTLINE)
evhttp_connection_free(evcon);
}
return;
}
if (what & EVBUFFER_TIMEOUT) {
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
} else if (what & EVBUFFER_EOF) {
@ -1482,6 +1488,7 @@ evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
if (*line == ' ' || *line == '\t') {
if (evhttp_append_to_last_header(headers, line) == -1)
goto error;
mm_free(line);
continue;
}
@ -1910,8 +1917,12 @@ evhttp_send_done(struct evhttp_connection *evcon, void *arg)
}
/* we have a persistent connection; try to accept another request. */
if (evhttp_associate_new_request_with_connection(evcon) == -1)
if (evhttp_associate_new_request_with_connection(evcon) == -1) {
evhttp_connection_free(evcon);
} else {
/* set up to watch for client close */
evhttp_connection_start_detectclose(evcon);
}
}
/*
@ -1966,8 +1977,6 @@ void
evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
struct evbuffer *databuf)
{
/* set up to watch for client close */
evhttp_connection_start_detectclose(req->evcon);
evhttp_response_code(req, code, reason);
evhttp_send(req, databuf);
@ -1977,8 +1986,6 @@ void
evhttp_send_reply_start(struct evhttp_request *req, int code,
const char *reason)
{
/* set up to watch for client close */
evhttp_connection_start_detectclose(req->evcon);
evhttp_response_code(req, code, reason);
if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
req->major == 1 && req->minor == 1 &&