.Nm evbuffer_write ,
.Nm evbuffer_read ,
.Nm evbuffer_find ,
-.Nm evbuffer_readline
+.Nm evbuffer_readline ,
+.Nm evhttp_start ,
+.Nm evhttp_free
.Nd execute a function when a specific event occurs
.Sh SYNOPSIS
.Fd #include <sys/time.h>
.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
.Ft "char *"
.Fn "evbuffer_readline" "struct evbuffer *buf"
+.Ft "struct evhttp *"
+.Fn "evhttp_start" "const char *address" "u_short port"
+.Ft "void"
+.Fn "evhttp_free" "struct evhttp* http"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft int
.Pp
If multiple bases are in use, bufferevent_base_set() must be called before
enabling the bufferevent for the first time.
+.Sh NON-BLOCKING HTTP SUPPORT
+.Nm libevent
+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 .
+When the HTTP server is no longer used, it can be freed via
+.Fn evhttp_free .
+.Pp
+To be notified of HTTP requests, a user needs to register callbacks with the
+HTTP server.
+This can be done by calling
+.Fn evhttp_set_cb .
+The second argument is the URI for which a callback is being registered.
+The corresponding callback will receive an
+.Va struct evhttp_request
+object that contains all information about the request.
+.Pp
+This section does not document all the possible function calls, please
+check
+.Va event.h
+for the public interfaces.
.Sh RETURN VALUES
Upon successful completion
.Fn event_add
struct evhttp *evhttp_start(const char *address, u_short port);
/*
- * Free the previously create HTTP server. Works only if not requests are
+ * Free the previously create HTTP server. Works only if no requests are
* currently being served.
*/
void evhttp_free(struct evhttp* http);
struct evhttp_request *evhttp_request_new(
void (*cb)(struct evhttp_request *, void *), void *arg);
void evhttp_request_free(struct evhttp_request *req);
+const char *evhttp_request_uri(struct evhttp_request *req);
/* Interfaces for dealing with HTTP headers */
-char *evhttp_find_header(struct evkeyvalq *, const char *);
-void evhttp_remove_header(struct evkeyvalq *, const char *);
+const char *evhttp_find_header(struct evkeyvalq *, const char *);
+int evhttp_remove_header(struct evkeyvalq *, const char *);
int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
void evhttp_clear_headers(struct evkeyvalq *);
+/* Miscellaneous utility functions */
void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+char *evhttp_htmlescape(const char *html);
#ifdef __cplusplus
}
#endif
/*
- * Copyright 2002, 2003, 2005 Niels Provos <provos@citi.umich.edu>
+ * Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
evhttp_make_header(buf, req);
/* Append the response buffer */
- evbuffer_add(buf, req->buffer->buffer, req->buffer->off);
+ evbuffer_add(buf,
+ EVBUFFER_DATA(req->buffer), EVBUFFER_LENGTH(req->buffer));
}
void
event_add(&req->ev, &tv);
}
+/*
+ * Create the headers need for an HTTP reply
+ */
static void
evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
{
}
}
+/*
+ * Create the headers needed for an HTTP reply
+ */
static void
evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req)
{
/* Add the POST data */
if (len > 0)
- evbuffer_add(buf, req->buffer->buffer, len);
+ evbuffer_add(buf, EVBUFFER_DATA(req->buffer), len);
}
}
return;
}
- if (req->buffer->off != 0) {
+ if (EVBUFFER_LENGTH(req->buffer) != 0) {
timerclear(&tv);
tv.tv_sec = HTTP_WRITE_TIMEOUT;
event_add(&req->ev, &tv);
return (0);
}
-char *
+const char *
evhttp_find_header(struct evkeyvalq *headers, const char *key)
{
struct evkeyval *header;
}
}
-void
+/*
+ * Returns 0, if the header was successfully removed.
+ * Returns -1, if the header could not be found.
+ */
+
+int
evhttp_remove_header(struct evkeyvalq *headers, const char *key)
{
struct evkeyval *header;
}
if (header == NULL)
- return;
+ return (-1);
/* Free and remove the header that we found */
TAILQ_REMOVE(headers, header, next);
free(header->key);
free(header->value);
free(header);
+
+ return (0);
}
int
return (-1);
}
if ((header->key = strdup(key)) == NULL) {
+ free(header);
event_warn("%s: strdup", __func__);
return (-1);
}
if ((header->value = strdup(value)) == NULL) {
+ free(header->key);
+ free(header);
event_warn("%s: strdup", __func__);
return (-1);
}
while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) {
char *skey, *svalue;
- if (strncmp(buffer->buffer, "\r\n", 2) == 0) {
+ if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
evbuffer_drain(buffer, 2);
/* Last header - Done */
done = 1;
*endp = '\0';
endp += 2;
- event_debug(("%s: Got: %s\n", __func__, buffer->buffer));
+ event_debug(("%s: Got: %s\n", __func__, EVBUFFER_DATA(buffer)));
/* Processing of header lines */
if (req->got_firstline == 0) {
switch (req->kind) {
case EVHTTP_REQUEST:
- if (evhttp_parse_request_line(req, buffer->buffer) == -1)
+ if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1)
return (-1);
break;
case EVHTTP_RESPONSE:
- if (evhttp_parse_response_line(req, buffer->buffer) == -1)
+ if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1)
return (-1);
break;
default:
req->got_firstline = 1;
} else {
/* Regular header */
- svalue = buffer->buffer;
+ svalue = EVBUFFER_DATA(buffer);
skey = strsep(&svalue, ":");
if (svalue == NULL)
return (-1);
}
/* Move the uncompleted headers forward */
- evbuffer_drain(buffer, endp - buffer->buffer);
+ evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
}
return (done);
evhttp_get_body(struct evhttp_request *req)
{
struct timeval tv;
- char *content_length;
- char *connection;
+ const char *content_length;
+ const char *connection;
struct evkeyvalq *headers = req->input_headers;
/* If this is a request without a body, then we are done */
req->ntoread = atoi(content_length);
event_debug(("%s: bytes to read: %d (in buffer %d)\n",
- __func__, req->ntoread, req->buffer->off));
+ __func__, req->ntoread, EVBUFFER_LENGTH(req->buffer)));
if (req->ntoread > 0)
- req->ntoread -= req->buffer->off;
+ req->ntoread -= EVBUFFER_LENGTH(req->buffer);
if (req->ntoread == 0) {
(*req->cb)(req, req->cb_arg);
/* Create the header from the store arguments */
evhttp_make_header(evbuf, req);
- /*
- * If this was a post request or for other reasons we need to append
- * our post data to the request.
- */
- evbuffer_add_buffer(evbuf, req->buffer);
-
-
/* Schedule the write */
req->save_cb = req->cb;
req->save_cbarg = req->cb_arg;
free(req);
}
+/*
+ * Allows for inspection of the request URI
+ */
+
+const char *
+evhttp_request_uri(struct evhttp_request *req) {
+ if (req->uri == NULL)
+ event_debug(("%s: request %p has no uri\n", req));
+ return (req->uri);
+}
+
/*
* Takes a file descriptor to read a request from.
* The callback is executed once the whole request has been read.
/*
- * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
+ * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
static struct evhttp *http;
void http_basic_cb(struct evhttp_request *req, void *arg);
+void http_post_cb(struct evhttp_request *req, void *arg);
struct evhttp *
http_setup(short *pport)
/* Register a callback for certain types of requests */
evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
+ evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
*pport = port;
return (myhttp);
event_dispatch();
- /*
- * At this point, we want to schedule a request to the HTTP
- * server using our start request method.
- */
-
evhttp_connection_free(evcon);
evhttp_free(http);
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_done, NULL);
/* Add the information that we care about */
exit(1);
}
- if (evhttp_find_header(req->input_headers,
- "Content-Type") == NULL) {
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
fprintf(stderr, "FAILED\n");
exit(1);
}
event_loopexit(NULL);
}
+/*
+ * HTTP POST test.
+ */
+
+void http_connect_forpostcb(struct evhttp_connection *evcon, void *arg);
+
+void
+http_post_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing HTTP POST Request: ");
+
+ http = http_setup(&port);
+
+ evcon = evhttp_connect("127.0.0.1", port, http_connect_forpostcb, NULL);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void http_postrequest_done(struct evhttp_request *, void *);
+
+#define POST_DATA "Okay. Not really printf"
+
+void
+http_connect_forpostcb(struct evhttp_connection *evcon, void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit (1);
+ }
+
+ /*
+ * At this point, we want to schedule an HTTP POST request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_postrequest_done, NULL);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evbuffer_add_printf(req->buffer, POST_DATA);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+void
+http_post_cb(struct evhttp_request *req, void *arg)
+{
+ event_debug((stderr, "%s: called\n", __func__));
+
+ /* Yes, we are expecting a post request */
+ if (req->type != EVHTTP_REQ_POST) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != strlen(POST_DATA)) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ if (strcmp(EVBUFFER_DATA(req->buffer), POST_DATA)) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_printf(evb, "This is funny");
+
+ evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
+
+ evbuffer_free(evb);
+}
+
+void
+http_postrequest_done(struct evhttp_request *req, void *arg)
+{
+ const char *what = "This is funny";
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+
void
http_suite(void)
{
http_basic_test();
http_connection_test();
+ http_post_test();
}