add preliminary cgi support, needs fixing for close handling
authorFelix Fietkau <nbd@openwrt.org>
Tue, 1 Jan 2013 22:28:19 +0000 (23:28 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 1 Jan 2013 22:28:19 +0000 (23:28 +0100)
CMakeLists.txt
cgi.c [new file with mode: 0644]
client.c
file.c
main.c
proc.c [new file with mode: 0644]
relay.c [new file with mode: 0644]
uhttpd.h

index c542df69685c5edb22c6756487feabfb4d50df6a..48addacbff76317b57d360f4b3f602202e4f283a 100644 (file)
@@ -1,12 +1,12 @@
 cmake_minimum_required(VERSION 2.6)
 
 PROJECT(uhttpd C)
-ADD_DEFINITIONS(-Os -Wall -Werror -Wmissing-declarations --std=gnu99 -g3)
+ADD_DEFINITIONS(-O2 -Wall -Werror -Wmissing-declarations --std=gnu99 -g3)
 
 IF(APPLE)
   INCLUDE_DIRECTORIES(/opt/local/include)
   LINK_DIRECTORIES(/opt/local/lib)
 ENDIF()
 
-ADD_EXECUTABLE(uhttpd main.c listen.c client.c utils.c file.c auth.c)
+ADD_EXECUTABLE(uhttpd main.c listen.c client.c utils.c file.c auth.c cgi.c relay.c proc.c)
 TARGET_LINK_LIBRARIES(uhttpd ubox ubus)
diff --git a/cgi.c b/cgi.c
new file mode 100644 (file)
index 0000000..cfd71fb
--- /dev/null
+++ b/cgi.c
@@ -0,0 +1,115 @@
+/*
+ * uhttpd - Tiny single-threaded httpd
+ *
+ *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <libubox/blobmsg.h>
+#include "uhttpd.h"
+
+static LIST_HEAD(interpreters);
+
+void uh_interpreter_add(const char *ext, const char *path)
+{
+       struct interpreter *in;
+       char *new_ext, *new_path;
+
+       in = calloc_a(sizeof(*in),
+               &new_ext, strlen(ext) + 1,
+               &new_path, strlen(path) + 1);
+
+       in->ext = strcpy(new_ext, ext);
+       in->path = strcpy(new_path, path);
+       list_add_tail(&in->list, &interpreters);
+}
+
+static void cgi_main(struct client *cl, struct path_info *pi, int fd)
+{
+       struct interpreter *ip = pi->ip;
+       struct env_var *var;
+
+       dup2(fd, 0);
+       dup2(fd, 1);
+       close(fd);
+       clearenv();
+       setenv("PATH", conf.cgi_path, 1);
+
+       for (var = uh_get_process_vars(cl, pi); var->name; var++) {
+               if (!var->value)
+                       continue;
+
+               setenv(var->name, var->value, 1);
+       }
+
+       chdir(pi->root);
+
+       if (ip)
+               execl(ip->path, ip->path, pi->phys, NULL);
+       else
+               execl(pi->phys, pi->phys, NULL);
+
+       printf("Status: 500 Internal Server Error\r\n\r\n"
+              "Unable to launch the requested CGI program:\n"
+              "  %s: %s\n", ip ? ip->path : pi->phys, strerror(errno));
+}
+
+static void cgi_handle_request(struct client *cl, const char *url, struct path_info *pi)
+{
+       unsigned int mode = S_IFREG | S_IXOTH;
+
+       if (!pi->ip && !((pi->stat.st_mode & mode) == mode)) {
+               uh_client_error(cl, 403, "Forbidden",
+                               "You don't have permission to access %s on this server.",
+                               url);
+               return;
+       }
+
+       if (!uh_create_process(cl, pi, cgi_main)) {
+               uh_client_error(cl, 500, "Internal Server Error",
+                               "Failed to create CGI process: %s", strerror(errno));
+               return;
+       }
+
+       return;
+}
+
+static bool check_cgi_path(struct path_info *pi, const char *url)
+{
+       struct interpreter *ip;
+       const char *path = pi->phys;
+       int path_len = strlen(path);
+
+       list_for_each_entry(ip, &interpreters, list) {
+               int len = strlen(ip->ext);
+
+               if (len >= path_len)
+                       continue;
+
+               if (strcmp(path + path_len - len, ip->ext) != 0)
+                       continue;
+
+               pi->ip = ip;
+               return true;
+       }
+
+       pi->ip = NULL;
+       return uh_path_match(conf.cgi_prefix, url);
+}
+
+struct dispatch_handler cgi_dispatch = {
+       .check_path = check_cgi_path,
+       .handle_request = cgi_handle_request,
+};
index 6e75c5bfcf279d0cba4e924a250493b0632dffd5..483a1a400f8f38afea6acee1edd53ae414f9f33d 100644 (file)
--- a/client.c
+++ b/client.c
@@ -174,7 +174,7 @@ static bool client_init_cb(struct client *cl, char *buf, int len)
 
 static void client_header_complete(struct client *cl)
 {
-       uh_handle_file_request(cl);
+       uh_handle_request(cl);
 }
 
 static int client_parse_header(struct client *cl, char *data)
diff --git a/file.c b/file.c
index 0fb17d61b98209e985befcdf7a3a2e415e5ccc38..4a10eaa2f27f15f7a290bf3b966693d75d4c9af1 100644 (file)
--- a/file.c
+++ b/file.c
@@ -32,6 +32,7 @@
 
 static char _tag[128];
 static LIST_HEAD(index_files);
+static LIST_HEAD(dispatch_handlers);
 
 struct index_file {
        struct list_head list;
@@ -577,7 +578,7 @@ static void uh_file_data(struct client *cl, struct path_info *pi, int fd)
        file_write_cb(cl);
 }
 
-static void uh_file_request(struct client *cl, struct path_info *pi, const char *url)
+static void uh_file_request(struct client *cl, const char *url, struct path_info *pi)
 {
        static const struct blobmsg_policy hdr_policy[__HDR_MAX] = {
                [HDR_IF_MODIFIED_SINCE] = { "if-modified-since", BLOBMSG_TYPE_STRING },
@@ -611,33 +612,78 @@ static void uh_file_request(struct client *cl, struct path_info *pi, const char
                goto error;
        }
 
+       cl->dispatch.file.hdr = NULL;
        return;
 
 error:
        uh_client_error(cl, 403, "Forbidden",
                        "You don't have permission to access %s on this server.",
                        url);
+       cl->dispatch.file.hdr = NULL;
+}
+
+void uh_dispatch_add(struct dispatch_handler *d)
+{
+       list_add_tail(&d->list, &dispatch_handlers);
+}
+
+static struct dispatch_handler *
+dispatch_find(const char *url, struct path_info *pi)
+{
+       struct dispatch_handler *d;
+
+       list_for_each_entry(d, &dispatch_handlers, list) {
+               if (pi) {
+                       if (d->check_url)
+                               continue;
+
+                       if (d->check_path(pi, url))
+                               return d;
+               } else {
+                       if (d->check_path)
+                               continue;
+
+                       if (d->check_url(url))
+                               return d;
+               }
+       }
+
+       return NULL;
 }
 
 static bool __handle_file_request(struct client *cl, const char *url)
 {
+       struct dispatch_handler *d;
        struct path_info *pi;
 
        pi = uh_path_lookup(cl, url);
        if (!pi)
                return false;
 
-       if (!pi->redirected) {
-               uh_file_request(cl, pi, url);
-               cl->dispatch.file.hdr = NULL;
-       }
+       if (pi->redirected)
+               return true;
+
+       d = dispatch_find(url, pi);
+       if (d)
+               d->handle_request(cl, url, pi);
+       else
+               uh_file_request(cl, url, pi);
 
        return true;
 }
 
-void uh_handle_file_request(struct client *cl)
+void uh_handle_request(struct client *cl)
 {
-       if (__handle_file_request(cl, cl->request.url) ||
+       struct dispatch_handler *d;
+       const char *url = cl->request.url;
+
+       d = dispatch_find(url, NULL);
+       if (d) {
+               d->handle_request(cl, url, NULL);
+               return;
+       }
+
+       if (__handle_file_request(cl, url) ||
            __handle_file_request(cl, conf.error_handler))
                return;
 
diff --git a/main.c b/main.c
index bc6893a9f93af2012e26c150ea914d976220e608..f61574ff9fda3a0ad10c39282a41e5d918809f74 100644 (file)
--- a/main.c
+++ b/main.c
@@ -142,13 +142,9 @@ static int usage(const char *name)
                "       -u string       URL prefix for HTTP/JSON handler\n"
                "       -U file         Override ubus socket path\n"
 #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) || defined(HAVE_UBUS)
                "       -t seconds      CGI, Lua and UBUS script timeout in seconds, default is 60\n"
-#endif
                "       -T seconds      Network timeout in seconds, default is 30\n"
                "       -d string       URL decode given string\n"
                "       -r string       Specify basic auth realm\n"
@@ -174,6 +170,21 @@ static void init_defaults(void)
        uh_index_add("default.htm");
 }
 
+static void fixup_prefix(char *str)
+{
+       int len;
+
+       if (!str || !str[0])
+               return;
+
+       len = strlen(str) - 1;
+
+       while (len > 0 && str[len] == '/')
+               len--;
+
+       str[len + 1] = 0;
+}
+
 int main(int argc, char **argv)
 {
        bool nofork = false;
@@ -184,6 +195,7 @@ int main(int argc, char **argv)
 
        BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX);
 
+       uh_dispatch_add(&cgi_dispatch);
        init_defaults();
        signal(SIGPIPE, SIG_IGN);
 
@@ -241,6 +253,23 @@ int main(int argc, char **argv)
                        conf.max_requests = atoi(optarg);
                        break;
 
+               case 'x':
+                       fixup_prefix(optarg);
+                       conf.cgi_prefix = optarg;
+                       break;
+
+               case 'i':
+                       port = strchr(optarg, '=');
+                       if (optarg[0] != '.' || !port) {
+                               fprintf(stderr, "Error: Invalid interpreter: %s\n",
+                                               optarg);
+                               exit(1);
+                       }
+
+                       *port++ = 0;
+                       uh_interpreter_add(optarg, port);
+                       break;
+
                case 't':
                        conf.script_timeout = atoi(optarg);
                        break;
diff --git a/proc.c b/proc.c
new file mode 100644 (file)
index 0000000..9d662ad
--- /dev/null
+++ b/proc.c
@@ -0,0 +1,241 @@
+/*
+ * uhttpd - Tiny single-threaded httpd
+ *
+ *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <libubox/blobmsg.h>
+#include "uhttpd.h"
+
+#define __headers \
+       __header(accept) \
+       __header(accept_charset) \
+       __header(accept_encoding) \
+       __header(accept_language) \
+       __header(authorization) \
+       __header(connection) \
+       __header(cookie) \
+       __header(host) \
+       __header(referer) \
+       __header(user_agent) \
+       __header(content_type) \
+       __header(content_length)
+
+#undef __header
+#define __header __enum_header
+enum client_hdr {
+       __headers
+       __HDR_MAX,
+};
+
+#undef __header
+#define __header __blobmsg_header
+static const struct blobmsg_policy hdr_policy[__HDR_MAX] = {
+       __headers
+};
+
+static const struct {
+       const char *name;
+       int idx;
+} proc_header_env[] = {
+       { "HTTP_ACCEPT", HDR_accept },
+       { "HTTP_ACCEPT_CHARSET", HDR_accept_charset },
+       { "HTTP_ACCEPT_ENCODING", HDR_accept_encoding },
+       { "HTTP_ACCEPT_LANGUAGE", HDR_accept_language },
+       { "HTTP_AUTHORIZATION", HDR_authorization },
+       { "HTTP_CONNECTION", HDR_connection },
+       { "HTTP_COOKIE", HDR_cookie },
+       { "HTTP_HOST", HDR_host },
+       { "HTTP_REFERER", HDR_referer },
+       { "HTTP_USER_AGENT", HDR_user_agent },
+       { "CONTENT_TYPE", HDR_content_type },
+       { "CONTENT_LENGTH", HDR_content_length },
+};
+
+enum extra_vars {
+       /* no update needed */
+       _VAR_GW,
+       _VAR_SOFTWARE,
+
+       /* updated by uh_get_process_vars */
+       VAR_SCRIPT_NAME,
+       VAR_SCRIPT_FILE,
+       VAR_DOCROOT,
+       VAR_QUERY,
+       VAR_REQUEST,
+       VAR_PROTO,
+       VAR_METHOD,
+       VAR_PATH_INFO,
+       VAR_USER,
+       VAR_REDIRECT,
+
+       __VAR_MAX,
+};
+
+static struct env_var extra_vars[] = {
+       [_VAR_GW] = { "GATEWAY_INTERFACE", "CGI/1.1" },
+       [_VAR_SOFTWARE] = { "SERVER_SOFTWARE", "uhttpd" },
+       [VAR_SCRIPT_NAME] = { "SCRIPT_NAME" },
+       [VAR_SCRIPT_FILE] = { "SCRIPT_FILENAME" },
+       [VAR_DOCROOT] = { "DOCUMENT_ROOT" },
+       [VAR_QUERY] = { "QUERY_STRING" },
+       [VAR_REQUEST] = { "REQUEST_URI" },
+       [VAR_PROTO] = { "SERVER_PROTOCOL" },
+       [VAR_METHOD] = { "REQUEST_METHOD" },
+       [VAR_PATH_INFO] = { "PATH_INFO" },
+       [VAR_USER] = { "REMOTE_USER" },
+       [VAR_REDIRECT] = { "REDIRECT_STATUS" },
+};
+
+struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi)
+{
+       struct http_request *req = &cl->request;
+       struct blob_attr *data = cl->hdr.head;
+       struct env_var *vars = (void *) uh_buf;
+       struct blob_attr *tb[__HDR_MAX];
+       static char buf[4];
+       int len;
+       int i;
+
+       len = ARRAY_SIZE(proc_header_env);
+       len += ARRAY_SIZE(extra_vars);
+       len *= sizeof(struct env_var);
+
+       BUILD_BUG_ON(sizeof(uh_buf) < len);
+
+       extra_vars[VAR_SCRIPT_NAME].value = pi->name;
+       extra_vars[VAR_SCRIPT_FILE].value = pi->phys;
+       extra_vars[VAR_DOCROOT].value = pi->root;
+       extra_vars[VAR_QUERY].value = pi->query ? pi->query : "";
+       extra_vars[VAR_REQUEST].value = req->url;
+       extra_vars[VAR_PROTO].value = http_versions[req->version];
+       extra_vars[VAR_METHOD].value = http_methods[req->method];
+       extra_vars[VAR_PATH_INFO].value = pi->info;
+       extra_vars[VAR_USER].value = req->realm ? req->realm->user : NULL;
+
+       snprintf(buf, sizeof(buf), "%d", req->redirect_status);
+       extra_vars[VAR_REDIRECT].value = buf;
+
+       blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(data), blob_len(data));
+       for (i = 0; i < ARRAY_SIZE(proc_header_env); i++) {
+               struct blob_attr *cur;
+
+               cur = tb[proc_header_env[i].idx];
+               vars[i].name = proc_header_env[i].name;
+               vars[i].value = cur ? blobmsg_data(cur) : "";
+       }
+
+       memcpy(&vars[i], extra_vars, sizeof(extra_vars));
+       i += ARRAY_SIZE(extra_vars);
+       vars[i].name = NULL;
+       vars[i].value = NULL;
+
+       return vars;
+}
+
+static void proc_close_fds(struct client *cl)
+{
+       close(cl->dispatch.proc.r.sfd.fd.fd);
+}
+
+static void proc_handle_close(struct relay *r, int ret)
+{
+       if (r->header_cb) {
+               uh_client_error(r->cl, 502, "Bad Gateway",
+                               "The process did not produce any response");
+               return;
+       }
+
+       uh_request_done(r->cl);
+}
+
+static void proc_handle_header(struct relay *r, const char *name, const char *val)
+{
+       static char status_buf[64];
+       struct client *cl = r->cl;
+       char *sep;
+       char buf[4];
+
+       if (strcmp(name, "Status")) {
+               sep = strchr(val, ' ');
+               if (sep != val + 3)
+                       return;
+
+               memcpy(buf, val, 3);
+               buf[3] = 0;
+               snprintf(status_buf, sizeof(status_buf), "%s", sep + 1);
+               cl->dispatch.proc.status_msg = status_buf;
+               return;
+       }
+
+       blobmsg_add_string(&cl->dispatch.proc.hdr, name, val);
+}
+
+static void proc_handle_header_end(struct relay *r)
+{
+       struct client *cl = r->cl;
+       struct blob_attr *cur;
+       int rem;
+
+       uh_http_header(cl, cl->dispatch.proc.status_code, cl->dispatch.proc.status_msg);
+       blob_for_each_attr(cur, cl->dispatch.proc.hdr.head, rem)
+               ustream_printf(cl->us, "%s: %s\r\n", blobmsg_name(cur), blobmsg_data(cur));
+
+       ustream_printf(cl->us, "\r\n");
+}
+
+static void proc_free(struct client *cl)
+{
+       uh_relay_free(&cl->dispatch.proc.r);
+}
+
+bool uh_create_process(struct client *cl, struct path_info *pi,
+                      void (*cb)(struct client *cl, struct path_info *pi, int fd))
+{
+       int fds[2];
+       int pid;
+
+       blob_buf_init(&cl->dispatch.proc.hdr, 0);
+       cl->dispatch.proc.status_code = 200;
+       cl->dispatch.proc.status_msg = "OK";
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
+               return false;
+
+       pid = fork();
+       if (pid < 0) {
+               close(fds[0]);
+               close(fds[1]);
+               return false;
+       }
+
+       if (!pid) {
+               close(fds[0]);
+               uh_close_fds();
+               cb(cl, pi, fds[1]);
+               exit(0);
+       }
+
+       close(fds[1]);
+       uh_relay_open(cl, &cl->dispatch.proc.r, fds[0], pid);
+       cl->dispatch.free = proc_free;
+       cl->dispatch.close_fds = proc_close_fds;
+       cl->dispatch.proc.r.header_cb = proc_handle_header;
+       cl->dispatch.proc.r.header_end = proc_handle_header_end;
+       cl->dispatch.proc.r.close = proc_handle_close;
+
+       return true;
+}
diff --git a/relay.c b/relay.c
new file mode 100644 (file)
index 0000000..30be3ec
--- /dev/null
+++ b/relay.c
@@ -0,0 +1,178 @@
+/*
+ * uhttpd - Tiny single-threaded httpd
+ *
+ *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <signal.h>
+#include "uhttpd.h"
+
+void uh_relay_free(struct relay *r)
+{
+       if (!r->cl)
+               return;
+
+       if (r->proc.pending)
+               kill(r->proc.pid, SIGKILL);
+
+       uloop_process_delete(&r->proc);
+       ustream_free(&r->sfd.stream);
+       close(r->sfd.fd.fd);
+
+       r->cl = NULL;
+}
+
+void uh_relay_close(struct relay *r, int ret)
+{
+       struct ustream *us = &r->sfd.stream;
+
+       if (!us->notify_read)
+               return;
+
+       us->notify_read = NULL;
+       us->notify_write = NULL;
+       us->notify_state = NULL;
+
+       if (r->close)
+               r->close(r, ret);
+}
+
+static void relay_error(struct relay *r)
+{
+       struct ustream *s = &r->sfd.stream;
+       int len;
+
+       s->eof = true;
+       ustream_get_read_buf(s, &len);
+       if (len)
+               ustream_consume(s, len);
+       ustream_state_change(s);
+}
+
+static void relay_process_headers(struct relay *r)
+{
+       struct ustream *s = &r->sfd.stream;
+       char *buf, *newline;
+       int len;
+
+       if (!r->header_cb)
+               return;
+
+       while (r->header_cb) {
+               int line_len;
+               char *val;
+
+               buf = ustream_get_read_buf(s, &len);
+               newline = strchr(buf, '\n');
+               if (!newline)
+                       break;
+
+               line_len = newline + 1 - buf;
+               if (newline > buf && newline[-1] == '\r') {
+                       newline--;
+                       line_len++;
+               }
+
+               *newline = 0;
+               if (newline == buf) {
+                       r->header_cb = NULL;
+                       if (r->header_end)
+                               r->header_end(r);
+                       break;
+               }
+
+               val = uh_split_header(buf);
+               if (!val) {
+                       relay_error(r);
+                       return;
+               }
+
+               r->header_cb(r, buf, val);
+               ustream_consume(s, line_len);
+       }
+}
+
+static void relay_read_cb(struct ustream *s, int bytes)
+{
+       struct relay *r = container_of(s, struct relay, sfd.stream);
+       struct client *cl = r->cl;
+       struct ustream *us = cl->us;
+       char *buf;
+       int len;
+
+       relay_process_headers(r);
+
+       if (r->header_cb) {
+               /*
+                * if eof, ensure that remaining data is discarded, so the
+                * state change cb will tear down the stream
+                */
+               if (s->eof)
+                       relay_error(r);
+               return;
+       }
+
+       if (!s->eof && ustream_pending_data(us, true)) {
+               ustream_set_read_blocked(s, true);
+               return;
+       }
+
+       buf = ustream_get_read_buf(s, &len);
+       uh_chunk_write(cl, buf, len);
+       ustream_consume(s, len);
+}
+
+static void relay_close_if_done(struct relay *r)
+{
+       struct ustream *s = &r->sfd.stream;
+
+       if (!s->eof || ustream_pending_data(s, false))
+               return;
+
+       uh_relay_close(r, r->ret);
+}
+
+static void relay_state_cb(struct ustream *s)
+{
+       struct relay *r = container_of(s, struct relay, sfd.stream);
+
+       if (r->process_done)
+               relay_close_if_done(r);
+}
+
+static void relay_proc_cb(struct uloop_process *proc, int ret)
+{
+       struct relay *r = container_of(proc, struct relay, proc);
+
+       r->process_done = true;
+       r->ret = ret;
+       relay_close_if_done(r);
+}
+
+void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid)
+{
+       struct ustream *us = &r->sfd.stream;
+
+       r->cl = cl;
+       ustream_fd_init(&r->sfd, fd);
+       us->notify_read = relay_read_cb;
+       us->notify_state = relay_state_cb;
+       us->string_data = true;
+
+       r->proc.pid = pid;
+       r->proc.cb = relay_proc_cb;
+       uloop_process_add(&r->proc);
+}
index c1b52f899e91ff6f390ab963cfc386a508f035f0..24ad83de82718e329353e3530735b7c35f5083c6 100644 (file)
--- a/uhttpd.h
+++ b/uhttpd.h
 #define UH_LIMIT_CLIENTS       64
 #define UH_LIMIT_HEADERS       64
 
