Add new error_cb for actual reporting of HTTP request errors.

It is useful to know why you callback called with NULL (i.e. it failed),
for example if you set max_body with evhttp_connection_set_max_body_size()
you must know that it failed because of body was longer than this size.

 (Commit message tweaked by Nick)
This commit is contained in:
Azat Khuzhin 2013-03-21 13:55:40 +04:00 committed by Nick Mathewson
parent f935e2159a
commit 7b077194cc
6 changed files with 88 additions and 32 deletions

View File

@ -977,7 +977,7 @@ evrpc_request_timeout(evutil_socket_t fd, short what, void *arg)
struct evhttp_connection *evcon = ctx->evcon; struct evhttp_connection *evcon = ctx->evcon;
EVUTIL_ASSERT(evcon != NULL); EVUTIL_ASSERT(evcon != NULL);
evhttp_connection_fail_(evcon, EVCON_HTTP_TIMEOUT); evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
} }
/* /*

View File

@ -29,14 +29,6 @@ enum message_read_status {
DATA_TOO_LONG = -3 DATA_TOO_LONG = -3
}; };
enum evhttp_connection_error {
EVCON_HTTP_TIMEOUT,
EVCON_HTTP_EOF,
EVCON_HTTP_INVALID_HEADER,
EVCON_HTTP_BUFFER_ERROR,
EVCON_HTTP_REQUEST_CANCEL
};
struct evbuffer; struct evbuffer;
struct addrinfo; struct addrinfo;
struct evhttp_request; struct evhttp_request;
@ -182,9 +174,10 @@ void evhttp_connection_reset_(struct evhttp_connection *);
/* connects if necessary */ /* connects if necessary */
int evhttp_connection_connect_(struct evhttp_connection *); int evhttp_connection_connect_(struct evhttp_connection *);
enum evhttp_request_error;
/* notifies the current request that it failed; resets connection */ /* notifies the current request that it failed; resets connection */
void evhttp_connection_fail_(struct evhttp_connection *, void evhttp_connection_fail_(struct evhttp_connection *,
enum evhttp_connection_error error); enum evhttp_request_error error);
enum message_read_status; enum message_read_status;

56
http.c
View File

