]> granicus.if.org Git - libevent/commitdiff
Add new error_cb for actual reporting of HTTP request errors.
authorAzat Khuzhin <a3at.mail@gmail.com>
Thu, 21 Mar 2013 09:55:40 +0000 (13:55 +0400)
committerNick Mathewson <nickm@torproject.org>
Thu, 25 Apr 2013 19:11:44 +0000 (15:11 -0400)
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
http-internal.h
http.c
include/event2/http.h
include/event2/http_struct.h
test/regress_http.c

diff --git a/evrpc.c b/evrpc.c
index fd1f67c004ab8c0ea12d26eafa3e7fb5f517f6cf..8d8ecc5aa87d5a1ac97b663745ed13ce391cd013 100644 (file)
--- 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);
 }
 
 /*
index 0d4475f5f9b7b9665e50c85cbe3d7d0c2c5111c0..fe14a230096b8f80931b3049b74e03558d37dce8 100644 (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;
 
diff --git a/http.c b/http.c
index f571d356d4a97c00149e3cfd3d15a51cdcb904a2..227050759415bea9dfbce1221ec8f9434bbdba16 100644 (file)
--- 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
  */
index 04338a7bbef2a188f9b2bb32e80bf59d3a294ab4..0428ea9e279091974109b32d56166153b9f95114 100644 (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);
 
index f245dff8ff65dff3c808e445e2339b74855583b3..25e19bd9991f38e3e0682859f73800d7f28eec2f 100644 (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
index 70b40d17f335c2ff37479699fe9148f5f2933c85..eff97950128c72ab6f89ad8ac96e881b616651a7 100644 (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);
 }
 
 /*