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
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) {
* 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);
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;
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);
}
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);
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 */
} 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;
}
/* 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;
}
}
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);
}
}
} 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) {
/* 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 */
/* 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 */
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 */
* the connection.
*/
evhttp_connection_fail_(evcon,
- EVCON_HTTP_REQUEST_CANCEL);
+ EVREQ_HTTP_REQUEST_CANCEL);
/* connection fail freed the request */
return;
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
*/
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);