+#define __enum_header(_name) HDR_##_name,
+#define __blobmsg_header(_name) [HDR_##_name] = { .name = #_name, .type = BLOBMSG_TYPE_STRING },
+
+struct client;
+
 struct config {
        const char *docroot;
        const char *realm;
@@ -52,16 +57,6 @@ struct config {
        int script_timeout;
 };
 
-struct path_info {
-       const char *root;
-       const char *phys;
-       const char *name;
-       const char *info;
-       const char *query;
-       int redirected;
-       struct stat stat;
-};
-
 struct auth_realm {
        struct list_head list;
        char *path;
@@ -89,12 +84,6 @@ struct http_request {
        const struct auth_realm *realm;
 };
 
-struct http_response {
-       int statuscode;
-       char *statusmsg;
-       char *headers[UH_LIMIT_HEADERS];
-};
-
 enum client_state {
        CLIENT_STATE_INIT,
        CLIENT_STATE_HEADER,
@@ -103,6 +92,50 @@ enum client_state {
        CLIENT_STATE_CLOSE,
 };
 
+struct interpreter {
+       struct list_head list;
+       char *path;
+       char *ext;
+};
+
+struct path_info {
+       const char *root;
+       const char *phys;
+       const char *name;
+       const char *info;
+       const char *query;
+       int redirected;
+       struct stat stat;
+       struct interpreter *ip;
+};
+
+struct env_var {
+       const char *name;
+       const char *value;
+};
+
+struct relay {
+       struct ustream_fd sfd;
+       struct uloop_process proc;
+       struct client *cl;
+
+       bool process_done;
+       int ret;
+       int header_ofs;
+
+       void (*header_cb)(struct relay *r, const char *name, const char *value);
+       void (*header_end)(struct relay *r);
+       void (*close)(struct relay *r, int ret);
+};
+
+struct dispatch_handler {
+       struct list_head list;
+
+       bool (*check_url)(const char *url);
+       bool (*check_path)(struct path_info *pi, const char *url);
+       void (*handle_request)(struct client *cl, const char *url, struct path_info *pi);
+};
+
 struct client {
        struct list_head list;
        int id;
@@ -117,7 +150,6 @@ struct client {
        enum client_state state;
 
        struct http_request request;
-       struct http_response response;
        struct sockaddr_in6 servaddr;
        struct sockaddr_in6 peeraddr;
 
@@ -132,6 +164,12 @@ struct client {
                                struct blob_attr **hdr;
                                int fd;
                        } file;
+                       struct {
+                               struct blob_buf hdr;
+                               struct relay r;
+                               int status_code;
+                               char *status_msg;
+                       } proc;
                };
        } dispatch;
 };
@@ -141,6 +179,7 @@ extern int n_clients;
 extern struct config conf;
 extern const char * const http_versions[];
 extern const char * const http_methods[];
+extern struct dispatch_handler cgi_dispatch;
 
 void uh_index_add(const char *filename);
 
@@ -164,11 +203,22 @@ void uh_http_header(struct client *cl, int code, const char *summary);
 void __printf(4, 5)
 uh_client_error(struct client *cl, int code, const char *summary, const char *fmt, ...);
 
-void uh_handle_file_request(struct client *cl);
+void uh_handle_request(struct client *cl);
 
 void uh_auth_add(const char *path, const char *user, const char *pass);
 
 void uh_close_listen_fds(void);
 void uh_close_fds(void);
 
+void uh_interpreter_add(const char *ext, const char *path);
+void uh_dispatch_add(struct dispatch_handler *d);
+
+void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid);
+void uh_relay_close(struct relay *r, int ret);
+void uh_relay_free(struct relay *r);
+
+struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi);
+bool uh_create_process(struct client *cl, struct path_info *pi,
+                      void (*cb)(struct client *cl, struct path_info *pi, int fd));
+
 #endif