summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Provos <provos@gmail.com>2006-11-29 02:14:20 +0000
committerNiels Provos <provos@gmail.com>2006-11-29 02:14:20 +0000
commit3ad232fd915cc05e5349770ee03c57202c9fa7bd (patch)
tree0762d42ef0cbd0d48c237b2b56a0d8602caad5b1
parent518d530a0fe995ef5e81a5cb85cfee34478fbac5 (diff)
downloadlibevent-3ad232fd915cc05e5349770ee03c57202c9fa7bd.tar.gz
merge from trunk:
make regression test work for poll and select some compilers don't like C99 inline variable declaration make persistent connections work; needs more testing introduce is connection close svn:r284
-rw-r--r--evdns.c2
-rw-r--r--evhttp.h46
-rw-r--r--http-internal.h49
-rw-r--r--http.c137
-rw-r--r--test/regress_http.c23
5 files changed, 174 insertions, 83 deletions
diff --git a/evdns.c b/evdns.c
index 0d852e61..6c16bbd2 100644
--- a/evdns.c
+++ b/evdns.c
@@ -75,7 +75,9 @@
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>
+#ifdef HAVE_STDINT_H
#include <stdint.h>
+#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
diff --git a/evhttp.h b/evhttp.h
index b3c46925..615ca1dc 100644
--- a/evhttp.h
+++ b/evhttp.h
@@ -83,6 +83,50 @@ void evhttp_send_reply(struct evhttp_request *, int, const char *,
/* 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 };
+
+/*
+ * the request structure that a server receives.
+ * WARNING: expect this structure to change. I will try to provide
+ * reasonable accessors.
+ */
+struct evhttp_request {
+ TAILQ_ENTRY(evhttp_request) next;
+
+ /* the connection object that this request belongs to */
+ struct evhttp_connection *evcon;
+ int flags;
+#define EVHTTP_REQ_OWN_CONNECTION 0x0001
+
+ struct evkeyvalq *input_headers;
+ struct evkeyvalq *output_headers;
+
+ /* address of the remote host and the port connection came from */
+ char *remote_host;
+ u_short remote_port;
+
+ enum evhttp_request_kind kind;
+ enum evhttp_cmd_type type;
+
+ char *uri; /* uri after HTTP request was parsed */
+
+ char major; /* HTTP Major number */
+ char minor; /* HTTP Minor number */
+
+ int got_firstline;
+ int response_code; /* HTTP Response code */
+ char *response_code_line; /* Readable response */
+
+ struct evbuffer *input_buffer; /* read data */
+ int ntoread;
+
+ struct evbuffer *output_buffer; /* outgoing post or data */
+
+ /* Callback */
+ void (*cb)(struct evhttp_request *, void *);
+ void *cb_arg;
+};
+
/*
* Creates a new request object that needs to be filled in with the request
* parameters. The callback is executed when the request completed or an
@@ -114,7 +158,7 @@ const char *evhttp_request_uri(struct evhttp_request *req);
/* Interfaces for dealing with HTTP headers */
-const char *evhttp_find_header(struct evkeyvalq *, const char *);
+const char *evhttp_find_header(const 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 *);
diff --git a/http-internal.h b/http-internal.h
index f5ad2861..17015e1e 100644
--- a/http-internal.h
+++ b/http-internal.h
@@ -30,6 +30,9 @@ enum evhttp_connection_state {
};
struct evhttp_connection {
+ /* we use tailq only if they were created for an http server */
+ TAILQ_ENTRY(evhttp_connection) next;
+
int fd;
struct event ev;
struct evbuffer *input_buffer;
@@ -44,51 +47,15 @@ struct evhttp_connection {
enum evhttp_connection_state state;
+ /* for server connections, the http server they are connected with */
+ struct evhttp *http_server;
+
TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
void (*cb)(struct evhttp_connection *, void *);
void *cb_arg;
};
-enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
-
-struct evhttp_request {
- TAILQ_ENTRY(evhttp_request) next;
-
- /* the connection object that this request belongs to */
- struct evhttp_connection *evcon;
- int flags;
-#define EVHTTP_REQ_OWN_CONNECTION 0x0001
-
- struct evkeyvalq *input_headers;
- struct evkeyvalq *output_headers;
-
- /* xxx: do we still need these? */
- char *remote_host;
- u_short remote_port;
-
- enum evhttp_request_kind kind;
- enum evhttp_cmd_type type;
-
- char *uri; /* uri after HTTP request was parsed */
-
- char major; /* HTTP Major number */
- char minor; /* HTTP Minor number */
-
- int got_firstline;
- int response_code; /* HTTP Response code */
- char *response_code_line; /* Readable response */
-
- struct evbuffer *input_buffer; /* read data */
- int ntoread;
-
- struct evbuffer *output_buffer; /* outgoing post or data */
-
- /* Callback */
- void (*cb)(struct evhttp_request *, void *);
- void *cb_arg;
-};
-
struct evhttp_cb {
TAILQ_ENTRY(evhttp_cb) next;
@@ -102,6 +69,7 @@ struct evhttp {
struct event bind_ev;
TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
+ TAILQ_HEAD(evconq, evhttp_connection) connections;
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg;
@@ -116,8 +84,7 @@ int evhttp_connection_connect(struct evhttp_connection *);
/* notifies the current request that it failed; resets connection */
void evhttp_connection_fail(struct evhttp_connection *);
-void evhttp_get_request(int, struct sockaddr *, socklen_t,
- void (*)(struct evhttp_request *, void *), void *);
+void evhttp_get_request(struct evhttp *, int, struct sockaddr *, socklen_t);
int evhttp_hostportfile(char *, char **, u_short *, char **);
diff --git a/http.c b/http.c
index 68cd94be..3ec8636b 100644
--- a/http.c
+++ b/http.c
@@ -76,6 +76,8 @@ static int make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
static int make_socket(int (*)(int, const struct sockaddr *, socklen_t),
const char *, short);
static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
+static int evhttp_associate_new_request_with_connection(
+ struct evhttp_connection *evcon);
void evhttp_write(int, short, void *);
@@ -191,8 +193,6 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
evhttp_remove_header(req->output_headers, "Accept-Encoding");
evhttp_remove_header(req->output_headers, "Proxy-Connection");
- evhttp_remove_header(req->output_headers, "Connection");
- evhttp_add_header(req->output_headers, "Connection", "close");
req->minor = 0;
/* Generate request line */
@@ -211,6 +211,13 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
}
}
+static int
+evhttp_is_connection_close(struct evkeyvalq* headers)
+{
+ const char *connection = evhttp_find_header(headers, "Connection");
+ return (connection != NULL && strcasecmp(connection, "close") == 0);
+}
+
/*
* Create the headers needed for an HTTP reply
*/
@@ -229,7 +236,21 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
evhttp_add_header(req->output_headers,
"Content-Type", "text/html; charset=ISO-8859-1");
}
- if (evhttp_find_header(req->output_headers, "Connection") == NULL) {
+
+ /*
+ * we need to add the content length if the user did not give it,
+ * this is required for persistent connections to work.
+ */
+ if (evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+ static char len[12];
+ snprintf(len, sizeof(len), "%ld",
+ EVBUFFER_LENGTH(req->output_buffer));
+ evhttp_add_header(req->output_headers, "Content-Length", len);
+ }
+
+ /* if the request asked for a close, we send a close, too */
+ if (evhttp_is_connection_close(req->input_headers)) {
+ evhttp_remove_header(req->output_headers, "Connection");
evhttp_add_header(req->output_headers, "Connection", "close");
}
}
@@ -405,16 +426,13 @@ evhttp_connection_done(struct evhttp_connection *evcon)
* on the connection, so that we can reply to it.
*/
if (evcon->flags & EVHTTP_CON_OUTGOING) {
- struct evkeyvalq *headers = req->input_headers;
- const char *connection;
-
TAILQ_REMOVE(&evcon->requests, req, next);
req->evcon = NULL;
/* check if we got asked to close the connection */
- connection = evhttp_find_header(headers, "Connection");
- if (connection != NULL && strcasecmp(connection, "close") == 0)
+ if (evhttp_is_connection_close(req->input_headers) ||
+ evhttp_is_connection_close(req->output_headers))
evhttp_connection_reset(evcon);
if (TAILQ_FIRST(&evcon->requests) != NULL) {
@@ -460,7 +478,7 @@ evhttp_read(int fd, short what, void *arg)
}
n = evbuffer_read(req->input_buffer, fd, req->ntoread);
- event_debug(("%s: got %d on %d\n", __func__, n, req->fd));
+ event_debug(("%s: got %d on %d\n", __func__, n, fd));
if (n == -1) {
event_warn("%s: evbuffer_read", __func__);
@@ -502,6 +520,11 @@ evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
void
evhttp_connection_free(struct evhttp_connection *evcon)
{
+ if (evcon->http_server != NULL) {
+ struct evhttp *http = evcon->http_server;
+ TAILQ_REMOVE(&http->connections, evcon, next);
+ }
+
if (event_initialized(&evcon->ev))
event_del(&evcon->ev);
@@ -717,7 +740,7 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
}
const char *
-evhttp_find_header(struct evkeyvalq *headers, const char *key)
+evhttp_find_header(const struct evkeyvalq *headers, const char *key)
{
struct evkeyval *header;
@@ -898,7 +921,8 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
req->ntoread = atoi(content_length);
event_debug(("%s: bytes to read: %d (in buffer %d)\n",
- __func__, req->ntoread, EVBUFFER_LENGTH(evcon->buffer)));
+ __func__, req->ntoread,
+ EVBUFFER_LENGTH(evcon->input_buffer)));
if (req->ntoread > 0)
req->ntoread -= EVBUFFER_LENGTH(evcon->input_buffer);
@@ -1128,20 +1152,26 @@ evhttp_start_read(struct evhttp_connection *evcon)
void
evhttp_send_done(struct evhttp_connection *evcon, void *arg)
{
+ int need_close;
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
TAILQ_REMOVE(&evcon->requests, req, next);
- if (req->flags & EVHTTP_REQ_OWN_CONNECTION) {
- const char *connection =
- evhttp_find_header(req->output_headers, "Connection");
- if (connection == NULL || strcasecmp(connection, "close")) {
- event_warnx("%s: persistent connection not supported",
- __func__);
- }
- evhttp_connection_free(evcon);
- }
-
+ need_close = evhttp_is_connection_close(req->input_headers) ||
+ evhttp_is_connection_close(req->output_headers);
+
evhttp_request_free(req);
+
+ if ((req->flags & EVHTTP_REQ_OWN_CONNECTION) == 0)
+ return;
+
+ if (need_close) {
+ evhttp_connection_free(evcon);
+ return;
+ }
+
+ /* we have a persistent connection; try to accept another request */
+ if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ evhttp_connection_free(evcon);
}
/*
@@ -1160,6 +1190,9 @@ evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
struct evbuffer *buf = evbuffer_new();
+ /* close the connection on error */
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
evhttp_response_code(req, error, reason);
evbuffer_add_printf(buf, fmt, error, reason);
@@ -1336,8 +1369,7 @@ accept_socket(int fd, short what, void *arg)
return;
}
- evhttp_get_request(nfd, (struct sockaddr *)&ss, addrlen,
- evhttp_handle_request, http);
+ evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen);
}
static int
@@ -1378,6 +1410,7 @@ evhttp_start(const char *address, u_short port)
}
TAILQ_INIT(&http->callbacks);
+ TAILQ_INIT(&http->connections);
if (bind_socket(http, address, port) == -1) {
free(http);
@@ -1391,12 +1424,18 @@ void
evhttp_free(struct evhttp* http)
{
struct evhttp_cb *http_cb;
+ struct evhttp_connection *evcon;
int fd = http->bind_ev.ev_fd;
/* Remove the accepting part */
event_del(&http->bind_ev);
close(fd);
+ while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
+ /* evhttp_connection_free removes the connection */
+ evhttp_connection_free(evcon);
+ }
+
while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
TAILQ_REMOVE(&http->callbacks, http_cb, next);
free(http_cb->what);
@@ -1522,12 +1561,11 @@ evhttp_request_uri(struct evhttp_request *req) {
* The callback is executed once the whole request has been read.
*/
-void
-evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
- void (*cb)(struct evhttp_request *, void *), void *arg)
+static struct evhttp_connection*
+evhttp_get_request_connection(
+ int fd, struct sockaddr *sa, socklen_t salen)
{
struct evhttp_connection *evcon;
- struct evhttp_request *req;
char *hostname, *portname;
name_from_addr(sa, salen, &hostname, &portname);
@@ -1536,17 +1574,23 @@ evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
/* we need a connection object to put the http request on */
if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
- return;
+ return (NULL);
evcon->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_CONNECTED;
- if ((req = evhttp_request_new(cb, arg)) == NULL) {
- evhttp_connection_free(evcon);
- return;
- }
-
evcon->fd = fd;
+ return (evcon);
+}
+
+static int
+evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
+{
+ struct evhttp *http = evcon->http_server;
+ struct evhttp_request *req;
+ if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
+ return (-1);
+
req->evcon = evcon; /* the request ends up owning the connection */
req->flags |= EVHTTP_REQ_OWN_CONNECTION;
@@ -1554,11 +1598,34 @@ evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
req->kind = EVHTTP_REQUEST;
- if ((req->remote_host = strdup(hostname)) == NULL)
+ if ((req->remote_host = strdup(evcon->address)) == NULL)
event_err(1, "%s: strdup", __func__);
- req->remote_port = atoi(portname);
+ req->remote_port = evcon->port;
evhttp_start_read(evcon);
+
+ return (0);
+}
+
+void
+evhttp_get_request(struct evhttp *http, int fd,
+ struct sockaddr *sa, socklen_t salen)
+{
+ struct evhttp_connection *evcon;
+
+ evcon = evhttp_get_request_connection(fd, sa, salen);
+ if (evcon == NULL)
+ return;
+
+ /*
+ * if we want to accept more than one request on a connection,
+ * we need to know which http server it belongs to.
+ */
+ evcon->http_server = http;
+ TAILQ_INSERT_TAIL(&http->connections, evcon, next);
+
+ if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ evhttp_connection_free(evcon);
}
diff --git a/test/regress_http.c b/test/regress_http.c
index cad764ce..2127d49a 100644
--- a/test/regress_http.c
+++ b/test/regress_http.c
@@ -163,7 +163,7 @@ http_errorcb(struct bufferevent *bev, short what, void *arg)
void
http_basic_cb(struct evhttp_request *req, void *arg)
{
- event_debug((stderr, "%s: called\n", __func__));
+ event_debug(("%s: called\n", __func__));
struct evbuffer *evb = evbuffer_new();
evbuffer_add_printf(evb, "This is funny");
@@ -194,7 +194,8 @@ http_basic_test(void)
http_request =
"GET /test HTTP/1.1\r\n"
- "Host: somehost \r\n"
+ "Host: somehost\r\n"
+ "Connection: close\r\n"
"\r\n";
bufferevent_write(bev, http_request, strlen(http_request));
@@ -217,14 +218,15 @@ http_basic_test(void)
void http_request_done(struct evhttp_request *, void *);
void
-http_connection_test(void)
+http_connection_test(int persistent)
{
short port = -1;
struct evhttp_connection *evcon = NULL;
struct evhttp_request *req = NULL;
test_ok = 0;
- fprintf(stdout, "Testing Basic HTTP Connection: ");
+ fprintf(stdout, "Testing Request Connection Pipeline %s: ",
+ persistent ? "(persistent)" : "");
http = http_setup(&port);
@@ -265,6 +267,13 @@ http_connection_test(void)
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "somehost");
+ /*
+ * if our connections are not supposed to be persistent; request
+ * a close from the server.
+ */
+ if (!persistent)
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
/* We give ownership of the request to the connection */
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
fprintf(stdout, "FAILED\n");
@@ -371,7 +380,7 @@ http_post_test(void)
void
http_post_cb(struct evhttp_request *req, void *arg)
{
- event_debug((stderr, "%s: called\n", __func__));
+ event_debug(("%s: called\n", __func__));
/* Yes, we are expecting a post request */
if (req->type != EVHTTP_REQ_POST) {
@@ -438,6 +447,7 @@ http_failure_readcb(struct bufferevent *bev, void *arg)
const char *what = "400 Bad Request";
if (evbuffer_find(bev->input, what, strlen(what)) != NULL) {
test_ok = 2;
+ bufferevent_disable(bev, EV_READ);
event_loopexit(NULL);
}
}
@@ -488,7 +498,8 @@ void
http_suite(void)
{
http_basic_test();
- http_connection_test();
+ http_connection_test(0 /* not-persistent */);
+ http_connection_test(1 /* persistent */);
http_post_test();
http_failure_test();
}