From 7b077194cc8fd38808e3f210fc090aedb5df25d5 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin <a3at.mail@gmail.com> Date: Thu, 21 Mar 2013 13:55:40 +0400 Subject: [PATCH] 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) --- evrpc.c | 2 +- http-internal.h | 11 ++----- http.c | 56 ++++++++++++++++++++++-------------- include/event2/http.h | 41 ++++++++++++++++++++++++++ include/event2/http_struct.h | 7 +++++ test/regress_http.c | 3 +- 6 files changed, 88 insertions(+), 32 deletions(-) diff --git a/evrpc.c b/evrpc.c index fd1f67c0..8d8ecc5a 100644 --- a/evrpc.c +++ b/evrpc.c @@ -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); } /* diff --git a/http-internal.h b/http-internal.h index 0d4475f5..fe14a230 100644 --- a/http-internal.h +++ b/http-internal.h @@ -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; diff --git a/http.c b/http.c index f571d356..22705075 100644 --- a/http.c +++ b/http.c @@ -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 */ diff --git a/include/event2/http.h b/include/event2/http.h index 04338a7b..0428ea9e 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -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); diff --git a/include/event2/http_struct.h b/include/event2/http_struct.h index f245dff8..25e19bd9 100644 --- a/include/event2/http_struct.h +++ b/include/event2/http_struct.h @@ -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 diff --git a/test/regress_http.c b/test/regress_http.c index 70b40d17..eff97950 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -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); } /* -- 2.40.0