mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
convert evhttp_connection to use bufferevents
svn:r742
This commit is contained in:
parent
0ec09b5507
commit
e44ef375ee
@ -76,6 +76,7 @@ Changes in current version:
|
|||||||
o fix a bug in which bufferevent_write_buffer would not schedule a write event
|
o fix a bug in which bufferevent_write_buffer would not schedule a write event
|
||||||
o provide bufferevent_input and bufferevent_output without requiring knowledge of the structure
|
o provide bufferevent_input and bufferevent_output without requiring knowledge of the structure
|
||||||
o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents
|
o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents
|
||||||
|
o convert evhttp_connection to use bufferevents.
|
||||||
|
|
||||||
Changes in 1.4.0:
|
Changes in 1.4.0:
|
||||||
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
|
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
enum evhttp_connection_error {
|
enum evhttp_connection_error {
|
||||||
EVCON_HTTP_TIMEOUT,
|
EVCON_HTTP_TIMEOUT,
|
||||||
EVCON_HTTP_EOF,
|
EVCON_HTTP_EOF,
|
||||||
EVCON_HTTP_INVALID_HEADER
|
EVCON_HTTP_INVALID_HEADER,
|
||||||
|
EVCON_HTTP_BUFFER_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
struct evbuffer;
|
struct evbuffer;
|
||||||
@ -42,10 +43,10 @@ struct evhttp_connection {
|
|||||||
TAILQ_ENTRY(evhttp_connection) (next);
|
TAILQ_ENTRY(evhttp_connection) (next);
|
||||||
|
|
||||||
evutil_socket_t fd;
|
evutil_socket_t fd;
|
||||||
struct event ev;
|
struct bufferevent *bufev;
|
||||||
|
|
||||||
|
struct event retry_ev; /* for retrying connects */
|
||||||
struct event close_ev;
|
struct event close_ev;
|
||||||
struct evbuffer *input_buffer;
|
|
||||||
struct evbuffer *output_buffer;
|
|
||||||
|
|
||||||
char *bind_address; /* address to use for binding the src */
|
char *bind_address; /* address to use for binding the src */
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ struct evhttp_connection {
|
|||||||
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
|
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
|
||||||
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
|
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
|
||||||
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
|
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
|
||||||
|
#define EVHTTP_CON_GOTHEADERS 0x0008 /* done reading headers */
|
||||||
|
|
||||||
int timeout; /* timeout in seconds for events */
|
int timeout; /* timeout in seconds for events */
|
||||||
int retry_cnt; /* retry count */
|
int retry_cnt; /* retry count */
|
||||||
@ -68,7 +70,7 @@ struct evhttp_connection {
|
|||||||
|
|
||||||
TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
|
TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
|
||||||
|
|
||||||
void (*cb)(struct evhttp_connection *, void *);
|
void (*cb)(struct evhttp_connection *, void *);
|
||||||
void *cb_arg;
|
void *cb_arg;
|
||||||
|
|
||||||
void (*closecb)(struct evhttp_connection *, void *);
|
void (*closecb)(struct evhttp_connection *, void *);
|
||||||
|
324
http.c
324
http.c
@ -164,8 +164,11 @@ static void evhttp_connection_stop_detectclose(
|
|||||||
struct evhttp_connection *evcon);
|
struct evhttp_connection *evcon);
|
||||||
static void evhttp_request_dispatch(struct evhttp_connection* evcon);
|
static void evhttp_request_dispatch(struct evhttp_connection* evcon);
|
||||||
|
|
||||||
void evhttp_read(evutil_socket_t, short, void *);
|
/* callbacks for bufferevent */
|
||||||
void evhttp_write(evutil_socket_t, short, void *);
|
static void evhttp_read_cb(struct bufferevent *, void *);
|
||||||
|
static void evhttp_read_header_cb(struct bufferevent *bufev, void *arg);
|
||||||
|
static void evhttp_write_cb(struct bufferevent *, void *);
|
||||||
|
static void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
|
||||||
|
|
||||||
#ifndef HAVE_STRSEP
|
#ifndef HAVE_STRSEP
|
||||||
/* strsep replacement for platforms that lack it. Only works if
|
/* strsep replacement for platforms that lack it. Only works if
|
||||||
@ -298,13 +301,8 @@ evhttp_write_buffer(struct evhttp_connection *evcon,
|
|||||||
evcon->cb = cb;
|
evcon->cb = cb;
|
||||||
evcon->cb_arg = arg;
|
evcon->cb_arg = arg;
|
||||||
|
|
||||||
/* check if the event is already pending */
|
bufferevent_disable(evcon->bufev, EV_READ);
|
||||||
if (event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL))
|
bufferevent_enable(evcon->bufev, EV_WRITE);
|
||||||
event_del(&evcon->ev);
|
|
||||||
|
|
||||||
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
|
|
||||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
|
||||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -321,7 +319,8 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
|
|||||||
|
|
||||||
/* Generate request line */
|
/* Generate request line */
|
||||||
method = evhttp_method(req->type);
|
method = evhttp_method(req->type);
|
||||||
evbuffer_add_printf(evcon->output_buffer, "%s %s HTTP/%d.%d\r\n",
|
evbuffer_add_printf(bufferevent_output(evcon->bufev),
|
||||||
|
"%s %s HTTP/%d.%d\r\n",
|
||||||
method, req->uri, req->major, req->minor);
|
method, req->uri, req->major, req->minor);
|
||||||
|
|
||||||
/* Add the content length on a post or put request if missing */
|
/* Add the content length on a post or put request if missing */
|
||||||
@ -398,7 +397,8 @@ static void
|
|||||||
evhttp_make_header_response(struct evhttp_connection *evcon,
|
evhttp_make_header_response(struct evhttp_connection *evcon,
|
||||||
struct evhttp_request *req)
|
struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
evbuffer_add_printf(evcon->output_buffer, "HTTP/%d.%d %d %s\r\n",
|
evbuffer_add_printf(bufferevent_output(evcon->bufev),
|
||||||
|
"HTTP/%d.%d %d %s\r\n",
|
||||||
req->major, req->minor, req->response_code,
|
req->major, req->minor, req->response_code,
|
||||||
req->response_code_line);
|
req->response_code_line);
|
||||||
|
|
||||||
@ -439,6 +439,7 @@ void
|
|||||||
evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
struct evkeyval *header;
|
struct evkeyval *header;
|
||||||
|
struct evbuffer *output = bufferevent_output(evcon->bufev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Depending if this is a HTTP request or response, we might need to
|
* Depending if this is a HTTP request or response, we might need to
|
||||||
@ -451,17 +452,17 @@ evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH(header, req->output_headers, next) {
|
TAILQ_FOREACH(header, req->output_headers, next) {
|
||||||
evbuffer_add_printf(evcon->output_buffer, "%s: %s\r\n",
|
evbuffer_add_printf(output, "%s: %s\r\n",
|
||||||
header->key, header->value);
|
header->key, header->value);
|
||||||
}
|
}
|
||||||
evbuffer_add(evcon->output_buffer, "\r\n", 2);
|
evbuffer_add(output, "\r\n", 2);
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(req->output_buffer) > 0) {
|
if (EVBUFFER_LENGTH(req->output_buffer) > 0) {
|
||||||
/*
|
/*
|
||||||
* For a request, we add the POST data, for a reply, this
|
* For a request, we add the POST data, for a reply, this
|
||||||
* is the regular data.
|
* is the regular data.
|
||||||
*/
|
*/
|
||||||
evbuffer_add_buffer(evcon->output_buffer, req->output_buffer);
|
evbuffer_add_buffer(output, req->output_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,6 +538,7 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
|
|||||||
*/
|
*/
|
||||||
return (-1);
|
return (-1);
|
||||||
case EVCON_HTTP_INVALID_HEADER:
|
case EVCON_HTTP_INVALID_HEADER:
|
||||||
|
case EVCON_HTTP_BUFFER_ERROR:
|
||||||
default: /* xxx: probably should just error on default */
|
default: /* xxx: probably should just error on default */
|
||||||
/* the callback looks at the uri to determine errors */
|
/* the callback looks at the uri to determine errors */
|
||||||
if (req->uri) {
|
if (req->uri) {
|
||||||
@ -563,6 +565,8 @@ evhttp_connection_fail(struct evhttp_connection *evcon,
|
|||||||
void *cb_arg;
|
void *cb_arg;
|
||||||
assert(req != NULL);
|
assert(req != NULL);
|
||||||
|
|
||||||
|
bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
|
||||||
|
|
||||||
if (evcon->flags & EVHTTP_CON_INCOMING) {
|
if (evcon->flags & EVHTTP_CON_INCOMING) {
|
||||||
/*
|
/*
|
||||||
* for incoming requests, there are two different
|
* for incoming requests, there are two different
|
||||||
@ -598,35 +602,10 @@ evhttp_connection_fail(struct evhttp_connection *evcon,
|
|||||||
(*cb)(NULL, cb_arg);
|
(*cb)(NULL, cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
evhttp_write(evutil_socket_t fd, short what, void *arg)
|
evhttp_write_cb(struct bufferevent *bufev, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
int n;
|
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = evbuffer_write(evcon->output_buffer, fd);
|
|
||||||
if (n == -1) {
|
|
||||||
event_debug(("%s: evbuffer_write", __func__));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
event_debug(("%s: write nothing", __func__));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) {
|
|
||||||
evhttp_add_event(&evcon->ev,
|
|
||||||
evcon->timeout, HTTP_WRITE_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Activate our call back */
|
/* Activate our call back */
|
||||||
if (evcon->cb != NULL)
|
if (evcon->cb != NULL)
|
||||||
@ -745,12 +724,13 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
|
|||||||
static void
|
static void
|
||||||
evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
struct evbuffer *buf = evcon->input_buffer;
|
struct evbuffer *buf = bufferevent_input(evcon->bufev);
|
||||||
|
|
||||||
if (req->chunked) {
|
if (req->chunked) {
|
||||||
int res = evhttp_handle_chunked_read(req, buf);
|
int res = evhttp_handle_chunked_read(req, buf);
|
||||||
if (res == 1) {
|
if (res == 1) {
|
||||||
/* finished last chunk */
|
/* finished last chunk */
|
||||||
|
bufferevent_disable(evcon->bufev, EV_READ);
|
||||||
evhttp_connection_done(evcon);
|
evhttp_connection_done(evcon);
|
||||||
return;
|
return;
|
||||||
} else if (res == -1) {
|
} else if (res == -1) {
|
||||||
@ -763,50 +743,33 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
|||||||
/* Read until connection close. */
|
/* Read until connection close. */
|
||||||
evbuffer_add_buffer(req->input_buffer, buf);
|
evbuffer_add_buffer(req->input_buffer, buf);
|
||||||
} else if (EVBUFFER_LENGTH(buf) >= req->ntoread) {
|
} else if (EVBUFFER_LENGTH(buf) >= req->ntoread) {
|
||||||
|
bufferevent_disable(evcon->bufev, EV_READ);
|
||||||
/* Completed content length */
|
/* Completed content length */
|
||||||
evbuffer_remove_buffer(buf, req->input_buffer, req->ntoread);
|
evbuffer_remove_buffer(buf, req->input_buffer, req->ntoread);
|
||||||
req->ntoread = 0;
|
req->ntoread = 0;
|
||||||
evhttp_connection_done(evcon);
|
evhttp_connection_done(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read more! */
|
/* Read more! */
|
||||||
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
|
bufferevent_setcb(evcon->bufev,
|
||||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
evhttp_read_cb,
|
||||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
|
evhttp_write_cb,
|
||||||
|
evhttp_error_cb,
|
||||||
|
evcon);
|
||||||
|
bufferevent_enable(evcon->bufev, EV_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads data into a buffer structure until no more data
|
* Gets called when more data becomes available
|
||||||
* can be read on the file descriptor or we have read all
|
|
||||||
* the data that we wanted to read.
|
|
||||||
* Execute callback when done.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
static void
|
||||||
evhttp_read(evutil_socket_t fd, short what, void *arg)
|
evhttp_read_cb(struct bufferevent *bufev, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
struct evbuffer *buf = evcon->input_buffer;
|
|
||||||
int n, len;
|
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
n = evbuffer_read(buf, fd, -1);
|
|
||||||
len = EVBUFFER_LENGTH(buf);
|
|
||||||
event_debug(("%s: got %d on %d\n", __func__, n, fd));
|
|
||||||
|
|
||||||
if (n == -1) {
|
|
||||||
event_debug(("%s: evbuffer_read", __func__));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
|
||||||
return;
|
|
||||||
} else if (n == 0) {
|
|
||||||
/* Connection closed */
|
|
||||||
evhttp_connection_done(evcon);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
evhttp_read_body(evcon, req);
|
evhttp_read_body(evcon, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -852,8 +815,11 @@ evhttp_connection_free(struct evhttp_connection *evcon)
|
|||||||
if (event_initialized(&evcon->close_ev))
|
if (event_initialized(&evcon->close_ev))
|
||||||
event_del(&evcon->close_ev);
|
event_del(&evcon->close_ev);
|
||||||
|
|
||||||
if (event_initialized(&evcon->ev))
|
if (event_initialized(&evcon->retry_ev))
|
||||||
event_del(&evcon->ev);
|
event_del(&evcon->retry_ev);
|
||||||
|
|
||||||
|
if (evcon->bufev != NULL)
|
||||||
|
bufferevent_free(evcon->bufev);
|
||||||
|
|
||||||
if (evcon->fd != -1)
|
if (evcon->fd != -1)
|
||||||
EVUTIL_CLOSESOCKET(evcon->fd);
|
EVUTIL_CLOSESOCKET(evcon->fd);
|
||||||
@ -864,12 +830,6 @@ evhttp_connection_free(struct evhttp_connection *evcon)
|
|||||||
if (evcon->address != NULL)
|
if (evcon->address != NULL)
|
||||||
mm_free(evcon->address);
|
mm_free(evcon->address);
|
||||||
|
|
||||||
if (evcon->input_buffer != NULL)
|
|
||||||
evbuffer_free(evcon->input_buffer);
|
|
||||||
|
|
||||||
if (evcon->output_buffer != NULL)
|
|
||||||
evbuffer_free(evcon->output_buffer);
|
|
||||||
|
|
||||||
mm_free(evcon);
|
mm_free(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,8 +870,7 @@ evhttp_request_dispatch(struct evhttp_connection* evcon)
|
|||||||
void
|
void
|
||||||
evhttp_connection_reset(struct evhttp_connection *evcon)
|
evhttp_connection_reset(struct evhttp_connection *evcon)
|
||||||
{
|
{
|
||||||
if (event_initialized(&evcon->ev))
|
bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
|
||||||
event_del(&evcon->ev);
|
|
||||||
|
|
||||||
if (evcon->fd != -1) {
|
if (evcon->fd != -1) {
|
||||||
/* inform interested parties about connection close */
|
/* inform interested parties about connection close */
|
||||||
@ -924,7 +883,7 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
|
|||||||
evcon->state = EVCON_DISCONNECTED;
|
evcon->state = EVCON_DISCONNECTED;
|
||||||
|
|
||||||
/* remove unneeded flags */
|
/* remove unneeded flags */
|
||||||
evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
|
evcon->flags &= ~(EVHTTP_CON_CLOSEDETECT|EVHTTP_CON_GOTHEADERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -964,23 +923,82 @@ evhttp_connection_retry(evutil_socket_t fd, short what, void *arg)
|
|||||||
evhttp_connection_connect(evcon);
|
evhttp_connection_connect(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
|
||||||
|
{
|
||||||
|
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
|
||||||
|
evtimer_set(&evcon->retry_ev, evhttp_connection_retry, evcon);
|
||||||
|
EVHTTP_BASE_SET(evcon, &evcon->retry_ev);
|
||||||
|
evhttp_add_event(&evcon->retry_ev,
|
||||||
|
MIN(3600, 2 << evcon->retry_cnt),
|
||||||
|
HTTP_CONNECT_TIMEOUT);
|
||||||
|
evcon->retry_cnt++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
evhttp_connection_reset(evcon);
|
||||||
|
|
||||||
|
/* for now, we just signal all requests by executing their callbacks */
|
||||||
|
while (TAILQ_FIRST(&evcon->requests) != NULL) {
|
||||||
|
struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
|
||||||
|
TAILQ_REMOVE(&evcon->requests, request, next);
|
||||||
|
request->evcon = NULL;
|
||||||
|
|
||||||
|
/* we might want to set an error here */
|
||||||
|
request->cb(request, request->cb_arg);
|
||||||
|
evhttp_request_free(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
|
||||||
|
{
|
||||||
|
struct evhttp_connection *evcon = arg;
|
||||||
|
|
||||||
|
switch (evcon->state) {
|
||||||
|
case EVCON_CONNECTING:
|
||||||
|
if (what == EVBUFFER_TIMEOUT) {
|
||||||
|
event_debug(("%s: connection timeout for \"%s:%d\" on %d",
|
||||||
|
__func__, evcon->address, evcon->port,
|
||||||
|
evcon->fd));
|
||||||
|
evhttp_connection_cb_cleanup(evcon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVCON_CONNECTED:
|
||||||
|
if (what == (EVBUFFER_READ|EVBUFFER_EOF) &&
|
||||||
|
(evcon->flags & EVHTTP_CON_GOTHEADERS)) {
|
||||||
|
/* EOF on read can be benign */
|
||||||
|
evhttp_connection_done(evcon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVCON_DISCONNECTED:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what & EVBUFFER_TIMEOUT) {
|
||||||
|
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
|
||||||
|
} else if (what & EVBUFFER_EOF) {
|
||||||
|
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
||||||
|
} else {
|
||||||
|
evhttp_connection_fail(evcon, EVCON_HTTP_BUFFER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call back for asynchronous connection attempt.
|
* Call back for asynchronous connection attempt.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
evhttp_connectioncb(evutil_socket_t fd, short what, void *arg)
|
evhttp_connection_cb(struct bufferevent *bufev, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
int error;
|
int error;
|
||||||
socklen_t errsz = sizeof(error);
|
socklen_t errsz = sizeof(error);
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
|
||||||
event_debug(("%s: connection timeout for \"%s:%d\" on %d",
|
|
||||||
__func__, evcon->address, evcon->port, evcon->fd));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the connection completed */
|
/* Check if the connection completed */
|
||||||
if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
|
if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
|
||||||
&errsz) == -1) {
|
&errsz) == -1) {
|
||||||
@ -1003,32 +1021,28 @@ evhttp_connectioncb(evutil_socket_t fd, short what, void *arg)
|
|||||||
/* Reset the retry count as we were successful in connecting */
|
/* Reset the retry count as we were successful in connecting */
|
||||||
evcon->retry_cnt = 0;
|
evcon->retry_cnt = 0;
|
||||||
evcon->state = EVCON_CONNECTED;
|
evcon->state = EVCON_CONNECTED;
|
||||||
|
evcon->flags &= ~EVHTTP_CON_GOTHEADERS;
|
||||||
|
|
||||||
|
/* reset the bufferevent cbs */
|
||||||
|
bufferevent_setcb(evcon->bufev,
|
||||||
|
evhttp_read_header_cb,
|
||||||
|
evhttp_write_cb,
|
||||||
|
evhttp_error_cb,
|
||||||
|
evcon);
|
||||||
|
|
||||||
|
if (evcon->timeout == -1)
|
||||||
|
bufferevent_settimeout(evcon->bufev,
|
||||||
|
HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
|
||||||
|
else
|
||||||
|
bufferevent_settimeout(evcon->bufev,
|
||||||
|
evcon->timeout, evcon->timeout);
|
||||||
|
|
||||||
/* try to start requests that have queued up on this connection */
|
/* try to start requests that have queued up on this connection */
|
||||||
evhttp_request_dispatch(evcon);
|
evhttp_request_dispatch(evcon);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
|
evhttp_connection_cb_cleanup(evcon);
|
||||||
evtimer_set(&evcon->ev, evhttp_connection_retry, evcon);
|
|
||||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
|
||||||
evhttp_add_event(&evcon->ev, MIN(3600, 2 << evcon->retry_cnt),
|
|
||||||
HTTP_CONNECT_TIMEOUT);
|
|
||||||
evcon->retry_cnt++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
evhttp_connection_reset(evcon);
|
|
||||||
|
|
||||||
/* for now, we just signal all requests by executing their callbacks */
|
|
||||||
while (TAILQ_FIRST(&evcon->requests) != NULL) {
|
|
||||||
struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
|
|
||||||
TAILQ_REMOVE(&evcon->requests, request, next);
|
|
||||||
request->evcon = NULL;
|
|
||||||
|
|
||||||
/* we might want to set an error here */
|
|
||||||
request->cb(request, request->cb_arg);
|
|
||||||
evhttp_request_free(request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1366,32 +1380,15 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
|||||||
evhttp_read_body(evcon, req);
|
evhttp_read_body(evcon, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
evhttp_read_header(evutil_socket_t fd, short what, void *arg)
|
evhttp_read_header_cb(struct bufferevent *bufev, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
int n, res;
|
int res;
|
||||||
|
int fd = evcon->fd;
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
res = evhttp_parse_lines(req, bufferevent_input(evcon->bufev));
|
||||||
event_debug(("%s: timeout on %d\n", __func__, fd));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = evbuffer_read(evcon->input_buffer, fd, -1);
|
|
||||||
if (n == 0) {
|
|
||||||
event_debug(("%s: no more data on %d", __func__, fd));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n == -1) {
|
|
||||||
event_debug(("%s: bad read on %d", __func__, fd));
|
|
||||||
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = evhttp_parse_lines(req, evcon->input_buffer);
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
/* Error while reading, terminate */
|
/* Error while reading, terminate */
|
||||||
event_debug(("%s: bad header lines on %d\n", __func__, fd));
|
event_debug(("%s: bad header lines on %d\n", __func__, fd));
|
||||||
@ -1399,11 +1396,15 @@ evhttp_read_header(evutil_socket_t fd, short what, void *arg)
|
|||||||
return;
|
return;
|
||||||
} else if (res == 0) {
|
} else if (res == 0) {
|
||||||
/* Need more header lines */
|
/* Need more header lines */
|
||||||
evhttp_add_event(&evcon->ev,
|
|
||||||
evcon->timeout, HTTP_READ_TIMEOUT);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disable reading for now */
|
||||||
|
bufferevent_disable(evcon->bufev, EV_READ);
|
||||||
|
|
||||||
|
/* we got all headers */
|
||||||
|
evcon->flags |= EVHTTP_CON_GOTHEADERS;
|
||||||
|
|
||||||
/* Done reading headers, do the real work */
|
/* Done reading headers, do the real work */
|
||||||
switch (req->kind) {
|
switch (req->kind) {
|
||||||
case EVHTTP_REQUEST:
|
case EVHTTP_REQUEST:
|
||||||
@ -1466,13 +1467,11 @@ evhttp_connection_new(const char *address, unsigned short port)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((evcon->input_buffer = evbuffer_new()) == NULL) {
|
if ((evcon->bufev = bufferevent_new(-1,
|
||||||
event_warn("%s: evbuffer_new failed", __func__);
|
evhttp_read_header_cb,
|
||||||
goto error;
|
evhttp_write_cb,
|
||||||
}
|
evhttp_error_cb, evcon)) == NULL) {
|
||||||
|
event_warn("%s: bufferevent_new failed", __func__);
|
||||||
if ((evcon->output_buffer = evbuffer_new()) == NULL) {
|
|
||||||
event_warn("%s: evbuffer_new failed", __func__);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1493,6 +1492,7 @@ void evhttp_connection_set_base(struct evhttp_connection *evcon,
|
|||||||
assert(evcon->base == NULL);
|
assert(evcon->base == NULL);
|
||||||
assert(evcon->state == EVCON_DISCONNECTED);
|
assert(evcon->state == EVCON_DISCONNECTED);
|
||||||
evcon->base = base;
|
evcon->base = base;
|
||||||
|
bufferevent_base_set(base, evcon->bufev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1500,6 +1500,13 @@ evhttp_connection_set_timeout(struct evhttp_connection *evcon,
|
|||||||
int timeout_in_secs)
|
int timeout_in_secs)
|
||||||
{
|
{
|
||||||
evcon->timeout = timeout_in_secs;
|
evcon->timeout = timeout_in_secs;
|
||||||
|
|
||||||
|
if (evcon->timeout == -1)
|
||||||
|
bufferevent_settimeout(evcon->bufev,
|
||||||
|
HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
|
||||||
|
else
|
||||||
|
bufferevent_settimeout(evcon->bufev,
|
||||||
|
evcon->timeout, evcon->timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1549,9 +1556,15 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set up a callback for successful connection setup */
|
/* Set up a callback for successful connection setup */
|
||||||
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
|
bufferevent_setfd(evcon->bufev, evcon->fd);
|
||||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
bufferevent_setcb(evcon->bufev,
|
||||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_CONNECT_TIMEOUT);
|
NULL /* evhttp_read_cb */,
|
||||||
|
evhttp_connection_cb,
|
||||||
|
evhttp_error_cb, evcon);
|
||||||
|
bufferevent_settimeout(evcon->bufev, 0,
|
||||||
|
evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
|
||||||
|
/* make sure that we get a write callback */
|
||||||
|
bufferevent_enable(evcon->bufev, EV_WRITE);
|
||||||
|
|
||||||
evcon->state = EVCON_CONNECTING;
|
evcon->state = EVCON_CONNECTING;
|
||||||
|
|
||||||
@ -1613,12 +1626,8 @@ void
|
|||||||
evhttp_start_read(struct evhttp_connection *evcon)
|
evhttp_start_read(struct evhttp_connection *evcon)
|
||||||
{
|
{
|
||||||
/* Set up an event to read the headers */
|
/* Set up an event to read the headers */
|
||||||
if (event_initialized(&evcon->ev))
|
bufferevent_disable(evcon->bufev, EV_WRITE);
|
||||||
event_del(&evcon->ev);
|
bufferevent_enable(evcon->bufev, EV_READ);
|
||||||
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
|
|
||||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
|
||||||
|
|
||||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1729,13 +1738,14 @@ evhttp_send_reply_start(struct evhttp_request *req, int code,
|
|||||||
void
|
void
|
||||||
evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
|
evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
|
||||||
{
|
{
|
||||||
|
struct evbuffer *output = bufferevent_output(req->evcon->bufev);
|
||||||
if (req->chunked) {
|
if (req->chunked) {
|
||||||
evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n",
|
evbuffer_add_printf(output, "%x\r\n",
|
||||||
(unsigned)EVBUFFER_LENGTH(databuf));
|
(unsigned)EVBUFFER_LENGTH(databuf));
|
||||||
}
|
}
|
||||||
evbuffer_add_buffer(req->evcon->output_buffer, databuf);
|
evbuffer_add_buffer(output, databuf);
|
||||||
if (req->chunked) {
|
if (req->chunked) {
|
||||||
evbuffer_add(req->evcon->output_buffer, "\r\n", 2);
|
evbuffer_add(output, "\r\n", 2);
|
||||||
}
|
}
|
||||||
evhttp_write_buffer(req->evcon, NULL, NULL);
|
evhttp_write_buffer(req->evcon, NULL, NULL);
|
||||||
}
|
}
|
||||||
@ -1744,12 +1754,13 @@ void
|
|||||||
evhttp_send_reply_end(struct evhttp_request *req)
|
evhttp_send_reply_end(struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = req->evcon;
|
struct evhttp_connection *evcon = req->evcon;
|
||||||
|
struct evbuffer *output = bufferevent_output(evcon->bufev);
|
||||||
|
|
||||||
if (req->chunked) {
|
if (req->chunked) {
|
||||||
evbuffer_add(req->evcon->output_buffer, "0\r\n\r\n", 5);
|
evbuffer_add(output, "0\r\n\r\n", 5);
|
||||||
evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
|
evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
|
||||||
req->chunked = 0;
|
req->chunked = 0;
|
||||||
} else if (!event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) {
|
} else if (EVBUFFER_LENGTH(output) == 0) {
|
||||||
/* let the connection know that we are done with the request */
|
/* let the connection know that we are done with the request */
|
||||||
evhttp_send_done(evcon, NULL);
|
evhttp_send_done(evcon, NULL);
|
||||||
} else {
|
} else {
|
||||||
@ -2291,14 +2302,17 @@ evhttp_get_request_connection(
|
|||||||
if (evcon == NULL)
|
if (evcon == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
/* associate the base if we have one*/
|
/* associate the base if we have one */
|
||||||
evhttp_connection_set_base(evcon, http->base);
|
if (http->base != NULL)
|
||||||
|
evhttp_connection_set_base(evcon, http->base);
|
||||||
|
|
||||||
evcon->flags |= EVHTTP_CON_INCOMING;
|
evcon->flags |= EVHTTP_CON_INCOMING;
|
||||||
evcon->state = EVCON_CONNECTED;
|
evcon->state = EVCON_CONNECTED;
|
||||||
|
|
||||||
evcon->fd = fd;
|
evcon->fd = fd;
|
||||||
|
|
||||||
|
bufferevent_setfd(evcon->bufev, fd);
|
||||||
|
|
||||||
return (evcon);
|
return (evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user