add preliminary cgi support, needs fixing for close handling
[project/uhttpd.git] / relay.c
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);
+}