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;
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
};
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 addrinfo;
struct evhttp_request;
@ -182,9 +174,10 @@ void evhttp_connection_reset_(struct evhttp_connection *);
/* connects if necessary */
int evhttp_connection_connect_(struct evhttp_connection *);
enum evhttp_request_error;
/* notifies the current request that it failed; resets connection */
void evhttp_connection_fail_(struct evhttp_connection *,
enum evhttp_connection_error error);
enum evhttp_request_error error);
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
evhttp_connection_incoming_fail(struct evhttp_request *req,
enum evhttp_connection_error error)
enum evhttp_request_error error)
{
switch (error) {
case EVCON_HTTP_TIMEOUT:
case EVCON_HTTP_EOF:
case EVREQ_HTTP_TIMEOUT:
case EVREQ_HTTP_EOF:
/*
* these are cases in which we probably should just
* close the connection and not send a reply. this
@ -647,9 +647,10 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
req->evcon = NULL;
}
return (-1);
case EVCON_HTTP_INVALID_HEADER:
case EVCON_HTTP_BUFFER_ERROR:
case EVCON_HTTP_REQUEST_CANCEL:
case EVREQ_HTTP_INVALID_HEADER:
case EVREQ_HTTP_BUFFER_ERROR:
case EVREQ_HTTP_REQUEST_CANCEL:
case EVREQ_HTTP_DATA_TOO_LONG:
default: /* xxx: probably should just error on default */
/* the callback looks at the uri to determine errors */
if (req->uri) {
@ -677,12 +678,14 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
* delegates to evhttp_connection_incoming_fail(). */
void
evhttp_connection_fail_(struct evhttp_connection *evcon,
enum evhttp_connection_error error)
enum evhttp_request_error error)
{
const int errsave = EVUTIL_SOCKET_ERROR();
struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
void (*cb)(struct evhttp_request *, void *);
void *cb_arg;
void (*error_cb)(enum evhttp_request_error, void *);
void *error_cb_arg;
EVUTIL_ASSERT(req != NULL);
bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
@ -701,8 +704,10 @@ evhttp_connection_fail_(struct evhttp_connection *evcon,
return;
}
error_cb = req->error_cb;
error_cb_arg = req->cb_arg;
/* 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 */
cb = req->cb;
cb_arg = req->cb_arg;
@ -732,6 +737,8 @@ evhttp_connection_fail_(struct evhttp_connection *evcon,
EVUTIL_SET_SOCKET_ERROR(errsave);
/* inform the user */
if (error_cb != NULL)
error_cb(error, error_cb_arg);
if (cb != NULL)
(*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)) {
case DATA_CORRUPTED:
case DATA_TOO_LONG:
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER);
evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
break;
case ALL_DATA_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);
return;
case DATA_CORRUPTED:
case DATA_TOO_LONG:/*separate error for this? XXX */
case DATA_TOO_LONG:
/* corrupted data */
evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER);
EVREQ_HTTP_DATA_TOO_LONG);
return;
case REQUEST_CANCELED:
/* request canceled */
@ -968,7 +975,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
} else if (req->ntoread < 0) {
/* Read until connection close. */
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;
}
@ -994,7 +1001,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
/* failed body length test */
event_debug(("Request body is too long"));
evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER);
EVREQ_HTTP_DATA_TOO_LONG);
return;
}
@ -1375,12 +1382,12 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
}
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)) {
evhttp_connection_fail_(evcon, EVCON_HTTP_EOF);
evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
} else if (what == BEV_EVENT_CONNECTED) {
} 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 {
if (evhttp_get_body_length(req) == -1) {
evhttp_connection_fail_(evcon,
EVCON_HTTP_INVALID_HEADER);
EVREQ_HTTP_INVALID_HEADER);
return;
}
if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
@ -2088,7 +2095,7 @@ evhttp_read_firstline(struct evhttp_connection *evcon,
/* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(evcon->fd)));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER);
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
} else if (res == MORE_DATA_EXPECTED) {
/* Need more header lines */
@ -2111,7 +2118,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
/* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(fd)));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER);
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
} else if (res == MORE_DATA_EXPECTED) {
/* Need more header lines */
@ -2153,7 +2160,7 @@ evhttp_read_header(struct evhttp_connection *evcon,
default:
event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
EV_SOCK_ARG(fd));
evhttp_connection_fail_(evcon, EVCON_HTTP_INVALID_HEADER);
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
break;
}
/* request may have been freed above */
@ -2454,7 +2461,7 @@ evhttp_cancel_request(struct evhttp_request *req)
* the connection.
*/
evhttp_connection_fail_(evcon,
EVCON_HTTP_REQUEST_CANCEL);
EVREQ_HTTP_REQUEST_CANCEL);
/* connection fail freed the request */
return;
@ -3764,6 +3771,13 @@ evhttp_request_set_chunked_cb(struct evhttp_request *req,
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
*/

View File

@ -470,6 +470,47 @@ struct evhttp_request *evhttp_request_new(
void evhttp_request_set_chunked_cb(struct evhttp_request *,
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. */
void evhttp_request_free(struct evhttp_request *req);

View File

@ -120,6 +120,13 @@ struct {
* the regular callback.
*/
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

View File

@ -56,6 +56,7 @@
#include "event2/event.h"
#include "event2/http.h"
#include "event2/http_struct.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
#include "event2/util.h"
@ -647,7 +648,7 @@ http_large_delay_cb(struct evhttp_request *req, void *arg)
tv.tv_usec = 500000;
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);
}
/*