#include "../lib/util/dlinklist.h"
#include "lib/tls/tls.h"
#include "lib/tsocket/tsocket.h"
-
-/* There's no Py_ssize_t in 2.4, apparently */
-#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
-typedef int Py_ssize_t;
-typedef inquiry lenfunc;
-typedef intargfunc ssizeargfunc;
-#endif
+#include "python/modules.h"
+#include "python/py3compat.h"
typedef struct {
PyObject_HEAD
py_name = PyTuple_GetItem(item, 0);
- if (!PyString_Check(py_name)) {
+ if (!PyStr_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)) {
+ if (!PyStr_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));
+ hdr->name = talloc_strdup(hdr, PyStr_AsString(py_name));
+ hdr->value = talloc_strdup(hdr, PyStr_AsString(py_value));
DLIST_ADD(headers, hdr);
}
PyTypeObject web_request_Type = {
- PyObject_HEAD_INIT(NULL) 0,
+ PyVarObject_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,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
};
typedef struct {
return NULL;
}
- DEBUG(0, ("WSGI App: %s", str));
+ DEBUG(0, ("%s", str));
Py_RETURN_NONE;
}
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));
+ while ((item = PyIter_Next(seq))) {
+ const char *str = NULL;
+ Py_ssize_t size = 0;
+ if (!IsPy3Bytes(item)) {
+ str = PyStr_AsUTF8AndSize(item, &size);
+ } else {
+ str = PyBytes_AsString(item);
+ size = PyBytes_Size(item);
+ }
+ if (size > 0) {
+ DEBUG(0, ("%s", str));
+ }
}
Py_RETURN_NONE;
};
PyTypeObject error_Stream_Type = {
- PyObject_HEAD_INIT(NULL) 0,
+ PyVarObject_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,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
};
typedef struct {
else
size = MIN(size, self->web->input.partial.length-self->offset);
- ret = PyString_FromStringAndSize((char *)self->web->input.partial.data+self->offset, size);
+ ret = PyBytes_FromStringAndSize((char *)self->web->input.partial.data+self->offset, size);
self->offset += size;
return ret;
static PyObject *py_input_readlines(PyObject *_self, PyObject *args, PyObject *kwargs)
{
const char *kwnames[] = { "hint", NULL };
- int hint;
+ int hint = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), &hint))
return NULL;
};
PyTypeObject input_Stream_Type = {
- PyObject_HEAD_INIT(NULL) 0,
+ PyVarObject_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,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
};
static PyObject *Py_InputHttpStream(struct websrv_context *web)
return (PyObject *)ret;
}
+static void DEBUG_Print_PyError(int level, const char *message)
+{
+ PyObject *old_stderr, *new_stderr;
+ PyObject *sys_module;
+ PyObject *ptype, *pvalue, *ptb;
+
+ PyErr_Fetch(&ptype, &pvalue, &ptb);
+
+ DEBUG(0, ("WSGI: Server exception occurred: %s\n", message));
+
+ sys_module = PyImport_ImportModule("sys");
+ if (sys_module == NULL) {
+ DEBUG(0, ("Unable to obtain sys module while printing error"));
+ return;
+ }
+
+ old_stderr = PyObject_GetAttrString(sys_module, "stderr");
+ if (old_stderr == NULL) {
+ DEBUG(0, ("Unable to obtain old stderr"));
+ Py_DECREF(sys_module);
+ return;
+ }
+
+ new_stderr = Py_ErrorHttpStream();
+ if (new_stderr == NULL) {
+ DEBUG(0, ("Unable to create error stream"));
+ Py_DECREF(sys_module);
+ Py_DECREF(old_stderr);
+ return;
+ }
+
+ PyObject_SetAttrString(sys_module, "stderr", new_stderr);
+ Py_DECREF(new_stderr);
+
+ PyErr_Restore(ptype, pvalue, ptb);
+ PyErr_Print();
+
+ PyObject_SetAttrString(sys_module, "stderr", old_stderr);
+ Py_DECREF(old_stderr);
+
+ Py_DECREF(sys_module);
+}
+
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;
+ PyObject *py_val;
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));
+
+ py_val = Py_ErrorHttpStream();
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "wsgi.errors", py_val);
+ Py_DECREF(py_val);
+
+ py_val = Py_BuildValue("(i,i)", 1, 0);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "wsgi.version", py_val);
+ Py_DECREF(py_val);
PyDict_SetItemString(env, "wsgi.multithread", Py_False);
- PyDict_SetItemString(env, "wsgi.multiprocess", Py_True);
+ PyDict_SetItemString(env, "wsgi.multiprocess", Py_False);
PyDict_SetItemString(env, "wsgi.run_once", Py_False);
- PyDict_SetItemString(env, "SERVER_PROTOCOL", PyString_FromString("HTTP/1.0"));
+ py_val = PyStr_FromString("HTTP/1.0");
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "SERVER_PROTOCOL", py_val);
+ Py_DECREF(py_val);
if (content_length > 0) {
- PyDict_SetItemString(env, "CONTENT_LENGTH", PyLong_FromLong(content_length));
+ py_val = PyLong_FromLong(content_length);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "CONTENT_LENGTH", py_val);
+ Py_DECREF(py_val);
}
- PyDict_SetItemString(env, "REQUEST_METHOD", PyString_FromString(request_method));
-
+ py_val = PyStr_FromString(request_method);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "REQUEST_METHOD", py_val);
+ Py_DECREF(py_val);
+
+ /* There is always a single wsgi app to which all requests are redirected,
+ * so SCRIPT_NAME will be / */
+ py_val = PyStr_FromString("/");
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "SCRIPT_NAME", py_val);
+ Py_DECREF(py_val);
questionmark = strchr(request_string, '?');
if (questionmark == NULL) {
- PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromString(request_string));
+ py_val = PyStr_FromString(request_string);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "PATH_INFO", py_val);
+ Py_DECREF(py_val);
} else {
- PyDict_SetItemString(env, "QUERY_STRING", PyString_FromString(questionmark+1));
- PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromStringAndSize(request_string, questionmark-request_string));
+ py_val = PyStr_FromString(questionmark+1);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "QUERY_STRING", py_val);
+ Py_DECREF(py_val);
+ py_val = PyStr_FromStringAndSize(request_string, questionmark-request_string);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "PATH_INFO", py_val);
+ Py_DECREF(py_val);
}
-
- PyDict_SetItemString(env, "SERVER_NAME", PyString_FromString(servername));
- PyDict_SetItemString(env, "SERVER_PORT", PyInt_FromLong(serverport));
+
+ py_val = PyStr_FromString(servername);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "SERVER_NAME", py_val);
+ Py_DECREF(py_val);
+ py_val = PyStr_FromFormat("%d", serverport);
+ if (py_val == NULL) goto error;
+ PyDict_SetItemString(env, "SERVER_PORT", py_val);
+ Py_DECREF(py_val);
+
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 {
+ py_val = PyStr_FromString(hdr->value);
+ PyDict_SetItemString(env, "CONTENT_TYPE", py_val);
+ Py_DECREF(py_val);
+ } else {
if (asprintf(&name, "HTTP_%s", hdr->name) < 0) {
- Py_DECREF(env);
- Py_DECREF(inputstream);
PyErr_NoMemory();
- return NULL;
+ goto error;
}
- PyDict_SetItemString(env, name, PyString_FromString(hdr->value));
+ py_val = PyStr_FromString(hdr->value);
+ PyDict_SetItemString(env, name, py_val);
+ Py_DECREF(py_val);
free(name);
}
}
if (tls) {
- py_scheme = PyString_FromString("https");
+ py_scheme = PyStr_FromString("https");
} else {
- py_scheme = PyString_FromString("http");
+ py_scheme = PyStr_FromString("http");
}
+ if (py_scheme == NULL) goto error;
PyDict_SetItemString(env, "wsgi.url_scheme", py_scheme);
+ Py_DECREF(py_scheme);
return env;
+error:
+ Py_DECREF(env);
+ return NULL;
+}
+
+static void wsgi_serve_500(struct websrv_context *web)
+{
+ struct http_header *headers = NULL;
+ const char *contents[] = {
+ "An internal server error occurred while handling this request. ",
+ "Please refer to the server logs for more details. ",
+ NULL
+ };
+ int i;
+
+ websrv_output_headers(web, "500 Internal Server Error", headers);
+ for (i = 0; contents[i]; i++) {
+ websrv_output(web, contents[i], strlen(contents[i]));
+ }
}
static void wsgi_process_http_input(struct web_server_data *wdata,
struct tsocket_address *my_address = web->conn->local_address;
const char *addr = "0.0.0.0";
uint16_t port = 0;
- web_request_Object *py_web = PyObject_New(web_request_Object, &web_request_Type);
+ web_request_Object *py_web;
+ PyObject *py_input_stream;
+
+ py_web = PyObject_New(web_request_Object, &web_request_Type);
+ if (py_web == NULL) {
+ DEBUG_Print_PyError(0, "Unable to allocate web request");
+ return;
+ }
py_web->web = web;
if (tsocket_address_is_inet(my_address, "ip")) {
port = tsocket_address_inet_port(my_address);
}
+ py_input_stream = Py_InputHttpStream(web);
+ if (py_input_stream == NULL) {
+ DEBUG_Print_PyError(0, "unable to create python input stream");
+ return;
+ }
+
py_environ = create_environ(tls_enabled(web->conn->socket),
web->input.content_length,
web->input.headers,
web->input.post_request?"POST":"GET",
addr,
port,
- Py_InputHttpStream(web),
+ py_input_stream,
web->input.url
);
+
+ Py_DECREF(py_input_stream);
+
if (py_environ == NULL) {
- DEBUG(0, ("Unable to create WSGI environment object\n"));
+ DEBUG_Print_PyError(0, "Unable to create WSGI environment object");
+ wsgi_serve_500(web);
return;
}
py_environ, PyObject_GetAttrString((PyObject *)py_web, "start_response"));
if (result == NULL) {
- DEBUG(0, ("error while running WSGI code\n"));
+ DEBUG_Print_PyError(0, "error while handling request");
+ wsgi_serve_500(web);
return;
}
iter = PyObject_GetIter(result);
Py_DECREF(result);
+ if (iter == NULL) {
+ DEBUG_Print_PyError(0, "application did not return iterable");
+ wsgi_serve_500(web);
+ return;
+ }
+
/* Now, iter over all the data returned */
while ((item = PyIter_Next(iter))) {
- websrv_output(web, PyString_AsString(item), PyString_Size(item));
+ Py_ssize_t size = 0;
+ char *value = NULL;
+ int ret = PyBytes_AsStringAndSize(item, &value, &size);
+ if (ret == -1) {
+ DEBUG_Print_PyError(0, "application failed to "
+ "extract string from iterable");
+ wsgi_serve_500(web);
+ return;
+ }
+ websrv_output(web, value, size);
Py_DECREF(item);
}
bool wsgi_initialize(struct web_server_data *wdata)
{
- PyObject *py_swat;
+ PyObject *py_web_server;
Py_Initialize();
+ py_update_path(); /* Ensure that we have the Samba paths at
+ * the start of the sys.path() */
+
if (PyType_Ready(&web_request_Type) < 0)
return false;
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"));
+ py_web_server = PyImport_ImportModule("samba.web_server");
+ if (py_web_server == NULL) {
+ DEBUG_Print_PyError(0, "Unable to find web server");
return false;
}
- wdata->private_data = py_swat;
+ wdata->private_data = py_web_server;
return true;
}