mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-09 04:19:10 -04:00
allow cancelation of user initiated http requests; this will allow cancelation of rpc requests eventually
svn:r812
This commit is contained in:
parent
03fafae006
commit
1080852e91
@ -97,7 +97,8 @@ Changes in current version:
|
||||
o deprecate timeout_* event functions by moving them to event_compat.h
|
||||
o Move windows gettimeofday replacement into a new evutil_gettimeofday().
|
||||
o Make configure script work on IRIX.
|
||||
|
||||
o provide a method for canceling ongoing http requests.
|
||||
|
||||
|
||||
Changes in 1.4.0:
|
||||
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
|
||||
|
@ -23,7 +23,8 @@ enum evhttp_connection_error {
|
||||
EVCON_HTTP_TIMEOUT,
|
||||
EVCON_HTTP_EOF,
|
||||
EVCON_HTTP_INVALID_HEADER,
|
||||
EVCON_HTTP_BUFFER_ERROR
|
||||
EVCON_HTTP_BUFFER_ERROR,
|
||||
EVCON_HTTP_REQUEST_CANCEL
|
||||
};
|
||||
|
||||
struct evbuffer;
|
||||
|
44
http.c
44
http.c
@ -483,6 +483,7 @@ evhttp_connection_incoming_fail(struct evhttp_request *req,
|
||||
return (-1);
|
||||
case EVCON_HTTP_INVALID_HEADER:
|
||||
case EVCON_HTTP_BUFFER_ERROR:
|
||||
case EVCON_HTTP_REQUEST_CANCEL:
|
||||
default: /* xxx: probably should just error on default */
|
||||
/* the callback looks at the uri to determine errors */
|
||||
if (req->uri) {
|
||||
@ -525,14 +526,23 @@ evhttp_connection_fail(struct evhttp_connection *evcon,
|
||||
return;
|
||||
}
|
||||
|
||||
/* save the callback for later; the cb might free our object */
|
||||
cb = req->cb;
|
||||
cb_arg = req->cb_arg;
|
||||
/* when the request was canceled, the callback is not executed */
|
||||
if (error != EVCON_HTTP_REQUEST_CANCEL) {
|
||||
/* save the callback for later; the cb might free our object */
|
||||
cb = req->cb;
|
||||
cb_arg = req->cb_arg;
|
||||
} else {
|
||||
cb = NULL;
|
||||
cb_arg = NULL;
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||
evhttp_request_free(req);
|
||||
|
||||
/* xxx: maybe we should fail all requests??? */
|
||||
/* do not fail all requests; the next request is going to get
|
||||
* send over a new connection. when a user cancels a request,
|
||||
* all other pending requests should be processed as normal
|
||||
*/
|
||||
|
||||
/* reset the connection */
|
||||
evhttp_connection_reset(evcon);
|
||||
@ -1574,6 +1584,32 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evhttp_cancel_request(struct evhttp_request *req)
|
||||
{
|
||||
struct evhttp_connection *evcon = req->evcon;
|
||||
if (evcon != NULL) {
|
||||
/* We need to remove it from the connection */
|
||||
if (TAILQ_FIRST(&evcon->requests) == req) {
|
||||
/* it's currently being worked on, so reset
|
||||
* the connection.
|
||||
*/
|
||||
evhttp_connection_fail(evcon,
|
||||
EVCON_HTTP_REQUEST_CANCEL);
|
||||
|
||||
/* connection fail freed the request */
|
||||
return;
|
||||
} else {
|
||||
/* otherwise, we can just remove it from the
|
||||
* queue
|
||||
*/
|
||||
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||
}
|
||||
}
|
||||
|
||||
evhttp_request_free(req);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads data from file descriptor into request structure
|
||||
* Request structure needs to be set up correctly.
|
||||
|
@ -274,11 +274,37 @@ void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
|
||||
void evhttp_connection_get_peer(struct evhttp_connection *evcon,
|
||||
char **address, ev_uint16_t *port);
|
||||
|
||||
/** The connection gets ownership of the request */
|
||||
/**
|
||||
Make an HTTP request over the specified connection.
|
||||
|
||||
The connection gets ownership of the request.
|
||||
|
||||
@param evcon the evhttp_connection object over which to send the request
|
||||
@param req the previously created and configured request object
|
||||
@param type the request type EVHTTP_REQ_GET, EVHTTP_REQ_POST, etc.
|
||||
@param uri the URI associated with the request
|
||||
@return 0 on success, -1 on failure
|
||||
@see evhttp_cancel_request()
|
||||
*/
|
||||
int evhttp_make_request(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req,
|
||||
enum evhttp_cmd_type type, const char *uri);
|
||||
|
||||
/**
|
||||
Cancels a pending HTTP request.
|
||||
|
||||
Cancels an ongoing HTTP request. The callback associated with this request
|
||||
is not executed and the request object is freed. If the request is
|
||||
currently being processed, e.g. it is ongoing, the corresponding
|
||||
evhttp_connection object is going to get reset.
|
||||
|
||||
A request cannot be canceled if its callback has executed already.
|
||||
|
||||
@param req the evhttp_request to cancel; req becomes invalid after this call.
|
||||
*/
|
||||
void evhttp_cancel_request(struct evhttp_request *req);
|
||||
|
||||
|
||||
/** Returns the request URI */
|
||||
const char *evhttp_request_get_uri(struct evhttp_request *req);
|
||||
/** Returns the input headers */
|
||||
|
@ -66,12 +66,13 @@ static struct event_base *base;
|
||||
|
||||
void http_suite(void);
|
||||
|
||||
void http_basic_cb(struct evhttp_request *req, void *arg);
|
||||
void http_chunked_cb(struct evhttp_request *req, void *arg);
|
||||
void http_post_cb(struct evhttp_request *req, void *arg);
|
||||
void http_put_cb(struct evhttp_request *req, void *arg);
|
||||
void http_delete_cb(struct evhttp_request *req, void *arg);
|
||||
void http_dispatcher_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_basic_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_chunked_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_post_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_put_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_delete_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_delay_cb(struct evhttp_request *req, void *arg);
|
||||
static void http_dispatcher_cb(struct evhttp_request *req, void *arg);
|
||||
|
||||
static struct evhttp *
|
||||
http_setup(short *pport, struct event_base *base)
|
||||
@ -98,6 +99,7 @@ http_setup(short *pport, struct event_base *base)
|
||||
evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
|
||||
evhttp_set_cb(myhttp, "/putit", http_put_cb, NULL);
|
||||
evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, NULL);
|
||||
evhttp_set_cb(myhttp, "/delay", http_delay_cb, NULL);
|
||||
evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL);
|
||||
|
||||
*pport = port;
|
||||
@ -207,7 +209,7 @@ http_errorcb(struct bufferevent *bev, short what, void *arg)
|
||||
event_loopexit(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
http_basic_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
@ -222,7 +224,7 @@ http_basic_cb(struct evhttp_request *req, void *arg)
|
||||
evbuffer_free(evb);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
http_chunked_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
@ -322,11 +324,32 @@ http_basic_test(void)
|
||||
fprintf(stdout, "OK\n");
|
||||
}
|
||||
|
||||
static void
|
||||
http_delay_reply(evutil_socket_t fd, short what, void *arg)
|
||||
{
|
||||
struct evhttp_request *req = arg;
|
||||
|
||||
evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL);
|
||||
|
||||
++test_ok;
|
||||
}
|
||||
|
||||
static void
|
||||
http_delay_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct timeval tv;
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 200 * 1000;
|
||||
|
||||
event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
* HTTP DELETE test, just piggyback on the basic test
|
||||
*/
|
||||
|
||||
void
|
||||
static void
|
||||
http_delete_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
@ -485,6 +508,120 @@ http_connection_test(int persistent)
|
||||
fprintf(stdout, "OK\n");
|
||||
}
|
||||
|
||||
static void
|
||||
http_request_never_call(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
http_do_cancel(evutil_socket_t fd, short what, void *arg)
|
||||
{
|
||||
struct evhttp_request *req = arg;
|
||||
struct timeval tv;
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500 * 1000;
|
||||
|
||||
evhttp_cancel_request(req);
|
||||
|
||||
event_loopexit(&tv);
|
||||
|
||||
++test_ok;
|
||||
}
|
||||
|
||||
static void
|
||||
http_cancel_test(void)
|
||||
{
|
||||
short port = -1;
|
||||
struct evhttp_connection *evcon = NULL;
|
||||
struct evhttp_request *req = NULL;
|
||||
struct timeval tv;
|
||||
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing Request Cancelation: ");
|
||||
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||
if (evcon == NULL) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, we want to schedule a request to the HTTP
|
||||
* server using our make request method.
|
||||
*/
|
||||
|
||||
req = evhttp_request_new(http_request_never_call, NULL);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100 * 1000;
|
||||
|
||||
event_once(-1, EV_TIMEOUT, http_do_cancel, req, &tv);
|
||||
|
||||
event_dispatch();
|
||||
|
||||
if (test_ok != 2) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* try to make another request over the same connection */
|
||||
test_ok = 0;
|
||||
|
||||
req = evhttp_request_new(http_request_done, NULL);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
/* make another request: request empty reply */
|
||||
test_ok = 0;
|
||||
|
||||
req = evhttp_request_new(http_request_empty_done, NULL);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Empty", "itis");
|
||||
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
if (test_ok != 1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
evhttp_free(http);
|
||||
|
||||
fprintf(stdout, "OK\n");
|
||||
}
|
||||
|
||||
static void
|
||||
http_request_done(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
@ -1811,6 +1948,7 @@ http_suite(void)
|
||||
http_base_test();
|
||||
http_bad_header_test();
|
||||
http_basic_test();
|
||||
http_cancel_test();
|
||||
http_connection_test(0 /* not-persistent */);
|
||||
http_connection_test(1 /* persistent */);
|
||||
http_virtual_host_test();
|
||||
|
Loading…
x
Reference in New Issue
Block a user