]> granicus.if.org Git - libevent/commitdiff
provide evhttp_new and evhttp_bind_socket instead of evhttp_start;
authorNiels Provos <provos@gmail.com>
Sun, 19 Aug 2007 02:41:23 +0000 (02:41 +0000)
committerNiels Provos <provos@gmail.com>
Sun, 19 Aug 2007 02:41:23 +0000 (02:41 +0000)
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

event.3
evhttp.h
http-internal.h
http.c
test/regress.c
test/regress_http.c

diff --git a/event.3 b/event.3
index a6648e9a9dcb359e18a5b5fd1ff99570cd0bf311..565aaa9e767dd4d16fa2e3b6207846da5d0ae727 100644 (file)
--- 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
 .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 evhttphttp"
+.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
index 16744163dbb17d58f77500fc42b7a6b52ed5dd5c..4597112ef530841a2f8fc0d8d629c0de36d1edbb 100644 (file)
--- 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);
index eab8e3b39da5fd0190d266793321361fc5cb3382..4bbb0fd4e90a6293728c159fcac4ae7e09e6a302 100644 (file)
@@ -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 ebdfbf0e71d00a7525862a7cccd2d84f8fc1d166..b20acedde4ecb40a9988890b32c59a72dfab3ebf 100644 (file)
--- 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;
 
index a75824a73329db32b8a5cef932d3fbd91b1a0132..112f870ff198722b36076a8acc882ec997c65e72 100644 (file)
@@ -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();
index 50c7bc475be508aff5a664c864f23e9898e49959..c15fc63c154008654df49a8f94b54f11c3c07d47 100644 (file)
@@ -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 */);