#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
#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);
+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 *);
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);
}
struct evhttp_request *req)
{
const char *method;
- ev_uint16_t flags;
+ /* 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");
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];
- if (sizeof(date) - evutil_date_rfc1123(date, sizeof(date), NULL) > 0) {
+ if ((signed)sizeof(date) > evutil_date_rfc1123(date, sizeof(date), NULL)) {
evhttp_add_header(headers, "Date", date);
}
}
* 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 */
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) {
/*
__func__, EV_SIZE_ARG(total_len)));
#endif
- evhttp_connection_reset_(evcon);
+ evhttp_connection_reset_(evcon, 1);
}
break;
case EVCON_DISCONNECTED:
evhttp_connection_free(struct evhttp_connection *evcon)
{
struct evhttp_request *req;
- int need_close = 0;
/* 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)) {
&evcon->read_more_deferred_cb);
if (evcon->bufev != NULL) {
- need_close =
- !(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE);
- if (evcon->fd == -1)
- evcon->fd = bufferevent_getfd(evcon->bufev);
-
bufferevent_free(evcon->bufev);
}
- if (evcon->fd != -1) {
- shutdown(evcon->fd, EVUTIL_SHUT_WR);
- if (need_close)
- evutil_closesocket(evcon->fd);
- }
-
if (evcon->bind_address != NULL)
mm_free(evcon->bind_address);
if (evcon->address != NULL)
mm_free(evcon->address);
+#ifndef _WIN32
+ if (evcon->unixsocket != NULL)
+ mm_free(evcon->unixsocket);
+#endif
+
mm_free(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;
- bufferevent_setcb(evcon->bufev, NULL, NULL, NULL, NULL);
-
/* 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)
- evcon->fd = bufferevent_getfd(evcon->bufev);
+ /* inform interested parties about connection close */
+ if (evhttp_connected(evcon) && evcon->closecb != NULL)
+ (*evcon->closecb)(evcon, evcon->closecb_arg);
- if (evcon->fd != -1) {
- /* 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);
- evcon->fd = -1;
- }
- err = bufferevent_setfd(evcon->bufev, -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_input(evcon->bufev);
err = evbuffer_drain(tmp, -1);
EVUTIL_ASSERT(!err && "drain input");
+}
- evcon->flags &= ~EVHTTP_CON_READING_ERROR;
+/** 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_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
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
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;
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;
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) {
- /* TODO: make sure the other fields in ext_method are
+ /* 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, req, req->remote_host));
+ __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. */
}
}
if (type == EVHTTP_REQ_CONNECT) {
- if ((req->uri_elems = evhttp_uri_parse_authority(req->uri)) == NULL) {
+ if ((req->uri_elems = evhttp_uri_parse_authority(req->uri, 0)) == NULL) {
return -1;
}
} else {
static int
evhttp_method_may_have_body_(struct evhttp_connection *evcon, enum evhttp_cmd_type type)
{
- ev_uint16_t flags;
+ /* 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;
}
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) {
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, ev_uint16_t 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;
evcon->retry_cnt = evcon->retry_max = 0;
- if ((evcon->address = mm_strdup(address)) == NULL) {
- event_warn("%s: strdup failed", __func__);
- goto error;
- }
-
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;
}
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;
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);
}
- if (bufferevent_setfd(evcon->bufev, evcon->fd))
- return (-1);
- } else {
- if (bufferevent_setfd(evcon->bufev, -1))
+ if (bufferevent_replacefd(evcon->bufev, fd))
return (-1);
}
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
/*
* 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 */
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)
{
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 (flags & EVHTTP_URI_QUERY_NONCONFORMANT) {
if (value == NULL)
event_debug(("Query Param: %s -> %s\n", key, decoded_value));
if (flags & EVHTTP_URI_QUERY_LAST_VAL)
evhttp_remove_header(headers, key);
- evhttp_add_header_internal(headers, key, decoded_value);
+ err = evhttp_add_header_internal(headers, key, decoded_value);
mm_free(decoded_value);
+ if (err)
+ goto error;
}
result = 0;
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;
- evhttp_get_request(http, nfd, peer_sa, peer_socklen);
+ struct bufferevent *bev = NULL;
+ if (bound->bevcb)
+ bev = bound->bevcb(http->base, bound->bevcbarg);
+
+ evhttp_get_request(http, nfd, peer_sa, peer_socklen, bev);
}
int
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)
{
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_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);
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) {
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;
#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
if (sa->sa_family == AF_UNIX) {
- struct sockaddr_un *sun = (struct sockaddr_un *)sa;
- sun->sun_path[0] = '\0';
+ struct sockaddr_un *sa_un = (struct sockaddr_un *)sa;
+ sa_un->sun_path[0] = '\0';
}
#endif
- name_from_addr(sa, salen, &hostname, &portname);
- if (hostname == NULL || portname == NULL) {
- if (hostname) mm_free(hostname);
- if (portname) mm_free(portname);
- return (NULL);
+#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;
+
+ name_from_addr(sa, salen, &hostname, &portname);
+ if (hostname == NULL || portname == NULL) {
+ if (hostname) mm_free(hostname);
+ if (portname) mm_free(portname);
+ return (NULL);
+ }
- event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
- __func__, hostname, portname, EV_SOCK_ARG(fd)));
+ 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 (http->bevcb != NULL) {
- bev = (*http->bevcb)(http->base, http->bevcbarg);
+ /* 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->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_READING_FIRSTLINE;
- evcon->fd = fd;
-
- if (bufferevent_setfd(evcon->bufev, fd))
+ if (bufferevent_replacefd(evcon->bufev, fd))
goto err;
if (bufferevent_enable(evcon->bufev, EV_READ))
goto err;
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;
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));
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;
+ }
+
+ 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);
- if (evhttp_associate_new_request_with_connection(evcon) == -1)
+ } 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;
}
static struct evhttp_uri *
-evhttp_uri_parse_authority(char *source_uri)
+evhttp_uri_parse_authority(char *source_uri, unsigned flags)
{
struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
char *end;
goto err;
}
uri->port = -1;
- uri->flags = 0;
+ uri->flags = flags;
end = end_of_authority(source_uri);
- if (parse_authority(uri, source_uri, end) < 0)
+ if (parse_authority(uri, source_uri, end, &uri->flags) < 0)
goto err;
uri->path = mm_strdup("");
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)
{