mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-11 13:24:43 -04:00
correct handling of trailing headers in chunked replies; from Scott Lamb.
svn:r887
This commit is contained in:
parent
21f76156b1
commit
9998c0cbc8
@ -111,7 +111,8 @@ Changes in current version:
|
||||
o Rename INPUT and OUTPUT to EVRPC_INPUT and EVRPC_OUTPUT. Retain INPUT/OUTPUT aliases on on-win32 platforms for backwards compatibility.
|
||||
o Do not use SO_REUSEADDR when connecting
|
||||
o Support 64-bit integers in RPC structs
|
||||
|
||||
o Correct handling of trailing headers in chunked replies; from Scott Lamb.
|
||||
|
||||
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 demote most http warnings to debug messages
|
||||
|
@ -19,6 +19,13 @@
|
||||
#define HTTP_PREFIX "http://"
|
||||
#define HTTP_DEFAULTPORT 80
|
||||
|
||||
enum message_read_status {
|
||||
ALL_DATA_READ = 1,
|
||||
MORE_DATA_EXPECTED = 0,
|
||||
DATA_CORRUPTED = -1,
|
||||
REQUEST_CANCELED = -2
|
||||
};
|
||||
|
||||
enum evhttp_connection_error {
|
||||
EVCON_HTTP_TIMEOUT,
|
||||
EVCON_HTTP_EOF,
|
||||
@ -34,9 +41,15 @@ struct evhttp_request;
|
||||
/* A stupid connection object - maybe make this a bufferevent later */
|
||||
|
||||
enum evhttp_connection_state {
|
||||
EVCON_DISCONNECTED, /* not currently connected not trying either */
|
||||
EVCON_CONNECTING, /* tries to currently connect */
|
||||
EVCON_CONNECTED /* connection is established */
|
||||
EVCON_DISCONNECTED, /**< not currently connected not trying either*/
|
||||
EVCON_CONNECTING, /**< tries to currently connect */
|
||||
EVCON_IDLE, /**< connection is established */
|
||||
EVCON_READING_FIRSTLINE,/**< reading Request-Line (incoming conn) or
|
||||
**< Status-Line (outgoing conn) */
|
||||
EVCON_READING_HEADERS, /**< reading request/response headers */
|
||||
EVCON_READING_BODY, /**< reading request/response body */
|
||||
EVCON_READING_TRAILER, /**< reading request/response chunked trailer */
|
||||
EVCON_WRITING /**< writing request/response headers/body */
|
||||
};
|
||||
|
||||
struct event_base;
|
||||
@ -60,7 +73,6 @@ 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_GOTHEADERS 0x0008 /* done reading headers */
|
||||
|
||||
int timeout; /* timeout in seconds for events */
|
||||
int retry_cnt; /* retry count */
|
||||
@ -134,10 +146,10 @@ void evhttp_connection_fail(struct evhttp_connection *,
|
||||
|
||||
void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, socklen_t);
|
||||
|
||||
int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
|
||||
int evhttp_parse_firstline(struct evhttp_request *, struct evbuffer*);
|
||||
int evhttp_parse_headers(struct evhttp_request *, struct evbuffer*);
|
||||
|
||||
void evhttp_start_read(struct evhttp_connection *);
|
||||
void evhttp_read_header(evutil_socket_t, short, void *);
|
||||
void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
|
||||
|
||||
void evhttp_write_buffer(struct evhttp_connection *,
|
||||
|
272
http.c
272
http.c
@ -207,10 +207,13 @@ static void evhttp_connection_start_detectclose(
|
||||
static void evhttp_connection_stop_detectclose(
|
||||
struct evhttp_connection *evcon);
|
||||
static void evhttp_request_dispatch(struct evhttp_connection* evcon);
|
||||
static void evhttp_read_firstline(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req);
|
||||
static void evhttp_read_header(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req);
|
||||
|
||||
/* callbacks for bufferevent */
|
||||
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);
|
||||
static int evhttp_decode_uri_internal(const char *uri, size_t length,
|
||||
@ -351,6 +354,24 @@ evhttp_write_buffer(struct evhttp_connection *evcon,
|
||||
bufferevent_enable(evcon->bufev, EV_WRITE);
|
||||
}
|
||||
|
||||
static int
|
||||
evhttp_connected(struct evhttp_connection *evcon)
|
||||
{
|
||||
switch (evcon->state) {
|
||||
case EVCON_DISCONNECTED:
|
||||
case EVCON_CONNECTING:
|
||||
return (0);
|
||||
case EVCON_IDLE:
|
||||
case EVCON_READING_FIRSTLINE:
|
||||
case EVCON_READING_HEADERS:
|
||||
case EVCON_READING_BODY:
|
||||
case EVCON_READING_TRAILER:
|
||||
case EVCON_WRITING:
|
||||
default:
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the headers need for an HTTP request
|
||||
*/
|
||||
@ -621,23 +642,29 @@ evhttp_write_cb(struct bufferevent *bufev, void *arg)
|
||||
(*evcon->cb)(evcon, evcon->cb_arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the connection state.
|
||||
* - If this is an outgoing connection, we've just processed the response;
|
||||
* idle or close the connection.
|
||||
* - If this is an incoming connection, we've just processed the request;
|
||||
* respond.
|
||||
*/
|
||||
static void
|
||||
evhttp_connection_done(struct evhttp_connection *evcon)
|
||||
{
|
||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||
int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
|
||||
|
||||
/*
|
||||
* if this is an incoming connection, we need to leave the request
|
||||
* on the connection, so that we can reply to it.
|
||||
*/
|
||||
if (con_outgoing) {
|
||||
/* idle or close the connection */
|
||||
int need_close;
|
||||
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||
req->evcon = NULL;
|
||||
|
||||
evcon->state = EVCON_IDLE;
|
||||
|
||||
need_close =
|
||||
evhttp_is_connection_close(req->flags, req->input_headers) ||
|
||||
evhttp_is_connection_close(req->flags, req->input_headers)||
|
||||
evhttp_is_connection_close(req->flags, req->output_headers);
|
||||
|
||||
/* check if we got asked to close the connection */
|
||||
@ -647,10 +674,9 @@ evhttp_connection_done(struct evhttp_connection *evcon)
|
||||
if (TAILQ_FIRST(&evcon->requests) != NULL) {
|
||||
/*
|
||||
* We have more requests; reset the connection
|
||||
* and deal with the next request. xxx: no
|
||||
* persistent connection right now
|
||||
* and deal with the next request.
|
||||
*/
|
||||
if (evcon->state != EVCON_CONNECTED)
|
||||
if (!evhttp_connected(evcon))
|
||||
evhttp_connection_connect(evcon);
|
||||
else
|
||||
evhttp_request_dispatch(evcon);
|
||||
@ -661,6 +687,12 @@ evhttp_connection_done(struct evhttp_connection *evcon)
|
||||
*/
|
||||
evhttp_connection_start_detectclose(evcon);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* incoming connection - we need to leave the request on the
|
||||
* connection so that we can reply to it.
|
||||
*/
|
||||
evcon->state = EVCON_WRITING;
|
||||
}
|
||||
|
||||
/* notify the user of the request */
|
||||
@ -686,14 +718,7 @@ evhttp_connection_done(struct evhttp_connection *evcon)
|
||||
* request was canceled by the user calling evhttp_cancel_request
|
||||
*/
|
||||
|
||||
enum chunked_read_status {
|
||||
ALL_DATA_READ = 1,
|
||||
MORE_DATA_EXPECTED = 0,
|
||||
DATA_CORRUPTED = -1,
|
||||
REQUEST_CANCELED = -2
|
||||
};
|
||||
|
||||
static enum chunked_read_status
|
||||
static enum message_read_status
|
||||
evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
|
||||
{
|
||||
int len;
|
||||
@ -747,6 +772,26 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
|
||||
return (MORE_DATA_EXPECTED);
|
||||
}
|
||||
|
||||
static void
|
||||
evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
{
|
||||
struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
|
||||
|
||||
switch (evhttp_parse_headers(req, buf)) {
|
||||
case DATA_CORRUPTED:
|
||||
evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
|
||||
break;
|
||||
case ALL_DATA_READ:
|
||||
bufferevent_disable(evcon->bufev, EV_READ);
|
||||
evhttp_connection_done(evcon);
|
||||
break;
|
||||
case MORE_DATA_EXPECTED:
|
||||
default:
|
||||
bufferevent_enable(evcon->bufev, EV_READ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
{
|
||||
@ -756,8 +801,8 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
switch (evhttp_handle_chunked_read(req, buf)) {
|
||||
case ALL_DATA_READ:
|
||||
/* finished last chunk */
|
||||
bufferevent_disable(evcon->bufev, EV_READ);
|
||||
evhttp_connection_done(evcon);
|
||||
evcon->state = EVCON_READING_TRAILER;
|
||||
evhttp_read_trailer(evcon, req);
|
||||
return;
|
||||
case DATA_CORRUPTED:
|
||||
/* corrupted data */
|
||||
@ -803,11 +848,6 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
}
|
||||
|
||||
/* Read more! */
|
||||
bufferevent_setcb(evcon->bufev,
|
||||
evhttp_read_cb,
|
||||
evhttp_write_cb,
|
||||
evhttp_error_cb,
|
||||
evcon);
|
||||
bufferevent_enable(evcon->bufev, EV_READ);
|
||||
}
|
||||
|
||||
@ -821,8 +861,33 @@ evhttp_read_cb(struct bufferevent *bufev, void *arg)
|
||||
struct evhttp_connection *evcon = arg;
|
||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||
|
||||
evhttp_read_body(evcon, req);
|
||||
/* note the request may have been freed in evhttp_read_body */
|
||||
switch (evcon->state) {
|
||||
case EVCON_READING_FIRSTLINE:
|
||||
evhttp_read_firstline(evcon, req);
|
||||
/* note the request may have been freed in
|
||||
* evhttp_read_body */
|
||||
break;
|
||||
case EVCON_READING_HEADERS:
|
||||
evhttp_read_header(evcon, req);
|
||||
/* note the request may have been freed in
|
||||
* evhttp_read_body */
|
||||
break;
|
||||
case EVCON_READING_BODY:
|
||||
evhttp_read_body(evcon, req);
|
||||
/* note the request may have been freed in
|
||||
* evhttp_read_body */
|
||||
break;
|
||||
case EVCON_READING_TRAILER:
|
||||
evhttp_read_trailer(evcon, req);
|
||||
break;
|
||||
case EVCON_DISCONNECTED:
|
||||
case EVCON_CONNECTING:
|
||||
case EVCON_IDLE:
|
||||
case EVCON_WRITING:
|
||||
default:
|
||||
event_errx(1, "%s: illegal connection state %d",
|
||||
__func__, evcon->state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -832,6 +897,8 @@ evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
|
||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||
assert(req != NULL);
|
||||
|
||||
assert(evcon->state == EVCON_WRITING);
|
||||
|
||||
/* We are done writing our header and are now expecting the response */
|
||||
req->kind = EVHTTP_RESPONSE;
|
||||
|
||||
@ -849,7 +916,7 @@ evhttp_connection_free(struct evhttp_connection *evcon)
|
||||
|
||||
/* notify interested parties that this connection is going down */
|
||||
if (evcon->fd != -1) {
|
||||
if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL)
|
||||
if (evhttp_connected(evcon) && evcon->closecb != NULL)
|
||||
(*evcon->closecb)(evcon, evcon->closecb_arg);
|
||||
}
|
||||
|
||||
@ -910,7 +977,9 @@ evhttp_request_dispatch(struct evhttp_connection* evcon)
|
||||
evhttp_connection_stop_detectclose(evcon);
|
||||
|
||||
/* we assume that the connection is connected already */
|
||||
assert(evcon->state == EVCON_CONNECTED);
|
||||
assert(evcon->state == EVCON_IDLE);
|
||||
|
||||
evcon->state = EVCON_WRITING;
|
||||
|
||||
/* Create the header from the store arguments */
|
||||
evhttp_make_header(evcon, req);
|
||||
@ -926,16 +995,13 @@ evhttp_connection_reset(struct evhttp_connection *evcon)
|
||||
|
||||
if (evcon->fd != -1) {
|
||||
/* inform interested parties about connection close */
|
||||
if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL)
|
||||
if (evhttp_connected(evcon) && evcon->closecb != NULL)
|
||||
(*evcon->closecb)(evcon, evcon->closecb_arg);
|
||||
|
||||
EVUTIL_CLOSESOCKET(evcon->fd);
|
||||
evcon->fd = -1;
|
||||
}
|
||||
evcon->state = EVCON_DISCONNECTED;
|
||||
|
||||
/* remove unneeded flags */
|
||||
evcon->flags &= ~(EVHTTP_CON_CLOSEDETECT|EVHTTP_CON_GOTHEADERS);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1004,6 +1070,7 @@ static void
|
||||
evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
|
||||
{
|
||||
struct evhttp_connection *evcon = arg;
|
||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||
|
||||
switch (evcon->state) {
|
||||
case EVCON_CONNECTING:
|
||||
@ -1016,9 +1083,9 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
|
||||
}
|
||||
break;
|
||||
|
||||
case EVCON_CONNECTED:
|
||||
if (what == (EVBUFFER_READ|EVBUFFER_EOF) &&
|
||||
(evcon->flags & EVHTTP_CON_GOTHEADERS)) {
|
||||
case EVCON_READING_BODY:
|
||||
if (!req->chunked && req->ntoread < 0
|
||||
&& what == (EVBUFFER_READ|EVBUFFER_EOF)) {
|
||||
/* EOF on read can be benign */
|
||||
evhttp_connection_done(evcon);
|
||||
return;
|
||||
@ -1026,6 +1093,11 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
|
||||
break;
|
||||
|
||||
case EVCON_DISCONNECTED:
|
||||
case EVCON_IDLE:
|
||||
case EVCON_READING_FIRSTLINE:
|
||||
case EVCON_READING_HEADERS:
|
||||
case EVCON_READING_TRAILER:
|
||||
case EVCON_WRITING:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1071,12 +1143,11 @@ evhttp_connection_cb(struct bufferevent *bufev, void *arg)
|
||||
|
||||
/* Reset the retry count as we were successful in connecting */
|
||||
evcon->retry_cnt = 0;
|
||||
evcon->state = EVCON_CONNECTED;
|
||||
evcon->flags &= ~EVHTTP_CON_GOTHEADERS;
|
||||
evcon->state = EVCON_IDLE;
|
||||
|
||||
/* reset the bufferevent cbs */
|
||||
bufferevent_setcb(evcon->bufev,
|
||||
evhttp_read_header_cb,
|
||||
evhttp_read_cb,
|
||||
evhttp_write_cb,
|
||||
evhttp_error_cb,
|
||||
evcon);
|
||||
@ -1309,63 +1380,74 @@ evhttp_add_header(struct evkeyvalq *headers,
|
||||
* request object given an event buffer.
|
||||
*
|
||||
* Returns
|
||||
* -1 on error
|
||||
* 0 when we need to read more headers
|
||||
* 1 when all headers have been read.
|
||||
* DATA_CORRUPTED on error
|
||||
* MORE_DATA_EXPECTED when we need to read more headers
|
||||
* ALL_DATA_READ when all headers have been read.
|
||||
*/
|
||||
|
||||
int
|
||||
evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
enum message_read_status
|
||||
evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer)
|
||||
{
|
||||
char *line;
|
||||
int done = 0;
|
||||
enum message_read_status status = ALL_DATA_READ;
|
||||
|
||||
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF);
|
||||
if (line == NULL)
|
||||
return (MORE_DATA_EXPECTED);
|
||||
|
||||
switch (req->kind) {
|
||||
case EVHTTP_REQUEST:
|
||||
if (evhttp_parse_request_line(req, line) == -1)
|
||||
status = DATA_CORRUPTED;
|
||||
break;
|
||||
case EVHTTP_RESPONSE:
|
||||
if (evhttp_parse_response_line(req, line) == -1)
|
||||
status = DATA_CORRUPTED;
|
||||
break;
|
||||
default:
|
||||
status = DATA_CORRUPTED;
|
||||
}
|
||||
|
||||
mm_free(line);
|
||||
return (status);
|
||||
}
|
||||
|
||||
enum message_read_status
|
||||
evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
{
|
||||
char *line;
|
||||
enum message_read_status status = MORE_DATA_EXPECTED;
|
||||
|
||||
struct evkeyvalq* headers = req->input_headers;
|
||||
while ((line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF)) != NULL) {
|
||||
while ((line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF))
|
||||
!= NULL) {
|
||||
char *skey, *svalue;
|
||||
|
||||
if (*line == '\0') { /* Last header - Done */
|
||||
done = 1;
|
||||
status = ALL_DATA_READ;
|
||||
mm_free(line);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Processing of header lines */
|
||||
if (req->got_firstline == 0) {
|
||||
switch (req->kind) {
|
||||
case EVHTTP_REQUEST:
|
||||
if (evhttp_parse_request_line(req, line) == -1)
|
||||
goto error;
|
||||
break;
|
||||
case EVHTTP_RESPONSE:
|
||||
if (evhttp_parse_response_line(req, line) == -1)
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
req->got_firstline = 1;
|
||||
} else {
|
||||
/* Regular header */
|
||||
svalue = line;
|
||||
skey = strsep(&svalue, ":");
|
||||
if (svalue == NULL)
|
||||
goto error;
|
||||
svalue = line;
|
||||
skey = strsep(&svalue, ":");
|
||||
if (svalue == NULL)
|
||||
goto error;
|
||||
|
||||
svalue += strspn(svalue, " ");
|
||||
svalue += strspn(svalue, " ");
|
||||
|
||||
if (evhttp_add_header(headers, skey, svalue) == -1)
|
||||
goto error;
|
||||
}
|
||||
if (evhttp_add_header(headers, skey, svalue) == -1)
|
||||
goto error;
|
||||
|
||||
mm_free(line);
|
||||
}
|
||||
|
||||
return (done);
|
||||
return (status);
|
||||
|
||||
error:
|
||||
mm_free(line);
|
||||
return (-1);
|
||||
return (DATA_CORRUPTED);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1417,6 +1499,7 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
evhttp_connection_done(evcon);
|
||||
return;
|
||||
}
|
||||
evcon->state = EVCON_READING_BODY;
|
||||
xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding");
|
||||
if (xfer_enc != NULL && strcasecmp(xfer_enc, "chunked") == 0) {
|
||||
req->chunked = 1;
|
||||
@ -1433,20 +1516,39 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
}
|
||||
|
||||
static void
|
||||
evhttp_read_header_cb(struct bufferevent *bufev, void *arg)
|
||||
evhttp_read_firstline(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req)
|
||||
{
|
||||
struct evhttp_connection *evcon = arg;
|
||||
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||
int res;
|
||||
int fd = evcon->fd;
|
||||
enum message_read_status res;
|
||||
|
||||
res = evhttp_parse_lines(req, bufferevent_get_input(evcon->bufev));
|
||||
if (res == -1) {
|
||||
res = evhttp_parse_firstline(req, bufferevent_get_input(evcon->bufev));
|
||||
if (res == DATA_CORRUPTED) {
|
||||
/* Error while reading, terminate */
|
||||
event_debug(("%s: bad header lines on %d\n", __func__, fd));
|
||||
evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
|
||||
return;
|
||||
} else if (res == 0) {
|
||||
} else if (res == MORE_DATA_EXPECTED) {
|
||||
/* Need more header lines */
|
||||
return;
|
||||
}
|
||||
|
||||
evhttp_read_header(evcon, req);
|
||||
}
|
||||
|
||||
static void
|
||||
evhttp_read_header(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req)
|
||||
{
|
||||
enum message_read_status res;
|
||||
int fd = evcon->fd;
|
||||
|
||||
res = evhttp_parse_headers(req, bufferevent_get_input(evcon->bufev));
|
||||
if (res == DATA_CORRUPTED) {
|
||||
/* Error while reading, terminate */
|
||||
event_debug(("%s: bad header lines on %d\n", __func__, fd));
|
||||
evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
|
||||
return;
|
||||
} else if (res == MORE_DATA_EXPECTED) {
|
||||
/* Need more header lines */
|
||||
return;
|
||||
}
|
||||
@ -1454,9 +1556,6 @@ evhttp_read_header_cb(struct bufferevent *bufev, void *arg)
|
||||
/* 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 */
|
||||
switch (req->kind) {
|
||||
case EVHTTP_REQUEST:
|
||||
@ -1531,7 +1630,7 @@ evhttp_connection_base_new(struct event_base *base,
|
||||
}
|
||||
|
||||
if ((evcon->bufev = bufferevent_new(-1,
|
||||
evhttp_read_header_cb,
|
||||
evhttp_read_cb,
|
||||
evhttp_write_cb,
|
||||
evhttp_error_cb, evcon)) == NULL) {
|
||||
event_warn("%s: bufferevent_new failed", __func__);
|
||||
@ -1674,7 +1773,7 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
||||
TAILQ_INSERT_TAIL(&evcon->requests, req, next);
|
||||
|
||||
/* If the connection object is not connected; make it so */
|
||||
if (evcon->state != EVCON_CONNECTED)
|
||||
if (!evhttp_connected(evcon))
|
||||
return (evhttp_connection_connect(evcon));
|
||||
|
||||
/*
|
||||
@ -1725,6 +1824,7 @@ evhttp_start_read(struct evhttp_connection *evcon)
|
||||
/* Set up an event to read the headers */
|
||||
bufferevent_disable(evcon->bufev, EV_WRITE);
|
||||
bufferevent_enable(evcon->bufev, EV_READ);
|
||||
evcon->state = EVCON_READING_FIRSTLINE;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2560,7 +2660,7 @@ evhttp_get_request_connection(
|
||||
return (NULL);
|
||||
|
||||
evcon->flags |= EVHTTP_CON_INCOMING;
|
||||
evcon->state = EVCON_CONNECTED;
|
||||
evcon->state = EVCON_READING_FIRSTLINE;
|
||||
|
||||
evcon->fd = fd;
|
||||
|
||||
|
@ -93,7 +93,6 @@ struct {
|
||||
char major; /* HTTP Major number */
|
||||
char minor; /* HTTP Minor number */
|
||||
|
||||
int got_firstline;
|
||||
int response_code; /* HTTP Response code */
|
||||
char *response_code_line; /* Readable response */
|
||||
|
||||
|
@ -181,15 +181,23 @@ http_readcb(struct bufferevent *bev, void *arg)
|
||||
if (evbuffer_find(EVBUFFER_INPUT(bev),
|
||||
(const unsigned char*) what, strlen(what)) != NULL) {
|
||||
struct evhttp_request *req = evhttp_request_new(NULL, NULL);
|
||||
int done;
|
||||
enum message_read_status done;
|
||||
|
||||
req->kind = EVHTTP_RESPONSE;
|
||||
done = evhttp_parse_lines(req, EVBUFFER_INPUT(bev));
|
||||
done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev));
|
||||
if (done != ALL_DATA_READ)
|
||||
goto out;
|
||||
|
||||
done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev));
|
||||
if (done != ALL_DATA_READ)
|
||||
goto out;
|
||||
|
||||
if (done == 1 &&
|
||||
evhttp_find_header(req->input_headers,
|
||||
"Content-Type") != NULL)
|
||||
test_ok++;
|
||||
|
||||
out:
|
||||
evhttp_request_free(req);
|
||||
bufferevent_disable(bev, EV_READ);
|
||||
if (base)
|
||||
@ -231,31 +239,53 @@ http_basic_cb(struct evhttp_request *req, void *arg)
|
||||
evbuffer_free(evb);
|
||||
}
|
||||
|
||||
static char const* const CHUNKS[] = {
|
||||
"This is funny",
|
||||
"but not hilarious.",
|
||||
"bwv 1052"
|
||||
};
|
||||
|
||||
struct chunk_req_state {
|
||||
struct evhttp_request *req;
|
||||
int i;
|
||||
};
|
||||
|
||||
static void
|
||||
http_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
|
||||
{
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
struct chunk_req_state *state = arg;
|
||||
struct timeval when = { 0, 0 };
|
||||
|
||||
evbuffer_add_printf(evb, CHUNKS[state->i]);
|
||||
evhttp_send_reply_chunk(state->req, evb);
|
||||
evbuffer_free(evb);
|
||||
|
||||
if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) {
|
||||
event_once(-1, EV_TIMEOUT,
|
||||
http_chunked_trickle_cb, state, &when);
|
||||
} else {
|
||||
evhttp_send_reply_end(state->req);
|
||||
free(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
http_chunked_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
struct timeval when = { 0, 0 };
|
||||
struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
|
||||
event_debug(("%s: called\n", __func__));
|
||||
|
||||
memset(state, 0, sizeof(struct chunk_req_state));
|
||||
state->req = req;
|
||||
|
||||
/* generate a chunked reply */
|
||||
evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
|
||||
|
||||
/* first chunk */
|
||||
evbuffer_add_printf(evb, "This is funny");
|
||||
evhttp_send_reply_chunk(req, evb);
|
||||
|
||||
/* second chunk */
|
||||
evbuffer_add_printf(evb, "but not hilarious.");
|
||||
evhttp_send_reply_chunk(req, evb);
|
||||
|
||||
/* third and last chunk */
|
||||
evbuffer_add_printf(evb, "bwv 1052");
|
||||
evhttp_send_reply_chunk(req, evb);
|
||||
|
||||
/* finish request */
|
||||
evhttp_send_reply_end(req);
|
||||
|
||||
evbuffer_free(evb);
|
||||
/* but trickle it across several iterations to ensure we're not
|
||||
* assuming it comes all at once */
|
||||
event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1555,12 +1585,15 @@ http_chunked_errorcb(struct bufferevent *bev, short what, void *arg)
|
||||
if ((what & EVBUFFER_EOF) != 0) {
|
||||
struct evhttp_request *req = evhttp_request_new(NULL, NULL);
|
||||
const char *header;
|
||||
int done;
|
||||
enum message_read_status done;
|
||||
|
||||
req->kind = EVHTTP_RESPONSE;
|
||||
done = evhttp_parse_lines(req, EVBUFFER_INPUT(bev));
|
||||
done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev));
|
||||
if (done != ALL_DATA_READ)
|
||||
goto out;
|
||||
|
||||
if (done != 1)
|
||||
done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev));
|
||||
if (done != ALL_DATA_READ)
|
||||
goto out;
|
||||
|
||||
header = evhttp_find_header(req->input_headers, "Transfer-Encoding");
|
||||
@ -1678,6 +1711,7 @@ http_chunked_test(void)
|
||||
struct timeval tv_start, tv_end;
|
||||
struct evhttp_connection *evcon = NULL;
|
||||
struct evhttp_request *req = NULL;
|
||||
int i;
|
||||
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing Chunked HTTP Reply: ");
|
||||
@ -1723,23 +1757,28 @@ http_chunked_test(void)
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
req = evhttp_request_new(http_chunked_request_done, NULL);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
/* make two requests to check the keepalive behavior */
|
||||
for (i = 0; i < 2; i++) {
|
||||
test_ok = 0;
|
||||
req = evhttp_request_new(http_chunked_request_done, NULL);
|
||||
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req,
|
||||
EVHTTP_REQ_GET, "/chunked") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
|
||||
event_dispatch();
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req,
|
||||
EVHTTP_REQ_GET, "/chunked") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (test_ok != 1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
event_dispatch();
|
||||
|
||||
if (test_ok != 1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
|
Loading…
x
Reference in New Issue
Block a user