@ -625,11 +625,11 @@ evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
static int static int
evhttp_connection_incoming_fail(struct evhttp_request *req, evhttp_connection_incoming_fail(struct evhttp_request *req,
enum evhttp_connection_error error) enum evhttp_request_error error)
{ {
switch (error) { switch (error) {
case EVCON_HTTP_TIMEOUT: case EVREQ_HTTP_TIMEOUT:
case EVCON_HTTP_EOF: case EVREQ_HTTP_EOF:
/* /*
* these are cases in which we probably should just * these are cases in which we probably should just
* close the connection and not send a reply. this * close the connection and not send a reply. this
@ -647,9 +647,10 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
req->evcon = NULL; req->evcon = NULL;
} }
return (-1); return (-1);
case EVCON_HTTP_INVALID_HEADER: case EVREQ_HTTP_INVALID_HEADER:
case EVCON_HTTP_BUFFER_ERROR: case EVREQ_HTTP_BUFFER_ERROR:
case EVCON_HTTP_REQUEST_CANCEL: case EVREQ_HTTP_REQUEST_CANCEL:
case EVREQ_HTTP_DATA_TOO_LONG:
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) {
@ -677,12 +678,14 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
* delegates to evhttp_connection_incoming_fail(). */ * delegates to evhttp_connection_incoming_fail(). */
void void
evhttp_connection_fail_(struct evhttp_connection *evcon, evhttp_connection_fail_(struct evhttp_connection *evcon,
enum evhttp_connection_error error) enum evhttp_request_error error)
{ {
const int errsave = EVUTIL_SOCKET_ERROR(); const int errsave = EVUTIL_SOCKET_ERROR();
struct evhttp_request* req = TAILQ_FIRST(&evcon->requests); struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
void (*cb)(struct evhttp_request *, void *); void (*cb)(struct evhttp_request *, void *);
void *cb_arg; void *cb_arg;
void (*error_cb)(enum evhttp_request_error, void *);
void *error_cb_arg;
EVUTIL_ASSERT(req != NULL); EVUTIL_ASSERT(req != NULL);
bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE); bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
@ -701,8 +704,10 @@ evhttp_connection_fail_(struct evhttp_connection *evcon,
return; return;
} }
error_cb = req->error_cb;
error_cb_arg = req->cb_arg;
/* when the request was canceled, the callback is not executed */ /* when the request was canceled, the callback is not executed */
if (error != EVCON_HTTP_REQUEST_CANCEL) { if (error != EVREQ_HTTP_REQUEST_CANCEL) {
/* save the callback for later; the cb might free our object */ /* save the callback for later; the cb might free our object */
cb = req->cb; cb = req->cb;
cb_arg = req->cb_arg; cb_arg = req->cb_arg;
@ -732,6 +737,8 @@ evhttp_connection_fail_(struct evhttp_connection *evcon,
EVUTIL_SET_SOCKET_ERROR(errsave); EVUTIL_SET_SOCKET_ERROR(errsave);
/* inform the user */ /* inform the user */
if (error_cb != NULL)
error_cb(error, error_cb_arg);
if (cb != NULL) if (cb != NULL)
(*cb)(NULL, cb_arg); (*cb)(NULL, cb_arg);
} }
@ -925,7 +932,7 @@ evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
switch (evhttp_parse_headers_(req, buf)) { switch (evhttp_parse_headers_(req, buf)) {
case DATA_CORRUPTED: case DATA_CORRUPTED:
case DATA_TOO_LONG: case DATA_TOO_LONG:
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER); evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
break; break;
case ALL_DATA_READ: case ALL_DATA_READ:
bufferevent_disable(evcon->bufev, EV_READ); bufferevent_disable(evcon->bufev, EV_READ);
@ -952,10 +959,10 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
evhttp_read_trailer(evcon, req); evhttp_read_trailer(evcon, req);
return; return;
case DATA_CORRUPTED: case DATA_CORRUPTED:
case DATA_TOO_LONG:/*separate error for this? XXX */ case DATA_TOO_LONG:
/* corrupted data */ /* corrupted data */
evhttp_connection_fail_(evcon, evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER); EVREQ_HTTP_DATA_TOO_LONG);
return; return;
case REQUEST_CANCELED: case REQUEST_CANCELED:
/* request canceled */ /* request canceled */
@ -968,7 +975,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
} else if (req->ntoread < 0) { } else if (req->ntoread < 0) {
/* Read until connection close. */ /* Read until connection close. */
if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) { if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) {
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER); evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return; return;
} }
@ -994,7 +1001,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
/* failed body length test */ /* failed body length test */
event_debug(("Request body is too long")); event_debug(("Request body is too long"));
evhttp_connection_fail_(evcon, evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER); EVREQ_HTTP_DATA_TOO_LONG);
return; return;
} }
@ -1375,12 +1382,12 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
} }
if (what & BEV_EVENT_TIMEOUT) { if (what & BEV_EVENT_TIMEOUT) {
evhttp_connection_fail_(evcon, EVCON_HTTP_TIMEOUT); evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
evhttp_connection_fail_(evcon, EVCON_HTTP_EOF); evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
} else if (what == BEV_EVENT_CONNECTED) { } else if (what == BEV_EVENT_CONNECTED) {
} else { } else {
evhttp_connection_fail_(evcon, EVCON_HTTP_BUFFER_ERROR); evhttp_connection_fail_(evcon, EVREQ_HTTP_BUFFER_ERROR);
} }
} }
@ -2032,7 +2039,7 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
} else { } else {
if (evhttp_get_body_length(req) == -1) { if (evhttp_get_body_length(req) == -1) {
evhttp_connection_fail_(evcon, evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER); EVREQ_HTTP_INVALID_HEADER);
return; return;
} }
if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) { if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
@ -2088,7 +2095,7 @@ evhttp_read_firstline(struct evhttp_connection *evcon,
/* Error while reading, terminate */ /* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n", event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(evcon->fd))); __func__, EV_SOCK_ARG(evcon->fd)));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER); evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return; return;
} else if (res == MORE_DATA_EXPECTED) { } else if (res == MORE_DATA_EXPECTED) {
/* Need more header lines */ /* Need more header lines */
@ -2111,7 +2118,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
/* Error while reading, terminate */ /* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n", event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(fd))); __func__, EV_SOCK_ARG(fd)));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER); evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return; return;
} else if (res == MORE_DATA_EXPECTED) { } else if (res == MORE_DATA_EXPECTED) {
/* Need more header lines */ /* Need more header lines */
@ -2153,7 +2160,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
default: default:
event_warnx("%s: bad header on "EV_SOCK_FMT, __func__, event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
EV_SOCK_ARG(fd)); EV_SOCK_ARG(fd));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER); evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
break; break;
} }
/* request may have been freed above */ /* request may have been freed above */
@ -2454,7 +2461,7 @@ evhttp_cancel_request(struct evhttp_request *req)
* the connection. * the connection.
*/ */
evhttp_connection_fail_(evcon, evhttp_connection_fail_(evcon,
EVCON_HTTP_REQUEST_CANCEL); EVREQ_HTTP_REQUEST_CANCEL);
/* connection fail freed the request */ /* connection fail freed the request */
return; return;
@ -3764,6 +3771,13 @@ evhttp_request_set_chunked_cb(struct evhttp_request *req,
req->chunk_cb = cb; req->chunk_cb = cb;
} }
void
evhttp_request_set_error_cb(struct evhttp_request *req,
void (*cb)(enum evhttp_request_error, void *))
{
req->error_cb = cb;
}
/* /*
* Allows for inspection of the request URI * Allows for inspection of the request URI
*/ */

