From f2a81fbc67b90551a50e07f4af5a266ac081ccd4 Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Mon, 5 May 2008 07:17:05 +0000 Subject: [PATCH] add support for virtual http hosts; no tests yet svn:r771 --- ChangeLog | 1 + configure.in | 4 ++-- evhttp.h | 35 +++++++++++++++++++++++++++++ http-internal.h | 7 ++++++ http.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d5d7231f..de35c9b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -88,6 +88,7 @@ Changes in current version: o reduce system calls for getting current time by caching it. o separate signal events from io events; making the code less complex. o support for periodic timeouts + o support for virtual HTTP hosts. Changes in 1.4.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. diff --git a/configure.in b/configure.in index 70a8e1af..cab67a0a 100644 --- a/configure.in +++ b/configure.in @@ -53,7 +53,7 @@ AM_CONDITIONAL(ZLIB_REGRESS, [test "$have_zlib" != "no"]) dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h sys/uio.h) +AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h sys/uio.h fnmatch.h) if test "x$ac_cv_header_sys_queue_h" = "xyes"; then AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h) AC_EGREP_CPP(yes, @@ -146,7 +146,7 @@ AC_C_INLINE AC_HEADER_TIME dnl Checks for library functions. -AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop signal sigaction strtoll) +AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop signal sigaction strtoll fnmatch) AC_CHECK_SIZEOF(long) diff --git a/evhttp.h b/evhttp.h index 6f53d2e4..eba72721 100644 --- a/evhttp.h +++ b/evhttp.h @@ -138,6 +138,41 @@ int evhttp_del_cb(struct evhttp *, const char *); void evhttp_set_gencb(struct evhttp *http, void (*cb)(struct evhttp_request *, void *), void *arg); +/** + Adds a virtual host to the http server. + + A virtual host is a newly initialized evhttp object that has request + callbacks set on it via evhttp_set_cb() or evhttp_set_gencb(). It + most not have any listing sockets associated with it. + + If the virtual host has not been removed by the time that evhttp_free() + is called on the main http server, it will be automatically freed, too. + + It is possible to have hierarchical vhosts. For example: A vhost + with the pattern *.example.com may have other vhosts with patterns + foo.example.com and bar.example.com associated with it. + + @param http the evhttp object to which to add a virtual host + @param pattern the glob pattern against which the hostname is matched. + The match is case insensitive and follows otherwise regular shell + matching. + @param vhost the virtual host to add the regular http server. + @return 0 on success, -1 on failure + @see evhttp_remove_virtual_host() +*/ +int evhttp_add_virtual_host(struct evhttp* http, const char *pattern, + struct evhttp* vhost); + +/** + Removes a virtual host from the http server. + + @param http the evhttp object from which to remove the virtual host + @param vhost the virtual host to remove from the regular http server. + @return 0 on success, -1 on failure + @see evhttp_add_virtual_host() +*/ +int evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost); + /** * Set the timeout for an HTTP request. * diff --git a/http-internal.h b/http-internal.h index c5bd5ddf..272a2a1c 100644 --- a/http-internal.h +++ b/http-internal.h @@ -99,11 +99,18 @@ struct evhttp_bound_socket { }; struct evhttp { + TAILQ_ENTRY(evhttp) next; + TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; struct evconq connections; + TAILQ_HEAD(vhostsq, evhttp) virtualhosts; + + /* NULL if this server is not a vhost */ + char *vhost_pattern; + int timeout; void (*gencb)(struct evhttp_request *req, void *); diff --git a/http.c b/http.c index 9f0c4f01..217a6dc5 100644 --- a/http.c +++ b/http.c @@ -79,6 +79,9 @@ #ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_FNMATCH_H +#include +#endif #undef timeout_pending #undef timeout_initialized @@ -1891,12 +1894,26 @@ evhttp_handle_request(struct evhttp_request *req, void *arg) { struct evhttp *http = arg; struct evhttp_cb *cb = NULL; + const char *hostname; if (req->uri == NULL) { evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } + /* handle potential virtual hosts */ + hostname = evhttp_find_header(req->input_headers, "Host"); + if (hostname != NULL) { + struct evhttp *vhost; + TAILQ_FOREACH(vhost, &http->virtualhosts, next) { + if (fnmatch(vhost->vhost_pattern, hostname, + FNM_CASEFOLD) == 0) { + evhttp_handle_request(req, vhost); + return; + } + } + } + if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) { (*cb->cb)(req, cb->cbarg); return; @@ -2017,6 +2034,7 @@ evhttp_new_object(void) TAILQ_INIT(&http->sockets); TAILQ_INIT(&http->callbacks); TAILQ_INIT(&http->connections); + TAILQ_INIT(&http->virtualhosts); return (http); } @@ -2054,6 +2072,7 @@ evhttp_free(struct evhttp* http) struct evhttp_cb *http_cb; struct evhttp_connection *evcon; struct evhttp_bound_socket *bound; + struct evhttp* vhost; evutil_socket_t fd; /* Remove the accepting part */ @@ -2077,10 +2096,51 @@ evhttp_free(struct evhttp* http) mm_free(http_cb->what); mm_free(http_cb); } + + while ((vhost = TAILQ_FIRST(&http->virtualhosts)) != NULL) { + TAILQ_REMOVE(&http->virtualhosts, vhost, next); + + evhttp_free(vhost); + } + + if (http->vhost_pattern != NULL) + mm_free(http->vhost_pattern); mm_free(http); } +int +evhttp_add_virtual_host(struct evhttp* http, const char *pattern, + struct evhttp* vhost) +{ + /* a vhost can only be a vhost once and should not have bound sockets */ + if (vhost->vhost_pattern != NULL || + TAILQ_FIRST(&vhost->sockets) != NULL) + return (-1); + + vhost->vhost_pattern = mm_strdup(pattern); + if (vhost->vhost_pattern == NULL) + return (-1); + + TAILQ_INSERT_TAIL(&http->virtualhosts, vhost, next); + + return (0); +} + +int +evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost) +{ + if (vhost->vhost_pattern == NULL) + return (-1); + + TAILQ_REMOVE(&http->virtualhosts, vhost, next); + + mm_free(vhost->vhost_pattern); + vhost->vhost_pattern = NULL; + + return (0); +} + void evhttp_set_timeout(struct evhttp* http, int timeout_in_secs) {