2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
6 Implementation of the WSGI interface described in PEP0333
7 (http://www.python.org/dev/peps/pep-0333)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "web_server/web_server.h"
25 #include "../lib/util/dlinklist.h"
26 #include "../lib/util/data_blob.h"
27 #include "lib/tls/tls.h"
30 #ifndef Py_RETURN_NONE
31 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
36 struct websrv_context
*web
;
39 static PyObject
*start_response(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
41 PyObject
*response_header
, *exc_info
= NULL
;
44 const char *kwnames
[] = {
45 "status", "response_header", "exc_info", NULL
47 web_request_Object
*py_web
= (web_request_Object
*)self
;
48 struct websrv_context
*web
= py_web
->web
;
49 struct http_header
*headers
= NULL
;
51 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "sO|O:start_response", discard_const_p(char *, kwnames
), &status
, &response_header
, &exc_info
)) {
57 if (!PyList_Check(response_header
)) {
58 PyErr_SetString(PyExc_TypeError
, "response_header should be list");
62 for (i
= 0; i
< PyList_Size(response_header
); i
++) {
63 struct http_header
*hdr
= talloc_zero(web
, struct http_header
);
64 PyObject
*item
= PyList_GetItem(response_header
, i
);
65 PyObject
*py_name
, *py_value
;
67 if (!PyTuple_Check(item
)) {
68 PyErr_SetString(PyExc_TypeError
, "Expected tuple");
72 if (PyTuple_Size(item
) != 2) {
73 PyErr_SetString(PyExc_TypeError
, "header tuple has invalid size, expected 2");
77 py_name
= PyTuple_GetItem(item
, 0);
79 if (!PyString_Check(py_name
)) {
80 PyErr_SetString(PyExc_TypeError
, "header name should be string");
84 py_value
= PyTuple_GetItem(item
, 1);
85 if (!PyString_Check(py_value
)) {
86 PyErr_SetString(PyExc_TypeError
, "header value should be string");
90 hdr
->name
= talloc_strdup(hdr
, PyString_AsString(py_name
));
91 hdr
->value
= talloc_strdup(hdr
, PyString_AsString(py_value
));
92 DLIST_ADD(headers
, hdr
);
95 websrv_output_headers(web
, status
, headers
);
100 static PyMethodDef web_request_methods
[] = {
101 { "start_response", (PyCFunction
)start_response
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
106 PyTypeObject web_request_Type
= {
107 PyObject_HEAD_INIT(NULL
) 0,
108 .tp_name
= "wsgi.Request",
109 .tp_methods
= web_request_methods
,
110 .tp_basicsize
= sizeof(web_request_Object
),
111 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
116 } error_Stream_Object
;
118 static PyObject
*py_error_flush(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
120 /* Nothing to do here */
124 static PyObject
*py_error_write(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
126 const char *kwnames
[] = { "str", NULL
};
129 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "s:write", discard_const_p(char *, kwnames
), &str
)) {
133 DEBUG(0, ("WSGI App: %s", str
));
138 static PyObject
*py_error_writelines(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
140 const char *kwnames
[] = { "seq", NULL
};
141 PyObject
*seq
= NULL
, *item
;
143 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "O:writelines", discard_const_p(char *, kwnames
), &seq
)) {
147 while ((item
= PyIter_Next(seq
))) {
148 char *str
= PyString_AsString(item
);
150 DEBUG(0, ("WSGI App: %s", str
));
156 static PyMethodDef error_Stream_methods
[] = {
157 { "flush", (PyCFunction
)py_error_flush
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
158 { "write", (PyCFunction
)py_error_write
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
159 { "writelines", (PyCFunction
)py_error_writelines
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
160 { NULL
, NULL
, 0, NULL
}
163 PyTypeObject error_Stream_Type
= {
164 PyObject_HEAD_INIT(NULL
) 0,
165 .tp_name
= "wsgi.ErrorStream",
166 .tp_basicsize
= sizeof(error_Stream_Object
),
167 .tp_methods
= error_Stream_methods
,
168 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
173 struct websrv_context
*web
;
175 } input_Stream_Object
;
177 static PyObject
*py_input_read(PyObject
*_self
, PyObject
*args
, PyObject
*kwargs
)
179 const char *kwnames
[] = { "size", NULL
};
181 input_Stream_Object
*self
= (input_Stream_Object
*)_self
;
184 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|i", discard_const_p(char *, kwnames
), &size
))
187 /* Don't read beyond buffer boundaries */
189 size
= self
->web
->input
.partial
.length
-self
->offset
;
191 size
= MIN(size
, self
->web
->input
.partial
.length
-self
->offset
);
193 ret
= PyString_FromStringAndSize((char *)self
->web
->input
.partial
.data
+self
->offset
, size
);
194 self
->offset
+= size
;
199 static PyObject
*py_input_readline(PyObject
*_self
)
202 PyErr_SetString(PyExc_NotImplementedError
,
203 "readline() not yet implemented");
207 static PyObject
*py_input_readlines(PyObject
*_self
, PyObject
*args
, PyObject
*kwargs
)
209 const char *kwnames
[] = { "hint", NULL
};
212 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|i", discard_const_p(char *, kwnames
), &hint
))
216 PyErr_SetString(PyExc_NotImplementedError
,
217 "readlines() not yet implemented");
221 static PyObject
*py_input___iter__(PyObject
*_self
)
224 PyErr_SetString(PyExc_NotImplementedError
,
225 "__iter__() not yet implemented");
229 static PyMethodDef input_Stream_methods
[] = {
230 { "read", (PyCFunction
)py_input_read
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
231 { "readline", (PyCFunction
)py_input_readline
, METH_NOARGS
, NULL
},
232 { "readlines", (PyCFunction
)py_input_readlines
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
233 { "__iter__", (PyCFunction
)py_input___iter__
, METH_NOARGS
, NULL
},
234 { NULL
, NULL
, 0, NULL
}
237 PyTypeObject input_Stream_Type
= {
238 PyObject_HEAD_INIT(NULL
) 0,
239 .tp_name
= "wsgi.InputStream",
240 .tp_basicsize
= sizeof(input_Stream_Object
),
241 .tp_methods
= input_Stream_methods
,
242 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
245 static PyObject
*Py_InputHttpStream(struct websrv_context
*web
)
247 input_Stream_Object
*ret
= PyObject_New(input_Stream_Object
, &input_Stream_Type
);
250 return (PyObject
*)ret
;
253 static PyObject
*Py_ErrorHttpStream(void)
255 error_Stream_Object
*ret
= PyObject_New(error_Stream_Object
, &error_Stream_Type
);
256 return (PyObject
*)ret
;
259 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
)
262 PyObject
*errorstream
;
264 struct http_header
*hdr
;
272 errorstream
= Py_ErrorHttpStream();
273 if (errorstream
== NULL
) {
275 Py_DECREF(inputstream
);
279 PyDict_SetItemString(env
, "wsgi.input", inputstream
);
280 PyDict_SetItemString(env
, "wsgi.errors", errorstream
);
281 PyDict_SetItemString(env
, "wsgi.version", Py_BuildValue("(i,i)", 1, 0));
282 PyDict_SetItemString(env
, "wsgi.multithread", Py_False
);
283 PyDict_SetItemString(env
, "wsgi.multiprocess", Py_True
);
284 PyDict_SetItemString(env
, "wsgi.run_once", Py_False
);
285 PyDict_SetItemString(env
, "SERVER_PROTOCOL", PyString_FromString("HTTP/1.0"));
286 if (content_length
> 0) {
287 PyDict_SetItemString(env
, "CONTENT_LENGTH", PyLong_FromLong(content_length
));
289 PyDict_SetItemString(env
, "REQUEST_METHOD", PyString_FromString(request_method
));
291 questionmark
= strchr(request_string
, '?');
292 if (questionmark
== NULL
) {
293 PyDict_SetItemString(env
, "SCRIPT_NAME", PyString_FromString(request_string
));
295 PyDict_SetItemString(env
, "QUERY_STRING", PyString_FromString(questionmark
+1));
296 PyDict_SetItemString(env
, "SCRIPT_NAME", PyString_FromStringAndSize(request_string
, questionmark
-request_string
));
299 PyDict_SetItemString(env
, "SERVER_NAME", PyString_FromString(servername
));
300 PyDict_SetItemString(env
, "SERVER_PORT", PyInt_FromLong(serverport
));
301 for (hdr
= headers
; hdr
; hdr
= hdr
->next
) {
303 if (!strcasecmp(hdr
->name
, "Content-Type")) {
304 PyDict_SetItemString(env
, "CONTENT_TYPE", PyString_FromString(hdr
->value
));
306 asprintf(&name
, "HTTP_%s", hdr
->name
);
307 PyDict_SetItemString(env
, name
, PyString_FromString(hdr
->value
));
313 py_scheme
= PyString_FromString("https");
315 py_scheme
= PyString_FromString("http");
317 PyDict_SetItemString(env
, "wsgi.url_scheme", py_scheme
);
322 static void wsgi_process_http_input(struct web_server_data
*wdata
,
323 struct websrv_context
*web
)
325 PyObject
*py_environ
, *result
, *item
, *iter
;
326 PyObject
*request_handler
= wdata
->private;
327 struct socket_address
*socket_address
;
329 web_request_Object
*py_web
= PyObject_New(web_request_Object
, &web_request_Type
);
332 socket_address
= socket_get_my_addr(web
->conn
->socket
, web
);
333 py_environ
= create_environ(tls_enabled(web
->conn
->socket
),
334 web
->input
.content_length
,
336 web
->input
.post_request
?"POST":"GET",
337 socket_address
->addr
,
338 socket_address
->port
,
339 Py_InputHttpStream(web
),
342 if (py_environ
== NULL
) {
343 DEBUG(0, ("Unable to create WSGI environment object\n"));
347 result
= PyObject_CallMethod(request_handler
, discard_const_p(char, "__call__"), discard_const_p(char, "OO"),
348 py_environ
, PyObject_GetAttrString((PyObject
*)py_web
, "start_response"));
350 if (result
== NULL
) {
351 DEBUG(0, ("error while running WSGI code\n"));
355 iter
= PyObject_GetIter(result
);
358 /* Now, iter over all the data returned */
360 while ((item
= PyIter_Next(iter
))) {
361 websrv_output(web
, PyString_AsString(item
), PyString_Size(item
));
368 bool wsgi_initialize(struct web_server_data
*wdata
)
374 if (PyType_Ready(&web_request_Type
) < 0)
377 if (PyType_Ready(&input_Stream_Type
) < 0)
380 if (PyType_Ready(&error_Stream_Type
) < 0)
383 wdata
->http_process_input
= wsgi_process_http_input
;
384 py_swat
= PyImport_Import(PyString_FromString("swat"));
385 if (py_swat
== NULL
) {
386 DEBUG(0, ("Unable to find SWAT\n"));
389 wdata
->private = py_swat
;