View File

@ -470,6 +470,47 @@ struct evhttp_request *evhttp_request_new(
void evhttp_request_set_chunked_cb(struct evhttp_request *, void evhttp_request_set_chunked_cb(struct evhttp_request *,
void (*cb)(struct evhttp_request *, void *)); void (*cb)(struct evhttp_request *, void *));
/**
* The different error types supported by evhttp
*
* @see evhttp_request_set_error_cb()
*/
enum evhttp_request_error {
/**
* Timeout reached, also @see evhttp_connection_set_timeout()
*/
EVREQ_HTTP_TIMEOUT,
/**
* EOF reached
*/
EVREQ_HTTP_EOF,
/**
* Error while reading header, or invalid header
*/
EVREQ_HTTP_INVALID_HEADER,
/**
* Error encountered while reading or writing
*/
EVREQ_HTTP_BUFFER_ERROR,
/**
* The evhttp_cancel_request() called on this request.
*/
EVREQ_HTTP_REQUEST_CANCEL,
/**
* Body is greater then evhttp_connection_set_max_body_size()
*/
EVREQ_HTTP_DATA_TOO_LONG
};
/**
* Set a callback for errors
* @see evhttp_request_error for error types.
*
* On error, both the error callback and the regular callback will be called,
* error callback is called before the regular callback.
**/
void evhttp_request_set_error_cb(struct evhttp_request *,
void (*)(enum evhttp_request_error, void *));
/** Frees the request object and removes associated events. */ /** Frees the request object and removes associated events. */
void evhttp_request_free(struct evhttp_request *req); void evhttp_request_free(struct evhttp_request *req);

View File

@ -120,6 +120,13 @@ struct {
* the regular callback. * the regular callback.
*/ */
void (*chunk_cb)(struct evhttp_request *, void *); void (*chunk_cb)(struct evhttp_request *, void *);
/*
* Error callback - called when error is occured.
* @see evhttp_request_error for error types.
*
* @see evhttp_request_set_error_cb()
*/
void (*error_cb)(enum evhttp_request_error, void *);
}; };
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -56,6 +56,7 @@
#include "event2/event.h" #include "event2/event.h"
#include "event2/http.h" #include "event2/http.h"
#include "event2/http_struct.h"
#include "event2/buffer.h" #include "event2/buffer.h"
#include "event2/bufferevent.h" #include "event2/bufferevent.h"
#include "event2/util.h" #include "event2/util.h"
@ -647,7 +648,7 @@ http_large_delay_cb(struct evhttp_request *req, void *arg)
tv.tv_usec = 500000; tv.tv_usec = 500000;
event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
evhttp_connection_fail_(delayed_client, EVCON_HTTP_EOF); evhttp_connection_fail_(delayed_client, EVREQ_HTTP_EOF);
} }
/* /*