uhttpd: - more robust handling of network failures on static file serving - support...
authorJo-Philipp Wich <jow@openwrt.org>
Sat, 14 Aug 2010 00:54:24 +0000 (00:54 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Sat, 14 Aug 2010 00:54:24 +0000 (00:54 +0000)
SVN-Revision: 22630

package/uhttpd/Makefile
package/uhttpd/files/uhttpd.config
package/uhttpd/files/uhttpd.init
package/uhttpd/src/uhttpd-cgi.c
package/uhttpd/src/uhttpd-cgi.h
package/uhttpd/src/uhttpd-file.c
package/uhttpd/src/uhttpd-utils.c
package/uhttpd/src/uhttpd-utils.h
package/uhttpd/src/uhttpd.c
package/uhttpd/src/uhttpd.h

index cb85ad8..de9eee9 100644 (file)
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=14
+PKG_RELEASE:=15
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_BUILD_DEPENDS := libcyassl liblua
index 534e8f8..a29910a 100644 (file)
@@ -27,6 +27,13 @@ config uhttpd main
        # Default is /cgi-bin
        option cgi_prefix       /cgi-bin
 
+       # List of extension->interpreter mappings.
+       # Files with an associated interpreter can
+       # be called outside of the CGI prefix and do
+       # not need to be executable.
+#      list interpreter        ".php=/usr/bin/php-cgi"
+#      list interpreter        ".cgi=/usr/bin/perl"
+
        # Lua url prefix and handler script.
        # Lua support is disabled if no prefix given.
 #      option lua_prefix       /luci
index 8221d85..f8f1754 100755 (executable)
@@ -56,7 +56,7 @@ start_instance()
 
        local cfg="$1"
        local realm="$(uci_get system.@system[0].hostname)"
-       local listen http https
+       local listen http https interpreter path
 
        append_arg "$cfg" home "-h"
        append_arg "$cfg" realm "-r" "${realm:-OpenWrt}"
@@ -78,6 +78,11 @@ start_instance()
                append UHTTPD_ARGS "-p $listen"
        done
 
+       config_get interpreter "$cfg" interpreter
+       for path in $interpreter; do
+               append UHTTPD_ARGS "-i $path"
+       done
+
        config_get https "$cfg" listen_https
        config_get UHTTPD_KEY  "$cfg" key  /etc/uhttpd.key
        config_get UHTTPD_CERT "$cfg" cert /etc/uhttpd.crt
index 0861249..f9dd981 100644 (file)
@@ -135,8 +135,10 @@ static int uh_cgi_error_500(struct client *cl, struct http_request *req, const c
 }
 
 
-void uh_cgi_request(struct client *cl, struct http_request *req, struct path_info *pi)
-{
+void uh_cgi_request(
+       struct client *cl, struct http_request *req,
+       struct path_info *pi, struct interpreter *ip
+) {
        int i, hdroff, bufoff;
        int hdrlen = 0;
        int buflen = 0;
@@ -199,9 +201,9 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                        dup2(rfd[1], 1);
                        dup2(wfd[0], 0);
 
-                       /* check for regular, world-executable file */
-                       if( (pi->stat.st_mode & S_IFREG) &&
-                           (pi->stat.st_mode & S_IXOTH)
+                       /* check for regular, world-executable file _or_ interpreter */
+                       if( ((pi->stat.st_mode & S_IFREG) &&
+                            (pi->stat.st_mode & S_IXOTH)) || (ip != NULL)
                        ) {
                                /* build environment */
                                clearenv();
@@ -320,14 +322,17 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                if( chdir(pi->root) )
                                        perror("chdir()");
 
-                               execl(pi->phys, pi->phys, NULL);
+                               if( ip != NULL )
+                                       execl(ip->path, ip->path, pi->phys, NULL);
+                               else
+                                       execl(pi->phys, pi->phys, NULL);
 
                                /* in case it fails ... */
                                printf(
                                        "Status: 500 Internal Server Error\r\n\r\n"
                                        "Unable to launch the requested CGI program:\n"
                                        "  %s: %s\n",
-                                               pi->phys, strerror(errno)
+                                               ip ? ip->path : pi->phys, strerror(errno)
                                );
                        }
 
index c90557d..cb84dae 100644 (file)
@@ -25,7 +25,8 @@
 #include <linux/limits.h>
 
 void uh_cgi_request(
-       struct client *cl, struct http_request *req, struct path_info *pi
+       struct client *cl, struct http_request *req,
+       struct path_info *pi, struct interpreter *ip
 );
 
 #endif
index ef9a77b..25a5f6e 100644 (file)
@@ -98,9 +98,9 @@ static char * uh_file_header_lookup(struct http_request *req, const char *name)
 }
 
 #define ensure_ret(x) \
-       do { if( x < 0 ) return; } while(0)
+       do { if( x < 0 ) return -1; } while(0)
 
-static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "Connection: close\r\n"));
 
@@ -110,26 +110,26 @@ static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req
                ensure_ret(uh_http_sendf(cl, NULL, "Last-Modified: %s\r\n", uh_file_unix2date(s->st_mtime)));
        }
 
