#include "event2/event-config.h"
#include "evconfig-private.h"
+#define member_size(type, member) sizeof(((type *)0)->member)
+
#ifdef EVENT__HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifndef _WIN32
#include <sys/socket.h>
#include <sys/stat.h>
-#else
+#else /* _WIN32 */
#include <winsock2.h>
#include <ws2tcpip.h>
+#endif /* _WIN32 */
+
+#ifdef EVENT__HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef EVENT__HAVE_AFUNIX_H
+#include <afunix.h>
#endif
#include <sys/queue.h>
#include <string.h>
#ifndef _WIN32
#include <syslog.h>
-#endif
+#endif /* !_WIN32 */
#include <signal.h>
-#include <time.h>
#ifdef EVENT__HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "event2/http_struct.h"
#include "event2/http_compat.h"
#include "event2/util.h"
+#include "event2/ws.h"
#include "event2/listener.h"
#include "log-internal.h"
#include "util-internal.h"
extern int debug;
-static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
+static evutil_socket_t create_bind_socket_nonblock(struct evutil_addrinfo *, int reuse);
static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
+static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri, unsigned flags);
static int evhttp_associate_new_request_with_connection(
struct evhttp_connection *evcon);
static void evhttp_connection_start_detectclose(
static int evhttp_add_header_internal(struct evkeyvalq *headers,
const char *key, const char *value);
static const char *evhttp_response_phrase_internal(int code);
-static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t);
+static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t, struct bufferevent *bev);
static void evhttp_write_buffer(struct evhttp_connection *,
void (*)(struct evhttp_connection *, void *), void *);
static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
+static int evhttp_method_may_have_body_(struct evhttp_connection *, enum evhttp_cmd_type);
/* callbacks for bufferevent */
static void evhttp_read_cb(struct bufferevent *, void *);
static void evhttp_write_cb(struct bufferevent *, void *);
static void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
-static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
- const char *hostname);
+static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp, const char *hostname);
+static const char *evhttp_method_(struct evhttp_connection *evcon,
+ enum evhttp_cmd_type type, ev_uint16_t *flags);
#ifndef EVENT__HAVE_STRSEP
/* strsep replacement for platforms that lack it. Only works if
}
/** Given an evhttp_cmd_type, returns a constant string containing the
- * equivalent HTTP command, or NULL if the evhttp_command_type is
+ * equivalent HTTP command, or NULL if the evhttp_cmd_type is
* unrecognized. */
static const char *
-evhttp_method(enum evhttp_cmd_type type)
+evhttp_method_(struct evhttp_connection *evcon,
+ enum evhttp_cmd_type type, ev_uint16_t *flags)
{
- const char *method;
+ struct evhttp_ext_method ext_method;
+ const char *method = NULL;
+ ev_uint16_t tmp_flags = EVHTTP_METHOD_HAS_BODY;
switch (type) {
case EVHTTP_REQ_GET:
break;
case EVHTTP_REQ_HEAD:
method = "HEAD";
+ tmp_flags &= ~EVHTTP_METHOD_HAS_BODY;
break;
case EVHTTP_REQ_PUT:
method = "PUT";
break;
case EVHTTP_REQ_TRACE:
method = "TRACE";
+ tmp_flags &= ~EVHTTP_METHOD_HAS_BODY;
break;
case EVHTTP_REQ_CONNECT:
method = "CONNECT";
case EVHTTP_REQ_PATCH:
method = "PATCH";
break;
+ case EVHTTP_REQ_PROPFIND:
+ method = "PROPFIND";
+ break;
+ case EVHTTP_REQ_PROPPATCH:
+ method = "PROPPATCH";
+ break;
+ case EVHTTP_REQ_MKCOL:
+ method = "MKCOL";
+ break;
+ case EVHTTP_REQ_LOCK:
+ method = "LOCK";
+ break;
+ case EVHTTP_REQ_UNLOCK:
+ method = "UNLOCK";
+ break;
+ case EVHTTP_REQ_COPY:
+ method = "COPY";
+ break;
+ case EVHTTP_REQ_MOVE:
+ method = "MOVE";
+ break;
default:
- method = NULL;
+ /* setup the structure to allow for the cmp.
+ *
+ * if the cmp function is set, it has the ability to
+ * modify method and flags. Other fields will be
+ * ignored.
+ *
+ * NOTE: the flags returned are OR'd with the current
+ * flags.
+ */
+ tmp_flags = 0;
+ ext_method.method = NULL;
+ ext_method.type = type;
+ ext_method.flags = tmp_flags;
+
+ if (evcon->ext_method_cmp != NULL &&
+ evcon->ext_method_cmp(&ext_method) == 0) {
+
+ if (ext_method.type != type) {
+ event_debug(("%s: callback modified type from %u to %u, not allowed",
+ __func__, type, ext_method.type));
+ return NULL;
+ }
+
+ method = ext_method.method;
+ tmp_flags |= ext_method.flags;
+ }
+
break;
}
+ event_debug(("%s: type=%04x => '%s' flags=%04x",
+ __func__, (int)type, method, tmp_flags));
+
+ if (flags)
+ *flags = tmp_flags;
return (method);
}
return (req->response_code != HTTP_NOCONTENT &&
req->response_code != HTTP_NOTMODIFIED &&
(req->response_code < 100 || req->response_code >= 200) &&
+ req->type != EVHTTP_REQ_CONNECT &&
req->type != EVHTTP_REQ_HEAD);
}
evcon->cb_arg = arg;
/* Disable the read callback: we don't actually care about data;
- * we only care about close detection. (We don't disable reading,
- * since we *do* want to learn about any close events.) */
+ * we only care about close detection. (We don't disable reading --
+ * EV_READ, since we *do* want to learn about any close events.) */
bufferevent_setcb(evcon->bufev,
NULL, /*read*/
evhttp_write_cb,
evhttp_error_cb,
evcon);
- bufferevent_enable(evcon->bufev, EV_WRITE);
+ bufferevent_enable(evcon->bufev, EV_READ|EV_WRITE);
}
static void
struct evhttp_request *req)
{
const char *method;
+ /* NOTE: some version of GCC reports a warning that flags may be uninitialized, hence assignment */
+ ev_uint16_t flags = 0;
evhttp_remove_header(req->output_headers, "Proxy-Connection");
/* Generate request line */
- method = evhttp_method(req->type);
+ if (!(method = evhttp_method_(evcon, req->type, &flags))) {
+ method = "NULL";
+ }
+
evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
"%s %s HTTP/%d.%d\r\n",
method, req->uri, req->major, req->minor);
- /* Add the content length on a post or put request if missing */
- if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
- evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+ /* Add the content length on a request if missing
+ * Always add it for POST and PUT requests as clients expect it */
+ if ((flags & EVHTTP_METHOD_HAS_BODY) &&
+ (evbuffer_get_length(req->output_buffer) > 0 ||
+ req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
+ evhttp_find_header(req->output_headers, "Content-Length") == NULL) {
char size[22];
evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
static int
evhttp_is_request_connection_close(struct evhttp_request *req)
{
+ if (req->type == EVHTTP_REQ_CONNECT)
+ return 0;
+
return
evhttp_is_connection_close(req->flags, req->input_headers) ||
evhttp_is_connection_close(req->flags, req->output_headers);
{
if (evhttp_find_header(headers, "Date") == NULL) {
char date[50];
-#ifndef _WIN32
- struct tm cur;
-#endif
- struct tm *cur_p;
- time_t t = time(NULL);
-#ifdef _WIN32
- cur_p = gmtime(&t);
-#else
- gmtime_r(&t, &cur);
- cur_p = &cur;
-#endif
- if (strftime(date, sizeof(date),
- "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
+ if ((signed)sizeof(date) > evutil_date_rfc1123(date, sizeof(date), NULL)) {
evhttp_add_header(headers, "Date", date);
}
}
}
}
+enum expect { NO, CONTINUE, OTHER };
+static enum expect evhttp_have_expect(struct evhttp_request *req, int input)
+{
+ const char *expect;
+ struct evkeyvalq *h = input ? req->input_headers : req->output_headers;
+
+ if (!(req->kind == EVHTTP_REQUEST) || !REQ_VERSION_ATLEAST(req, 1, 1))
+ return NO;
+
+ expect = evhttp_find_header(h, "Expect");
+ if (!expect)
+ return NO;
+
+ return !evutil_ascii_strcasecmp(expect, "100-continue") ? CONTINUE : OTHER;
+}
+
+
/** Generate all headers appropriate for sending the http request in req (or
* the response, if we're sending a response), and write them to evcon's
* bufferevent. Also writes all data from req->output_buffer */
}
evbuffer_add(output, "\r\n", 2);
- if (evbuffer_get_length(req->output_buffer) > 0) {
+ if (evhttp_have_expect(req, 0) != CONTINUE &&
+ evbuffer_get_length(req->output_buffer)) {
/*
* For a request, we add the POST data, for a reply, this
* is the regular data.
*/
- /* XXX We might want to support waiting (a limited amount of
- time) for a continue status line from the server before
- sending POST/PUT message bodies. */
evbuffer_add_buffer(output, req->output_buffer);
}
}
evhttp_connection_incoming_fail(struct evhttp_request *req,
enum evhttp_request_error error)
{
+ switch (error) {
+ case EVREQ_HTTP_DATA_TOO_LONG:
+ req->response_code = HTTP_ENTITYTOOLARGE;
+ break;
+ default:
+ req->response_code = HTTP_BADREQUEST;
+ }
+
switch (error) {
case EVREQ_HTTP_TIMEOUT:
case EVREQ_HTTP_EOF:
* case may happen when a browser keeps a persistent
* connection open and we timeout on the read. when
* the request is still being used for sending, we
- * need to disassociated it from the connection here.
+ * need to disassociate it from the connection here.
*/
if (!req->userdone) {
/* remove it so that it will not be freed */
static inline void
evhttp_request_free_auto(struct evhttp_request *req)
{
- if (!(req->flags & EVHTTP_USER_OWNED)) {
+ if (!(req->flags & EVHTTP_USER_OWNED))
evhttp_request_free(req);
- }
}
static void
evhttp_request_free_auto(req);
}
+static void
+evhttp_set_timeout_tv_(struct timeval *tv, const struct timeval *timeout, int def)
+{
+ if (timeout == NULL && def != -1) {
+ tv->tv_sec = def;
+ tv->tv_usec = 0;
+ return;
+ }
+
+ if (timeout) {
+ *tv = *timeout;
+ } else {
+ evutil_timerclear(tv);
+ }
+}
+static void
+evhttp_set_timeout_(struct timeval *tv, int timeout, int def)
+{
+ if (timeout == -1) {
+ timeout = def;
+ }
+
+ if (timeout == -1) {
+ evutil_timerclear(tv);
+ } else {
+ struct timeval timeout_tv;
+ timeout_tv.tv_sec = timeout;
+ timeout_tv.tv_usec = 0;
+ *tv = timeout_tv;
+ }
+}
+
/* Called when evcon has experienced a (non-recoverable? -NM) error, as
* given in error. If it's an outgoing connection, reset the connection,
* retry any pending requests, and inform the user. If it's incoming,
evhttp_request_free_(evcon, req);
/* reset the connection */
- evhttp_connection_reset_(evcon);
+ evhttp_connection_reset_(evcon, 1);
/* We are trying the next request that was queued on us */
if (TAILQ_FIRST(&evcon->requests) != NULL)
evhttp_connection_connect_(evcon);
+ else
+ if ((evcon->flags & EVHTTP_CON_OUTGOING) &&
+ (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ evhttp_connection_free(evcon);
+ }
/* The call to evhttp_connection_reset_ overwrote errno.
* Let's restore the original errno, so that the user's
/* check if we got asked to close the connection */
if (need_close)
- evhttp_connection_reset_(evcon);
+ evhttp_connection_reset_(evcon, 1);
if (TAILQ_FIRST(&evcon->requests) != NULL) {
/*
}
}
+static void
+evhttp_lingering_close(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
+
+ size_t n = evbuffer_get_length(buf);
+ if (n > (size_t) req->ntoread)
+ n = (size_t) req->ntoread;
+ req->ntoread -= n;
+ req->body_size += n;
+
+ event_debug(("Request body is too long, left " EV_I64_FMT,
+ EV_I64_ARG(req->ntoread)));
+
+ evbuffer_drain(buf, n);
+ if (!req->ntoread)
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
+}
+static void
+evhttp_lingering_fail(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ if (evcon->flags & EVHTTP_CON_LINGERING_CLOSE)
+ evhttp_lingering_close(evcon, req);
+ else
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
+}
+
static void
evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
{
(size_t)req->ntoread > req->evcon->max_body_size)) {
/* XXX: The above casted comparison must checked for overflow */
/* failed body length test */
- event_debug(("Request body is too long"));
- evhttp_connection_fail_(evcon,
- EVREQ_HTTP_DATA_TOO_LONG);
+
+ evhttp_lingering_fail(evcon, req);
return;
}
}
}
- if (req->ntoread == 0) {
+ if (!req->ntoread) {
bufferevent_disable(evcon->bufev, EV_READ);
/* Completed content length */
evhttp_connection_done(evcon);
__func__, EV_SIZE_ARG(total_len)));
#endif
- evhttp_connection_reset_(evcon);
+ evhttp_connection_reset_(evcon, 1);
}
break;
case EVCON_DISCONNECTED:
evhttp_deferred_read_cb(struct event_callback *cb, void *data)
{
struct evhttp_connection *evcon = data;
- evhttp_read_cb(evcon->bufev, evcon);
+ struct bufferevent *bev = evcon->bufev;
+ if (bev->readcb)
+ (bev->readcb)(evcon->bufev, evcon);
}
static void
{
/* This is after writing the request to the server */
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
+ struct evbuffer *output = bufferevent_get_output(evcon->bufev);
EVUTIL_ASSERT(req != NULL);
EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
- /* We need to wait until we've written all of our output data before we can continue */
- if (evbuffer_get_length(bufferevent_get_output(evcon->bufev)) > 0) { return; }
+ /* We need to wait until we've written all of our output data before we can
+ * continue */
+ if (evbuffer_get_length(output) > 0)
+ return;
/* We are done writing our header and are now expecting the response */
req->kind = EVHTTP_RESPONSE;
struct evhttp_request *req;
/* notify interested parties that this connection is going down */
- if (evcon->fd != -1) {
- if (evhttp_connected(evcon) && evcon->closecb != NULL)
- (*evcon->closecb)(evcon, evcon->closecb_arg);
- }
+ if (evhttp_connected(evcon) && evcon->closecb != NULL)
+ (*evcon->closecb)(evcon, evcon->closecb_arg);
/* remove all requests that might be queued on this
* connection. for server connections, this should be empty.
if (evcon->http_server != NULL) {
struct evhttp *http = evcon->http_server;
TAILQ_REMOVE(&http->connections, evcon, next);
+ http->connection_cnt--;
}
if (event_initialized(&evcon->retry_ev)) {
event_debug_unassign(&evcon->retry_ev);
}
- if (evcon->bufev != NULL)
- bufferevent_free(evcon->bufev);
-
event_deferred_cb_cancel_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
- if (evcon->fd != -1) {
- bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
- shutdown(evcon->fd, EVUTIL_SHUT_WR);
- if (!(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE)) {
- evutil_closesocket(evcon->fd);
- }
+ if (evcon->bufev != NULL) {
+ bufferevent_free(evcon->bufev);
}
if (evcon->bind_address != NULL)
if (evcon->address != NULL)
mm_free(evcon->address);
+#ifndef _WIN32
+ if (evcon->unixsocket != NULL)
+ mm_free(evcon->unixsocket);
+#endif
+
mm_free(evcon);
}
if (req == NULL)
return;
+ EVUTIL_ASSERT(req->kind == EVHTTP_REQUEST);
+
/* delete possible close detection events */
evhttp_connection_stop_detectclose(evcon);
evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
}
-/* Reset our connection state: disables reading/writing, closes our fd (if
-* any), clears out buffers, and puts us in state DISCONNECTED. */
-void
-evhttp_connection_reset_(struct evhttp_connection *evcon)
+/** Hard-reset our connection state
+ *
+ * This will:
+ * - reset fd
+ * - clears out buffers
+ * - call closecb
+ */
+static void
+evhttp_connection_reset_hard_(struct evhttp_connection *evcon)
{
struct evbuffer *tmp;
+ int err;
/* XXXX This is not actually an optimal fix. Instead we ought to have
- an API for "stop connecting", or use bufferevent_setfd to turn off
+ an API for "stop connecting", or use bufferevent_replacefd to turn off
connecting. But for Libevent 2.0, this seems like a minimal change
least likely to disrupt the rest of the bufferevent and http code.
*/
bufferevent_disable_hard_(evcon->bufev, EV_READ|EV_WRITE);
- if (evcon->fd != -1) {
- /* inform interested parties about connection close */
- if (evhttp_connected(evcon) && evcon->closecb != NULL)
- (*evcon->closecb)(evcon, evcon->closecb_arg);
+ /* inform interested parties about connection close */
+ if (evhttp_connected(evcon) && evcon->closecb != NULL)
+ (*evcon->closecb)(evcon, evcon->closecb_arg);
- shutdown(evcon->fd, EVUTIL_SHUT_WR);
- evutil_closesocket(evcon->fd);
- bufferevent_setfd(evcon->bufev, -1);
- evcon->fd = -1;
- }
+ /** FIXME: manipulating with fd is unwanted */
+ err = bufferevent_replacefd(evcon->bufev, -1);
+ EVUTIL_ASSERT(!err && "setfd");
/* we need to clean up any buffered data */
tmp = bufferevent_get_output(evcon->bufev);
- evbuffer_drain(tmp, evbuffer_get_length(tmp));
+ err = evbuffer_drain(tmp, -1);
+ EVUTIL_ASSERT(!err && "drain output");
tmp = bufferevent_get_input(evcon->bufev);
- evbuffer_drain(tmp, evbuffer_get_length(tmp));
+ err = evbuffer_drain(tmp, -1);
+ EVUTIL_ASSERT(!err && "drain input");
+}
+
+/** Reset our connection state
+ *
+ * This will:
+ * - disables reading/writing
+ * - puts us in DISCONNECTED state
+ *
+ * @param hard - hard reset will (@see evhttp_connection_reset_hard_())
+ */
+void
+evhttp_connection_reset_(struct evhttp_connection *evcon, int hard)
+{
+ bufferevent_setcb(evcon->bufev, NULL, NULL, NULL, NULL);
+
+ if (hard) {
+ evhttp_connection_reset_hard_(evcon);
+ }
+ evcon->flags &= ~EVHTTP_CON_READING_ERROR;
evcon->state = EVCON_DISCONNECTED;
}
evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
{
evcon->flags |= EVHTTP_CON_CLOSEDETECT;
-
bufferevent_enable(evcon->bufev, EV_READ);
}
evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
{
evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
-
bufferevent_disable(evcon->bufev, EV_READ);
}
evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
{
struct evcon_requestq requests;
+ EVUTIL_ASSERT(evcon->flags & EVHTTP_CON_OUTGOING);
+
+ evhttp_connection_reset_(evcon, 1);
- evhttp_connection_reset_(evcon);
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
struct timeval tv_retry = evcon->initial_retry_timeout;
int i;
request->cb(request, request->cb_arg);
evhttp_request_free_auto(request);
}
+
+ if (TAILQ_FIRST(&evcon->requests) == NULL
+ && (evcon->flags & EVHTTP_CON_AUTOFREE)) {
+ evhttp_connection_free(evcon);
+ }
+
+}
+
+static void
+evhttp_connection_read_on_write_error(struct evhttp_connection *evcon,
+ struct evhttp_request *req)
+{
+ struct evbuffer *buf;
+
+ /** Second time, we can't read anything */
+ if (evcon->flags & EVHTTP_CON_READING_ERROR) {
+ evcon->flags &= ~EVHTTP_CON_READING_ERROR;
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
+ return;
+ }
+
+ req->kind = EVHTTP_RESPONSE;
+
+ buf = bufferevent_get_output(evcon->bufev);
+ evbuffer_unfreeze(buf, 1);
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+ evbuffer_freeze(buf, 1);
+
+ evhttp_start_read_(evcon);
+ evcon->flags |= EVHTTP_CON_READING_ERROR;
}
static void
struct evhttp_connection *evcon = arg;
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
- if (evcon->fd == -1)
- evcon->fd = bufferevent_getfd(bufev);
-
switch (evcon->state) {
case EVCON_CONNECTING:
if (what & BEV_EVENT_TIMEOUT) {
event_debug(("%s: connection timeout for \"%s:%d\" on "
EV_SOCK_FMT,
__func__, evcon->address, evcon->port,
- EV_SOCK_ARG(evcon->fd)));
+ EV_SOCK_ARG(bufferevent_getfd(bufev))));
evhttp_connection_cb_cleanup(evcon);
return;
}
* disconnected.
*/
EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
- evhttp_connection_reset_(evcon);
+ evhttp_connection_reset_(evcon, 1);
/*
* If we have no more requests that need completion
if (what & BEV_EVENT_TIMEOUT) {
evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
+ if (what & BEV_EVENT_WRITING &&
+ evcon->flags & EVHTTP_CON_READ_ON_WRITE_ERROR) {
+ evhttp_connection_read_on_write_error(evcon, req);
+ return;
+ }
+
+ if (what & BEV_EVENT_READING &&
+ evcon->flags & EVHTTP_CON_READ_ON_WRITE_ERROR &&
+ evbuffer_get_length(bufferevent_get_input(bufev))) {
+ event_deferred_cb_schedule_(get_deferred_queue(evcon),
+ &evcon->read_more_deferred_cb);
+ return;
+ }
+
evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
} else if (what == BEV_EVENT_CONNECTED) {
} else {
evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
{
struct evhttp_connection *evcon = arg;
- int error;
- ev_socklen_t errsz = sizeof(error);
-
- if (evcon->fd == -1)
- evcon->fd = bufferevent_getfd(bufev);
if (!(what & BEV_EVENT_CONNECTED)) {
/* some operating systems return ECONNREFUSED immediately
return;
}
- if (evcon->fd == -1) {
- event_debug(("%s: bufferevent_getfd returned -1",
- __func__));
- goto cleanup;
- }
-
- /* Check if the connection completed */
- if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
- &errsz) == -1) {
- event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT,
- __func__, evcon->address, evcon->port,
- EV_SOCK_ARG(evcon->fd)));
- goto cleanup;
- }
-
- if (error) {
- event_debug(("%s: connect failed for \"%s:%d\" on "
- EV_SOCK_FMT": %s",
- __func__, evcon->address, evcon->port,
- EV_SOCK_ARG(evcon->fd),
- evutil_socket_error_to_string(error)));
- goto cleanup;
- }
-
/* We are connected to the server now */
event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n",
__func__, evcon->address, evcon->port,
- EV_SOCK_ARG(evcon->fd)));
+ EV_SOCK_ARG(bufferevent_getfd(bufev))));
/* Reset the retry count as we were successful in connecting */
evcon->retry_cnt = 0;
evhttp_error_cb,
evcon);
- if (!evutil_timerisset(&evcon->timeout)) {
- const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
- const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
- bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
- } else {
- bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
- }
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_read, &evcon->timeout_write);
/* try to start requests that have queued up on this connection */
evhttp_request_dispatch(evcon);
int n = sscanf(version, "HTTP/%d.%d%c", &major, &minor, &ch);
if (n != 2 || major > 1) {
event_debug(("%s: bad version %s on message %p from %s",
- __func__, version, req, req->remote_host));
+ __func__, version, (void *)req, req->remote_host));
return (-1);
}
req->major = major;
return (-1);
}
+ if (req->response_code_line != NULL)
+ mm_free(req->response_code_line);
if ((req->response_code_line = mm_strdup(readable)) == NULL) {
event_warn("%s: strdup", __func__);
return (-1);
/* Parse the first line of a HTTP request */
static int
-evhttp_parse_request_line(struct evhttp_request *req, char *line)
+evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
{
+ char *eos = line + len;
char *method;
char *uri;
char *version;
const char *hostname;
const char *scheme;
size_t method_len;
- enum evhttp_cmd_type type;
+ enum evhttp_cmd_type type = 0;
+
+ while (eos > line && *(eos-1) == ' ') {
+ *(eos-1) = '\0';
+ --eos;
+ --len;
+ }
+ if (len < strlen("GET / HTTP/1.0"))
+ return -1;
/* Parse the request line */
method = strsep(&line, " ");
- if (line == NULL)
- return (-1);
- uri = strsep(&line, " ");
- if (line == NULL)
- return (-1);
- version = strsep(&line, " ");
- if (line != NULL)
- return (-1);
+ if (!line)
+ return -1;
+ uri = line;
+ version = strrchr(uri, ' ');
+ if (!version || uri == version)
+ return -1;
+ *version = '\0';
+ version++;
method_len = (uri - method) - 1;
- type = EVHTTP_REQ_UNKNOWN_;
/* First line */
switch (method_len) {
}
break;
case 4:
- /* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
+ /* The method length is 4 bytes, leaving only the methods POST, HEAD, LOCK, COPY and MOVE */
switch (*method) {
case 'P':
if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
type = EVHTTP_REQ_HEAD;
}
break;
+ case 'L':
+ if (method[3] == 'K' && method[2] == 'C' && method[1] == 'O') {
+ type = EVHTTP_REQ_LOCK;
+ }
+ break;
+ case 'C':
+ if (method[3] == 'Y' && method[2] == 'P' && method[1] == 'O') {
+ type = EVHTTP_REQ_COPY;
+ }
+ break;
+ case 'M':
+ if (method[3] == 'E' && method[2] == 'V' && method[1] == 'O') {
+ type = EVHTTP_REQ_MOVE;
+ }
+ break;
default:
break;
}
break;
case 5:
- /* Method length is 5 bytes, which can only encompass PATCH and TRACE */
+ /* Method length is 5 bytes, which can only encompass PATCH, TRACE and MKCOL */
switch (*method) {
case 'P':
if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
type = EVHTTP_REQ_TRACE;
}
+ break;
+ case 'M':
+ if (method[4] == 'L' && method[3] == 'O' && method[2] == 'C' && method[1] == 'K') {
+ type = EVHTTP_REQ_MKCOL;
+ }
break;
default:
break;
}
break;
case 6:
- /* Method length is 6, only valid method 6 bytes in length is DELEte */
-
- /* If the first byte isn't 'D' then it's invalid */
- if (*method != 'D') {
- break;
- }
-
- if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
- type = EVHTTP_REQ_DELETE;
+ /* Method length is 6, only valid methods 6 bytes in length is DELETE and UNLOCK */
+ switch (*method) {
+ case 'D':
+ if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' &&
+ method[2] == 'L' && method[1] == 'E') {
+ type = EVHTTP_REQ_DELETE;
+ }
+ break;
+ case 'U':
+ if (method[5] == 'K' && method[4] == 'C' && method[3] == 'O' &&
+ method[2] == 'L' && method[1] == 'N') {
+ type = EVHTTP_REQ_UNLOCK;
+ }
+ break;
+ default:
+ break;
}
-
break;
case 7:
/* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
default:
break;
}
+ break;
+ case 8:
+ /* Method length is 8, only valid method 8 bytes in length is PROPFIND */
+
+ /* If the first byte isn't 'P' then it's invalid */
+ if (*method != 'P') {
+ break;
+ }
+
+ if (method[7] == 'D' && method[6] == 'N' && method[5] == 'I' &&
+ method[4] == 'F' && method[3] == 'P' && method[2] == 'O' &&
+ method[1] == 'R') {
+ type = EVHTTP_REQ_PROPFIND;
+ }
+
+ break;
+ case 9:
+ /* Method length is 9, only valid method 9 bytes in length is PROPPATCH */
+
+ /* If the first byte isn't 'P' then it's invalid */
+ if (*method != 'P') {
+ break;
+ }
+
+ if (method[8] == 'H' && method[7] == 'C' && method[6] == 'T' &&
+ method[5] == 'A' && method[4] == 'P' && method[3] == 'P' &&
+ method[2] == 'O' && method[1] == 'R') {
+ type = EVHTTP_REQ_PROPPATCH;
+ }
+
break;
} /* switch */
- if ((int)type == EVHTTP_REQ_UNKNOWN_) {
- event_debug(("%s: bad method %s on request %p from %s",
- __func__, method, req, req->remote_host));
- /* No error yet; we'll give a better error later when
- * we see that req->type is unsupported. */
+ if (!type) {
+ /* check extended methods, we only care about the
+ * type set by the cmp function if the cmp function
+ * returns a 0 value.
+ */
+ struct evhttp_ext_method ext_method;
+
+ ext_method.method = method;
+ ext_method.type = 0;
+ ext_method.flags = 0;
+
+ if (req->evcon->ext_method_cmp &&
+ req->evcon->ext_method_cmp(&ext_method) == 0) {
+ /* make sure the other fields in ext_method are
+ * not changed by the callback.
+ */
+ if (strcmp(ext_method.method, method) != 0) {
+ event_warn("%s: modifying the 'method' field of ext_method_cmp's "
+ "parameter is not allowed", __func__);
+ return -1;
+ }
+ if (ext_method.flags != 0) {
+ event_warn("%s: modifying the 'flags' field of ext_method_cmp's "
+ "parameter is not allowed", __func__);
+ return -1;
+ }
+ type = ext_method.type;
+ }
}
-
+
+ if (!type) {
+ event_debug(("%s: bad method %s on request %p from %s",
+ __func__, method, (void *)req, req->remote_host));
+ /* No error yet; we'll give a better error later when
+ * we see that req->type is unsupported. */
+ }
+
req->type = type;
if (evhttp_parse_http_version(version, req) < 0)
- return (-1);
+ return -1;
if ((req->uri = mm_strdup(uri)) == NULL) {
event_debug(("%s: mm_strdup", __func__));
- return (-1);
+ return -1;
}
- if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
- EVHTTP_URI_NONCONFORMANT)) == NULL) {
- return -1;
+ if (type == EVHTTP_REQ_CONNECT) {
+ if ((req->uri_elems = evhttp_uri_parse_authority(req->uri, 0)) == NULL) {
+ return -1;
+ }
+ } else {
+ if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
+ EVHTTP_URI_NONCONFORMANT)) == NULL) {
+ return -1;
+ }
}
/* If we have an absolute-URI, check to see if it is an http request
!evhttp_find_vhost(req->evcon->http_server, NULL, hostname))
req->flags |= EVHTTP_PROXY_REQUEST;
- return (0);
+ return 0;
}
const char *
char *line;
enum message_read_status status = ALL_DATA_READ;
- size_t line_length;
+ size_t len;
/* XXX try */
- line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
+ line = evbuffer_readln(buffer, &len, EVBUFFER_EOL_CRLF);
if (line == NULL) {
if (req->evcon != NULL &&
evbuffer_get_length(buffer) > req->evcon->max_headers_size)
return (MORE_DATA_EXPECTED);
}
- if (req->evcon != NULL &&
- line_length > req->evcon->max_headers_size) {
+ if (req->evcon != NULL && len > req->evcon->max_headers_size) {
mm_free(line);
return (DATA_TOO_LONG);
}
- req->headers_size = line_length;
+ req->headers_size = len;
switch (req->kind) {
case EVHTTP_REQUEST:
- if (evhttp_parse_request_line(req, line) == -1)
+ if (evhttp_parse_request_line(req, line, len) == -1)
status = DATA_CORRUPTED;
break;
case EVHTTP_RESPONSE:
enum message_read_status status = MORE_DATA_EXPECTED;
struct evkeyvalq* headers = req->input_headers;
- size_t line_length;
- while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
+ size_t len;
+ while ((line = evbuffer_readln(buffer, &len, EVBUFFER_EOL_CRLF))
!= NULL) {
char *skey, *svalue;
- req->headers_size += line_length;
+ req->headers_size += len;
if (req->evcon != NULL &&
req->headers_size > req->evcon->max_headers_size) {
req->ntoread = -1;
else if (content_length == NULL &&
evutil_ascii_strcasecmp(connection, "Close") != 0) {
- /* Bad combination, we don't know when it will end */
- event_warnx("%s: we got no content length, but the "
- "server wants to keep the connection open: %s.",
- __func__, connection);
- return (-1);
+ req->ntoread = 0;
} else if (content_length == NULL) {
req->ntoread = -1;
} else {
}
static int
-evhttp_method_may_have_body(enum evhttp_cmd_type type)
+evhttp_method_may_have_body_(struct evhttp_connection *evcon, enum evhttp_cmd_type type)
{
- switch (type) {
- case EVHTTP_REQ_POST:
- case EVHTTP_REQ_PUT:
- case EVHTTP_REQ_PATCH:
- return 1;
- case EVHTTP_REQ_TRACE:
- return 0;
- /* XXX May any of the below methods have a body? */
- case EVHTTP_REQ_GET:
- case EVHTTP_REQ_HEAD:
- case EVHTTP_REQ_DELETE:
- case EVHTTP_REQ_OPTIONS:
- case EVHTTP_REQ_CONNECT:
- return 0;
- default:
- return 0;
- }
+ /* NOTE: some version of GCC reports a warning that flags may be uninitialized, hence assignment */
+ ev_uint16_t flags = 0;
+ evhttp_method_(evcon, type, &flags);
+ return (flags & EVHTTP_METHOD_HAS_BODY) ? 1 : 0;
}
static void
/* If this is a request without a body, then we are done */
if (req->kind == EVHTTP_REQUEST &&
- !evhttp_method_may_have_body(req->type)) {
+ !evhttp_method_may_have_body_(evcon, req->type)) {
evhttp_connection_done(evcon);
return;
}
req->ntoread = -1;
} else {
if (evhttp_get_body_length(req) == -1) {
- evhttp_connection_fail_(evcon,
- EVREQ_HTTP_INVALID_HEADER);
+ evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
}
if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
}
/* Should we send a 100 Continue status line? */
- if (req->kind == EVHTTP_REQUEST && REQ_VERSION_ATLEAST(req, 1, 1)) {
- const char *expect;
-
- expect = evhttp_find_header(req->input_headers, "Expect");
- if (expect) {
- if (!evutil_ascii_strcasecmp(expect, "100-continue")) {
+ switch (evhttp_have_expect(req, 1)) {
+ case CONTINUE:
/* XXX It would be nice to do some sanity
checking here. Does the resource exist?
Should the resource accept post requests? If
send their message body. */
if (req->ntoread > 0) {
/* ntoread is ev_int64_t, max_body_size is ev_uint64_t */
- if ((req->evcon->max_body_size <= EV_INT64_MAX) && (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
- evhttp_send_error(req, HTTP_ENTITYTOOLARGE, NULL);
+ if ((req->evcon->max_body_size <= EV_INT64_MAX) &&
+ (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
+ evhttp_lingering_fail(evcon, req);
return;
}
}
if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
evhttp_send_continue(evcon, req);
- } else {
- evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
- NULL);
- return;
- }
- }
+ break;
+ case OTHER:
+ evhttp_send_error(req, HTTP_EXPECTATIONFAILED, NULL);
+ return;
+ case NO: break;
}
evhttp_read_body(evcon, req);
if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
/* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
- __func__, EV_SOCK_ARG(evcon->fd)));
+ __func__, EV_SOCK_ARG(bufferevent_getfd(evcon->bufev))));
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
} else if (res == MORE_DATA_EXPECTED) {
struct evhttp_request *req)
{
enum message_read_status res;
- evutil_socket_t fd = evcon->fd;
+ evutil_socket_t fd = bufferevent_getfd(evcon->bufev);
res = evhttp_parse_headers_(req, bufferevent_get_input(evcon->bufev));
if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
case EVHTTP_RESPONSE:
/* Start over if we got a 100 Continue response. */
if (req->response_code == 100) {
- evhttp_start_read_(evcon);
+ struct evbuffer *output = bufferevent_get_output(evcon->bufev);
+ evbuffer_add_buffer(output, req->output_buffer);
+ evhttp_start_write_(evcon);
return;
}
if (!evhttp_response_needs_body(req)) {
*/
struct evhttp_connection *
-evhttp_connection_new(const char *address, unsigned short port)
+evhttp_connection_new(const char *address, ev_uint16_t port)
{
return (evhttp_connection_base_new(NULL, NULL, address, port));
}
-struct evhttp_connection *
-evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
- const char *address, unsigned short port)
+static struct evhttp_connection *
+evhttp_connection_new_(struct event_base *base, struct bufferevent* bev)
{
- struct evhttp_connection *evcon = NULL;
-
- event_debug(("Attempting connection to %s:%d\n", address, port));
+ struct evhttp_connection *evcon;
if ((evcon = mm_calloc(1, sizeof(struct evhttp_connection))) == NULL) {
event_warn("%s: calloc failed", __func__);
goto error;
}
- evcon->fd = -1;
- evcon->port = port;
-
evcon->max_headers_size = EV_SIZE_MAX;
evcon->max_body_size = EV_SIZE_MAX;
- evutil_timerclear(&evcon->timeout);
- evcon->retry_cnt = evcon->retry_max = 0;
+ evcon->timeout_connect.tv_sec = HTTP_CONNECT_TIMEOUT;
+ evcon->timeout_read.tv_sec = HTTP_READ_TIMEOUT;
+ evcon->timeout_write.tv_sec = HTTP_WRITE_TIMEOUT;
+ evcon->initial_retry_timeout.tv_sec = HTTP_INITIAL_RETRY_TIMEOUT;
- if ((evcon->address = mm_strdup(address)) == NULL) {
- event_warn("%s: strdup failed", __func__);
- goto error;
- }
+ evcon->retry_cnt = evcon->retry_max = 0;
if (bev == NULL) {
- if (!(bev = bufferevent_socket_new(base, -1, 0))) {
+ if (!(bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE))) {
event_warn("%s: bufferevent_socket_new failed", __func__);
goto error;
}
evcon->state = EVCON_DISCONNECTED;
TAILQ_INIT(&evcon->requests);
- evcon->initial_retry_timeout.tv_sec = 2;
- evcon->initial_retry_timeout.tv_usec = 0;
-
if (base != NULL) {
evcon->base = base;
if (bufferevent_get_base(bev) != base)
bufferevent_get_priority(bev),
evhttp_deferred_read_cb, evcon);
- evcon->dns_base = dnsbase;
evcon->ai_family = AF_UNSPEC;
return (evcon);
return (NULL);
}
+#ifndef _WIN32
+struct evhttp_connection *
+evhttp_connection_base_bufferevent_unix_new(struct event_base *base, struct bufferevent* bev, const char *unixsocket)
+{
+ struct evhttp_connection *evcon;
+
+ if (strlen(unixsocket) >= member_size(struct sockaddr_un, sun_path)) {
+ event_warn("%s: unix socket too long", __func__);
+ return NULL;
+ }
+
+ evcon = evhttp_connection_new_(base, bev);
+ if (evcon == NULL)
+ goto error;
+
+ if ((evcon->unixsocket = mm_strdup(unixsocket)) == NULL) {
+ event_warn("%s: strdup failed", __func__);
+ goto error;
+ }
+
+ evcon->ai_family = AF_UNIX;
+
+ return (evcon);
+ error:
+ if (evcon != NULL)
+ evhttp_connection_free(evcon);
+ return (NULL);
+}
+#endif
+
+struct evhttp_connection *
+evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
+ const char *address, unsigned short port)
+{
+ struct evhttp_connection *evcon;
+
+ event_debug(("Attempting connection to %s:%d\n", address, port));
+
+ evcon = evhttp_connection_new_(base, bev);
+ if (evcon == NULL)
+ goto error;
+
+ if ((evcon->address = mm_strdup(address)) == NULL) {
+ event_warn("%s: strdup failed", __func__);
+ goto error;
+ }
+ evcon->port = port;
+ evcon->dns_base = dnsbase;
+
+ return (evcon);
+error:
+ if (evcon != NULL)
+ evhttp_connection_free(evcon);
+ return (NULL);
+}
+
+
struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
{
return evcon->bufev;
struct evhttp_connection *
evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
- const char *address, unsigned short port)
+ const char *address, ev_uint16_t port)
{
return evhttp_connection_base_bufferevent_new(base, dnsbase, NULL, address, port);
}
int evhttp_connection_set_flags(struct evhttp_connection *evcon,
int flags)
{
- if (flags & ~(EVHTTP_CON_REUSE_CONNECTED_ADDR)) {
- return 1;
- }
+ int avail_flags = 0;
+ avail_flags |= EVHTTP_CON_REUSE_CONNECTED_ADDR;
+ avail_flags |= EVHTTP_CON_READ_ON_WRITE_ERROR;
- evcon->flags &= ~(EVHTTP_CON_REUSE_CONNECTED_ADDR);
+ if (flags & ~avail_flags || flags > EVHTTP_CON_PUBLIC_FLAGS_END)
+ return 1;
+ evcon->flags &= ~avail_flags;
- evcon->flags |= EVHTTP_CON_REUSE_CONNECTED_ADDR;
+ evcon->flags |= flags;
return 0;
}
+void
+evhttp_connection_set_ext_method_cmp(struct evhttp_connection *evcon,
+ evhttp_ext_method_cb cmp)
+{
+ evcon->ext_method_cmp = cmp;
+}
+
void
evhttp_connection_set_base(struct evhttp_connection *evcon,
struct event_base *base)
void
evhttp_connection_set_timeout(struct evhttp_connection *evcon,
- int timeout_in_secs)
+ int timeout)
{
- if (timeout_in_secs == -1)
- evhttp_connection_set_timeout_tv(evcon, NULL);
- else {
- struct timeval tv;
- tv.tv_sec = timeout_in_secs;
- tv.tv_usec = 0;
- evhttp_connection_set_timeout_tv(evcon, &tv);
+ if (timeout != -1) {
+ evcon->flags |= EVHTTP_CON_TIMEOUT_ADJUSTED;
+ } else {
+ evcon->flags &= ~EVHTTP_CON_TIMEOUT_ADJUSTED;
}
+ evhttp_set_timeout_(&evcon->timeout_read, timeout, HTTP_READ_TIMEOUT);
+ evhttp_set_timeout_(&evcon->timeout_write, timeout, HTTP_WRITE_TIMEOUT);
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_read, &evcon->timeout_write);
}
-
void
evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon,
const struct timeval* tv)
{
if (tv) {
- evcon->timeout = *tv;
- bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
+ evcon->flags |= EVHTTP_CON_TIMEOUT_ADJUSTED;
} else {
- const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
- const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
- evutil_timerclear(&evcon->timeout);
- bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
+ evcon->flags &= ~EVHTTP_CON_TIMEOUT_ADJUSTED;
}
+ evhttp_set_timeout_tv_(&evcon->timeout_read, tv, HTTP_READ_TIMEOUT);
+ evhttp_set_timeout_tv_(&evcon->timeout_write, tv, HTTP_WRITE_TIMEOUT);
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_read, &evcon->timeout_write);
+}
+void evhttp_connection_set_connect_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv)
+{
+ evcon->flags |= EVHTTP_CON_TIMEOUT_ADJUSTED;
+ evhttp_set_timeout_tv_(&evcon->timeout_connect, tv, -1);
+ if (evcon->state == EVCON_CONNECTING)
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_connect, &evcon->timeout_connect);
+}
+void evhttp_connection_set_read_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv)
+{
+ evcon->flags |= EVHTTP_CON_TIMEOUT_ADJUSTED;
+ evhttp_set_timeout_tv_(&evcon->timeout_read, tv, -1);
+ if (evcon->state != EVCON_CONNECTING)
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_read, &evcon->timeout_write);
+}
+void evhttp_connection_set_write_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv)
+{
+ evcon->flags |= EVHTTP_CON_TIMEOUT_ADJUSTED;
+ evhttp_set_timeout_tv_(&evcon->timeout_write, tv, -1);
+ if (evcon->state != EVCON_CONNECTING)
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_read, &evcon->timeout_write);
}
void
void
evhttp_connection_get_peer(struct evhttp_connection *evcon,
- char **address, ev_uint16_t *port)
+ const char **address, ev_uint16_t *port)
{
*address = evcon->address;
*port = evcon->port;
if (evcon->state == EVCON_CONNECTING)
return (0);
- evhttp_connection_reset_(evcon);
+ /* Do not do hard reset, since this will reset the fd, but someone may
+ * change some options for it (i.e. setsockopt(), #875)
+ *
+ * However don't think that this options will be preserved for all
+ * connection lifetime, they will be reseted in the following cases:
+ * - evhttp_connection_set_local_address()
+ * - evhttp_connection_set_local_port()
+ * - evhttp_connection_set_retries()
+ * */
+ evhttp_connection_reset_(evcon, 0);
EVUTIL_ASSERT(!(evcon->flags & EVHTTP_CON_INCOMING));
evcon->flags |= EVHTTP_CON_OUTGOING;
if (evcon->bind_address || evcon->bind_port) {
- evcon->fd = bind_socket(
- evcon->bind_address, evcon->bind_port, 0 /*reuse*/);
- if (evcon->fd == -1) {
+ int fd = bind_socket(evcon->bind_address, evcon->bind_port,
+ 0 /*reuse*/);
+ if (fd == -1) {
event_debug(("%s: failed to bind to \"%s\"",
__func__, evcon->bind_address));
return (-1);
}
- bufferevent_setfd(evcon->bufev, evcon->fd);
- } else {
- bufferevent_setfd(evcon->bufev, -1);
+ if (bufferevent_replacefd(evcon->bufev, fd))
+ return (-1);
}
/* Set up a callback for successful connection setup */
NULL /* evhttp_write_cb */,
evhttp_connection_cb,
evcon);
- if (!evutil_timerisset(&evcon->timeout)) {
- const struct timeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 };
- bufferevent_set_timeouts(evcon->bufev, &conn_tv, &conn_tv);
- } else {
- bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
- }
+ bufferevent_set_timeouts(evcon->bufev,
+ &evcon->timeout_connect, &evcon->timeout_connect);
/* make sure that we get a write callback */
- bufferevent_enable(evcon->bufev, EV_WRITE);
+ if (bufferevent_enable(evcon->bufev, EV_WRITE))
+ return (-1);
evcon->state = EVCON_CONNECTING;
socklen = sizeof(struct sockaddr_in6);
}
ret = bufferevent_socket_connect(evcon->bufev, sa, socklen);
- } else {
+ }
+#ifndef _WIN32
+ else if (evcon->unixsocket) {
+ struct sockaddr_un sockaddr;
+ sockaddr.sun_family = AF_UNIX;
+ strcpy(sockaddr.sun_path, evcon->unixsocket);
+ ret = bufferevent_socket_connect(evcon->bufev, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ }
+#endif
+ else {
ret = bufferevent_socket_connect_hostname(evcon->bufev,
evcon->dns_base, evcon->ai_family, address, evcon->port);
}
if (ret < 0) {
evcon->state = old_state;
- event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
+ event_sock_warn(bufferevent_getfd(evcon->bufev), "%s: connection to \"%s\" failed",
__func__, evcon->address);
/* some operating systems return ECONNREFUSED immediately
* when connecting to a local address. the cleanup is going
TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+ /* We do not want to conflict with retry_ev */
+ if (evcon->retry_cnt)
+ return (0);
+
/* If the connection object is not connected; make it so */
if (!evhttp_connected(evcon)) {
int res = evhttp_connection_connect_(evcon);
if (res != 0)
TAILQ_REMOVE(&evcon->requests, req, next);
- return res;
+ return (res);
}
/*
}
}
+void
+evhttp_start_write_(struct evhttp_connection *evcon)
+{
+ bufferevent_disable(evcon->bufev, EV_WRITE);
+ bufferevent_enable(evcon->bufev, EV_READ);
+
+ evcon->state = EVCON_WRITING;
+ evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
+}
+
static void
evhttp_send_done(struct evhttp_connection *evcon, void *arg)
{
/*
* Returns an error page.
*/
-
void
evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
{
-
-#define ERR_FORMAT "<HTML><HEAD>\n" \
- "<TITLE>%d %s</TITLE>\n" \
- "</HEAD><BODY>\n" \
- "<H1>%s</H1>\n" \
- "</BODY></HTML>\n"
+#define ERR_FORMAT "<html><head>" \
+ "<title>%d %s</title>" \
+ "</head><body>" \
+ "<h1>%d %s</h1>%s" \
+ "</body></html>"
struct evbuffer *buf = evbuffer_new();
+ struct evhttp *http = req->evcon->http_server;
+
if (buf == NULL) {
/* if we cannot allocate memory; we just drop the connection */
evhttp_connection_free(req->evcon);
return;
}
- if (reason == NULL) {
- reason = evhttp_response_phrase_internal(error);
- }
evhttp_response_code_(req, error, reason);
- evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
+ /* Output error using callback for connection's evhttp, if available */
+ if ((http->errorcb == NULL) ||
+ ((*http->errorcb)(req, buf, error, reason, http->errorcbarg) < 0))
+ {
+ const char *heading = evhttp_response_phrase_internal(error);
+
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+ evbuffer_add_printf(buf, ERR_FORMAT,
+ error, heading, error, heading,
+ (reason ? reason : ""));
+ }
evhttp_send_page_(req, buf);
evbuffer_free(buf);
#undef ERR_FORMAT
}
+static void
+evhttp_send_notfound(struct evhttp_request *req, const char *url)
+{
+#define REASON_FORMAT "<p>The requested URL %s was not found on this server.</p>"
+ char *escaped_url = NULL;
+ char *reason = NULL;
+ size_t reason_len;
+
+ url = (url != NULL ? url : req->uri);
+ if (url != NULL)
+ escaped_url = evhttp_htmlescape(url);
+
+ if (escaped_url != NULL) {
+ reason_len = strlen(REASON_FORMAT)+strlen(escaped_url)+1;
+ reason = mm_malloc(reason_len);
+ }
+
+ if ((escaped_url != NULL) && (reason != NULL))
+ evutil_snprintf(reason, reason_len, REASON_FORMAT, escaped_url);
+
+ evhttp_send_error(req, HTTP_NOTFOUND, reason);
+
+ if (reason != NULL)
+ mm_free(reason);
+ if (escaped_url != NULL)
+ mm_free(escaped_url);
+#undef REASON_FORMAT
+}
+
/* Requires that headers and response code are already set up */
const char *reason)
{
evhttp_response_code_(req, code, reason);
+
+ if (req->evcon == NULL)
+ return;
+
if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
REQ_VERSION_ATLEAST(req, 1, 1) &&
evhttp_response_needs_body(req)) {
evhttp_write_buffer(evcon, cb, arg);
}
+struct bufferevent *
+evhttp_start_ws_(struct evhttp_request *req)
+{
+ struct evhttp_connection *evcon = req->evcon;
+ struct bufferevent *bufev;
+
+ evhttp_response_code_(req, HTTP_SWITCH_PROTOCOLS, "Switching Protocols");
+
+ if (req->evcon == NULL)
+ return NULL;
+
+ evhttp_make_header(req->evcon, req);
+ evhttp_write_buffer(req->evcon, NULL, NULL);
+
+ TAILQ_REMOVE(&evcon->requests, req, next);
+
+ bufev = evcon->bufev;
+ evcon->bufev = NULL;
+ evcon->closecb = NULL;
+
+ evhttp_request_free(req);
+ evhttp_connection_free(evcon);
+ return bufev;
+}
+
void
evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
{
{
struct evbuffer *buf = evbuffer_new();
const char *p, *end;
- char *result;
+ char *result = NULL;
- if (buf == NULL)
- return (NULL);
+ if (!buf) {
+ goto out;
+ }
- if (len >= 0)
- end = uri+len;
- else
- end = uri+strlen(uri);
+ if (len >= 0) {
+ if (uri + len < uri) {
+ goto out;
+ }
+
+ end = uri + len;
+ } else {
+ size_t slen = strlen(uri);
+
+ if (slen >= EV_SSIZE_MAX) {
+ /* we don't want to mix signed and unsigned */
+ goto out;
+ }
+
+ if (uri + slen < uri) {
+ goto out;
+ }
+
+ end = uri + slen;
+ }
for (p = uri; p < end; p++) {
if (CHAR_IS_UNRESERVED(*p)) {
evbuffer_add_printf(buf, "%%%02X", (unsigned char)(*p));
}
}
+
evbuffer_add(buf, "", 1); /* NUL-terminator. */
result = mm_malloc(evbuffer_get_length(buf));
+
if (result)
evbuffer_remove(buf, result, evbuffer_get_length(buf));
- evbuffer_free(buf);
- return (result);
+out:
+ if (buf)
+ evbuffer_free(buf);
+ return result;
}
char *
static int
evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
- int is_whole_uri)
+ int is_whole_uri, unsigned flags)
{
char *line=NULL;
- char *argument;
char *p;
const char *query_part;
int result = -1;
goto error;
}
- p = argument = line;
+ p = line;
while (p != NULL && *p != '\0') {
char *key, *value, *decoded_value;
- argument = strsep(&p, "&");
+ int err;
- value = argument;
+ value = strsep(&p, "&");
key = strsep(&value, "=");
- if (value == NULL || *key == '\0') {
- goto error;
+ if (flags & EVHTTP_URI_QUERY_NONCONFORMANT) {
+ if (value == NULL)
+ value = (char *)"";
+ if (*key == '\0')
+ continue;
+ } else {
+ if (value == NULL || *key == '\0')
+ goto error;
}
if ((decoded_value = mm_malloc(strlen(value) + 1)) == NULL) {
evhttp_decode_uri_internal(value, strlen(value),
decoded_value, 1 /*always_decode_plus*/);
event_debug(("Query Param: %s -> %s\n", key, decoded_value));
- evhttp_add_header_internal(headers, key, decoded_value);
+ if (flags & EVHTTP_URI_QUERY_LAST_VAL)
+ evhttp_remove_header(headers, key);
+ err = evhttp_add_header_internal(headers, key, decoded_value);
mm_free(decoded_value);
+ if (err)
+ goto error;
}
result = 0;
int
evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
{
- return evhttp_parse_query_impl(uri, headers, 1);
+ return evhttp_parse_query_impl(uri, headers, 1, 0);
}
int
evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers)
{
- return evhttp_parse_query_impl(uri, headers, 0);
+ return evhttp_parse_query_impl(uri, headers, 0, 0);
+}
+int
+evhttp_parse_query_str_flags(const char *uri, struct evkeyvalq *headers, unsigned flags)
+{
+ return evhttp_parse_query_impl(uri, headers, 0, flags);
}
static struct evhttp_cb *
/* we have a new request on which the user needs to take action */
req->userdone = 0;
- if (req->type == 0 || req->uri == NULL) {
- evhttp_send_error(req, HTTP_BADREQUEST, NULL);
+ bufferevent_disable(req->evcon->bufev, EV_READ);
+
+ if (req->uri == NULL) {
+ evhttp_send_error(req, req->response_code, NULL);
return;
}
if (http->gencb) {
(*http->gencb)(req, http->gencbarg);
return;
- } else {
- /* We need to send a 404 here */
-#define ERR_FORMAT "<html><head>" \
- "<title>404 Not Found</title>" \
- "</head><body>" \
- "<h1>Not Found</h1>" \
- "<p>The requested URL %s was not found on this server.</p>"\
- "</body></html>\n"
-
- char *escaped_html;
- struct evbuffer *buf;
-
- if ((escaped_html = evhttp_htmlescape(req->uri)) == NULL) {
- evhttp_connection_free(req->evcon);
- return;
- }
-
- if ((buf = evbuffer_new()) == NULL) {
- mm_free(escaped_html);
- evhttp_connection_free(req->evcon);
- return;
- }
-
- evhttp_response_code_(req, HTTP_NOTFOUND, "Not Found");
-
- evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
-
- mm_free(escaped_html);
-
- evhttp_send_page_(req, buf);
-
- evbuffer_free(buf);
-#undef ERR_FORMAT
- }
+ } else
+ evhttp_send_notfound(req, NULL);
}
/* Listener callback when a connection arrives at a server. */
static void
accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
{
- struct evhttp *http = arg;
+ struct evhttp_bound_socket *bound = arg;
+
+ struct evhttp *http = bound->http;
+
+ struct bufferevent *bev = NULL;
+ if (bound->bevcb)
+ bev = bound->bevcb(http->base, bound->bevcbarg);
- evhttp_get_request(http, nfd, peer_sa, peer_socklen);
+ evhttp_get_request(http, nfd, peer_sa, peer_socklen, bev);
}
int
{
evutil_socket_t fd;
struct evhttp_bound_socket *bound;
+ int serrno;
if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1)
return (NULL);
if (listen(fd, 128) == -1) {
+ serrno = EVUTIL_SOCKET_ERROR();
event_sock_warn(fd, "%s: listen", __func__);
evutil_closesocket(fd);
+ EVUTIL_SET_SOCKET_ERROR(serrno);
return (NULL);
}
return (NULL);
bound->listener = listener;
+ bound->bevcb = NULL;
+ bound->http = http;
TAILQ_INSERT_TAIL(&http->sockets, bound, next);
- evconnlistener_set_cb(listener, accept_socket_cb, http);
+ evconnlistener_set_cb(listener, accept_socket_cb, bound);
return bound;
}
return bound->listener;
}
+void
+evhttp_bound_set_bevcb(struct evhttp_bound_socket *bound,
+ struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg)
+{
+ bound->bevcb = cb;
+ bound->bevcbarg = cbarg;
+}
+
void
evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound)
{
return (NULL);
}
- evutil_timerclear(&http->timeout);
+ evutil_timerclear(&http->timeout_read);
+ evutil_timerclear(&http->timeout_write);
+
evhttp_set_max_headers_size(http, EV_SIZE_MAX);
evhttp_set_max_body_size(http, EV_SIZE_MAX);
evhttp_set_default_content_type(http, "text/html; charset=ISO-8859-1");
TAILQ_INIT(&http->sockets);
TAILQ_INIT(&http->callbacks);
TAILQ_INIT(&http->connections);
+ TAILQ_INIT(&http->ws_sessions);
TAILQ_INIT(&http->virtualhosts);
TAILQ_INIT(&http->aliases);
*/
struct evhttp *
-evhttp_start(const char *address, unsigned short port)
+evhttp_start(const char *address, ev_uint16_t port)
{
struct evhttp *http = NULL;
{
struct evhttp_cb *http_cb;
struct evhttp_connection *evcon;
+ struct evws_connection *evws;
struct evhttp_bound_socket *bound;
struct evhttp* vhost;
struct evhttp_server_alias *alias;
evhttp_connection_free(evcon);
}
+ while ((evws = TAILQ_FIRST(&http->ws_sessions)) != NULL) {
+ evws_connection_free(evws);
+ }
+
while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
TAILQ_REMOVE(&http->callbacks, http_cb, next);
mm_free(http_cb->what);
}
void
-evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
+evhttp_set_timeout(struct evhttp* http, int timeout)
{
- if (timeout_in_secs == -1) {
- evhttp_set_timeout_tv(http, NULL);
- } else {
- struct timeval tv;
- tv.tv_sec = timeout_in_secs;
- tv.tv_usec = 0;
- evhttp_set_timeout_tv(http, &tv);
- }
+ evhttp_set_timeout_(&http->timeout_read, timeout, -1);
+ evhttp_set_timeout_(&http->timeout_write, timeout, -1);
}
-
void
evhttp_set_timeout_tv(struct evhttp* http, const struct timeval* tv)
{
- if (tv) {
- http->timeout = *tv;
- } else {
- evutil_timerclear(&http->timeout);
- }
+ evhttp_set_timeout_tv_(&http->timeout_read, tv, -1);
+ evhttp_set_timeout_tv_(&http->timeout_write, tv, -1);
+}
+void
+evhttp_set_read_timeout_tv(struct evhttp* http, const struct timeval* tv)
+{
+ evhttp_set_timeout_tv_(&http->timeout_read, tv, -1);
+}
+void
+evhttp_set_write_timeout_tv(struct evhttp* http, const struct timeval* tv)
+{
+ evhttp_set_timeout_tv_(&http->timeout_write, tv, -1);
+}
+
+int evhttp_set_flags(struct evhttp *http, int flags)
+{
+ int avail_flags = 0;
+ avail_flags |= EVHTTP_SERVER_LINGERING_CLOSE;
+
+ if (flags & ~avail_flags)
+ return 1;
+ http->flags &= ~avail_flags;
+
+ http->flags |= flags;
+
+ return 0;
}
void
http->default_max_body_size = max_body_size;
}
+void
+evhttp_set_max_connections(struct evhttp* http, int max_connections)
+{
+ if (max_connections < 0)
+ http->connection_max = 0;
+ else
+ http->connection_max = max_connections;
+}
+
+int
+evhttp_get_connection_count(struct evhttp* http)
+{
+ return http->connection_cnt;
+}
+
void
evhttp_set_default_content_type(struct evhttp *http,
const char *content_type) {
}
void
-evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods)
+evhttp_set_allowed_methods(struct evhttp* http, ev_uint32_t methods)
{
http->allowed_methods = methods;
}
+void
+evhttp_set_ext_method_cmp(struct evhttp *http, evhttp_ext_method_cb cmp)
+{
+ http->ext_method_cmp = cmp;
+}
+
int
evhttp_set_cb(struct evhttp *http, const char *uri,
void (*cb)(struct evhttp_request *, void *), void *cbarg)
http->bevcbarg = cbarg;
}
+void
+evhttp_set_newreqcb(struct evhttp *http,
+ int (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ http->newreqcb = cb;
+ http->newreqcbarg = cbarg;
+}
+void
+evhttp_set_errorcb(struct evhttp *http,
+ int (*cb)(struct evhttp_request *, struct evbuffer *, int, const char *, void *),
+ void *cbarg)
+{
+ http->errorcb = cb;
+ http->errorcbarg = cbarg;
+}
+
/*
* Request related functions
*/
const char *
evhttp_request_get_uri(const struct evhttp_request *req) {
if (req->uri == NULL)
- event_debug(("%s: request %p has no uri\n", __func__, req));
+ event_debug(("%s: request %p has no uri\n", __func__, (void *)req));
return (req->uri);
}
evhttp_request_get_evhttp_uri(const struct evhttp_request *req) {
if (req->uri_elems == NULL)
event_debug(("%s: request %p has no uri elems\n",
- __func__, req));
+ __func__, (void *)req));
return (req->uri_elems);
}
static struct evhttp_connection*
evhttp_get_request_connection(
struct evhttp* http,
- evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
+ evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen,
+ struct bufferevent* bev)
{
struct evhttp_connection *evcon;
- char *hostname = NULL, *portname = NULL;
- struct bufferevent* bev = NULL;
- name_from_addr(sa, salen, &hostname, &portname);
- if (hostname == NULL || portname == NULL) {
- if (hostname) mm_free(hostname);
- if (portname) mm_free(portname);
- return (NULL);
+#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
+ if (sa->sa_family == AF_UNIX) {
+ struct sockaddr_un *sa_un = (struct sockaddr_un *)sa;
+ sa_un->sun_path[0] = '\0';
+ }
+#endif
+
+#ifndef _WIN32
+ if (sa->sa_family == AF_UNIX) {
+ struct sockaddr_un *sockaddr = (struct sockaddr_un *)sa;
+
+ event_debug(("%s: new request from unix socket on "
+ EV_SOCK_FMT"\n", __func__, EV_SOCK_ARG(fd)));
+
+ /* we need a connection object to put the http request on */
+ if (!bev && http->bevcb != NULL) {
+ bev = (*http->bevcb)(http->base, http->bevcbarg);
+ }
+
+ evcon = evhttp_connection_base_bufferevent_unix_new(http->base,
+ bev, sockaddr->sun_path);
}
+ else
+#endif
+ {
+ char *hostname = NULL, *portname = NULL;
- event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
- __func__, hostname, portname, EV_SOCK_ARG(fd)));
+ name_from_addr(sa, salen, &hostname, &portname);
+ if (hostname == NULL || portname == NULL) {
+ if (hostname) mm_free(hostname);
+ if (portname) mm_free(portname);
+ return (NULL);
+ }
- /* we need a connection object to put the http request on */
- if (http->bevcb != NULL) {
- bev = (*http->bevcb)(http->base, http->bevcbarg);
+ event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
+ __func__, hostname, portname, EV_SOCK_ARG(fd)));
+
+ /* we need a connection object to put the http request on */
+ if (!bev && http->bevcb != NULL) {
+ bev = (*http->bevcb)(http->base, http->bevcbarg);
+ }
+ evcon = evhttp_connection_base_bufferevent_new(
+ http->base, NULL, bev, hostname, atoi(portname));
+ mm_free(hostname);
+ mm_free(portname);
}
- evcon = evhttp_connection_base_bufferevent_new(
- http->base, NULL, bev, hostname, atoi(portname));
- mm_free(hostname);
- mm_free(portname);
if (evcon == NULL)
return (NULL);
evcon->max_headers_size = http->default_max_headers_size;
evcon->max_body_size = http->default_max_body_size;
+ if (http->flags & EVHTTP_SERVER_LINGERING_CLOSE)
+ evcon->flags |= EVHTTP_CON_LINGERING_CLOSE;
evcon->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_READING_FIRSTLINE;
- evcon->fd = fd;
-
- bufferevent_enable(evcon->bufev, EV_READ);
- bufferevent_disable(evcon->bufev, EV_WRITE);
- bufferevent_setfd(evcon->bufev, fd);
+ if (bufferevent_replacefd(evcon->bufev, fd))
+ goto err;
+ if (bufferevent_enable(evcon->bufev, EV_READ))
+ goto err;
+ if (bufferevent_disable(evcon->bufev, EV_WRITE))
+ goto err;
+ bufferevent_socket_set_conn_address_(evcon->bufev, sa, salen);
return (evcon);
+
+err:
+ evhttp_connection_free(evcon);
+ return (NULL);
}
static int
if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
return (-1);
- if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
- event_warn("%s: strdup", __func__);
- evhttp_request_free(req);
- return (-1);
+ if (evcon->address != NULL) {
+ if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
+ event_warn("%s: strdup", __func__);
+ evhttp_request_free(req);
+ return (-1);
+ }
}
req->remote_port = evcon->port;
req->evcon = evcon; /* the request ends up owning the connection */
req->flags |= EVHTTP_REQ_OWN_CONNECTION;
- /* We did not present the request to the user user yet, so treat it as
- * if the user was done with the request. This allows us to free the
- * request on a persistent connection if the client drops it without
- * sending a request.
+ /* We did not present the request to the user yet, so treat it
+ * as if the user was done with the request. This allows us
+ * to free the request on a persistent connection if the
+ * client drops it without sending a request.
*/
req->userdone = 1;
-
- TAILQ_INSERT_TAIL(&evcon->requests, req, next);
-
req->kind = EVHTTP_REQUEST;
+ if (http->newreqcb && http->newreqcb(req, http->newreqcbarg) == -1) {
+ evhttp_request_free(req);
+ return (-1);
+ }
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
evhttp_start_read_(evcon);
static void
evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
- struct sockaddr *sa, ev_socklen_t salen)
+ struct sockaddr *sa, ev_socklen_t salen,
+ struct bufferevent *bev)
{
struct evhttp_connection *evcon;
- evcon = evhttp_get_request_connection(http, fd, sa, salen);
+ evcon = evhttp_get_request_connection(http, fd, sa, salen, bev);
if (evcon == NULL) {
event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
__func__, EV_SOCK_ARG(fd));
}
/* the timeout can be used by the server to close idle connections */
- if (evutil_timerisset(&http->timeout))
- evhttp_connection_set_timeout_tv(evcon, &http->timeout);
+ if (evutil_timerisset(&http->timeout_read))
+ evhttp_connection_set_read_timeout_tv(evcon, &http->timeout_read);
+ if (evutil_timerisset(&http->timeout_write))
+ evhttp_connection_set_write_timeout_tv(evcon, &http->timeout_write);
/*
* 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;
+ evcon->ext_method_cmp = http->ext_method_cmp;
TAILQ_INSERT_TAIL(&http->connections, evcon, next);
+ http->connection_cnt++;
+
+ /* send "service unavailable" if we've reached the connection limit */
+ if (http->connection_max && http->connection_max < http->connection_cnt) {
+ struct evhttp_request *req;
+
+ if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL) {
+ evhttp_connection_free(evcon);
+ return;
+ }
- if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ req->evcon = evcon; /* the request owns the connection */
+ req->flags |= EVHTTP_REQ_OWN_CONNECTION;
+ req->kind = EVHTTP_REQUEST;
+ /* note, req->remote_host not needed since we don't read */
+
+ TAILQ_INSERT_TAIL(&evcon->requests, req, next);
+
+ /* send error to client */
+ evcon->state = EVCON_WRITING;
+ bufferevent_enable(evcon->bufev, EV_READ); /* enable close events */
+ evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
+
+ } else if (evhttp_associate_new_request_with_connection(evcon) == -1)
evhttp_connection_free(evcon);
}
}
/* Create a non-blocking socket and bind it */
-/* todo: rename this function */
static evutil_socket_t
-bind_socket_ai(struct evutil_addrinfo *ai, int reuse)
+create_bind_socket_nonblock(struct evutil_addrinfo *ai, int reuse)
{
evutil_socket_t fd;
/* just create an unbound socket */
if (address == NULL && port == 0)
- return bind_socket_ai(NULL, 0);
+ return create_bind_socket_nonblock(NULL, 0);
aitop = make_addrinfo(address, port);
if (aitop == NULL)
return (-1);
- fd = bind_socket_ai(aitop, reuse);
+ fd = create_bind_socket_nonblock(aitop, reuse);
evutil_freeaddrinfo(aitop);
char *userinfo; /* userinfo (typically username:pass), or NULL */
char *host; /* hostname, IP address, or NULL */
int port; /* port, or zero */
+#ifndef _WIN32
+ char *unixsocket; /* unix domain socket or NULL */
+#endif
char *path; /* path, or "". */
char *query; /* query, or NULL */
char *fragment; /* fragment or NULL */
}
static int
-parse_authority(struct evhttp_uri *uri, char *s, char *eos)
+parse_authority(struct evhttp_uri *uri, char *s, char *eos, unsigned *flags)
{
+ size_t len;
char *cp, *port;
+
EVUTIL_ASSERT(eos);
if (eos == s) {
uri->host = mm_strdup("");
} else {
cp = s;
}
+
+#ifndef _WIN32
+ if (*flags & EVHTTP_URI_UNIX_SOCKET && !strncmp(cp, "unix:", 5)) {
+ char *e = strchr(cp + 5, ':');
+ if (e) {
+ *e = '\0';
+ uri->unixsocket = mm_strdup(cp + 5);
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+#endif
+
/* Optionally, we end with ":port" */
for (port=eos-1; port >= cp && EVUTIL_ISDIGIT_(*port); --port)
;
/* Now, cp..eos holds the "host" port, which can be an IPv4Address,
* an IP-Literal, or a reg-name */
EVUTIL_ASSERT(eos >= cp);
+ len = eos-cp;
if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
/* IPv6address, IP-Literal, or junk. */
if (! bracket_addr_ok(cp, eos))
return -1;
+ if (*flags & EVHTTP_URI_HOST_STRIP_BRACKETS)
+ len = eos-cp-2;
} else {
/* Make sure the host part is ok. */
if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */
return -1;
}
- uri->host = mm_malloc(eos-cp+1);
+
+ uri->host = mm_malloc(len+1);
if (uri->host == NULL) {
event_warn("%s: malloc", __func__);
return -1;
}
- memcpy(uri->host, cp, eos-cp);
- uri->host[eos-cp] = '\0';
+ if (*cp == '[' && *flags & EVHTTP_URI_HOST_STRIP_BRACKETS) {
+ memcpy(uri->host, cp+1, len);
+ *flags |= _EVHTTP_URI_HOST_HAS_BRACKETS;
+ } else {
+ memcpy(uri->host, cp, len);
+ }
+ uri->host[len] = '\0';
return 0;
}
readp += 2;
authority = readp;
path = end_of_authority(readp);
- if (parse_authority(uri, authority, path) < 0)
+ if (parse_authority(uri, authority, path, &uri->flags) < 0)
goto err;
readp = path;
got_authority = 1;
return NULL;
}
+static struct evhttp_uri *
+evhttp_uri_parse_authority(char *source_uri, unsigned flags)
+{
+ struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
+ char *end;
+
+ if (uri == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto err;
+ }
+ uri->port = -1;
+ uri->flags = flags;
+
+ end = end_of_authority(source_uri);
+ if (parse_authority(uri, source_uri, end, &uri->flags) < 0)
+ goto err;
+
+ uri->path = mm_strdup("");
+ if (uri->path == NULL) {
+ event_warn("%s: strdup", __func__);
+ goto err;
+ }
+
+ return uri;
+err:
+ if (uri)
+ evhttp_uri_free(uri);
+ return NULL;
+}
+
void
evhttp_uri_free(struct evhttp_uri *uri)
{
URI_FREE_STR_(scheme);
URI_FREE_STR_(userinfo);
URI_FREE_STR_(host);
+#ifndef _WIN32
+ URI_FREE_STR_(unixsocket);
+#endif
URI_FREE_STR_(path);
URI_FREE_STR_(query);
URI_FREE_STR_(fragment);
URI_ADD_(scheme);
evbuffer_add(tmp, ":", 1);
}
+#ifndef _WIN32
+ if (uri->unixsocket) {
+ evbuffer_add(tmp, "//", 2);
+ if (uri->userinfo)
+ evbuffer_add_printf(tmp, "%s@", uri->userinfo);
+ evbuffer_add_printf(tmp, "unix:%s:", uri->unixsocket);
+ }
+ else
+#endif
if (uri->host) {
evbuffer_add(tmp, "//", 2);
if (uri->userinfo)
evbuffer_add_printf(tmp,"%s@", uri->userinfo);
- URI_ADD_(host);
+ if (uri->flags & _EVHTTP_URI_HOST_HAS_BRACKETS) {
+ evbuffer_add(tmp, "[", 1);
+ URI_ADD_(host);
+ evbuffer_add(tmp, "]", 1);
+ } else {
+ URI_ADD_(host);
+ }
if (uri->port >= 0)
evbuffer_add_printf(tmp,":%d", uri->port);
evbuffer_free(tmp);
return NULL;
}
- evbuffer_remove(tmp, buf, joined_size);
+ evbuffer_remove(tmp, buf, joined_size);
output = buf;
err:
{
return uri->host;
}
+#ifndef _WIN32
+const char *
+evhttp_uri_get_unixsocket(const struct evhttp_uri *uri)
+{
+ return uri->unixsocket;
+}
+#endif
int
evhttp_uri_get_port(const struct evhttp_uri *uri)
{
int
evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
{
+ size_t len = 0;
+
if (host) {
+ len = strlen(host);
+
if (host[0] == '[') {
- if (! bracket_addr_ok(host, host+strlen(host)))
+ if (! bracket_addr_ok(host, host+len))
return -1;
} else {
- if (! regname_ok(host, host+strlen(host)))
+ if (! regname_ok(host, host+len))
return -1;
}
}
- URI_SET_STR_(host);
+ if (host && host[0] == '[' && uri->flags & EVHTTP_URI_HOST_STRIP_BRACKETS) {
+ char *new_host;
+
+ len -= 2;
+ new_host = mm_realloc(uri->host, len+1);
+ if (!new_host) {
+ free(uri->host);
+ uri->host = NULL;
+ } else {
+ memcpy(new_host, host+1, len);
+ new_host[len] = '\0';
+ uri->host = new_host;
+ }
+ uri->flags |= _EVHTTP_URI_HOST_HAS_BRACKETS;
+ } else {
+ URI_SET_STR_(host);
+ uri->flags &= ~_EVHTTP_URI_HOST_HAS_BRACKETS;
+ }
+
+ return 0;
+}
+#ifndef _WIN32
+int
+evhttp_uri_set_unixsocket(struct evhttp_uri *uri, const char *unixsocket)
+{
+ URI_SET_STR_(unixsocket);
return 0;
}
+#endif
int
evhttp_uri_set_port(struct evhttp_uri *uri, int port)
{