mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-09 12:28:19 -04:00
provide evhttp_new and evhttp_bind_socket instead of evhttp_start;
using evhttp_new, it is possible to associate an event_base with the http server so that multi-threaded applications can have their own http server per thread; add appropriate testing. svn:r397
This commit is contained in:
parent
35983cd60f
commit
67947ce381
14
event.3
14
event.3
@ -78,7 +78,8 @@
|
||||
.Nm evbuffer_read ,
|
||||
.Nm evbuffer_find ,
|
||||
.Nm evbuffer_readline ,
|
||||
.Nm evhttp_start ,
|
||||
.Nm evhttp_new ,
|
||||
.Nm evhttp_bind_socket ,
|
||||
.Nm evhttp_free
|
||||
.Nd execute a function when a specific event occurs
|
||||
.Sh SYNOPSIS
|
||||
@ -181,9 +182,11 @@
|
||||
.Ft "char *"
|
||||
.Fn "evbuffer_readline" "struct evbuffer *buf"
|
||||
.Ft "struct evhttp *"
|
||||
.Fn "evhttp_start" "const char *address" "u_short port"
|
||||
.Fn "evhttp_new"
|
||||
.Ft int
|
||||
.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "u_short port"
|
||||
.Ft "void"
|
||||
.Fn "evhttp_free" "struct evhttp* http"
|
||||
.Fn "evhttp_free" "struct evhttp *http"
|
||||
.Ft int
|
||||
.Fa (*event_sigcb)(void) ;
|
||||
.Ft volatile sig_atomic_t
|
||||
@ -539,7 +542,10 @@ enabling the bufferevent for the first time.
|
||||
provides a very thin HTTP layer that can be used both to host an HTTP
|
||||
server and also to make HTTP requests.
|
||||
An HTTP server can be created by calling
|
||||
.Fn evhttp_start .
|
||||
.Fn evhttp_new .
|
||||
It can be bound to any port and address with the
|
||||
.Fn evhttp_bind_socket
|
||||
function.
|
||||
When the HTTP server is no longer used, it can be freed via
|
||||
.Fn evhttp_free .
|
||||
.Pp
|
||||
|
32
evhttp.h
32
evhttp.h
@ -62,11 +62,20 @@ struct evhttp;
|
||||
struct evhttp_request;
|
||||
struct evkeyvalq;
|
||||
|
||||
/* Start an HTTP server on the specified address and port */
|
||||
struct evhttp *evhttp_start(const char *address, u_short port);
|
||||
/*
|
||||
* creates a new HTTP server; if base is specified events from the http server
|
||||
* are going to be created on that event base.
|
||||
*/
|
||||
struct evhttp *evhttp_new(struct event_base *base);
|
||||
|
||||
/*
|
||||
* Free the previously create HTTP server. Works only if no requests are
|
||||
* binds the http server to specific port and can be called multiple times
|
||||
* to bind the same http server to multiple different ports.
|
||||
*/
|
||||
int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port);
|
||||
|
||||
/*
|
||||
* Free the previously created HTTP server. Works only if no requests are
|
||||
* currently being served.
|
||||
*/
|
||||
void evhttp_free(struct evhttp* http);
|
||||
@ -95,7 +104,15 @@ void evhttp_send_reply_start(struct evhttp_request *, int, const char *);
|
||||
void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *);
|
||||
void evhttp_send_reply_end(struct evhttp_request *);
|
||||
|
||||
/* Interfaces for making requests */
|
||||
/*
|
||||
* Start an HTTP server on the specified address and port
|
||||
* DEPRECATED: it does not allow an event base to be specified
|
||||
*/
|
||||
struct evhttp *evhttp_start(const char *address, u_short port);
|
||||
|
||||
/*
|
||||
* Interfaces for making requests
|
||||
*/
|
||||
enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
|
||||
|
||||
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
|
||||
@ -189,6 +206,13 @@ void evhttp_connection_set_retries(struct evhttp_connection *evcon,
|
||||
void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
|
||||
void (*)(struct evhttp_connection *, void *), void *);
|
||||
|
||||
/*
|
||||
* Associates an event base with the connection - can only be called
|
||||
* on a freshly created connection object that has not been used yet.
|
||||
*/
|
||||
void evhttp_connection_set_base(struct evhttp_connection *evcon,
|
||||
struct event_base *base);
|
||||
|
||||
/* Get the remote address and port associated with this connection. */
|
||||
void evhttp_connection_get_peer(struct evhttp_connection *evcon,
|
||||
char **address, u_short *port);
|
||||
|
@ -35,6 +35,8 @@ enum evhttp_connection_state {
|
||||
EVCON_CONNECTED /* connection is established */
|
||||
};
|
||||
|
||||
struct event_base;
|
||||
|
||||
struct evhttp_connection {
|
||||
/* we use tailq only if they were created for an http server */
|
||||
TAILQ_ENTRY(evhttp_connection) next;
|
||||
@ -69,6 +71,8 @@ struct evhttp_connection {
|
||||
|
||||
void (*closecb)(struct evhttp_connection *, void *);
|
||||
void *closecb_arg;
|
||||
|
||||
struct event_base *base;
|
||||
};
|
||||
|
||||
struct evhttp_cb {
|
||||
@ -93,6 +97,8 @@ struct evhttp {
|
||||
|
||||
void (*gencb)(struct evhttp_request *req, void *);
|
||||
void *gencbarg;
|
||||
|
||||
struct event_base *base;
|
||||
};
|
||||
|
||||
/* resets the connection; can be reused for more requests */
|
||||
|
66
http.c
66
http.c
@ -116,6 +116,11 @@ fake_freeaddrinfo(struct addrinfo *ai)
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#endif
|
||||
|
||||
/* wrapper for setting the base from the http server */
|
||||
#define EVHTTP_BASE_SET(x, y) do { \
|
||||
if ((x)->base != NULL) event_base_set((x)->base, y); \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
event_make_socket_nonblocking(int fd)
|
||||
{
|
||||
@ -277,6 +282,7 @@ evhttp_write_buffer(struct evhttp_connection *evcon,
|
||||
event_del(&evcon->ev);
|
||||
|
||||
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
@ -709,6 +715,7 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||
}
|
||||
/* Read more! */
|
||||
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
@ -865,6 +872,7 @@ evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
|
||||
event_del(&evcon->close_ev);
|
||||
event_set(&evcon->close_ev, evcon->fd, EV_READ,
|
||||
evhttp_detect_close_cb, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
event_add(&evcon->close_ev, NULL);
|
||||
}
|
||||
|
||||
@ -931,6 +939,7 @@ evhttp_connectioncb(int fd, short what, void *arg)
|
||||
cleanup:
|
||||
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
|
||||
evtimer_set(&evcon->ev, evhttp_connection_retry, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
evhttp_add_event(&evcon->ev, MIN(3600, 2 << evcon->retry_cnt),
|
||||
HTTP_CONNECT_TIMEOUT);
|
||||
evcon->retry_cnt++;
|
||||
@ -1403,6 +1412,14 @@ evhttp_connection_new(const char *address, unsigned short port)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void evhttp_connection_set_base(struct evhttp_connection *evcon,
|
||||
struct event_base *base)
|
||||
{
|
||||
assert(evcon->base == NULL);
|
||||
assert(evcon->state == EVCON_DISCONNECTED);
|
||||
evcon->base = base;
|
||||
}
|
||||
|
||||
void
|
||||
evhttp_connection_set_timeout(struct evhttp_connection *evcon,
|
||||
int timeout_in_secs)
|
||||
@ -1454,6 +1471,7 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
|
||||
|
||||
/* Set up a callback for successful connection setup */
|
||||
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_CONNECT_TIMEOUT);
|
||||
|
||||
evcon->state = EVCON_CONNECTING;
|
||||
@ -1519,6 +1537,7 @@ evhttp_start_read(struct evhttp_connection *evcon)
|
||||
if (event_initialized(&evcon->ev))
|
||||
event_del(&evcon->ev);
|
||||
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
|
||||
EVHTTP_BASE_SET(evcon, &evcon->ev);
|
||||
|
||||
evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
|
||||
}
|
||||
@ -1897,8 +1916,8 @@ accept_socket(int fd, short what, void *arg)
|
||||
evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen);
|
||||
}
|
||||
|
||||
static int
|
||||
bind_socket(struct evhttp *http, const char *address, u_short port)
|
||||
int
|
||||
evhttp_bind_socket(struct evhttp *http, const char *address, u_short port)
|
||||
{
|
||||
struct event *ev = &http->bind_ev;
|
||||
int fd;
|
||||
@ -1908,11 +1927,13 @@ bind_socket(struct evhttp *http, const char *address, u_short port)
|
||||
|
||||
if (listen(fd, 10) == -1) {
|
||||
event_warn("%s: listen", __func__);
|
||||
close(fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Schedule the socket for accepting */
|
||||
event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
|
||||
EVHTTP_BASE_SET(http, ev);
|
||||
event_add(ev, NULL);
|
||||
|
||||
event_debug(("Bound to port %d - Awaiting connections ... ", port));
|
||||
@ -1920,14 +1941,10 @@ bind_socket(struct evhttp *http, const char *address, u_short port)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a web server on the specified address and port.
|
||||
*/
|
||||
|
||||
struct evhttp *
|
||||
evhttp_start(const char *address, u_short port)
|
||||
static struct evhttp*
|
||||
evhttp_new_object()
|
||||
{
|
||||
struct evhttp *http;
|
||||
struct evhttp *http = NULL;
|
||||
|
||||
if ((http = calloc(1, sizeof(struct evhttp))) == NULL) {
|
||||
event_warn("%s: calloc", __func__);
|
||||
@ -1939,7 +1956,29 @@ evhttp_start(const char *address, u_short port)
|
||||
TAILQ_INIT(&http->callbacks);
|
||||
TAILQ_INIT(&http->connections);
|
||||
|
||||
if (bind_socket(http, address, port) == -1) {
|
||||
return (http);
|
||||
}
|
||||
|
||||
struct evhttp *
|
||||
evhttp_new(struct event_base *base)
|
||||
{
|
||||
struct evhttp *http = evhttp_new_object();
|
||||
|
||||
http->base = base;
|
||||
|
||||
return (http);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a web server on the specified address and port.
|
||||
*/
|
||||
|
||||
struct evhttp *
|
||||
evhttp_start(const char *address, u_short port)
|
||||
{
|
||||
struct evhttp *http = evhttp_new_object();
|
||||
|
||||
if (evhttp_bind_socket(http, address, port) == -1) {
|
||||
free(http);
|
||||
return (NULL);
|
||||
}
|
||||
@ -2122,6 +2161,7 @@ evhttp_request_uri(struct evhttp_request *req) {
|
||||
|
||||
static struct evhttp_connection*
|
||||
evhttp_get_request_connection(
|
||||
struct evhttp* http,
|
||||
int fd, struct sockaddr *sa, socklen_t salen)
|
||||
{
|
||||
struct evhttp_connection *evcon;
|
||||
@ -2134,6 +2174,10 @@ evhttp_get_request_connection(
|
||||
/* we need a connection object to put the http request on */
|
||||
if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* associate the base if we have one*/
|
||||
evhttp_connection_set_base(evcon, http->base);
|
||||
|
||||
evcon->flags |= EVHTTP_CON_INCOMING;
|
||||
evcon->state = EVCON_CONNECTED;
|
||||
|
||||
@ -2172,7 +2216,7 @@ evhttp_get_request(struct evhttp *http, int fd,
|
||||
{
|
||||
struct evhttp_connection *evcon;
|
||||
|
||||
evcon = evhttp_get_request_connection(fd, sa, salen);
|
||||
evcon = evhttp_get_request_connection(http, fd, sa, salen);
|
||||
if (evcon == NULL)
|
||||
return;
|
||||
|
||||
|
@ -71,7 +71,7 @@ static int roff;
|
||||
static int usepersist;
|
||||
static struct timeval tset;
|
||||
static struct timeval tcalled;
|
||||
static struct event_base *event_base;
|
||||
static struct event_base *global_base;
|
||||
|
||||
#define TEST1 "this is a test"
|
||||
#define SECONDS 1
|
||||
@ -594,7 +594,7 @@ void
|
||||
test_evbuffer(void) {
|
||||
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
setup_test("Evbuffer: ");
|
||||
setup_test("Testing Evbuffer: ");
|
||||
|
||||
evbuffer_add_printf(evb, "%s/%d", "hello", 1);
|
||||
|
||||
@ -742,10 +742,10 @@ test_priorities(int npriorities)
|
||||
struct test_pri_event one, two;
|
||||
struct timeval tv;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Priorities %d: ", npriorities);
|
||||
snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities);
|
||||
setup_test(buf);
|
||||
|
||||
event_base_priority_init(event_base, npriorities);
|
||||
event_base_priority_init(global_base, npriorities);
|
||||
|
||||
memset(&one, 0, sizeof(one));
|
||||
memset(&two, 0, sizeof(two));
|
||||
@ -1042,7 +1042,12 @@ main (int argc, char **argv)
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
/* Initalize the event library */
|
||||
event_base = event_init();
|
||||
global_base = event_init();
|
||||
|
||||
/* use the global event base and need to be called first */
|
||||
test_priorities(1);
|
||||
test_priorities(2);
|
||||
test_priorities(3);
|
||||
|
||||
test_evbuffer();
|
||||
test_evbuffer_find();
|
||||
@ -1072,10 +1077,6 @@ main (int argc, char **argv)
|
||||
#endif
|
||||
test_loopexit();
|
||||
|
||||
test_priorities(1);
|
||||
test_priorities(2);
|
||||
test_priorities(3);
|
||||
|
||||
test_multiple_events_for_same_fd();
|
||||
|
||||
test_want_only_once();
|
||||
|
@ -61,22 +61,24 @@ extern int pair[];
|
||||
extern int test_ok;
|
||||
|
||||
static struct evhttp *http;
|
||||
/* set if a test needs to call loopexit on a base */
|
||||
static struct event_base *base;
|
||||
|
||||
void http_basic_cb(struct evhttp_request *req, void *arg);
|
||||
void http_post_cb(struct evhttp_request *req, void *arg);
|
||||
void http_dispatcher_cb(struct evhttp_request *req, void *arg);
|
||||
|
||||
static struct evhttp *
|
||||
http_setup(short *pport)
|
||||
http_setup(short *pport, struct event_base *base)
|
||||
{
|
||||
int i;
|
||||
struct evhttp *myhttp;
|
||||
short port = -1;
|
||||
|
||||
/* Try a few different ports */
|
||||
myhttp = evhttp_new(base);
|
||||
for (i = 0; i < 50; ++i) {
|
||||
myhttp = evhttp_start("127.0.0.1", 8080 + i);
|
||||
if (myhttp != NULL) {
|
||||
if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) {
|
||||
port = 8080 + i;
|
||||
break;
|
||||
}
|
||||
@ -130,7 +132,8 @@ http_readcb(struct bufferevent *bev, void *arg)
|
||||
|
||||
event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input)));
|
||||
|
||||
if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) {
|
||||
if (evbuffer_find(bev->input,
|
||||
(const unsigned char*) what, strlen(what)) != NULL) {
|
||||
struct evhttp_request *req = evhttp_request_new(NULL, NULL);
|
||||
int done;
|
||||
|
||||
@ -143,7 +146,10 @@ http_readcb(struct bufferevent *bev, void *arg)
|
||||
test_ok++;
|
||||
evhttp_request_free(req);
|
||||
bufferevent_disable(bev, EV_READ);
|
||||
event_loopexit(NULL);
|
||||
if (base)
|
||||
event_base_loopexit(base, NULL);
|
||||
else
|
||||
event_loopexit(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +194,7 @@ http_basic_test(void)
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing Basic HTTP Server: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
fd = http_connect("127.0.0.1", port);
|
||||
|
||||
@ -232,7 +238,7 @@ http_connection_test(int persistent)
|
||||
fprintf(stdout, "Testing Request Connection Pipeline %s: ",
|
||||
persistent ? "(persistent)" : "");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||
if (evcon == NULL) {
|
||||
@ -379,7 +385,7 @@ http_dispatcher_test(void)
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing HTTP Dispatcher: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||
if (evcon == NULL) {
|
||||
@ -437,7 +443,7 @@ http_post_test(void)
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing HTTP POST Request: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||
if (evcon == NULL) {
|
||||
@ -573,7 +579,7 @@ http_failure_test(void)
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing Bad HTTP Request: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
fd = http_connect("127.0.0.1", port);
|
||||
|
||||
@ -661,7 +667,7 @@ http_close_detection(void)
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing Connection Close Detection: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
/* 2 second timeout */
|
||||
evhttp_set_timeout(http, 2);
|
||||
@ -754,9 +760,64 @@ fail:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
http_base_test(void)
|
||||
{
|
||||
struct bufferevent *bev;
|
||||
int fd;
|
||||
char *http_request;
|
||||
short port = -1;
|
||||
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing HTTP Server Event Base: ");
|
||||
|
||||
base = event_init();
|
||||
|
||||
/*
|
||||
* create another bogus base - which is being used by all subsequen
|
||||
* tests - yuck!
|
||||
*/
|
||||
event_init();
|
||||
|
||||
http = http_setup(&port, base);
|
||||
|
||||
fd = http_connect("127.0.0.1", port);
|
||||
|
||||
/* Stupid thing to send a request */
|
||||
bev = bufferevent_new(fd, http_readcb, http_writecb,
|
||||
http_errorcb, NULL);
|
||||
bufferevent_base_set(base, bev);
|
||||
|
||||
http_request =
|
||||
"GET /test HTTP/1.1\r\n"
|
||||
"Host: somehost\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
bufferevent_write(bev, http_request, strlen(http_request));
|
||||
|
||||
event_base_dispatch(base);
|
||||
|
||||
bufferevent_free(bev);
|
||||
close(fd);
|
||||
|
||||
evhttp_free(http);
|
||||
|
||||
event_base_free(base);
|
||||
base = NULL;
|
||||
|
||||
if (test_ok != 2) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stdout, "OK\n");
|
||||
}
|
||||
|
||||
void
|
||||
http_suite(void)
|
||||
{
|
||||
http_base_test();
|
||||
http_bad_header_test();
|
||||
http_basic_test();
|
||||
http_connection_test(0 /* not-persistent */);
|
||||
|
Loading…
x
Reference in New Issue
Block a user