-       ensure_ret(uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL))));
+       return uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL)));
 }
 
-static void uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 200 OK\r\n", req->version));
-       uh_file_response_ok_hdrs(cl, req, s);
+       return uh_file_response_ok_hdrs(cl, req, s);
 }
 
-static void uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 304 Not Modified\r\n", req->version));
-       uh_file_response_ok_hdrs(cl, req, s);
+       return uh_file_response_ok_hdrs(cl, req, s);
 }
 
-static void uh_file_response_412(struct client *cl, struct http_request *req)
+static int uh_file_response_412(struct client *cl, struct http_request *req)
 {
-       ensure_ret(uh_http_sendf(cl, NULL,
+       return uh_http_sendf(cl, NULL,
                "HTTP/%.1f 412 Precondition Failed\r\n"
-               "Connection: close\r\n", req->version));
+               "Connection: close\r\n", req->version);
 }
 
 static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s)
@@ -350,7 +350,7 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
                        uh_file_if_none_match(cl, req, &pi->stat)
                ) {
                        /* write status */
-                       uh_file_response_200(cl, req, &pi->stat);
+                       ensure_out(uh_file_response_200(cl, req, &pi->stat));
 
                        ensure_out(uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name)));
                        ensure_out(uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size));
@@ -385,7 +385,7 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
        else if( (pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists )
        {
                /* write status */
-               uh_file_response_200(cl, req, NULL);
+               ensure_out(uh_file_response_200(cl, req, NULL));
 
                if( req->version > 1.0 )
                        ensure_out(uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1));
index 4a1423c..e68926e 100644 (file)
@@ -605,8 +605,7 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
 }
 
 
