mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-04-28 19:03:37 +00:00
351 lines
11 KiB
C
351 lines
11 KiB
C
#include "uwsgi_python.h"
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
extern struct uwsgi_python up;
|
|
extern struct uwsgi_plugin python_plugin;
|
|
extern PyTypeObject uwsgi_InputType;
|
|
|
|
/*
|
|
|
|
Albeit PEP 333/3333 is clear about what kind of return object we must
|
|
expect from a WSGI callable, we CAN use the buffer api to optimize for lower-level
|
|
returns type: bytes, bytearray, array.array
|
|
|
|
to enable this optimization add --wsgi-accept-buffer
|
|
|
|
NOTE: this is a violation of the standards !!! use only if you know the implications !!!
|
|
|
|
*/
|
|
|
|
int uwsgi_python_send_body(struct wsgi_request *wsgi_req, PyObject *chunk) {
|
|
char *content = NULL;
|
|
size_t content_len = 0;
|
|
|
|
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
|
Py_buffer pbuf;
|
|
int has_buffer = 0;
|
|
#endif
|
|
|
|
if (!up.wsgi_accept_buffer && !wsgi_req->is_raw) goto strict;
|
|
|
|
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
|
if (PyObject_CheckBuffer(chunk)) {
|
|
if (!PyObject_GetBuffer(chunk, &pbuf, PyBUF_SIMPLE)) {
|
|
content = (char *) pbuf.buf;
|
|
content_len = (size_t) pbuf.len;
|
|
has_buffer = 1;
|
|
goto found;
|
|
}
|
|
}
|
|
#else
|
|
if (PyObject_CheckReadBuffer(chunk)) {
|
|
#ifdef UWSGI_PYTHON_OLD
|
|
int buffer_len = 0;
|
|
if (!PyObject_AsCharBuffer(chunk, (const char **) &content, &buffer_len)) {
|
|
#else
|
|
if (!PyObject_AsCharBuffer(chunk, (const char **) &content, (Py_ssize_t *) &content_len)) {
|
|
#endif
|
|
PyErr_Clear();
|
|
goto found;
|
|
}
|
|
#ifdef UWSGI_PYTHON_OLD
|
|
content_len = buffer_len;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
strict:
|
|
// fallback
|
|
if (PyString_Check(chunk)) {
|
|
content = PyString_AsString(chunk);
|
|
content_len = PyString_Size(chunk);
|
|
}
|
|
|
|
found:
|
|
if (content) {
|
|
UWSGI_RELEASE_GIL
|
|
uwsgi_response_write_body_do(wsgi_req, content, content_len);
|
|
UWSGI_GET_GIL
|
|
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
|
if (has_buffer) PyBuffer_Release(&pbuf);
|
|
#endif
|
|
uwsgi_py_check_write_errors {
|
|
uwsgi_py_write_exception(wsgi_req);
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
this is a hack for supporting non-file object passed to wsgi.file_wrapper
|
|
*/
|
|
static void uwsgi_python_consume_file_wrapper_read(struct wsgi_request *wsgi_req, PyObject *pychunk) {
|
|
PyObject *read_method_args = NULL;
|
|
PyObject *read_method = PyObject_GetAttrString(pychunk, "read");
|
|
if (wsgi_req->sendfile_fd_chunk > 0) {
|
|
read_method_args = PyTuple_New(1);
|
|
PyTuple_SetItem(read_method_args, 0, PyInt_FromLong(wsgi_req->sendfile_fd_chunk));
|
|
}
|
|
else {
|
|
read_method_args = PyTuple_New(0);
|
|
}
|
|
for(;;) {
|
|
PyObject *read_method_output = PyEval_CallObject(read_method, read_method_args);
|
|
if (PyErr_Occurred()) {
|
|
uwsgi_manage_exception(wsgi_req, 0);
|
|
break;
|
|
}
|
|
if (!read_method_output) break;
|
|
if (PyString_Check(read_method_output)) {
|
|
char *content = PyString_AsString(read_method_output);
|
|
size_t content_len = PyString_Size(read_method_output);
|
|
if (content_len == 0) {
|
|
Py_DECREF(read_method_output);
|
|
break;
|
|
}
|
|
UWSGI_RELEASE_GIL
|
|
uwsgi_response_write_body_do(wsgi_req, content, content_len);
|
|
UWSGI_GET_GIL
|
|
}
|
|
Py_DECREF(read_method_output);
|
|
if (wsgi_req->sendfile_fd_chunk == 0) break;
|
|
}
|
|
Py_DECREF(read_method_args);
|
|
Py_DECREF(read_method);
|
|
}
|
|
|
|
void *uwsgi_request_subhandler_wsgi(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) {
|
|
|
|
|
|
PyObject *zero;
|
|
int i;
|
|
PyObject *pydictkey, *pydictvalue;
|
|
char *path_info;
|
|
|
|
for (i = 0; i < wsgi_req->var_cnt; i += 2) {
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("%.*s: %.*s\n", wsgi_req->hvec[i].iov_len, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i+1].iov_len, wsgi_req->hvec[i+1].iov_base);
|
|
#endif
|
|
#ifdef PYTHREE
|
|
pydictkey = PyUnicode_DecodeLatin1(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, NULL);
|
|
pydictvalue = PyUnicode_DecodeLatin1(wsgi_req->hvec[i + 1].iov_base, wsgi_req->hvec[i + 1].iov_len, NULL);
|
|
#else
|
|
pydictkey = PyString_FromStringAndSize(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len);
|
|
pydictvalue = PyString_FromStringAndSize(wsgi_req->hvec[i + 1].iov_base, wsgi_req->hvec[i + 1].iov_len);
|
|
#endif
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("%p %d %p %d\n", pydictkey, wsgi_req->hvec[i].iov_len, pydictvalue, wsgi_req->hvec[i + 1].iov_len);
|
|
#endif
|
|
PyDict_SetItem(wsgi_req->async_environ, pydictkey, pydictvalue);
|
|
Py_DECREF(pydictkey);
|
|
Py_DECREF(pydictvalue);
|
|
}
|
|
|
|
if (wsgi_req->uh->modifier1 == UWSGI_MODIFIER_MANAGE_PATH_INFO) {
|
|
wsgi_req->uh->modifier1 = python_plugin.modifier1;
|
|
pydictkey = PyDict_GetItemString(wsgi_req->async_environ, "SCRIPT_NAME");
|
|
if (pydictkey) {
|
|
if (PyString_Check(pydictkey)) {
|
|
pydictvalue = PyDict_GetItemString(wsgi_req->async_environ, "PATH_INFO");
|
|
if (pydictvalue) {
|
|
if (PyString_Check(pydictvalue)) {
|
|
path_info = PyString_AsString(pydictvalue);
|
|
PyDict_SetItemString(wsgi_req->async_environ, "PATH_INFO", PyString_FromString(path_info + PyString_Size(pydictkey)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// create wsgi.input custom object
|
|
wsgi_req->async_input = (PyObject *) PyObject_New(uwsgi_Input, &uwsgi_InputType);
|
|
((uwsgi_Input*)wsgi_req->async_input)->wsgi_req = wsgi_req;
|
|
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.input", wsgi_req->async_input);
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.file_wrapper", wi->sendfile);
|
|
|
|
if (uwsgi.async > 0) {
|
|
PyDict_SetItemString(wsgi_req->async_environ, "x-wsgiorg.fdevent.readable", wi->eventfd_read);
|
|
PyDict_SetItemString(wsgi_req->async_environ, "x-wsgiorg.fdevent.writable", wi->eventfd_write);
|
|
PyDict_SetItemString(wsgi_req->async_environ, "x-wsgiorg.fdevent.timeout", Py_None);
|
|
}
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.version", wi->gateway_version);
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.errors", wi->error);
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.run_once", Py_False);
|
|
|
|
|
|
|
|
if (uwsgi.threads > 1) {
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.multithread", Py_True);
|
|
}
|
|
else {
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.multithread", Py_False);
|
|
}
|
|
if (uwsgi.numproc == 1) {
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.multiprocess", Py_False);
|
|
}
|
|
else {
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.multiprocess", Py_True);
|
|
}
|
|
|
|
|
|
if (wsgi_req->scheme_len > 0) {
|
|
zero = UWSGI_PYFROMSTRINGSIZE(wsgi_req->scheme, wsgi_req->scheme_len);
|
|
}
|
|
else if (wsgi_req->https_len > 0) {
|
|
if (!strncasecmp(wsgi_req->https, "on", 2) || wsgi_req->https[0] == '1') {
|
|
zero = UWSGI_PYFROMSTRING("https");
|
|
}
|
|
else {
|
|
zero = UWSGI_PYFROMSTRING("http");
|
|
}
|
|
}
|
|
else {
|
|
zero = UWSGI_PYFROMSTRING("http");
|
|
}
|
|
PyDict_SetItemString(wsgi_req->async_environ, "wsgi.url_scheme", zero);
|
|
Py_DECREF(zero);
|
|
|
|
wsgi_req->async_app = wi->callable;
|
|
|
|
// export .env only in non-threaded mode
|
|
if (uwsgi.threads < 2) {
|
|
PyDict_SetItemString(up.embedded_dict, "env", wsgi_req->async_environ);
|
|
}
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.version", wi->uwsgi_version);
|
|
|
|
if (uwsgi.cores > 1) {
|
|
zero = PyInt_FromLong(wsgi_req->async_id);
|
|
PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.core", zero);
|
|
Py_DECREF(zero);
|
|
}
|
|
|
|
PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.node", wi->uwsgi_node);
|
|
|
|
// call
|
|
PyTuple_SetItem(wsgi_req->async_args, 0, wsgi_req->async_environ);
|
|
return python_call(wsgi_req->async_app, wsgi_req->async_args, uwsgi.catch_exceptions, wsgi_req);
|
|
}
|
|
|
|
int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) {
|
|
|
|
PyObject *pychunk;
|
|
|
|
// return or yield ?
|
|
// in strict mode we do not optimize apps directly returning strings (or bytes)
|
|
if (!up.wsgi_strict) {
|
|
if (uwsgi_python_send_body(wsgi_req, (PyObject *)wsgi_req->async_result)) goto clear;
|
|
}
|
|
|
|
if (wsgi_req->sendfile_obj == wsgi_req->async_result) {
|
|
if (wsgi_req->sendfile_fd >= 0) {
|
|
UWSGI_RELEASE_GIL
|
|
uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0);
|
|
UWSGI_GET_GIL
|
|
}
|
|
// we do not have an iterable, check for read() method
|
|
else if (PyObject_HasAttrString((PyObject *)wsgi_req->async_result, "read")) {
|
|
uwsgi_python_consume_file_wrapper_read(wsgi_req, (PyObject *)wsgi_req->async_result);
|
|
}
|
|
uwsgi_py_check_write_errors {
|
|
uwsgi_py_write_exception(wsgi_req);
|
|
}
|
|
goto clear;
|
|
}
|
|
|
|
// ok its a yield
|
|
if (!wsgi_req->async_placeholder) {
|
|
wsgi_req->async_placeholder = PyObject_GetIter(wsgi_req->async_result);
|
|
if (!wsgi_req->async_placeholder) {
|
|
goto exception;
|
|
}
|
|
if (uwsgi.async > 0) {
|
|
return UWSGI_AGAIN;
|
|
}
|
|
}
|
|
|
|
pychunk = PyIter_Next(wsgi_req->async_placeholder);
|
|
|
|
if (!pychunk) {
|
|
exception:
|
|
if (PyErr_Occurred()) {
|
|
uwsgi_manage_exception(wsgi_req, uwsgi.catch_exceptions);
|
|
}
|
|
goto clear;
|
|
}
|
|
|
|
|
|
|
|
|
|
int ret = uwsgi_python_send_body(wsgi_req, pychunk);
|
|
if (ret != 0) {
|
|
if (ret < 0) {
|
|
Py_DECREF(pychunk);
|
|
goto clear;
|
|
}
|
|
}
|
|
else if (wsgi_req->sendfile_obj == pychunk) {
|
|
if (wsgi_req->sendfile_fd >= 0) {
|
|
UWSGI_RELEASE_GIL
|
|
uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0);
|
|
UWSGI_GET_GIL
|
|
}
|
|
// we do not have an iterable, check for read() method
|
|
else if (PyObject_HasAttrString(pychunk, "read")) {
|
|
uwsgi_python_consume_file_wrapper_read(wsgi_req, pychunk);
|
|
}
|
|
|
|
uwsgi_py_check_write_errors {
|
|
uwsgi_py_write_exception(wsgi_req);
|
|
Py_DECREF(pychunk);
|
|
goto clear;
|
|
}
|
|
}
|
|
|
|
|
|
Py_DECREF(pychunk);
|
|
return UWSGI_AGAIN;
|
|
|
|
clear:
|
|
|
|
if (wsgi_req->sendfile_fd != -1) {
|
|
Py_DECREF((PyObject *)wsgi_req->async_sendfile);
|
|
}
|
|
|
|
if (wsgi_req->async_placeholder) {
|
|
// CALL close() ALWAYS if we are working with an iterator !!!
|
|
if (PyObject_HasAttrString((PyObject *)wsgi_req->async_result, "close")) {
|
|
PyObject *close_method = PyObject_GetAttrString((PyObject *)wsgi_req->async_result, "close");
|
|
PyObject *close_method_args = PyTuple_New(0);
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("calling close() for %.*s %p %p\n", wsgi_req->uri_len, wsgi_req->uri, close_method, close_method_args);
|
|
#endif
|
|
PyObject *close_method_output = PyEval_CallObject(close_method, close_method_args);
|
|
if (PyErr_Occurred()) {
|
|
uwsgi_manage_exception(wsgi_req, 0);
|
|
}
|
|
Py_DECREF(close_method_args);
|
|
Py_XDECREF(close_method_output);
|
|
Py_DECREF(close_method);
|
|
}
|
|
Py_DECREF((PyObject *)wsgi_req->async_placeholder);
|
|
}
|
|
|
|
Py_DECREF((PyObject *)wsgi_req->async_result);
|
|
PyErr_Clear();
|
|
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
|