#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"
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 *);
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");
{
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 */
if (evcon->address != NULL)
mm_free(evcon->address);
+#ifndef _WIN32
+ if (evcon->unixsocket != NULL)
+ mm_free(evcon->unixsocket);
+#endif
+
mm_free(evcon);
}
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.
(*evcon->closecb)(evcon, evcon->closecb_arg);
/** FIXME: manipulating with fd is unwanted */
- err = bufferevent_setfd(evcon->bufev, -1);
+ err = bufferevent_replacefd(evcon->bufev, -1);
EVUTIL_ASSERT(!err && "setfd");
/* we need to clean up any buffered data */
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;
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. */
}
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;
}
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->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, BEV_OPT_CLOSE_ON_FREE))) {
event_warn("%s: bufferevent_socket_new failed", __func__);
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;
return (-1);
}
- if (bufferevent_setfd(evcon->bufev, fd))
+ 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);
}
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;
int err;
- argument = strsep(&p, "&");
- value = argument;
+ value = strsep(&p, "&");
key = strsep(&value, "=");
if (flags & EVHTTP_URI_QUERY_NONCONFORMANT) {
if (value == NULL)
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;
- evhttp_get_request(http, nfd, peer_sa, peer_socklen);
+ 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, 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);
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) {
}
#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;
- 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));
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 */
} 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)
;
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)
{
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)
{
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)
{