From: Niels Provos Date: Sun, 19 Aug 2007 02:41:23 +0000 (+0000) Subject: provide evhttp_new and evhttp_bind_socket instead of evhttp_start; X-Git-Tag: release-2.0.1-alpha~595 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=67947ce38192f31fd67d9cac63ba5baaf112885b;p=libevent 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 --- diff --git a/event.3 b/event.3 index a6648e9a..565aaa9e 100644 --- a/event.3 +++ b/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 diff --git a/evhttp.h b/evhttp.h index 16744163..4597112e 100644 --- a/evhttp.h +++ b/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); + +/* + * 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 create HTTP server. Works only if no requests are + * 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); diff --git a/http-internal.h b/http-internal.h index eab8e3b3..4bbb0fd4 100644 --- a/http-internal.h +++ b/http-internal.h @@ -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 */ diff --git a/http.c b/http.c index ebdfbf0e..b20acedd 100644 --- a/http.c +++ b/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; diff --git a/test/regress.c b/test/regress.c index a75824a7..112f870f 100644 --- a/test/regress.c +++ b/test/regress.c @@ -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(); diff --git a/test/regress_http.c b/test/regress_http.c index 50c7bc47..c15fc63c 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -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 */);