.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 evhttp* http"
+.Fn "evhttp_free" "struct evhttp *http"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft volatile sig_atomic_t
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
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);
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 };
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);
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;
void (*closecb)(struct evhttp_connection *, void *);
void *closecb_arg;
+
+ struct event_base *base;
};
struct evhttp_cb {
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg;
+
+ struct event_base *base;
};
/* resets the connection; can be reused for more requests */
#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)
{
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);
}
}
/* 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);
}
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);
}
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++;
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)
/* 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;
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);
}
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;
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));
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__);
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);
}
static struct evhttp_connection*
evhttp_get_request_connection(
+ struct evhttp* http,
int fd, struct sockaddr *sa, socklen_t salen)
{
struct evhttp_connection *evcon;
/* 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;
{
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;
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
test_evbuffer(void) {
struct evbuffer *evb = evbuffer_new();
- setup_test("Evbuffer: ");
+ setup_test("Testing Evbuffer: ");
evbuffer_add_printf(evb, "%s/%d", "hello", 1);
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));
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();
#endif
test_loopexit();
- test_priorities(1);
- test_priorities(2);
- test_priorities(3);
-
test_multiple_events_for_same_fd();
test_want_only_once();
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;
}
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;
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);
}
}
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);
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) {
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) {
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) {
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);
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);
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 */);