static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
static int evhttp_associate_new_request_with_connection(
struct evhttp_connection *evcon);
+static void evhttp_connection_start_detectclose(
+ struct evhttp_connection *evcon);
+static void evhttp_connection_stop_detectclose(
+ struct evhttp_connection *evcon);
+static void evhttp_request_dispatch(struct evhttp_connection* evcon);
void evhttp_write(int, short, void *);
TAILQ_REMOVE(&evcon->requests, req, next);
req->evcon = NULL;
+ int need_close =
+ evhttp_is_connection_close(req->input_headers) ||
+ evhttp_is_connection_close(req->output_headers);
/* check if we got asked to close the connection */
- if (evhttp_is_connection_close(req->input_headers) ||
- evhttp_is_connection_close(req->output_headers))
+ if (need_close)
evhttp_connection_reset(evcon);
if (TAILQ_FIRST(&evcon->requests) != NULL) {
* and deal with the next request. xxx: no
* persistent connection right now
*/
- evhttp_connection_connect(evcon);
+ if (evcon->state != EVCON_CONNECTED)
+ evhttp_connection_connect(evcon);
+ else
+ evhttp_request_dispatch(evcon);
+ } else if (!need_close) {
+ /*
+ * The connection is going to be persistent, but we
+ * need to detect if the other side closes it.
+ */
+ evhttp_connection_start_detectclose(evcon);
}
}
free(evcon);
}
-void
+static void
evhttp_request_dispatch(struct evhttp_connection* evcon)
{
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
if (req == NULL)
return;
+ /* delete possible close detection events */
+ evhttp_connection_stop_detectclose(evcon);
+
/* we assume that the connection is connected already */
assert(evcon->state = EVCON_CONNECTED);
evcon->fd = -1;
}
evcon->state = EVCON_DISCONNECTED;
+
+ /* remove unneeded flags */
+ evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+}
+
+static void
+evhttp_detect_close_cb(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ evhttp_connection_reset(evcon);
+}
+
+static void
+evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
+{
+ assert((evcon->flags & EVHTTP_REQ_OWN_CONNECTION) == 0);
+ evcon->flags |= EVHTTP_CON_CLOSEDETECT;
+
+ event_del(&evcon->ev);
+ event_set(&evcon->ev, evcon->fd, EV_READ,
+ evhttp_detect_close_cb, evcon);
+ event_add(&evcon->ev, NULL);
+}
+
+static void
+evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
+{
+ evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
+ event_del(&evcon->ev);
}
/*
* Call back for asynchronous connection attempt.
*/
-void
+static void
evhttp_connectioncb(int fd, short what, void *arg)
{
struct evhttp_connection *evcon = arg;
int done = 0;
struct evkeyvalq* headers = req->input_headers;
- while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) {
+ while ((endp = evbuffer_find(buffer, (u_char *)"\r\n", 2)) != NULL) {
char *skey, *svalue;
- if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
+ if (strncmp((char *)EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
evbuffer_drain(buffer, 2);
/* Last header - Done */
done = 1;
if (req->got_firstline == 0) {
switch (req->kind) {
case EVHTTP_REQUEST:
- if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1)
+ if (evhttp_parse_request_line(
+ req, EVBUFFER_DATA(buffer)) == -1)
return (-1);
break;
case EVHTTP_RESPONSE:
- if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1)
+ if (evhttp_parse_response_line(
+ req, EVBUFFER_DATA(buffer)) == -1)
return (-1);
break;
default:
req->got_firstline = 1;
} else {
/* Regular header */
- svalue = EVBUFFER_DATA(buffer);
+ svalue = (char *)EVBUFFER_DATA(buffer);
skey = strsep(&svalue, ":");
if (svalue == NULL)
return (-1);
int n, res;
if (what == EV_TIMEOUT) {
- event_warnx("%s: timeout on %d\n", __func__, fd);
+ event_debug(("%s: timeout on %d\n", __func__, fd));
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
return;
}
assert(req->evcon == NULL);
req->evcon = evcon;
- assert(!(req->flags && EVHTTP_REQ_OWN_CONNECTION));
+ assert(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
TAILQ_INSERT_TAIL(&evcon->requests, req, next);
need_close = evhttp_is_connection_close(req->input_headers) ||
evhttp_is_connection_close(req->output_headers);
+ assert(req->flags & EVHTTP_REQ_OWN_CONNECTION);
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 */
+ /* we have a persistent connection; try to accept another request. */
if (evhttp_associate_new_request_with_connection(evcon) == -1)
evhttp_connection_free(evcon);
}
return (NULL);
}
+ http->timeout = -1;
+
TAILQ_INIT(&http->callbacks);
TAILQ_INIT(&http->connections);
free(http);
}
+void
+evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
+{
+ http->timeout = timeout_in_secs;
+}
+
void
evhttp_set_cb(struct evhttp *http, const char *uri,
void (*cb)(struct evhttp_request *, void *), void *cbarg)
if (evcon == NULL)
return;
+ /* the timeout can be used by the server to close idle connections */
+ if (http->timeout != -1)
+ evhttp_connection_set_timeout(evcon, http->timeout);
+
/*
* if we want to accept more than one request on a connection,
* we need to know which http server it belongs to.
fprintf(stdout, "OK\n");
}
+static void
+close_detect_done(struct evhttp_request *req, void *arg)
+{
+ if (req == NULL || req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+static void
+close_detect_launch(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct evhttp_request *req;
+
+ req = evhttp_request_new(close_detect_done, NULL);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ /* We give ownership of the request to the connection */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+static void
+close_detect_cb(struct evhttp_request *req, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ struct timeval tv;
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ timerclear(&tv);
+ tv.tv_sec = 3; /* longer than the http time out */
+
+ /* launch a new request on the persistent connection in 6 seconds */
+ event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
+}
+
+
+void
+http_close_detection()
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing Connection Close Detection: ");
+
+ http = http_setup(&port);
+
+ /* 2 second timeout */
+ evhttp_set_timeout(http, 2);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ /*
+ * At this point, we want to schedule a request to the HTTP
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(close_detect_cb, evcon);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ /* We give ownership of the request to the connection */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ fprintf(stdout, "OK\n");
+}
void
http_suite(void)
http_basic_test();
http_connection_test(0 /* not-persistent */);
http_connection_test(1 /* persistent */);
+ http_close_detection();
http_post_test();
http_failure_test();
}