diff --git a/hello_world.py b/hello_world.py index 9509c649..e5328f3e 100644 --- a/hello_world.py +++ b/hello_world.py @@ -1,5 +1,5 @@ import uwsgi -uwsgi.cache_set('/', "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello World from cache") +#uwsgi.cache_set('/', "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello World from cache") def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return "

Hello World

" diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index e357c05d..2a9649b9 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -22,9 +22,8 @@ PyMethodDef uwsgi_eventfd_read_method[] = { {"uwsgi_eventfd_read", py_eventfd_re PyMethodDef uwsgi_eventfd_write_method[] = { {"uwsgi_eventfd_write", py_eventfd_write, METH_VARARGS, ""}}; #endif -int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThreadState *interpreter) { +int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThreadState *interpreter, int app_type) { - PyObject *zero; PyObject *app_list = NULL, *applications = NULL; int id = uwsgi.apps_cnt; @@ -212,48 +211,23 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre } #endif - // check function args - // by defaut it is a WSGI app - wi->argc = 2; - zero = PyObject_GetAttrString(wi->callable, "__code__"); - if (!zero) { - zero = PyObject_GetAttrString(wi->callable, "__call__"); - if (zero) { - zero = PyObject_GetAttrString(wi->callable, "__code__"); - } - else { - uwsgi_log("WARNING: unable to get the number of callable args. Fallback to WSGI\n"); - } - } + wi->argc = 1; - // avoid __code__ attr error propagation - PyErr_Clear(); - - if (zero) { - zero = PyObject_GetAttrString(zero, "co_argcount"); - wi->argc = (int) PyInt_AsLong(zero); - } - - if (wi->argc == 2) { + if (app_type == PYTHON_APP_TYPE_WSGI) { #ifdef UWSGI_DEBUG - uwsgi_log("-- WSGI callable detected --\n"); + uwsgi_log("-- WSGI callable selected --\n"); #endif wi->request_subhandler = uwsgi_request_subhandler_wsgi; wi->response_subhandler = uwsgi_response_subhandler_wsgi; + wi->argc = 2; } -#ifdef UWSGI_WEB3 - else if (wi->argc == 1) { + else if (app_type == PYTHON_APP_TYPE_WEB3) { #ifdef UWSGI_DEBUG - uwsgi_log("-- Web3 callable detected --\n"); + uwsgi_log("-- Web3 callable selected --\n"); #endif wi->request_subhandler = uwsgi_request_subhandler_web3; wi->response_subhandler = uwsgi_response_subhandler_web3; } -#endif - else { - uwsgi_log("-- INVALID callable detected --\n"); - goto doh; - } #ifdef UWSGI_ASYNC wi->args = malloc(sizeof(PyObject*)*uwsgi.cores); @@ -270,7 +244,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre } // add start_response on WSGI app - if (wi->argc == 2) { + if (app_type == PYTHON_APP_TYPE_WSGI) { if (PyTuple_SetItem(wi->args[i], 1, up.wsgi_spitout)) { uwsgi_log("unable to set start_response in args tuple\n"); exit(1); @@ -280,7 +254,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre #else wi->wsgi_args = PyTuple_New(wi->argc); - if (wi->argc == 2) { + if (app_type == PYTHON_APP_TYPE_WSGI) { if (PyTuple_SetItem(wi->wsgi_args, 1, up.wsgi_spitout)) { uwsgi_log("unable to set start_response in args tuple\n"); exit(1); @@ -288,7 +262,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre } #endif - if (wi->argc == 2) { + if (app_type == PYTHON_APP_TYPE_WSGI) { #ifdef UWSGI_SENDFILE // prepare sendfile() for WSGI app wi->sendfile = PyCFunction_New(uwsgi_sendfile_method, NULL); @@ -329,12 +303,12 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre PyThreadState_Swap(up.main_thread); } - if (wi->argc == 1) { - uwsgi_log( "Web3 application %d (SCRIPT_NAME=%.*s) ready on interpreter %p", id, wi->mountpoint_len, wi->mountpoint, wi->interpreter); - } - else { + if (app_type == PYTHON_APP_TYPE_WSGI) { uwsgi_log( "WSGI application %d (SCRIPT_NAME=%.*s) ready on interpreter %p pid: %d", id, wi->mountpoint_len, wi->mountpoint, wi->interpreter, (int) getpid()); } + else if (app_type == PYTHON_APP_TYPE_WEB3) { + uwsgi_log( "Web3 application %d (SCRIPT_NAME=%.*s) ready on interpreter %p pid: %d", id, wi->mountpoint_len, wi->mountpoint, wi->interpreter, (int) getpid()); + } if (!wsgi_req->script_name_len) { uwsgi_rawlog(" (default app)"); @@ -355,7 +329,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre wsgi_req->script_name = PyString_AsString(app_mnt); wsgi_req->script_name_len = strlen(wsgi_req->script_name); - init_uwsgi_app(LOADER_CALLABLE, PyDict_GetItem(applications, app_mnt), wsgi_req, wi->interpreter); + init_uwsgi_app(LOADER_CALLABLE, PyDict_GetItem(applications, app_mnt), wsgi_req, wi->interpreter, app_type); } } diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index b0a567ac..fb52444e 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -31,6 +31,9 @@ struct option uwsgi_python_options[] = { {"pyargv", required_argument, 0, LONG_ARGS_PYARGV}, {"optimize", required_argument, 0, 'O'}, {"paste", required_argument, 0, LONG_ARGS_PASTE}, + {"web3", required_argument, 0, LONG_ARGS_WEB3}, + {"pump", required_argument, 0, LONG_ARGS_PUMP}, + {"wsgi-lite", required_argument, 0, LONG_ARGS_WSGI_LITE}, #ifdef UWSGI_INI {"ini-paste", required_argument, 0, LONG_ARGS_INI_PASTE}, #endif @@ -742,6 +745,15 @@ int uwsgi_python_manage_options(int i, char *optarg) { } return 1; #endif + case LONG_ARGS_WEB3: + up.web3 = optarg; + return 1; + case LONG_ARGS_PUMP: + up.pump = optarg; + return 1; + case LONG_ARGS_WSGI_LITE: + up.wsgi_lite = optarg; + return 1; case LONG_ARGS_PASTE: up.paste = optarg; return 1; @@ -758,9 +770,9 @@ int uwsgi_python_mount_app(char *mountpoint, char *app) { uwsgi.wsgi_req->script_name = mountpoint; uwsgi.wsgi_req->script_name_len = strlen(mountpoint); if (uwsgi.single_interpreter) { - return init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, up.main_thread); + return init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } - return init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, NULL); + return init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, NULL, PYTHON_APP_TYPE_WSGI); } @@ -889,17 +901,26 @@ void uwsgi_python_init_apps() { if (up.wsgi_config != NULL) { - init_uwsgi_app(LOADER_UWSGI, up.wsgi_config, uwsgi.wsgi_req, up.main_thread); + init_uwsgi_app(LOADER_UWSGI, up.wsgi_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.file_config != NULL) { - init_uwsgi_app(LOADER_FILE, up.file_config, uwsgi.wsgi_req, up.main_thread); + init_uwsgi_app(LOADER_FILE, up.file_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.paste != NULL) { - init_uwsgi_app(LOADER_PASTE, up.paste, uwsgi.wsgi_req, up.main_thread); + init_uwsgi_app(LOADER_PASTE, up.paste, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.eval != NULL) { - init_uwsgi_app(LOADER_EVAL, up.eval, uwsgi.wsgi_req, up.main_thread); + init_uwsgi_app(LOADER_EVAL, up.eval, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); + } + if (up.web3 != NULL) { + init_uwsgi_app(LOADER_UWSGI, up.web3, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WEB3); + } + if (up.pump != NULL) { + init_uwsgi_app(LOADER_UWSGI, up.pump, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_PUMP); + } + if (up.wsgi_lite != NULL) { + init_uwsgi_app(LOADER_UWSGI, up.wsgi_lite, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI_LITE); } if (uwsgi.profiler) { @@ -979,16 +1000,16 @@ int uwsgi_python_xml(char *node, char *content) { } if (!strcmp("script", node)) { - return init_uwsgi_app(LOADER_UWSGI, content, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_UWSGI, content, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } else if (!strcmp("file", node)) { - return init_uwsgi_app(LOADER_FILE, content, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_FILE, content, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } else if (!strcmp("eval", node)) { - return init_uwsgi_app(LOADER_EVAL, content, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_EVAL, content, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } else if (!strcmp("wsgi", node)) { - return init_uwsgi_app(LOADER_EVAL, content, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_EVAL, content, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } else if (!strcmp("module", node)) { uwsgi.wsgi_req->module = content; @@ -999,10 +1020,10 @@ int uwsgi_python_xml(char *node, char *content) { uwsgi.wsgi_req->callable++; uwsgi.wsgi_req->callable_len = strlen(uwsgi.wsgi_req->callable); uwsgi.wsgi_req->module_len = strlen(uwsgi.wsgi_req->module); - return init_uwsgi_app(LOADER_DYN, uwsgi.wsgi_req, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_DYN, uwsgi.wsgi_req, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } else { - return init_uwsgi_app(LOADER_UWSGI, content, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_UWSGI, content, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } return 1; } @@ -1014,7 +1035,7 @@ int uwsgi_python_xml(char *node, char *content) { else if (!strcmp("callable", node)) { uwsgi.wsgi_req->callable = content; uwsgi.wsgi_req->callable_len = strlen(content); - return init_uwsgi_app(LOADER_DYN, uwsgi.wsgi_req, uwsgi.wsgi_req, interpreter); + return init_uwsgi_app(LOADER_DYN, uwsgi.wsgi_req, uwsgi.wsgi_req, interpreter, PYTHON_APP_TYPE_WSGI); } return 0; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 37f4e243..4436fdd2 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -15,6 +15,15 @@ #define LONG_ARGS_RELOAD_OS_ENV LONG_ARGS_PYTHON_BASE + 5 #define LONG_ARGS_PYIMPORT LONG_ARGS_PYTHON_BASE + 6 #define LONG_ARGS_POST_PYMODULE_ALIAS LONG_ARGS_PYTHON_BASE + 7 +#define LONG_ARGS_WEB3 LONG_ARGS_PYTHON_BASE + 8 +#define LONG_ARGS_PUMP LONG_ARGS_PYTHON_BASE + 9 +#define LONG_ARGS_WSGI_LITE LONG_ARGS_PYTHON_BASE + 10 + +#define PYTHON_APP_TYPE_WSGI 0 +#define PYTHON_APP_TYPE_WEB3 1 +#define PYTHON_APP_TYPE_WSGI2 2 +#define PYTHON_APP_TYPE_PUMP 3 +#define PYTHON_APP_TYPE_WSGI_LITE 4 #if PY_MINOR_VERSION == 4 && PY_MAJOR_VERSION == 2 #define Py_ssize_t ssize_t @@ -83,6 +92,16 @@ PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, Py_ssize_t); #define LOADER_MAX 8 +typedef struct uwsgi_Input { + PyObject_HEAD + char readline[1024]; + size_t readline_size; + size_t readline_max_size; + size_t readline_pos; + size_t pos; + struct wsgi_request *wsgi_req; +} uwsgi_Input; + struct uwsgi_python { char *home; @@ -116,6 +135,9 @@ struct uwsgi_python { char *paste; char *eval; + char *web3; + char *pump; + char *wsgi_lite; char *callable; @@ -166,7 +188,7 @@ void uwsgi_paste_config(char *); void uwsgi_file_config(char *); void uwsgi_eval_config(char *); -int init_uwsgi_app(int, void *, struct wsgi_request *wsgi_req, PyThreadState *); +int init_uwsgi_app(int, void *, struct wsgi_request *, PyThreadState *, int); PyObject *py_eventfd_read(PyObject *, PyObject *); @@ -188,10 +210,8 @@ PyObject *py_uwsgi_spit(PyObject *, PyObject *); void init_pyargv(void); -#ifdef UWSGI_WEB3 void *uwsgi_request_subhandler_web3(struct wsgi_request *, struct uwsgi_app *); int uwsgi_response_subhandler_web3(struct wsgi_request *); -#endif PyObject *uwsgi_uwsgi_loader(void *); PyObject *uwsgi_dyn_loader(void *); diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index 4b58db8d..5f21c9e8 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -3,7 +3,7 @@ import os,sys from distutils import sysconfig NAME='python' -GCC_LIST = ['python_plugin', 'pyutils', 'pyloader', 'wsgi_handlers', 'wsgi_headers', 'wsgi_subhandler', 'gil', 'uwsgi_pymodule', 'profiler', 'symimporter'] +GCC_LIST = ['python_plugin', 'pyutils', 'pyloader', 'wsgi_handlers', 'wsgi_headers', 'wsgi_subhandler', 'web3_subhandler', 'gil', 'uwsgi_pymodule', 'profiler', 'symimporter'] CFLAGS = ['-I' + sysconfig.get_python_inc(), '-I' + sysconfig.get_python_inc(plat_specific=True) ] LDFLAGS = [] diff --git a/plugins/python/web3_subhandler.c b/plugins/python/web3_subhandler.c index d6a108cc..c1964f6e 100644 --- a/plugins/python/web3_subhandler.c +++ b/plugins/python/web3_subhandler.c @@ -1,20 +1,70 @@ -#include "uwsgi.h" +#include "uwsgi_python.h" extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; +extern PyTypeObject uwsgi_InputType; void *uwsgi_request_subhandler_web3(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { - PyObject *zero, *wsgi_socket; + PyObject *zero; - wsgi_socket = PyFile_FromFile(wsgi_req->async_post, "web3_input", "r", NULL); - PyDict_SetItemString(wsgi_req->async_environ, "web3.input", wsgi_socket); - Py_DECREF(wsgi_socket); + int i; + PyObject *pydictkey, *pydictvalue; + char *path_info; - zero = PyTuple_New(2); - PyTuple_SetItem(zero, 0, PyInt_FromLong(1)); - PyTuple_SetItem(zero, 1, PyInt_FromLong(0)); - PyDict_SetItemString(wsgi_req->async_environ, "web3.version", zero); - Py_DECREF(zero); + 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 + 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 = 0; + 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))); + } + } + } + } + } + + // if async_post is mapped as a file, directly use it as wsgi.input + if (wsgi_req->async_post) { +#ifdef PYTHREE + wsgi_req->async_input = PyFile_FromFd(fileno(wsgi_req->async_post), "web3_input", "rb", 0, NULL, NULL, NULL, 0); +#else + wsgi_req->async_input = PyFile_FromFile(wsgi_req->async_post, "web3_input", "r", NULL); +#endif + } + else { + // 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; + ((uwsgi_Input*)wsgi_req->async_input)->pos = 0; + ((uwsgi_Input*)wsgi_req->async_input)->readline_pos = 0; + ((uwsgi_Input*)wsgi_req->async_input)->readline_max_size = 0; + + } + + PyDict_SetItemString(wsgi_req->async_environ, "web3.input", wsgi_req->async_input); + + PyDict_SetItemString(wsgi_req->async_environ, "web3.version", wi->uwsgi_version); zero = PyFile_FromFile(stderr, "web3_input", "w", NULL); PyDict_SetItemString(wsgi_req->async_environ, "web3.errors", zero); @@ -48,12 +98,30 @@ void *uwsgi_request_subhandler_web3(struct wsgi_request *wsgi_req, struct uwsgi_ Py_DECREF(zero); - wsgi_req->async_app = wi->wsgi_callable; + wsgi_req->async_app = wi->callable; - PyDict_SetItemString(uwsgi.embedded_dict, "env", wsgi_req->async_environ); + // export .env only in non-threaded mode + if (uwsgi.threads < 2) { + PyDict_SetItemString(up.embedded_dict, "env", wsgi_req->async_environ); + } - // TODO: fix this - //PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.version", uwsgi_version); + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.version", wi->uwsgi_version); + + if (uwsgi.cores > 1) { + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.core", PyInt_FromLong(wsgi_req->async_id)); + } + + // cache this ? + if (uwsgi.cluster_fd >= 0) { + zero = PyString_FromString(uwsgi.cluster); + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.cluster", zero); + Py_DECREF(zero); + zero = PyString_FromString(uwsgi.hostname); + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.cluster_node", zero); + Py_DECREF(zero); + } + + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.node", wi->uwsgi_node); // call @@ -68,16 +136,7 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { PyObject *pychunk; ssize_t wsize; - // return or yield ? (PyString on python2 PyBytes on python3) - if (PyString_Check((PyObject *)wsgi_req->async_result)) { - if ((wsize = write(wsgi_req->poll.fd, PyString_AsString(wsgi_req->async_result), PyString_Size(wsgi_req->async_result))) < 0) { - uwsgi_error("write()"); - goto clear; - } - wsgi_req->response_size += wsize; - goto clear; - } - + UWSGI_GET_GIL // ok its a yield if (!wsgi_req->async_placeholder) { @@ -86,19 +145,36 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { uwsgi_log("invalid Web3 response.\n"); goto clear; } - if (py_uwsgi_spit(NULL, (PyObject *)wsgi_req->async_result) == Py_None) { + PyObject *spit_args = PyTuple_New(2); + PyTuple_SetItem(spit_args, 0, PyTuple_GetItem((PyObject *)wsgi_req->async_result, 1)); + PyTuple_SetItem(spit_args, 1, PyTuple_GetItem((PyObject *)wsgi_req->async_result, 2)); + Py_INCREF((PyObject *)wsgi_req->async_result); + + if (py_uwsgi_spit(NULL, spit_args) == Py_None) { + Py_DECREF(spit_args); goto clear; } + Py_DECREF(spit_args); - wsgi_req->async_result = PyTuple_GetItem((PyObject *)wsgi_req->async_result, 0); + wsgi_req->async_placeholder = PyTuple_GetItem((PyObject *)wsgi_req->async_result, 0); - wsgi_req->async_placeholder = PyObject_GetIter( (PyObject *)wsgi_req->async_result ); + if (PyString_Check((PyObject *)wsgi_req->async_placeholder)) { + if ((wsize = wsgi_req->socket->proto_write(wsgi_req, PyString_AsString(wsgi_req->async_placeholder), PyString_Size(wsgi_req->async_placeholder))) < 0) { + uwsgi_error("write()"); + goto clear; + } + wsgi_req->response_size += wsize; + goto clear; + } + + wsgi_req->async_placeholder = PyObject_GetIter( (PyObject *)wsgi_req->async_placeholder ); if (!wsgi_req->async_placeholder) { - goto clear2; + goto clear; } #ifdef UWSGI_ASYNC if (uwsgi.async > 1) { + UWSGI_RELEASE_GIL return UWSGI_AGAIN; } } @@ -121,7 +197,7 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { if (PyString_Check(pychunk)) { - if ((wsize = write(wsgi_req->poll.fd, PyString_AsString(pychunk), PyString_Size(pychunk))) < 0) { + if ((wsize = wsgi_req->socket->proto_write(wsgi_req, PyString_AsString(pychunk), PyString_Size(pychunk))) < 0) { uwsgi_error("write()"); Py_DECREF(pychunk); goto clear; @@ -131,34 +207,22 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { Py_DECREF(pychunk); + UWSGI_RELEASE_GIL return UWSGI_AGAIN; clear: + if (wsgi_req->async_input) { + Py_DECREF((PyObject *)wsgi_req->async_input); + } if (wsgi_req->async_environ) { PyDict_Clear(wsgi_req->async_environ); } - if (wsgi_req->async_post && !wsgi_req->fd_closed) { - fclose(wsgi_req->async_post); - if (!uwsgi.post_buffering || wsgi_req->post_cl <= (size_t) uwsgi.post_buffering) { - wsgi_req->fd_closed = 1; - } - } Py_XDECREF((PyObject *)wsgi_req->async_placeholder); -clear2: + Py_DECREF((PyObject *)wsgi_req->async_result); PyErr_Clear(); -#ifdef UWSGI_DEBUG - if (wsgi_req->async_placeholder) { - uwsgi_debug("wsgi_req->async_placeholder: %d\n", ((PyObject *)wsgi_req->async_placeholder)->ob_refcnt); - } - if (wsgi_req->async_result) { - uwsgi_debug("wsgi_req->async_result: %d\n", ((PyObject *)wsgi_req->async_result)->ob_refcnt); - } - if (wsgi_req->async_app) { - uwsgi_debug("wsgi_req->async_app: %d\n", ((PyObject *)wsgi_req->async_app)->ob_refcnt); - } -#endif + UWSGI_RELEASE_GIL return UWSGI_OK; } diff --git a/plugins/python/wsgi_handlers.c b/plugins/python/wsgi_handlers.c index 5c4eb6d6..ce3fbbbf 100644 --- a/plugins/python/wsgi_handlers.c +++ b/plugins/python/wsgi_handlers.c @@ -4,16 +4,6 @@ extern struct uwsgi_server uwsgi; extern struct uwsgi_python up; -typedef struct uwsgi_Input { - PyObject_HEAD - char readline[1024]; - size_t readline_size; - size_t readline_max_size; - size_t readline_pos; - size_t pos; - struct wsgi_request *wsgi_req; -} uwsgi_Input; - PyObject *uwsgi_Input_iter(PyObject *self) { Py_INCREF(self); return self; @@ -62,7 +52,7 @@ PyObject *uwsgi_Input_getline(uwsgi_Input *self) { else { rlen = read(wsgi_req->poll.fd, self->readline, 1024); } - if (rlen < 0) { + if (rlen <= 0) { UWSGI_GET_GIL return PyErr_Format(PyExc_IOError, "error reading wsgi.input data"); } @@ -166,7 +156,7 @@ static PyObject *uwsgi_Input_read(uwsgi_Input *self, PyObject *args) { } rlen = read(self->wsgi_req->poll.fd, tmp_buf+tmp_pos, remains); - if (rlen < 0) { + if (rlen <= 0) { free(tmp_buf); UWSGI_GET_GIL return PyErr_Format(PyExc_IOError, "error reading wsgi.input data"); @@ -327,11 +317,6 @@ PyObject *py_eventfd_write(PyObject * self, PyObject * args) { int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) { - int i; - - PyObject *pydictkey, *pydictvalue; - - char *path_info; struct uwsgi_app *wi; int tmp_stderr; @@ -406,10 +391,10 @@ int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) { UWSGI_GET_GIL if (uwsgi.single_interpreter) { - wsgi_req->app_id = init_uwsgi_app(LOADER_DYN, (void *) wsgi_req, wsgi_req, up.main_thread); + wsgi_req->app_id = init_uwsgi_app(LOADER_DYN, (void *) wsgi_req, wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } else { - wsgi_req->app_id = init_uwsgi_app(LOADER_DYN, (void *) wsgi_req, wsgi_req, NULL); + wsgi_req->app_id = init_uwsgi_app(LOADER_DYN, (void *) wsgi_req, wsgi_req, NULL, PYTHON_APP_TYPE_WSGI); } UWSGI_RELEASE_GIL if (uwsgi.threads > 1) { @@ -485,60 +470,6 @@ int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) { Py_INCREF((PyObject *)wsgi_req->async_environ); - 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 - 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 = 0; - 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))); - } - } - } - } - } - - - - // if async_post is mapped as a file, directly use it as wsgi.input - if (wsgi_req->async_post) { -#ifdef PYTHREE - wsgi_req->async_input = PyFile_FromFd(fileno(wsgi_req->async_post), "wsgi_input", "rb", 0, NULL, NULL, NULL, 0); -#else - wsgi_req->async_input = PyFile_FromFile(wsgi_req->async_post, "wsgi_input", "r", NULL); -#endif - } - else { - // 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; - ((uwsgi_Input*)wsgi_req->async_input)->pos = 0; - ((uwsgi_Input*)wsgi_req->async_input)->readline_pos = 0; - ((uwsgi_Input*)wsgi_req->async_input)->readline_max_size = 0; - - } - - PyDict_SetItemString(wsgi_req->async_environ, "wsgi.input", wsgi_req->async_input); - wsgi_req->async_result = wi->request_subhandler(wsgi_req, wi); UWSGI_RELEASE_GIL diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index 1dd2a99a..ee7ab2b1 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -2,10 +2,66 @@ extern struct uwsgi_server uwsgi; extern struct uwsgi_python up; +extern PyTypeObject uwsgi_InputType; 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 + 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 = 0; + 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))); + } + } + } + } + } + + // if async_post is mapped as a file, directly use it as wsgi.input + if (wsgi_req->async_post) { +#ifdef PYTHREE + wsgi_req->async_input = PyFile_FromFd(fileno(wsgi_req->async_post), "wsgi_input", "rb", 0, NULL, NULL, NULL, 0); +#else + wsgi_req->async_input = PyFile_FromFile(wsgi_req->async_post, "wsgi_input", "r", NULL); +#endif + } + else { + // 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; + ((uwsgi_Input*)wsgi_req->async_input)->pos = 0; + ((uwsgi_Input*)wsgi_req->async_input)->readline_pos = 0; + ((uwsgi_Input*)wsgi_req->async_input)->readline_max_size = 0; + + } + + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.input", wsgi_req->async_input); #ifdef UWSGI_SENDFILE PyDict_SetItemString(wsgi_req->async_environ, "wsgi.file_wrapper", wi->sendfile); diff --git a/tests/web3.py b/tests/web3.py new file mode 100644 index 00000000..2370e63c --- /dev/null +++ b/tests/web3.py @@ -0,0 +1,9 @@ +class AppClass(object): + + def __call__(self, environ): + status = b'200 OK' + headers = [(b'Content-type', b'text/plain')] + body = [b'Hello world!\n'] + return body, status, headers + +application = AppClass()