-static char uh_realms[UH_LIMIT_AUTHREALMS * sizeof(struct auth_realm)] = { 0 };
-static int uh_realm_count = 0;
+static struct auth_realm *uh_realms = NULL;
 
 struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
 {
@@ -614,11 +613,8 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
        struct passwd *pwd;
        struct spwd *spwd;
 
-       if( uh_realm_count < UH_LIMIT_AUTHREALMS )
+       if((new = (struct auth_realm *)malloc(sizeof(struct auth_realm))) != NULL)
        {
-               new = (struct auth_realm *)
-                       &uh_realms[uh_realm_count * sizeof(struct auth_realm)];
-
                memset(new, 0, sizeof(struct auth_realm));
 
                memcpy(new->path, path,
@@ -655,9 +651,13 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
 
                if( new->pass[0] )
                {
-                       uh_realm_count++;
+                       new->next = uh_realms;
+                       uh_realms = new;
+
                        return new;
                }
+
+               free(new);
        }
 
        return NULL;
@@ -677,11 +677,8 @@ int uh_auth_check(
        protected = 0;
 
        /* check whether at least one realm covers the requested url */
-       for( i = 0; i < uh_realm_count; i++ )
+       for( realm = uh_realms; realm; realm = realm->next )
        {
-               realm = (struct auth_realm *)
-                       &uh_realms[i * sizeof(struct auth_realm)];
-
                rlen = strlen(realm->path);
 
                if( (plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen) )
@@ -721,11 +718,8 @@ int uh_auth_check(
                if( user && pass )
                {
                        /* find matching realm */
-                       for( i = 0, realm = NULL; i < uh_realm_count; i++ )
+                       for( realm = uh_realms; realm; realm = realm->next )
                        {
-                               realm = (struct auth_realm *)
-                                       &uh_realms[i * sizeof(struct auth_realm)];
-
                                rlen = strlen(realm->path);
 
                                if( (plen >= rlen) &&
@@ -769,22 +763,17 @@ int uh_auth_check(
 }
 
 
-static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
-static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
-
-static int uh_listener_count = 0;
-static int uh_client_count = 0;
-
+static struct listener *uh_listeners = NULL;
+static struct client *uh_clients = NULL;
 
 struct listener * uh_listener_add(int sock, struct config *conf)
 {
        struct listener *new = NULL;
        socklen_t sl;
 
-       if( uh_listener_count < UH_LIMIT_LISTENERS )
+       if( (new = (struct listener *)malloc(sizeof(struct listener))) != NULL )
        {
-               new = (struct listener *)
-                       &uh_listeners[uh_listener_count * sizeof(struct listener)];
+               memset(new, 0, sizeof(struct listener));
 
                new->socket = sock;
                new->conf   = conf;
@@ -794,24 +783,22 @@ struct listener * uh_listener_add(int sock, struct config *conf)
                memset(&(new->addr), 0, sl);
                getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
 
-               uh_listener_count++;
+               new->next = uh_listeners;
+               uh_listeners = new;
+
+               return new;
        }
 
-       return new;
+       return NULL;
 }
 
 struct listener * uh_listener_lookup(int sock)
 {
        struct listener *cur = NULL;
-       int i;
-
-       for( i = 0; i < uh_listener_count; i++ )
-       {
-               cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
 
+       for( cur = uh_listeners; cur; cur = cur->next )
                if( cur->socket == sock )
                        return cur;
-       }
 
        return NULL;
 }
@@ -822,10 +809,9 @@ struct client * uh_client_add(int sock, struct listener *serv)
        struct client *new = NULL;
        socklen_t sl;
 
-       if( uh_client_count < UH_LIMIT_CLIENTS )
+       if( (new = (struct client *)malloc(sizeof(struct client))) != NULL )
        {
-               new = (struct client *)
-                       &uh_clients[uh_client_count * sizeof(struct client)];
+               memset(new, 0, sizeof(struct client));
 
                new->socket = sock;
                new->server = serv;
@@ -840,7 +826,8 @@ struct client * uh_client_add(int sock, struct listener *serv)
                memset(&(new->servaddr), 0, sl);
                getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
 
-               uh_client_count++;
+               new->next = uh_clients;
+               uh_clients = new;
        }
 
        return new;
@@ -849,30 +836,72 @@ struct client * uh_client_add(int sock, struct listener *serv)
 struct client * uh_client_lookup(int sock)
 {
        struct client *cur = NULL;
-       int i;
-
-       for( i = 0; i < uh_client_count; i++ )
-       {
-               cur = (struct client *) &uh_clients[i * sizeof(struct client)];
 
+       for( cur = uh_clients; cur; cur = cur->next )
                if( cur->socket == sock )
                        return cur;
-       }
 
        return NULL;
 }
 
 void uh_client_remove(int sock)
 {
-       struct client *del = uh_client_lookup(sock);
+       struct client *cur = NULL;
+       struct client *prv = NULL;
+
+       for( cur = uh_clients; cur; prv = cur, cur = cur->next )
+       {
+               if( cur->socket == sock )
+               {
+                       if( prv )
+                               prv->next = cur->next;
+                       else
+                               uh_clients = cur->next;
+
+                       free(cur);
+                       break;
+               }
+       }
+}
+
+
+#ifdef HAVE_CGI
+static struct interpreter *uh_interpreters = NULL;
+
+struct interpreter * uh_interpreter_add(const char *extn, const char *path)
+{
+       struct interpreter *new = NULL;
 
-       if( del )
+       if( (new = (struct interpreter *)
+                       malloc(sizeof(struct interpreter))) != NULL )
        {
-               memmove(del, del + 1,
-                       sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
+               memset(new, 0, sizeof(struct interpreter));
+
+               memcpy(new->extn, extn, min(strlen(extn), sizeof(new->extn)-1));
+               memcpy(new->path, path, min(strlen(path), sizeof(new->path)-1));
+
+               new->next = uh_interpreters;
+               uh_interpreters = new;
 
-               uh_client_count--;
+               return new;
        }
+
+       return NULL;
 }
 
+struct interpreter * uh_interpreter_lookup(const char *path)
+{
+       struct interpreter *cur = NULL;
+       const char *e;
+
+       for( cur = uh_interpreters; cur; cur = cur->next )
+       {
+               e = &path[max(strlen(path) - strlen(cur->extn), 0)];
 
+               if( !strcmp(e, cur->extn) )
+                       return cur;
+       }
+
+       return NULL;
+}
+#endif
index 1b18265..95535d6 100644 (file)
@@ -101,4 +101,9 @@ struct client * uh_client_add(int sock, struct listener *serv);
 struct client * uh_client_lookup(int sock);
 void uh_client_remove(int sock);
 
+#ifdef HAVE_CGI
+struct interpreter * uh_interpreter_add(const char *extn, const char *path);
+struct interpreter * uh_interpreter_lookup(const char *path);
+#endif
+
 #endif
index 764ff7d..6406e45 100644 (file)
@@ -51,8 +51,8 @@ static void uh_config_parse(struct config *conf)
 {
        FILE *c;
        char line[512];
-       char *user = NULL;
-       char *pass = NULL;
+       char *col1 = NULL;
+       char *col2 = NULL;
        char *eol  = NULL;
 
        const char *path = conf->file ? conf->file : "/etc/httpd.conf";
@@ -66,35 +66,51 @@ static void uh_config_parse(struct config *conf)
                {
                        if( (line[0] == '/') && (strchr(line, ':') != NULL) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(pass = strchr(user, ':')) || (*pass++ = 0) ||
-                                       !(eol = strchr(pass, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+                                       !(eol = strchr(col2, '\n')) || (*eol++  = 0) )
                                                continue;
 
-                               if( !uh_auth_add(line, user, pass) )
+                               if( !uh_auth_add(line, col1, col2) )
                                {
                                        fprintf(stderr,
                                                "Notice: No password set for user %s, ignoring "
-                                               "authentication on %s\n", user, line
+                                               "authentication on %s\n", col1, line
                                        );
                                }
                        }
                        else if( !strncmp(line, "I:", 2) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
                                        continue;
 
-                               conf->index_file = strdup(user);
+                               conf->index_file = strdup(col1);
                        }
                        else if( !strncmp(line, "E404:", 5) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
                                                continue;
 
-                               conf->error_handler = strdup(user);
+                               conf->error_handler = strdup(col1);
                        }
+#ifdef HAVE_CGI
+                       else if( (line[0] == '.') && (strchr(line, ':') != NULL) )
+                       {
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
+                                               continue;
+
+                               if( !uh_interpreter_add(line, col1) )
+                               {
+                                       fprintf(stderr,
+                                               "Unable to add interpreter %s for extension %s: "
+                                               "Out of memory\n", col1, line
+                                       );
+                               }
+                       }
+#endif
                }
 
                fclose(c);
@@ -162,11 +178,7 @@ static int uh_socket_bind(
                /* add listener to global list */
                if( ! (l = uh_listener_add(sock, conf)) )
                {
-                       fprintf(stderr,
-                               "uh_listener_add(): Can not create more than "
-                               "%i listen sockets\n", UH_LIMIT_LISTENERS
-                       );
-
+                       fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
                        goto error;
                }
 
@@ -397,6 +409,7 @@ static struct http_request * uh_http_header_recv(struct client *cl)
        return NULL;
 }
 
+#if defined(HAVE_LUA) || defined(HAVE_CGI)
 static int uh_path_match(const char *prefix, const char *url)
 {
        if( (strstr(url, prefix) == url) &&
@@ -409,23 +422,193 @@ static int uh_path_match(const char *prefix, const char *url)
 
        return 0;
 }
+#endif
 
+static void uh_dispatch_request(
+       struct client *cl, struct http_request *req, struct path_info *pin
+) {
+#ifdef HAVE_CGI
+       struct interpreter *ipr = NULL;
 
-int main (int argc, char **argv)
+       if( uh_path_match(cl->server->conf->cgi_prefix, pin->name) ||
+               (ipr = uh_interpreter_lookup(pin->phys)) )
+       {
+               uh_cgi_request(cl, req, pin, ipr);
+       }
+       else
+#endif
+       {
+               uh_file_request(cl, req, pin);
+       }
+}
+
+static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
 {
+       /* master file descriptor list */
+       fd_set used_fds, read_fds;
+
+       /* working structs */
+       struct http_request *req;
+       struct path_info *pin;
+       struct client *cl;
+
+       /* maximum file descriptor number */
+       int new_fd, cur_fd = 0;
+
+       /* clear the master and temp sets */
+       FD_ZERO(&used_fds);
+       FD_ZERO(&read_fds);
+
+       /* backup server descriptor set */
+       used_fds = serv_fds;
+
+       /* loop */
+       while(run)
+       {
+               /* create a working copy of the used fd set */
+               read_fds = used_fds;
+
+               /* sleep until socket activity */
+               if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
+               {
+                       perror("select()");
+                       exit(1);
+               }
+
+               /* run through the existing connections looking for data to be read */
+               for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
+               {
+                       /* is a socket managed by us */
+                       if( FD_ISSET(cur_fd, &read_fds) )
+                       {
+                               /* is one of our listen sockets */
+                               if( FD_ISSET(cur_fd, &serv_fds) )
+                               {
+                                       /* handle new connections */
+                                       if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
+                                       {
+                                               /* add to global client list */
+                                               if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
+                                               {
+#ifdef HAVE_TLS
+                                                       /* setup client tls context */
+                                                       if( conf->tls )
+                                                               conf->tls_accept(cl);
+#endif
+
+                                                       /* add client socket to global fdset */
+                                                       FD_SET(new_fd, &used_fds);
+                                                       fd_cloexec(new_fd);
+                                                       max_fd = max(max_fd, new_fd);
+                                               }
+
+                                               /* insufficient resources */
+                                               else
+                                               {
+                                                       fprintf(stderr,
+                                                               "uh_client_add(): Cannot allocate memory\n");
+
+                                                       close(new_fd);
+                                               }
+                                       }
+                               }
+
+                               /* is a client socket */
+                               else
+                               {
+                                       if( ! (cl = uh_client_lookup(cur_fd)) )
+                                       {
+                                               /* this should not happen! */
+                                               fprintf(stderr,
+                                                       "uh_client_lookup(): No entry for fd %i!\n",
+                                                       cur_fd);
+
+                                               goto cleanup;
+                                       }
+
+                                       /* parse message header */
+                                       if( (req = uh_http_header_recv(cl)) != NULL )
+                                       {
+                                               /* RFC1918 filtering required? */
+                                               if( conf->rfc1918_filter &&
+                                                   sa_rfc1918(&cl->peeraddr) &&
+                                                   !sa_rfc1918(&cl->servaddr) )
+                                               {
+                                                       uh_http_sendhf(cl, 403, "Forbidden",
+                                                               "Rejected request from RFC1918 IP "
+                                                               "to public server address");
+                                               }
+                                               else
 #ifdef HAVE_LUA
-       /* Lua runtime */
-       lua_State *L = NULL;
+                                               /* Lua request? */
+                                               if( conf->lua_state &&
+                                                   uh_path_match(conf->lua_prefix, req->url) )
+                                               {
+                                                       conf->lua_request(cl, req, conf->lua_state);
+                                               }
+                                               else
 #endif
+                                               /* dispatch request */
+                                               if( (pin = uh_path_lookup(cl, req->url)) != NULL )
+                                               {
+                                                       /* auth ok? */
+                                                       if( uh_auth_check(cl, req, pin) )
+                                                               uh_dispatch_request(cl, req, pin);
+                                               }
 
+                                               /* 404 */
+                                               else
+                                               {
+                                                       /* Try to invoke an error handler */
+                                                       pin = uh_path_lookup(cl, conf->error_handler);
+
+                                                       if( pin && uh_auth_check(cl, req, pin) )
+                                                       {
+                                                               req->redirect_status = 404;
+                                                               uh_dispatch_request(cl, req, pin);
+                                                       }
+                                                       else
+                                                       {
+                                                               uh_http_sendhf(cl, 404, "Not Found",
+                                                                       "No such file or directory");
+                                                       }
+                                               }
+                                       }
+
+#ifdef HAVE_TLS
+                                       /* free client tls context */
+                                       if( conf->tls )
+                                               conf->tls_close(cl);
+#endif
+
+                                       cleanup:
+
+                                       /* close client socket */
+                                       close(cur_fd);
+                                       FD_CLR(cur_fd, &used_fds);
+
+                                       /* remove from global client list */
+                                       uh_client_remove(cur_fd);
+                               }
+                       }
+               }
+       }
+
+#ifdef HAVE_LUA
+       /* destroy the Lua state */
+       if( conf->lua_state != NULL )
+               conf->lua_close(conf->lua_state);
+#endif
+}
+
+
+int main (int argc, char **argv)
+{
        /* master file descriptor list */
        fd_set used_fds, serv_fds, read_fds;
 
        /* working structs */
        struct addrinfo hints;
-       struct http_request *req;
-       struct path_info *pin;
-       struct client *cl;
        struct sigaction sa;
        struct config conf;
 
@@ -433,7 +616,7 @@ int main (int argc, char **argv)
        sigset_t ss;
 
        /* maximum file descriptor number */
-       int new_fd, cur_fd, max_fd = 0;
+       int cur_fd, max_fd = 0;
 
 #ifdef HAVE_TLS
        int tls = 0;
@@ -525,7 +708,7 @@ int main (int argc, char **argv)
 #endif
 
        while( (opt = getopt(argc, argv,
-               "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+               "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:")) > 0
        ) {
                switch(opt)
                {
@@ -658,6 +841,21 @@ int main (int argc, char **argv)
                        case 'x':
                                conf.cgi_prefix = optarg;
                                break;
+
+                       /* interpreter */
+                       case 'i':
+                               if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
+                               {
+                                       *port++ = 0;
+                                       uh_interpreter_add(optarg, port);
+                               }
+                               else
+                               {
+                                       fprintf(stderr, "Error: Invalid interpreter: %s\n",
+                                               optarg);
+                                       exit(1);
+                               }
+                               break;
 #endif
 
 #ifdef HAVE_LUA
@@ -740,6 +938,7 @@ int main (int argc, char **argv)
 #endif
 #ifdef HAVE_CGI
                                        "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
+                                       "       -i .ext=path    Use interpreter at path for files with the given extension\n"
 #endif
 #if defined(HAVE_CGI) || defined(HAVE_LUA)
                                        "       -t seconds      CGI and Lua script timeout in seconds, default is 60\n"
@@ -830,7 +1029,7 @@ int main (int argc, char **argv)
                        if( ! conf.lua_prefix )
                                conf.lua_prefix = "/lua";
 
-                       L = conf.lua_init(conf.lua_handler);
+                       conf.lua_state = conf.lua_init(conf.lua_handler);
                }
        }
 #endif
@@ -865,166 +1064,13 @@ int main (int argc, char **argv)
                }
        }
 
-       /* backup server descriptor set */
-       used_fds = serv_fds;
-
-       /* loop */
-       while(run)
-       {
-               /* create a working copy of the used fd set */
-               read_fds = used_fds;
-
-               /* sleep until socket activity */
-               if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
-               {
-                       perror("select()");
-                       exit(1);
-               }
-
-               /* run through the existing connections looking for data to be read */
-               for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
-               {
-                       /* is a socket managed by us */
-                       if( FD_ISSET(cur_fd, &read_fds) )
-                       {
-                               /* is one of our listen sockets */
-                               if( FD_ISSET(cur_fd, &serv_fds) )
-                               {
-                                       /* handle new connections */
-                                       if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
-                                       {
-                                               /* add to global client list */
-                                               if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
-                                               {
-#ifdef HAVE_TLS
-                                                       /* setup client tls context */
-                                                       if( conf.tls )
-                                                               conf.tls_accept(cl);
-#endif
-
-                                                       /* add client socket to global fdset */
-                                                       FD_SET(new_fd, &used_fds);
-                                                       fd_cloexec(new_fd);
-                                                       max_fd = max(max_fd, new_fd);
-                                               }
-
-                                               /* insufficient resources */
-                                               else
-                                               {
-                                                       fprintf(stderr,
-                                                               "uh_client_add(): Can not manage more than "
-                                                               "%i client sockets, connection dropped\n",
-                                                               UH_LIMIT_CLIENTS
-                                                       );
-
-                                                       close(new_fd);
-                                               }
-                                       }
-                               }
-
-                               /* is a client socket */
-                               else
-                               {
-                                       if( ! (cl = uh_client_lookup(cur_fd)) )
-                                       {
-                                               /* this should not happen! */
-                                               fprintf(stderr,
-                                                       "uh_client_lookup(): No entry for fd %i!\n",
-                                                       cur_fd);
-
-                                               goto cleanup;
-                                       }
-
-                                       /* parse message header */
-                                       if( (req = uh_http_header_recv(cl)) != NULL )
-                                       {
-                                               /* RFC1918 filtering required? */
-                                               if( conf.rfc1918_filter && sa_rfc1918(&cl->peeraddr) &&
-                                                   !sa_rfc1918(&cl->servaddr) )
-                                               {
-                                                       uh_http_sendhf(cl, 403, "Forbidden",
-                                                               "Rejected request from RFC1918 IP to public server address");
-                                               }
-                                               else
-#ifdef HAVE_LUA
-                                               /* Lua request? */
-                                               if( L && uh_path_match(conf.lua_prefix, req->url) )
-                                               {
-                                                       conf.lua_request(cl, req, L);
-                                               }
-                                               else
-#endif
-                                               /* dispatch request */
-                                               if( (pin = uh_path_lookup(cl, req->url)) != NULL )
-                                               {
-                                                       /* auth ok? */
-                                                       if( uh_auth_check(cl, req, pin) )
-                                                       {
-#ifdef HAVE_CGI
-                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
-                                                               {
-                                                                       uh_cgi_request(cl, req, pin);
-                                                               }
-                                                               else
-#endif
-                                                               {
-                                                                       uh_file_request(cl, req, pin);
-                                                               }
-                                                       }
-                                               }
-
-                                               /* 404 */
-                                               else
-                                               {
-                                                       /* Try to invoke an error handler */
-                                                       pin = uh_path_lookup(cl, conf.error_handler);
-
-                                                       if( pin && uh_auth_check(cl, req, pin) )
-                                                       {
-                                                               req->redirect_status = 404;
-
-#ifdef HAVE_CGI
-                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
-                                                               {
-                                                                       uh_cgi_request(cl, req, pin);
-                                                               }
-                                                               else
-#endif
-                                                               {
-                                                                       uh_file_request(cl, req, pin);
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               uh_http_sendhf(cl, 404, "Not Found",
-                                                                       "No such file or directory");
-                                                       }
-                                               }
-                                       }
-
-#ifdef HAVE_TLS
-                                       /* free client tls context */
-                                       if( conf.tls )
-                                               conf.tls_close(cl);
-#endif
-
-                                       cleanup:
-
-                                       /* close client socket */
-                                       close(cur_fd);
-                                       FD_CLR(cur_fd, &used_fds);
-
-                                       /* remove from global client list */
-                                       uh_client_remove(cur_fd);
-                               }
-                       }
-               }
-       }
+       /* server main loop */
+       uh_mainloop(&conf, serv_fds, max_fd);
 
 #ifdef HAVE_LUA
        /* destroy the Lua state */
-       if( L != NULL )
-               conf.lua_close(L);
+       if( conf.lua_state != NULL )
+               conf.lua_close(conf.lua_state);
 #endif
 
        return 0;
index fd2176e..78cca7b 100644 (file)
@@ -48,9 +48,7 @@
 #define UH_LIMIT_MSGHEAD       4096
 #define UH_LIMIT_HEADERS       64
 
-#define UH_LIMIT_LISTENERS     16
 #define UH_LIMIT_CLIENTS       64
-#define UH_LIMIT_AUTHREALMS    8
 
 #define UH_HTTP_MSG_GET                0
 #define UH_HTTP_MSG_HEAD       1
@@ -58,6 +56,7 @@
 
 struct listener;
 struct client;
+struct interpreter;
 struct http_request;
 
 struct config {
@@ -76,6 +75,7 @@ struct config {
 #ifdef HAVE_LUA
        char *lua_prefix;
        char *lua_handler;
+       lua_State *lua_state;
        lua_State * (*lua_init) (const char *handler);
        void (*lua_close) (lua_State *L);
        void (*lua_request) (struct client *cl, struct http_request *req, lua_State *L);
@@ -105,6 +105,7 @@ struct listener {
 #ifdef HAVE_TLS
        SSL_CTX *tls;
 #endif
+       struct listener *next;
 };
 
 struct client {
@@ -117,12 +118,14 @@ struct client {
 #ifdef HAVE_TLS
        SSL *tls;
 #endif
+       struct client *next;
 };
 
 struct auth_realm {
        char path[PATH_MAX];
        char user[32];
        char pass[128];
+       struct auth_realm *next;
 };
 
 struct http_request {
@@ -140,5 +143,13 @@ struct http_response {
        char *headers[UH_LIMIT_HEADERS];
 };
 
+#ifdef HAVE_CGI
+struct interpreter {
+       char path[PATH_MAX];
+       char extn[32];
+       struct interpreter *next;
+};
+#endif
+
 #endif