summaryrefslogtreecommitdiff
path: root/source4/web_server
diff options
context:
space:
mode:
authorbubulle <bubulle@alioth.debian.org>2009-07-06 18:17:09 +0000
committerbubulle <bubulle@alioth.debian.org>2009-07-06 18:17:09 +0000
commit1e4514a1edfdd1dde65a7567a7d7328de40e3493 (patch)
tree889204356442a0e13d8b3b2deaa1e0b310cea940 /source4/web_server
parent956e238a6ed95a84c85c14ac898ffee166c35287 (diff)
downloadsamba-1e4514a1edfdd1dde65a7567a7d7328de40e3493.tar.gz
merge upstream 3.4.0
git-svn-id: svn://svn.debian.org/svn/pkg-samba/trunk/samba@2936 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'source4/web_server')
-rw-r--r--source4/web_server/config.mk14
-rw-r--r--source4/web_server/swat/__init__.py39
-rw-r--r--source4/web_server/web_server.c364
-rw-r--r--source4/web_server/web_server.h65
-rw-r--r--source4/web_server/wsgi.c391
5 files changed, 873 insertions, 0 deletions
diff --git a/source4/web_server/config.mk b/source4/web_server/config.mk
new file mode 100644
index 0000000000..ff587508fc
--- /dev/null
+++ b/source4/web_server/config.mk
@@ -0,0 +1,14 @@
+# web server subsystem
+
+#######################
+# Start SUBSYSTEM WEB
+[MODULE::WEB]
+INIT_FUNCTION = server_service_web_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = LIBTLS smbcalls process_model LIBPYTHON
+# End SUBSYSTEM WEB
+#######################
+
+WEB_OBJ_FILES = $(addprefix $(web_serversrcdir)/, web_server.o wsgi.o)
+
+$(eval $(call proto_header_template,$(web_serversrcdir)/proto.h,$(WEB_OBJ_FILES:.o=.c)))
diff --git a/source4/web_server/swat/__init__.py b/source4/web_server/swat/__init__.py
new file mode 100644
index 0000000000..d434bb260b
--- /dev/null
+++ b/source4/web_server/swat/__init__.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# Implementation of SWAT that uses WSGI
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+def __call__(environ, start_response):
+ status = '200 OK'
+ response_headers = [('Content-type','text/html')]
+ start_response(status, response_headers)
+ yield '<table>\n'
+
+ for key, value in environ.items():
+ if isinstance(value, str):
+ yield '\t<tr><td><b>%s</b></td><td>%s</td></tr>\n' % (key, value)
+
+ yield '</table>\n'
+
+if __name__ == '__main__':
+ from wsgiref import simple_server
+ httpd = simple_server.make_server('localhost', 8090, __call__)
+ print "Serving HTTP on port 8090..."
+ httpd.serve_forever()
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
new file mode 100644
index 0000000000..2a2bfbb13b
--- /dev/null
+++ b/source4/web_server/web_server.c
@@ -0,0 +1,364 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ web server startup
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Jelmer Vernooij 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "web_server/web_server.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "lib/tls/tls.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+/* don't allow connections to hang around forever */
+#define HTTP_TIMEOUT 120
+
+/*
+ destroy a web connection
+*/
+static int websrv_destructor(struct websrv_context *web)
+{
+ return 0;
+}
+
+/*
+ called when a connection times out. This prevents a stuck connection
+ from hanging around forever
+*/
+static void websrv_timeout(struct tevent_context *event_context,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct websrv_context *web = talloc_get_type(private_data, struct websrv_context);
+ struct stream_connection *conn = web->conn;
+ web->conn = NULL;
+ /* TODO: send a message to any running esp context on this connection
+ to stop running */
+ stream_terminate_connection(conn, "websrv_timeout: timed out");
+}
+
+/*
+ setup for a raw http level error
+*/
+void http_error(struct websrv_context *web, const char *status, const char *info)
+{
+ char *s;
+ s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n",
+ status, status, info);
+ if (s == NULL) {
+ stream_terminate_connection(web->conn, "http_error: out of memory");
+ return;
+ }
+ websrv_output_headers(web, status, NULL);
+ websrv_output(web, s, strlen(s));
+}
+
+void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
+{
+ char *s;
+ DATA_BLOB b;
+ struct http_header *hdr;
+
+ s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
+ if (s == NULL) return;
+ for (hdr = headers; hdr; hdr = hdr->next) {
+ s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
+ }
+
+ s = talloc_asprintf_append_buffer(s, "\r\n");
+
+ b = web->output.content;
+ web->output.content = data_blob_string_const(s);
+ websrv_output(web, b.data, b.length);
+ data_blob_free(&b);
+}
+
+void websrv_output(struct websrv_context *web, void *data, size_t length)
+{
+ data_blob_append(web, &web->output.content, data, length);
+ EVENT_FD_NOT_READABLE(web->conn->event.fde);
+ EVENT_FD_WRITEABLE(web->conn->event.fde);
+ web->output.output_pending = true;
+}
+
+
+/*
+ parse one line of header input
+*/
+NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
+{
+ if (line[0] == 0) {
+ web->input.end_of_headers = true;
+ } else if (strncasecmp(line,"GET ", 4)==0) {
+ web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
+ } else if (strncasecmp(line,"POST ", 5)==0) {
+ web->input.post_request = true;
+ web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
+ } else if (strchr(line, ':') == NULL) {
+ http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
+ web->input.content_length = strtoul(&line[16], NULL, 10);
+ } else {
+ struct http_header *hdr = talloc_zero(web, struct http_header);
+ char *colon = strchr(line, ':');
+ if (colon == NULL) {
+ http_error(web, "500 Internal Server Error", "invalidly formatted header");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ hdr->name = talloc_strndup(hdr, line, colon-line);
+ hdr->value = talloc_strdup(hdr, colon+1);
+ DLIST_ADD(web->input.headers, hdr);
+ }
+
+ /* ignore all other headers for now */
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a web connection becomes readable
+*/
+static void websrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct web_server_data *wdata;
+ struct websrv_context *web = talloc_get_type(conn->private_data,
+ struct websrv_context);
+ NTSTATUS status;
+ uint8_t buf[1024];
+ size_t nread;
+ uint8_t *p;
+ DATA_BLOB b;
+
+ /* not the most efficient http parser ever, but good enough for us */
+ status = socket_recv(conn->socket, buf, sizeof(buf), &nread);
+ if (NT_STATUS_IS_ERR(status)) goto failed;
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ if (!data_blob_append(web, &web->input.partial, buf, nread))
+ goto failed;
+
+ /* parse any lines that are available */
+ b = web->input.partial;
+ while (!web->input.end_of_headers &&
+ (p=(uint8_t *)memchr(b.data, '\n', b.length))) {
+ const char *line = (const char *)b.data;
+ *p = 0;
+ if (p != b.data && p[-1] == '\r') {
+ p[-1] = 0;
+ }
+ status = http_parse_header(web, line);
+ if (!NT_STATUS_IS_OK(status)) return;
+ b.length -= (p - b.data) + 1;
+ b.data = p+1;
+ }
+
+ /* keep any remaining bytes in web->input.partial */
+ if (b.length == 0) {
+ b.data = NULL;
+ }
+ b = data_blob_talloc(web, b.data, b.length);
+ data_blob_free(&web->input.partial);
+ web->input.partial = b;
+
+ /* we finish when we have both the full headers (terminated by
+ a blank line) and any post data, as indicated by the
+ content_length */
+ if (web->input.end_of_headers &&
+ web->input.partial.length >= web->input.content_length) {
+ if (web->input.partial.length > web->input.content_length) {
+ web->input.partial.data[web->input.content_length] = 0;
+ }
+ EVENT_FD_NOT_READABLE(web->conn->event.fde);
+
+ /* the reference/unlink code here is quite subtle. It
+ is needed because the rendering of the web-pages, and
+ in particular the esp/ejs backend, is semi-async. So
+ we could well end up in the connection timeout code
+ while inside http_process_input(), but we must not
+ destroy the stack variables being used by that
+ rendering process when we handle the timeout. */
+ if (!talloc_reference(web->task, web)) goto failed;
+ wdata = talloc_get_type(web->task->private_data, struct web_server_data);
+ if (wdata == NULL) goto failed;
+ wdata->http_process_input(wdata, web);
+ talloc_unlink(web->task, web);
+ }
+ return;
+
+failed:
+ stream_terminate_connection(conn, "websrv_recv: failed");
+}
+
+
+
+/*
+ called when a web connection becomes writable
+*/
+static void websrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct websrv_context *web = talloc_get_type(conn->private_data,
+ struct websrv_context);
+ NTSTATUS status;
+ size_t nsent;
+ DATA_BLOB b;
+
+ b = web->output.content;
+ b.data += web->output.nsent;
+ b.length -= web->output.nsent;
+
+ status = socket_send(conn->socket, &b, &nsent);
+ if (NT_STATUS_IS_ERR(status)) {
+ stream_terminate_connection(web->conn, "socket_send: failed");
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ web->output.nsent += nsent;
+
+ if (web->output.content.length == web->output.nsent) {
+ stream_terminate_connection(web->conn, "websrv_send: finished sending");
+ }
+}
+
+/*
+ establish a new connection to the web server
+*/
+static void websrv_accept(struct stream_connection *conn)
+{
+ struct task_server *task = talloc_get_type(conn->private_data, struct task_server);
+ struct web_server_data *wdata = talloc_get_type(task->private_data, struct web_server_data);
+ struct websrv_context *web;
+ struct socket_context *tls_socket;
+
+ web = talloc_zero(conn, struct websrv_context);
+ if (web == NULL) goto failed;
+
+ web->task = task;
+ web->conn = conn;
+ conn->private_data = web;
+ talloc_set_destructor(web, websrv_destructor);
+
+ event_add_timed(conn->event.ctx, web,
+ timeval_current_ofs(HTTP_TIMEOUT, 0),
+ websrv_timeout, web);
+
+ /* Overwrite the socket with a (possibly) TLS socket */
+ tls_socket = tls_init_server(wdata->tls_params, conn->socket,
+ conn->event.fde, "GPHO");
+ /* We might not have TLS, or it might not have initilised */
+ if (tls_socket) {
+ talloc_unlink(conn, conn->socket);
+ talloc_steal(conn, tls_socket);
+ conn->socket = tls_socket;
+ } else {
+ DEBUG(3, ("TLS not available for web_server connections\n"));
+ }
+
+ return;
+
+failed:
+ talloc_free(conn);
+}
+
+
+static const struct stream_server_ops web_stream_ops = {
+ .name = "web",
+ .accept_connection = websrv_accept,
+ .recv_handler = websrv_recv,
+ .send_handler = websrv_send,
+};
+
+/*
+ startup the web server task
+*/
+static void websrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ uint16_t port = lp_web_port(task->lp_ctx);
+ const struct model_ops *model_ops;
+ struct web_server_data *wdata;
+
+ task_server_set_title(task, "task[websrv]");
+
+ /* run the web server as a single process */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) goto failed;
+
+ if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(NULL, lp_interfaces(task->lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = stream_setup_socket(task->event_ctx,
+ task->lp_ctx, model_ops,
+ &web_stream_ops,
+ "ipv4", address,
+ &port, lp_socket_options(task->lp_ctx),
+ task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ talloc_free(ifaces);
+ } else {
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx,
+ model_ops, &web_stream_ops,
+ "ipv4", lp_socket_address(task->lp_ctx),
+ &port, lp_socket_options(task->lp_ctx), task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ /* startup the esp processor - unfortunately we can't do this
+ per connection as that wouldn't allow for session variables */
+ wdata = talloc_zero(task, struct web_server_data);
+ if (wdata == NULL)goto failed;
+
+ task->private_data = wdata;
+
+ wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
+ if (wdata->tls_params == NULL) goto failed;
+
+ if (!wsgi_initialize(wdata)) goto failed;
+
+ return;
+
+failed:
+ task_server_terminate(task, "websrv_task_init: failed to startup web server task");
+}
+
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_web_init(void)
+{
+ return register_server_service("web", websrv_task_init);
+}
diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h
new file mode 100644
index 0000000000..3b02feaf7d
--- /dev/null
+++ b/source4/web_server/web_server.h
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "smbd/process_model.h"
+
+struct websrv_context;
+
+struct web_server_data {
+ struct tls_params *tls_params;
+ void (*http_process_input)(struct web_server_data *wdata,
+ struct websrv_context *web);
+ void *private_data;
+};
+
+struct http_header {
+ char *name;
+ char *value;
+ struct http_header *prev, *next;
+};
+
+/*
+ context of one open web connection
+*/
+struct websrv_context {
+ struct task_server *task;
+ struct stream_connection *conn;
+ struct websrv_request_input {
+ bool tls_detect;
+ bool tls_first_char;
+ uint8_t first_byte;
+ DATA_BLOB partial;
+ bool end_of_headers;
+ char *url;
+ unsigned content_length;
+ bool post_request;
+ struct http_header *headers;
+ } input;
+ struct websrv_request_output {
+ bool output_pending;
+ DATA_BLOB content;
+ bool headers_sent;
+ unsigned nsent;
+ } output;
+ struct session_data *session;
+};
+
+
+#include "web_server/proto.h"
+
diff --git a/source4/web_server/wsgi.c b/source4/web_server/wsgi.c
new file mode 100644
index 0000000000..4d6b441f17
--- /dev/null
+++ b/source4/web_server/wsgi.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+
+ Implementation of the WSGI interface described in PEP0333
+ (http://www.python.org/dev/peps/pep-0333)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "web_server/web_server.h"
+#include "../lib/util/dlinklist.h"
+#include "../lib/util/data_blob.h"
+#include "lib/tls/tls.h"
+#include <Python.h>
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ struct websrv_context *web;
+} web_request_Object;
+
+static PyObject *start_response(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *response_header, *exc_info = NULL;
+ char *status;
+ int i;
+ const char *kwnames[] = {
+ "status", "response_header", "exc_info", NULL
+ };
+ web_request_Object *py_web = (web_request_Object *)self;
+ struct websrv_context *web = py_web->web;
+ struct http_header *headers = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O:start_response", discard_const_p(char *, kwnames), &status, &response_header, &exc_info)) {
+ return NULL;
+ }
+
+ /* FIXME: exc_info */
+
+ if (!PyList_Check(response_header)) {
+ PyErr_SetString(PyExc_TypeError, "response_header should be list");
+ return NULL;
+ }
+
+ for (i = 0; i < PyList_Size(response_header); i++) {
+ struct http_header *hdr = talloc_zero(web, struct http_header);
+ PyObject *item = PyList_GetItem(response_header, i);
+ PyObject *py_name, *py_value;
+
+ if (!PyTuple_Check(item)) {
+ PyErr_SetString(PyExc_TypeError, "Expected tuple");
+ return NULL;
+ }
+
+ if (PyTuple_Size(item) != 2) {
+ PyErr_SetString(PyExc_TypeError, "header tuple has invalid size, expected 2");
+ return NULL;
+ }
+
+ py_name = PyTuple_GetItem(item, 0);
+
+ if (!PyString_Check(py_name)) {
+ PyErr_SetString(PyExc_TypeError, "header name should be string");
+ return NULL;
+ }
+
+ py_value = PyTuple_GetItem(item, 1);
+ if (!PyString_Check(py_value)) {
+ PyErr_SetString(PyExc_TypeError, "header value should be string");
+ return NULL;
+ }
+
+ hdr->name = talloc_strdup(hdr, PyString_AsString(py_name));
+ hdr->value = talloc_strdup(hdr, PyString_AsString(py_value));
+ DLIST_ADD(headers, hdr);
+ }
+
+ websrv_output_headers(web, status, headers);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef web_request_methods[] = {
+ { "start_response", (PyCFunction)start_response, METH_VARARGS|METH_KEYWORDS, NULL },
+ { NULL }
+};
+
+
+PyTypeObject web_request_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.Request",
+ .tp_methods = web_request_methods,
+ .tp_basicsize = sizeof(web_request_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+typedef struct {
+ PyObject_HEAD
+} error_Stream_Object;
+
+static PyObject *py_error_flush(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ /* Nothing to do here */
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_error_write(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "str", NULL };
+ char *str = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:write", discard_const_p(char *, kwnames), &str)) {
+ return NULL;
+ }
+
+ DEBUG(0, ("WSGI App: %s", str));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_error_writelines(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "seq", NULL };
+ PyObject *seq = NULL, *item;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:writelines", discard_const_p(char *, kwnames), &seq)) {
+ return NULL;
+ }
+
+ while ((item = PyIter_Next(seq))) {
+ char *str = PyString_AsString(item);
+
+ DEBUG(0, ("WSGI App: %s", str));
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef error_Stream_methods[] = {
+ { "flush", (PyCFunction)py_error_flush, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "write", (PyCFunction)py_error_write, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "writelines", (PyCFunction)py_error_writelines, METH_VARARGS|METH_KEYWORDS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject error_Stream_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.ErrorStream",
+ .tp_basicsize = sizeof(error_Stream_Object),
+ .tp_methods = error_Stream_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+typedef struct {
+ PyObject_HEAD
+ struct websrv_context *web;
+ size_t offset;
+} input_Stream_Object;
+
+static PyObject *py_input_read(PyObject *_self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "size", NULL };
+ PyObject *ret;
+ input_Stream_Object *self = (input_Stream_Object *)_self;
+ int size = -1;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), &size))
+ return NULL;
+
+ /* Don't read beyond buffer boundaries */
+ if (size == -1)
+ size = self->web->input.partial.length-self->offset;
+ else
+ size = MIN(size, self->web->input.partial.length-self->offset);
+
+ ret = PyString_FromStringAndSize((char *)self->web->input.partial.data+self->offset, size);
+ self->offset += size;
+
+ return ret;
+}
+
+static PyObject *py_input_readline(PyObject *_self)
+{
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "readline() not yet implemented");
+ return NULL;
+}
+
+static PyObject *py_input_readlines(PyObject *_self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "hint", NULL };
+ int hint;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), &hint))
+ return NULL;
+
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "readlines() not yet implemented");
+ return NULL;
+}
+
+static PyObject *py_input___iter__(PyObject *_self)
+{
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "__iter__() not yet implemented");
+ return NULL;
+}
+
+static PyMethodDef input_Stream_methods[] = {
+ { "read", (PyCFunction)py_input_read, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "readline", (PyCFunction)py_input_readline, METH_NOARGS, NULL },
+ { "readlines", (PyCFunction)py_input_readlines, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "__iter__", (PyCFunction)py_input___iter__, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject input_Stream_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.InputStream",
+ .tp_basicsize = sizeof(input_Stream_Object),
+ .tp_methods = input_Stream_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+static PyObject *Py_InputHttpStream(struct websrv_context *web)
+{
+ input_Stream_Object *ret = PyObject_New(input_Stream_Object, &input_Stream_Type);
+ ret->web = web;
+ ret->offset = 0;
+ return (PyObject *)ret;
+}
+
+static PyObject *Py_ErrorHttpStream(void)
+{
+ error_Stream_Object *ret = PyObject_New(error_Stream_Object, &error_Stream_Type);
+ return (PyObject *)ret;
+}
+
+static PyObject *create_environ(bool tls, int content_length, struct http_header *headers, const char *request_method, const char *servername, int serverport, PyObject *inputstream, const char *request_string)
+{
+ PyObject *env;
+ PyObject *errorstream;
+ PyObject *py_scheme;
+ struct http_header *hdr;
+ char *questionmark;
+
+ env = PyDict_New();
+ if (env == NULL) {
+ return NULL;
+ }
+
+ errorstream = Py_ErrorHttpStream();
+ if (errorstream == NULL) {
+ Py_DECREF(env);
+ Py_DECREF(inputstream);
+ return NULL;
+ }
+
+ PyDict_SetItemString(env, "wsgi.input", inputstream);
+ PyDict_SetItemString(env, "wsgi.errors", errorstream);
+ PyDict_SetItemString(env, "wsgi.version", Py_BuildValue("(i,i)", 1, 0));
+ PyDict_SetItemString(env, "wsgi.multithread", Py_False);
+ PyDict_SetItemString(env, "wsgi.multiprocess", Py_True);
+ PyDict_SetItemString(env, "wsgi.run_once", Py_False);
+ PyDict_SetItemString(env, "SERVER_PROTOCOL", PyString_FromString("HTTP/1.0"));
+ if (content_length > 0) {
+ PyDict_SetItemString(env, "CONTENT_LENGTH", PyLong_FromLong(content_length));
+ }
+ PyDict_SetItemString(env, "REQUEST_METHOD", PyString_FromString(request_method));
+
+ questionmark = strchr(request_string, '?');
+ if (questionmark == NULL) {
+ PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromString(request_string));
+ } else {
+ PyDict_SetItemString(env, "QUERY_STRING", PyString_FromString(questionmark+1));
+ PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromStringAndSize(request_string, questionmark-request_string));
+ }
+
+ PyDict_SetItemString(env, "SERVER_NAME", PyString_FromString(servername));
+ PyDict_SetItemString(env, "SERVER_PORT", PyInt_FromLong(serverport));
+ for (hdr = headers; hdr; hdr = hdr->next) {
+ char *name;
+ if (!strcasecmp(hdr->name, "Content-Type")) {
+ PyDict_SetItemString(env, "CONTENT_TYPE", PyString_FromString(hdr->value));
+ } else {
+ asprintf(&name, "HTTP_%s", hdr->name);
+ PyDict_SetItemString(env, name, PyString_FromString(hdr->value));
+ free(name);
+ }
+ }
+
+ if (tls) {
+ py_scheme = PyString_FromString("https");
+ } else {
+ py_scheme = PyString_FromString("http");
+ }
+ PyDict_SetItemString(env, "wsgi.url_scheme", py_scheme);
+
+ return env;
+}
+
+static void wsgi_process_http_input(struct web_server_data *wdata,
+ struct websrv_context *web)
+{
+ PyObject *py_environ, *result, *item, *iter;
+ PyObject *request_handler = (PyObject *)wdata->private_data;
+ struct socket_address *socket_address;
+
+ web_request_Object *py_web = PyObject_New(web_request_Object, &web_request_Type);
+ py_web->web = web;
+
+ socket_address = socket_get_my_addr(web->conn->socket, web);
+ py_environ = create_environ(tls_enabled(web->conn->socket),
+ web->input.content_length,
+ web->input.headers,
+ web->input.post_request?"POST":"GET",
+ socket_address->addr,
+ socket_address->port,
+ Py_InputHttpStream(web),
+ web->input.url
+ );
+ if (py_environ == NULL) {
+ DEBUG(0, ("Unable to create WSGI environment object\n"));
+ return;
+ }
+
+ result = PyObject_CallMethod(request_handler, discard_const_p(char, "__call__"), discard_const_p(char, "OO"),
+ py_environ, PyObject_GetAttrString((PyObject *)py_web, "start_response"));
+
+ if (result == NULL) {
+ DEBUG(0, ("error while running WSGI code\n"));
+ return;
+ }
+
+ iter = PyObject_GetIter(result);
+ Py_DECREF(result);
+
+ /* Now, iter over all the data returned */
+
+ while ((item = PyIter_Next(iter))) {
+ websrv_output(web, PyString_AsString(item), PyString_Size(item));
+ Py_DECREF(item);
+ }
+
+ Py_DECREF(iter);
+}
+
+bool wsgi_initialize(struct web_server_data *wdata)
+{
+ PyObject *py_swat;
+
+ Py_Initialize();
+
+ if (PyType_Ready(&web_request_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&input_Stream_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&error_Stream_Type) < 0)
+ return false;
+
+ wdata->http_process_input = wsgi_process_http_input;
+ py_swat = PyImport_Import(PyString_FromString("swat"));
+ if (py_swat == NULL) {
+ DEBUG(0, ("Unable to find SWAT\n"));
+ return false;
+ }
+ wdata->private_data = py_swat;
+ return true;
+}