diff --git a/buildconf/default.ini b/buildconf/default.ini new file mode 100644 index 00000000..039dd36f --- /dev/null +++ b/buildconf/default.ini @@ -0,0 +1,33 @@ +[uwsgi] +xml = true +ini = true +snmp = true +sctp = false +erlang = false +spooler = false +embedded = false +udp = true +multicast = false +threading = true +sendfile = true +nagios = true +proxy = true +minterpreters = true +async = true +ugreen = false +http = true +evdis = false +ldap = true +routing = false +stackless = false +debug = true +unbit = false +xml_implementation = libxml2 +plugins = python +bin_name = uwsgi +plugin_dir = . +embedded_plugins = + +[python] +paste = true +web3 = true diff --git a/plugins/python/.python_plugin.c.swp b/plugins/python/.python_plugin.c.swp new file mode 100644 index 00000000..7197237d Binary files /dev/null and b/plugins/python/.python_plugin.c.swp differ diff --git a/plugins/python/gil.c b/plugins/python/gil.c new file mode 100644 index 00000000..a713903f --- /dev/null +++ b/plugins/python/gil.c @@ -0,0 +1,19 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +void gil_real_get() { + PyEval_AcquireLock(); + PyThreadState_Swap((PyThreadState *) pthread_getspecific(up.upt_save_key)); +} + +void gil_real_release() { + pthread_setspecific(up.upt_save_key, (void *) PyThreadState_Swap(NULL)); + PyEval_ReleaseLock(); +} + + + +void gil_fake_get() {} +void gil_fake_release() {} diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c new file mode 100644 index 00000000..953cab99 --- /dev/null +++ b/plugins/python/pyloader.c @@ -0,0 +1,593 @@ +#include "uwsgi_python.h" + +/* notes + + +exit(1) on every malloc error: apps can be dinamically loaded so on memory problem +it is better to let the master process manager respawn the worker. + + +*/ + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +#ifdef UWSGI_SENDFILE +PyMethodDef uwsgi_sendfile_method[] = {{"uwsgi_sendfile", py_uwsgi_sendfile, METH_VARARGS, ""}}; +#endif + +#ifdef UWSGI_ASYNC +PyMethodDef uwsgi_eventfd_read_method[] = { {"uwsgi_eventfd_read", py_eventfd_read, METH_VARARGS, ""}}; +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, int new_interpreter) { + + PyObject *zero; + + int id = uwsgi.apps_cnt; + +#ifdef UWSGI_ASYNC + int i; +#endif + + char *mountpoint; + + struct uwsgi_app *wi; + + + if (wsgi_req->script_name_len == 0) { + wsgi_req->script_name = ""; + if (!uwsgi.vhost) id = 0; + } + else if (wsgi_req->script_name_len == 1) { + if (wsgi_req->script_name[0] == '/') { + if (!uwsgi.vhost) id = 0; + } + } + + + if (uwsgi.vhost && wsgi_req->host_len > 0) { + mountpoint = uwsgi_concat3n(wsgi_req->host, wsgi_req->host_len, "|", 1, wsgi_req->script_name, wsgi_req->script_name_len); + } + else { + mountpoint = uwsgi_strncopy(wsgi_req->script_name, wsgi_req->script_name_len); + } + + if (uwsgi_get_app_id(mountpoint, strlen(mountpoint)) != -1) { + uwsgi_log( "mountpoint %.*s already configured. skip.\n", strlen(mountpoint), mountpoint); + free(mountpoint); + return -1; + } + + + wi = &uwsgi.apps[id]; + + memset(wi, 0, sizeof(struct uwsgi_app)); + wi->mountpoint = mountpoint; + wi->mountpoint_len = strlen(mountpoint); + + // dynamic chdir ? + if (wsgi_req->chdir_len > 0) { + wi->chdir = uwsgi_strncopy(wsgi_req->chdir, wsgi_req->chdir_len); +#ifdef UWSGI_DEBUG + uwsgi_debug("chdir to %s\n", wi->chdir); +#endif + if (chdir(wi->chdir)) { + uwsgi_error("chdir()"); + } + } + + // Initialize a new environment for the new interpreter + + if (new_interpreter && id) { + wi->interpreter = Py_NewInterpreter(); + if (!wi->interpreter) { + uwsgi_log( "unable to initialize the new python interpreter\n"); + exit(1); + } + PyThreadState_Swap(wi->interpreter); + init_pyargv(); + +#ifdef UWSGI_EMBEDDED + // we need to inizialize an embedded module for every interpreter + init_uwsgi_embedded_module(); +#endif + init_uwsgi_vars(); + + } + else { + wi->interpreter = up.main_thread; + } + + + wi->callable = up.loaders[loader](arg1); + + if (!wi->callable) { + uwsgi_log("unable to load app SCRIPT_NAME=%s\n", mountpoint); + goto doh; + } + +#ifdef UWSGI_ASYNC + wi->environ = malloc(sizeof(PyObject*)*uwsgi.cores); + if (!wi->environ) { + uwsgi_error("malloc()"); + exit(1); + } + + for(i=0;ienviron[i] = PyDict_New(); + if (!wi->environ[i]) { + uwsgi_log("unable to allocate new env dictionary for app\n"); + exit(1); + } + } +#else + wi->environ = PyDict_New(); + if (!wi->environ) { + uwsgi_log("unable to allocate new env dictionary for app\n"); + exit(1); + } +#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"); + } + } + + // 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) { +#ifdef UWSGI_DEBUG + uwsgi_log("-- WSGI callable detected --\n"); +#endif + wi->request_subhandler = uwsgi_request_subhandler_wsgi; + wi->response_subhandler = uwsgi_response_subhandler_wsgi; + } +#ifdef UWSGI_WEB3 + else if (wi->argc == 1) { +#ifdef UWSGI_DEBUG + uwsgi_log("-- Web3 callable detected --\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); + if (!wi->args) { + uwsgi_error("malloc()"); + exit(1); + } + + for(i=0;iargs[i] = PyTuple_New(wi->argc); + if (!wi->args[i]) { + uwsgi_log("unable to allocate new tuple for app args\n"); + exit(1); + } + + // add start_response on WSGI app + if (wi->argc == 2) { + if (PyTuple_SetItem(wi->args[i], 1, up.wsgi_spitout)) { + uwsgi_log("unable to set start_response in args tuple\n"); + exit(1); + } + } + } +#else + + wi->wsgi_args = PyTuple_New(wi->argc); + if (wi->argc == 2) { + if (PyTuple_SetItem(wi->wsgi_args, 1, up.wsgi_spitout)) { + uwsgi_log("unable to set start_response in args tuple\n"); + exit(1); + } + } +#endif + + if (wi->argc == 2) { +#ifdef UWSGI_SENDFILE + // prepare sendfile() for WSGI app + wi->sendfile = PyCFunction_New(uwsgi_sendfile_method, NULL); +#endif + +#ifdef UWSGI_ASYNC + wi->eventfd_read = PyCFunction_New(uwsgi_eventfd_read_method, NULL); + wi->eventfd_write = PyCFunction_New(uwsgi_eventfd_write_method, NULL); +#endif + } + + if (new_interpreter && id) { + // if we have multiple threads we need to initialize a PyThreadState for each one + if (uwsgi.threads > 1) { + for(i=0;its[id] = PyThreadState_New( ((PyThreadState *)wi->interpreter)->interp); + if (!uwsgi.workers[uwsgi.mywid].cores[i]->ts[id]) { + uwsgi_log("unable to allocate new PyThreadState structure for app %s", mountpoint); + goto doh; + } + } + PyThreadState_Swap((PyThreadState *) pthread_getspecific(up.upt_save_key)); + } + else { + 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 { + uwsgi_log( "WSGI application %d (SCRIPT_NAME=%.*s) ready on interpreter %p", id, wi->mountpoint_len, wi->mountpoint, wi->interpreter); + } + + if (id == 0) { + uwsgi_log(" (default app)"); + uwsgi.default_app = 0; + if (uwsgi.vhost) uwsgi.apps_cnt++; + } + else { + uwsgi.apps_cnt++; + } + + uwsgi_log("\n"); + + return id; + +doh: + free(mountpoint); + PyErr_Print(); + if (new_interpreter && id) { + Py_EndInterpreter(wi->interpreter); + if (uwsgi.threads > 1) { + PyThreadState_Swap((PyThreadState *) pthread_getspecific(up.upt_save_key)); + } + else { + PyThreadState_Swap(up.main_thread); + } + } + return -1; +} + +char *get_uwsgi_pymodule(char *module) { + + char *quick_callable; + + if ( (quick_callable = strchr(module, ':')) ) { + quick_callable[0] = 0 ; + quick_callable++; + return quick_callable; + } + + return NULL; +} + +PyObject *get_uwsgi_pydict(char *module) { + + PyObject *wsgi_module, *wsgi_dict; + + wsgi_module = PyImport_ImportModule(module); + if (!wsgi_module) { + PyErr_Print(); + return NULL; + } + + wsgi_dict = PyModule_GetDict(wsgi_module); + if (!wsgi_dict) { + PyErr_Print(); + return NULL; + } + + return wsgi_dict; + +} + +PyObject *uwsgi_uwsgi_loader(void *arg1) { + + PyObject *wsgi_dict; + + char *quick_callable; + + PyObject *tmp_callable; + + char *module = (char *) arg1 ; + + quick_callable = get_uwsgi_pymodule(module) ; + if (quick_callable == NULL) { + if (up.callable) { + quick_callable = up.callable ; + } + else { + quick_callable = "application"; + } + } + + wsgi_dict = get_uwsgi_pydict(module); + if (!wsgi_dict) { + return NULL; + } + + // quick callable -> thanks gunicorn for the idea + // we have extended the concept a bit... + if (quick_callable[strlen(quick_callable) -2 ] == '(' && quick_callable[strlen(quick_callable) -1] ==')') { + quick_callable[strlen(quick_callable) -2 ] = 0 ; + tmp_callable = PyDict_GetItemString(wsgi_dict, quick_callable); + if (tmp_callable) { + return python_call(tmp_callable, PyTuple_New(0), 0); + } + } + + return PyDict_GetItemString(wsgi_dict, quick_callable); + +} + +/* this is the mount loader, it loads app on mountpoint automagically */ +PyObject *uwsgi_mount_loader(void *arg1) { + + PyObject *callable = NULL ; + char *what = (char *) arg1; + + if ( !strcmp(what+strlen(what)-3, ".py") || !strcmp(what+strlen(what)-3, ".wsgi")) { + callable = uwsgi_file_loader((void *)what); + } +#ifdef UWSGI_PASTE + else if (!strcmp(what+strlen(what)-4, ".ini")) { + callable = uwsgi_paste_loader((void *)what); + } +#endif + else { + callable = uwsgi_uwsgi_loader((void *)what); + } + + return callable; +} + + +/* this is the dynamic loader, it loads app ireading nformation from a wsgi_request */ +PyObject *uwsgi_dyn_loader(void *arg1) { + + PyObject *callable = NULL ; + char *tmpstr; + + struct wsgi_request *wsgi_req = (struct wsgi_request *) arg1; + + // MANAGE UWSGI_SCRIPT + if (wsgi_req->wsgi_script_len > 0) { + tmpstr = uwsgi_strncopy(wsgi_req->wsgi_script, wsgi_req->wsgi_script_len); + callable = uwsgi_uwsgi_loader((void *)tmpstr); + free(tmpstr); + } + // MANAGE UWSGI_MODULE + else if (wsgi_req->wsgi_module_len > 0) { + if (wsgi_req->wsgi_callable_len > 0) { + tmpstr = uwsgi_concat3n(wsgi_req->wsgi_module, wsgi_req->wsgi_module_len, ":", 1, wsgi_req->wsgi_callable, wsgi_req->wsgi_callable_len); + } + else { + tmpstr = uwsgi_strncopy(wsgi_req->wsgi_module, wsgi_req->wsgi_module_len); + } + callable = uwsgi_uwsgi_loader((void *)tmpstr); + free(tmpstr); + } + // MANAGE UWSGI_FILE + else if (wsgi_req->wsgi_file_len > 0) { + tmpstr = uwsgi_strncopy(wsgi_req->wsgi_file, wsgi_req->wsgi_file_len); + callable = uwsgi_file_loader((void *)tmpstr); + free(tmpstr); + } +#ifdef UWSGI_PASTE + // MANAGE UWSGI_PASTE + else if (wsgi_req->wsgi_paste_len > 0) { + tmpstr = uwsgi_strncopy(wsgi_req->wsgi_paste, wsgi_req->wsgi_paste_len); + callable = uwsgi_paste_loader((void *)tmpstr); + free(tmpstr); + } +#endif + + return callable; +} + + +/* trying to emulate Graham's mod_wsgi, this will allows easy and fast migrations */ +PyObject *uwsgi_file_loader(void *arg1) { + + char *filename = (char *) arg1; + FILE *wsgifile; + struct _node *wsgi_file_node = NULL; + PyObject *wsgi_compiled_node, *wsgi_file_module, *wsgi_file_dict; + PyObject *wsgi_file_callable; + + wsgifile = fopen(filename, "r"); + if (!wsgifile) { + uwsgi_error("fopen()"); + exit(1); + } + + wsgi_file_node = PyParser_SimpleParseFile(wsgifile, filename, Py_file_input); + if (!wsgi_file_node) { + PyErr_Print(); + uwsgi_log( "failed to parse file %s\n", filename); + exit(1); + } + + fclose(wsgifile); + + wsgi_compiled_node = (PyObject *) PyNode_Compile(wsgi_file_node, filename); + + if (!wsgi_compiled_node) { + PyErr_Print(); + uwsgi_log( "failed to compile wsgi file %s\n", filename); + exit(1); + } + + wsgi_file_module = PyImport_ExecCodeModule("uwsgi_wsgi_file", wsgi_compiled_node); + if (!wsgi_file_module) { + PyErr_Print(); + exit(1); + } + + Py_DECREF(wsgi_compiled_node); + + wsgi_file_dict = PyModule_GetDict(wsgi_file_module); + if (!wsgi_file_dict) { + PyErr_Print(); + exit(1); + } + + + wsgi_file_callable = PyDict_GetItemString(wsgi_file_dict, "application"); + if (!wsgi_file_callable) { + PyErr_Print(); + uwsgi_log( "unable to find \"application\" callable in file %s\n", filename); + exit(1); + } + + if (!PyFunction_Check(wsgi_file_callable) && !PyCallable_Check(wsgi_file_callable)) { + uwsgi_log( "\"application\" must be a callable object in file %s\n", filename); + exit(1); + } + + + return wsgi_file_callable; + +} + +#ifdef UWSGI_PASTE +PyObject *uwsgi_paste_loader(void *arg1) { + + char *paste = (char *) arg1; + PyObject *paste_module, *paste_dict, *paste_loadapp; + PyObject *paste_arg, *paste_app; + + uwsgi_log( "Loading paste environment: %s\n", paste); + paste_module = PyImport_ImportModule("paste.deploy"); + if (!paste_module) { + PyErr_Print(); + exit(1); + } + + paste_dict = PyModule_GetDict(paste_module); + if (!paste_dict) { + PyErr_Print(); + exit(1); + } + + paste_loadapp = PyDict_GetItemString(paste_dict, "loadapp"); + if (!paste_loadapp) { + PyErr_Print(); + exit(1); + } + + paste_arg = PyTuple_New(1); + if (!paste_arg) { + PyErr_Print(); + exit(1); + } + + if (PyTuple_SetItem(paste_arg, 0, PyString_FromString(paste))) { + PyErr_Print(); + exit(1); + } + + paste_app = PyEval_CallObject(paste_loadapp, paste_arg); + if (!paste_app) { + PyErr_Print(); + exit(1); + } + + + return paste_app; +} + +#endif + +PyObject *uwsgi_eval_loader(void *arg1) { + + char *code = (char *) arg1; + + PyObject *wsgi_eval_module, *wsgi_eval_callable = NULL; + + struct _node *wsgi_eval_node = NULL; + PyObject *wsgi_compiled_node ; + + wsgi_eval_node = PyParser_SimpleParseString(code, Py_file_input); + if (!wsgi_eval_node) { + PyErr_Print(); + uwsgi_log( "failed to parse code\n"); + exit(1); + } + + wsgi_compiled_node = (PyObject *) PyNode_Compile(wsgi_eval_node, "uwsgi_eval_config"); + + if (!wsgi_compiled_node) { + PyErr_Print(); + uwsgi_log( "failed to compile eval code\n"); + exit(1); + } + + + wsgi_eval_module = PyImport_ExecCodeModule("uwsgi_eval_config", wsgi_compiled_node); + if (!wsgi_eval_module) { + PyErr_Print(); + exit(1); + } + + + Py_DECREF(wsgi_compiled_node); + + up.loader_dict = PyModule_GetDict(wsgi_eval_module); + if (!up.loader_dict) { + PyErr_Print(); + exit(1); + } + + + if (up.callable) { + wsgi_eval_callable = PyDict_GetItemString(up.loader_dict, up.callable); + } + else { + wsgi_eval_callable = PyDict_GetItemString(up.loader_dict, "application"); + } + + if (wsgi_eval_callable) { + uwsgi_log( "found the \"%s\" callable\n", up.callable); + if (!PyFunction_Check(wsgi_eval_callable) && !PyCallable_Check(wsgi_eval_callable)) { + uwsgi_log( "you must define a callable object in your code\n"); + exit(1); + } + } + + return wsgi_eval_callable; + +} + +PyObject *uwsgi_callable_loader(void *arg1) { + return (PyObject *) arg1; +} + +PyObject *uwsgi_string_callable_loader(void *arg1) { + char *callable = (char *) arg1; + + return PyDict_GetItem(up.loader_dict, UWSGI_PYFROMSTRING(callable)); +} diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c new file mode 100644 index 00000000..70778aba --- /dev/null +++ b/plugins/python/python_plugin.c @@ -0,0 +1,614 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +struct uwsgi_python up; + +struct option uwsgi_python_options[] = { + + {"wsgi-file", required_argument, 0, LONG_ARGS_WSGI_FILE}, + {"file", required_argument, 0, LONG_ARGS_FILE_CONFIG}, + {"eval", required_argument, 0, LONG_ARGS_EVAL_CONFIG}, + {"module", required_argument, 0, 'w'}, + {"callable", required_argument, 0, LONG_ARGS_CALLABLE}, + {"test", required_argument, 0, 'j'}, + {"home", required_argument, 0, 'H'}, + {"pythonpath", required_argument, 0, LONG_ARGS_PYTHONPATH}, + {"python-path", required_argument, 0, LONG_ARGS_PYTHONPATH}, + {"pp", required_argument, 0, LONG_ARGS_PYTHONPATH}, + {"pyargv", required_argument, 0, LONG_ARGS_PYARGV}, + {"optimize", required_argument, 0, 'O'}, +#ifdef UWSGI_PASTE + {"paste", required_argument, 0, LONG_ARGS_PASTE}, +#ifdef UWSGI_INI + {"ini-paste", required_argument, 0, LONG_ARGS_INI_PASTE}, +#endif +#endif + {"catch-exceptions", no_argument, &up.catch_exceptions, 1}, + {"ignore-script-name", no_argument, &up.ignore_script_name, 1}, + {"no-site", no_argument, &Py_NoSiteFlag, 1}, + + {0, 0, 0, 0}, + +}; + +/* this routine will be called after each fork to reinitialize the various locks */ +void uwsgi_python_pthread_prepare(void) { + pthread_mutex_lock(&up.lock_pyloaders); +} + +void uwsgi_python_pthread_parent(void) { + pthread_mutex_unlock(&up.lock_pyloaders); +} + +void uwsgi_python_pthread_child(void) { + pthread_mutex_init(&up.lock_pyloaders, NULL); +} + + +// fake method +PyMethodDef null_methods[] = { + { NULL, NULL}, +}; + +PyMethodDef uwsgi_spit_method[] = { {"uwsgi_spit", py_uwsgi_spit, METH_VARARGS, ""} }; +PyMethodDef uwsgi_write_method[] = { {"uwsgi_write", py_uwsgi_write, METH_VARARGS, ""} }; + + +int uwsgi_python_init() { + + uwsgi_log("Python version: %s\n", Py_GetVersion()); + + if (up.home != NULL) { + uwsgi_log("Setting PythonHome to %s...\n", up.home); +#ifdef PYTHREE + wchar_t *wpyhome; + wpyhome = malloc((sizeof(wchar_t) * strlen(up.home)) + 2); + if (!wpyhome) { + uwsgi_error("malloc()"); + exit(1); + } + mbstowcs(wpyhome, up.home, strlen(up.home)); + Py_SetPythonHome(wpyhome); + free(wpyhome); +#else + Py_SetPythonHome(up.home); +#endif + } + +#ifdef PYTHREE + wchar_t pname[6]; + mbstowcs(pname, "uWSGI", 6); + Py_SetProgramName(pname); +#else + + Py_SetProgramName("uWSGI"); +#endif + + + Py_Initialize(); + + Py_OptimizeFlag = up.optimize; + + up.wsgi_spitout = PyCFunction_New(uwsgi_spit_method, NULL); + up.wsgi_writeout = PyCFunction_New(uwsgi_write_method, NULL); + + up.main_thread = PyThreadState_Get(); + + if (up.test_module != NULL) { + if (PyImport_ImportModule(up.test_module)) { + exit(0); + } + exit(1); + } + + init_uwsgi_vars(); + + // setup app loaders +#ifdef UWSGI_MINTERPRETERS + up.loaders[LOADER_DYN] = uwsgi_dyn_loader; +#endif + up.loaders[LOADER_UWSGI] = uwsgi_uwsgi_loader; + up.loaders[LOADER_FILE] = uwsgi_file_loader; +#ifdef UWSGI_PASTE + up.loaders[LOADER_PASTE] = uwsgi_paste_loader; +#endif + up.loaders[LOADER_EVAL] = uwsgi_eval_loader; + up.loaders[LOADER_MOUNT] = uwsgi_mount_loader; + up.loaders[LOADER_CALLABLE] = uwsgi_callable_loader; + up.loaders[LOADER_STRING_CALLABLE] = uwsgi_string_callable_loader; + + // by default set a fake GIL (little impact on performance) + up.gil_get = gil_fake_get; + up.gil_release = gil_fake_release; + + return 1; + +} + +void uwsgi_python_post_fork() { + + PyObject *random_module, *random_dict, *random_seed; + + // reinitialize the random seed (thanks Jonas Borgström) + random_module = PyImport_ImportModule("random"); + if (random_module) { + random_dict = PyModule_GetDict(random_module); + if (random_dict) { + random_seed = PyDict_GetItemString(random_dict, "seed"); + if (random_seed) { + PyObject *random_args = PyTuple_New(1); + // pass no args + PyTuple_SetItem(random_args, 0, Py_None); + PyEval_CallObject( random_seed, random_args ); + if (PyErr_Occurred()) { + PyErr_Print(); + } + } + } + } + +#ifdef UWSGI_EMBEDDED + // call the post_fork_hook + PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi"); + if (uwsgi_dict) { + PyObject *pfh = PyDict_GetItemString(uwsgi_dict, "post_fork_hook"); + if (pfh) { + python_call(pfh, PyTuple_New(0), 0); + } + } + PyErr_Clear(); +#endif + + UWSGI_RELEASE_GIL + +} + +void init_uwsgi_vars() { + + int i; + PyObject *pysys, *pysys_dict, *pypath; + +#ifdef UWSGI_MINTERPRETERS + char venv_version[15] ; + PyObject *site_module; +#endif + + /* add cwd to pythonpath */ + pysys = PyImport_ImportModule("sys"); + if (!pysys) { + PyErr_Print(); + exit(1); + } + pysys_dict = PyModule_GetDict(pysys); + pypath = PyDict_GetItemString(pysys_dict, "path"); + if (!pypath) { + PyErr_Print(); + exit(1); + } + +#ifdef UWSGI_MINTERPRETERS + // simulate a pythonhome directive + if (uwsgi.wsgi_req->pyhome_len > 0) { + + PyObject *venv_path = UWSGI_PYFROMSTRINGSIZE(uwsgi.wsgi_req->pyhome, uwsgi.wsgi_req->pyhome_len) ; + +#ifdef UWSGI_DEBUG + uwsgi_debug("setting dynamic virtualenv to %.*s\n", uwsgi.wsgi_req->pyhome_len, uwsgi.wsgi_req->pyhome); +#endif + + PyDict_SetItemString(pysys_dict, "prefix", venv_path); + PyDict_SetItemString(pysys_dict, "exec_prefix", venv_path); + + venv_version[14] = 0 ; + if (snprintf(venv_version, 15, "/lib/python%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION) == -1) { + return ; + } + + // check here + PyString_Concat( &venv_path, PyString_FromString(venv_version) ); + + if ( PyList_Insert(pypath, 0, venv_path) ) { + PyErr_Print(); + } + + site_module = PyImport_ImportModule("site"); + if (site_module) { + PyImport_ReloadModule(site_module); + } + + } +#endif + + if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(".") ) != 0) { + PyErr_Print(); + } + + for (i = 0; i < up.python_path_cnt; i++) { + if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(up.python_path[i]) ) != 0) { + PyErr_Print(); + } + else { + uwsgi_log( "added %s to pythonpath.\n", up.python_path[i]); + } + } + +} + + +void uwsgi_uwsgi_config(char *module) { + +#ifdef UWSGI_EMBEDDED + PyObject *uwsgi_module, *uwsgi_dict; +#endif + PyObject *applications; + PyObject *app_list; + Py_ssize_t i; + PyObject *app_mnt, *app_app = NULL; + char *quick_callable ; + + quick_callable = get_uwsgi_pymodule(module); + if (quick_callable == NULL) { + if (up.callable) { + quick_callable = up.callable ; + } + else { + quick_callable = "application"; + } + } + + up.loader_dict = get_uwsgi_pydict(module); + if (!up.loader_dict) { + exit(1); + } + + uwsgi_log( "...getting the applications list from the '%s' module...\n", module); + +#ifdef UWSGI_EMBEDDED + uwsgi_module = PyImport_ImportModule("uwsgi"); + if (!uwsgi_module) { + PyErr_Print(); + exit(1); + } + + uwsgi_dict = PyModule_GetDict(uwsgi_module); + if (!uwsgi_dict) { + PyErr_Print(); + exit(1); + } + + applications = PyDict_GetItemString(uwsgi_dict, "applications"); + if (!PyDict_Check(applications)) { + uwsgi_log( "uwsgi.applications dictionary is not defined, trying with the \"applications\" one...\n"); +#endif + applications = PyDict_GetItemString(up.loader_dict, "applications"); + if (!applications) { + uwsgi_log( "applications dictionary is not defined, trying with the \"application\" callable.\n"); + quick_callable = uwsgi_concat3(module, ":", quick_callable); + if (init_uwsgi_app(LOADER_UWSGI, (void *) quick_callable, uwsgi.wsgi_req, 0) < 0) { + uwsgi_log( "...goodbye cruel world...\n"); + exit(1); + } + free(quick_callable); + return; + } +#ifdef UWSGI_EMBEDDED + } +#endif + + if (!PyDict_Check(applications)) { + uwsgi_log( "The 'applications' object must be a dictionary.\n"); + exit(1); + } + + app_list = PyDict_Keys(applications); + if (!app_list) { + PyErr_Print(); + exit(1); + } + if (PyList_Size(app_list) < 1) { + uwsgi_log( "You must define an app.\n"); + exit(1); + } + + for (i = 0; i < PyList_Size(app_list); i++) { + app_mnt = PyList_GetItem(app_list, i); + + if (!PyString_Check(app_mnt)) { + uwsgi_log( "the app mountpoint must be a bytestring.\n"); + exit(1); + } + + + uwsgi.wsgi_req->script_name = PyString_AsString(app_mnt); + uwsgi.wsgi_req->script_name_len = strlen(uwsgi.wsgi_req->script_name); + + app_app = PyDict_GetItem(applications, app_mnt); + + if (!PyString_Check(app_app) && !PyFunction_Check(app_app) && !PyCallable_Check(app_app)) { + uwsgi_log( "the app callable must be a string, a function or a callable. (found %s)\n", app_app->ob_type->tp_name); + exit(1); + } + +#ifdef PYTHREE + if (PyUnicode_Check(app_app)) { +#else + if (PyString_Check(app_app)) { +#endif + if (init_uwsgi_app(LOADER_STRING_CALLABLE, (void *) PyString_AsString(app_app), uwsgi.wsgi_req, 0) < 0) { + uwsgi_log( "...goodbye cruel world...\n"); + exit(1); + } + + + } + else { + if (init_uwsgi_app(LOADER_CALLABLE, (void *) app_app, uwsgi.wsgi_req, 0) < 0) { + uwsgi_log( "...goodbye cruel world...\n"); + exit(1); + } + } + + Py_DECREF(app_mnt); + Py_DECREF(app_app); + } + +} + + +#ifdef PYTHREE +static PyModuleDef uwsgi_module3 = { + PyModuleDef_HEAD_INIT, + "uwsgi", + NULL, + -1, + null_methods, +}; +PyObject *init_uwsgi3(void) { + return PyModule_Create(&uwsgi_module3); +} +#endif + + +#ifdef UWSGI_EMBEDDED +void init_uwsgi_embedded_module() { + PyObject *new_uwsgi_module, *zero; + int i; + + /* initialize for stats */ + uwsgi.workers_tuple = PyTuple_New(uwsgi.numproc); + for (i = 0; i < uwsgi.numproc; i++) { + zero = PyDict_New(); + Py_INCREF(zero); + PyTuple_SetItem(uwsgi.workers_tuple, i, zero); + } + + + +#ifdef PYTHREE + PyImport_AppendInittab("uwsgi", init_uwsgi3); + new_uwsgi_module = PyImport_AddModule("uwsgi"); +#else + new_uwsgi_module = Py_InitModule("uwsgi", null_methods); +#endif + if (new_uwsgi_module == NULL) { + uwsgi_log( "could not initialize the uwsgi python module\n"); + exit(1); + } + + uwsgi.embedded_dict = PyModule_GetDict(new_uwsgi_module); + if (!uwsgi.embedded_dict) { + uwsgi_log( "could not get uwsgi module __dict__\n"); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "version", PyString_FromString(UWSGI_VERSION))) { + PyErr_Print(); + exit(1); + } + + if (uwsgi.mode) { + if (PyDict_SetItemString(uwsgi.embedded_dict, "mode", PyString_FromString(uwsgi.mode))) { + PyErr_Print(); + exit(1); + } + } + + if (uwsgi.pidfile) { + if (PyDict_SetItemString(uwsgi.embedded_dict, "pidfile", PyString_FromString(uwsgi.pidfile))) { + PyErr_Print(); + exit(1); + } + } + + + if (PyDict_SetItemString(uwsgi.embedded_dict, "SPOOL_RETRY", PyInt_FromLong(17))) { + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "numproc", PyInt_FromLong(uwsgi.numproc))) { + PyErr_Print(); + exit(1); + } + +#ifdef UNBIT + if (PyDict_SetItemString(uwsgi.embedded_dict, "unbit", Py_True)) { +#else + if (PyDict_SetItemString(uwsgi.embedded_dict, "unbit", Py_None)) { +#endif + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "buffer_size", PyInt_FromLong(uwsgi.buffer_size))) { + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "started_on", PyInt_FromLong(uwsgi.start_tv.tv_sec))) { + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "start_response", up.wsgi_spitout)) { + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "fastfuncs", PyList_New(256))) { + PyErr_Print(); + exit(1); + } + + + if (PyDict_SetItemString(uwsgi.embedded_dict, "applications", Py_None)) { + PyErr_Print(); + exit(1); + } + + if (uwsgi.is_a_reload) { + if (PyDict_SetItemString(uwsgi.embedded_dict, "is_a_reload", Py_True)) { + PyErr_Print(); + exit(1); + } + } + else { + if (PyDict_SetItemString(uwsgi.embedded_dict, "is_a_reload", Py_False)) { + PyErr_Print(); + exit(1); + } + } + + uwsgi.embedded_args = PyTuple_New(2); + if (!uwsgi.embedded_args) { + PyErr_Print(); + exit(1); + } + + if (PyDict_SetItemString(uwsgi.embedded_dict, "message_manager_marshal", Py_None)) { + PyErr_Print(); + exit(1); + } + + uwsgi.fastfuncslist = PyDict_GetItemString(uwsgi.embedded_dict, "fastfuncs"); + if (!uwsgi.fastfuncslist) { + PyErr_Print(); + exit(1); + } + + init_uwsgi_module_advanced(new_uwsgi_module); + +#ifdef UWSGI_SPOOLER + if (uwsgi.spool_dir != NULL) { + init_uwsgi_module_spooler(new_uwsgi_module); + } +#endif + + + if (uwsgi.sharedareasize > 0 && uwsgi.sharedarea) { + init_uwsgi_module_sharedarea(new_uwsgi_module); + } +} +#endif + + + + +int uwsgi_python_manage_options(int i, char *optarg) { + + switch(i) { + case 'w': + up.wsgi_config = optarg; + return 1; + case LONG_ARGS_PYTHONPATH: + uwsgi_log("found PYTHONPATH\n"); + if (up.python_path_cnt < MAX_PYTHONPATH) { + up.python_path[up.python_path_cnt] = optarg; + up.python_path_cnt++; + } + else { + uwsgi_log( "you can specify at most %d --pythonpath options\n", MAX_PYTHONPATH); + } + return 1; + case LONG_ARGS_PYARGV: + up.argv = optarg; + return 1; + case 'j': + up.test_module = optarg; + return 1; + case 'H': + up.home = optarg; + return 1; + case LONG_ARGS_CALLABLE: + up.callable = optarg; + return 1; + } + + return 0; +} + +void uwsgi_python_init_app() { + + if (up.wsgi_config != NULL) { + init_uwsgi_app(LOADER_UWSGI, up.wsgi_config, uwsgi.wsgi_req, 0); + } + + if (up.file_config != NULL) { + init_uwsgi_app(LOADER_FILE, up.file_config, uwsgi.wsgi_req, 0); + } +#ifdef UWSGI_PASTE + if (up.paste != NULL) { + init_uwsgi_app(LOADER_PASTE, up.paste, uwsgi.wsgi_req, 0); + } +#endif + if (up.eval != NULL) { + init_uwsgi_app(LOADER_EVAL, up.eval, uwsgi.wsgi_req, 0); + } + +} + +void uwsgi_python_enable_threads() { + + PyEval_InitThreads(); + if (pthread_key_create(&up.upt_save_key, NULL)) { + uwsgi_error("pthread_key_create()"); + exit(1); + } + pthread_setspecific(up.upt_save_key, (void *) PyThreadState_Get()); + pthread_mutex_init(&up.lock_pyloaders, NULL); + pthread_atfork(uwsgi_python_pthread_prepare, uwsgi_python_pthread_parent, uwsgi_python_pthread_child); + up.gil_get = gil_real_get; + up.gil_release = gil_real_release; + + uwsgi_log("threads support enabled\n"); +} + +void uwsgi_python_init_thread() { + + // set a new ThreadState for each thread + PyThreadState *pts; + pts = PyThreadState_New(up.main_thread->interp); + pthread_setspecific(up.upt_save_key, (void *) pts); + +} + +struct uwsgi_plugin python_plugin = { + + .name = "python", + .modifier1 = 0, + .init = uwsgi_python_init, + .post_fork = uwsgi_python_post_fork, + .options = uwsgi_python_options, + .manage_opt = uwsgi_python_manage_options, + .request = uwsgi_request_wsgi, + .after_request = uwsgi_after_request_wsgi, + .init_apps = uwsgi_python_init_app, + .enable_threads = uwsgi_python_enable_threads, + .init_thread = uwsgi_python_init_thread, + /* + .magic = uwsgi_python_magic, + .help = uwsgi_python_help, + */ + +}; + diff --git a/plugins/python/pyutils.c b/plugins/python/pyutils.c new file mode 100644 index 00000000..edebef52 --- /dev/null +++ b/plugins/python/pyutils.c @@ -0,0 +1,100 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +int manage_python_response(struct wsgi_request *wsgi_req) { + // use standard WSGI response parse + return uwsgi_response_subhandler_wsgi(wsgi_req); +} + +PyObject *python_call(PyObject *callable, PyObject *args, int catch) { + + PyObject *pyret; + + uwsgi_log("CALLING %p %p\n", callable, args); + pyret = PyEval_CallObject(callable, args); + uwsgi_log("CALLED\n"); + + if (PyErr_Occurred()) { + if (!catch) { + PyErr_Print(); + } + } + +#ifdef UWSGI_DEBUG + if (pyret) { + uwsgi_debug("called %p %p %d\n", callable, args, pyret->ob_refcnt); + } +#endif + + uwsgi_log("called\n"); + + return pyret; +} + + + +int uwsgi_python_call(struct wsgi_request *wsgi_req, PyObject *callable, PyObject *args) { + + wsgi_req->async_result = python_call(callable, args, 0); + + if (wsgi_req->async_result) { + while ( manage_python_response(wsgi_req) != UWSGI_OK) { +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + return UWSGI_AGAIN; + } +#endif + } + } + + return UWSGI_OK; +} + +void init_pyargv() { + + char *ap; + +#ifdef PYTHREE + wchar_t pname[6]; + mbstowcs(pname, "uwsgi", 6); + uwsgi.py_argv[0] = pname; +#else + uwsgi.py_argv[0] = "uwsgi"; +#endif + + if (up.argv != NULL && !up.argc) { + up.argc++; +#ifdef PYTHREE + wchar_t *wcargv = malloc( sizeof( wchar_t ) * (strlen(up.argv)+1)); + if (!wcargv) { + uwsgi_error("malloc()"); + exit(1); + } + memset(wcargv, 0, sizeof( wchar_t ) * (strlen(up.argv)+1)); +#endif + +#ifdef __sun__ + // FIX THIS !!! + ap = strtok(up.argv, " "); + while ((ap = strtok(NULL, " ")) != NULL) { +#else + while ((ap = strsep(&up.argv, " \t")) != NULL) { +#endif + if (*ap != '\0') { +#ifdef PYTHREE + mbstowcs( wcargv + strlen(ap), ap, strlen(ap)); + uwsgi.py_argv[up.argc] = wcargv + strlen(ap); +#else + uwsgi.py_argv[up.argc] = ap; +#endif + up.argc++; + } + if (up.argc + 1 > MAX_PYARGV) + break; + } + } + + PySys_SetArgv(up.argc, &up.argv); +} diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h new file mode 100644 index 00000000..276d2fec --- /dev/null +++ b/plugins/python/uwsgi_python.h @@ -0,0 +1,169 @@ +#include "../../uwsgi.h" +#include + + +#define MAX_PYTHONPATH 64 + +#ifdef UWSGI_STACKLESS +#include +#endif + +#if PY_MINOR_VERSION == 4 && PY_MAJOR_VERSION == 2 +#define Py_ssize_t ssize_t +#endif + +#if PY_MAJOR_VERSION > 2 +#define PYTHREE +#endif + +#ifdef UWSGI_THREADING +#define UWSGI_GET_GIL (*up.gil_get)(); +#define UWSGI_RELEASE_GIL (*up.gil_release)(); +#else +#define UWSGI_GET_GIL +#define UWSGI_RELEASE_GIL +#endif + + +PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, Py_ssize_t); + +#ifdef PYTHREE +#define UWSGI_PYFROMSTRING(x) PyUnicode_FromString(x) +#define UWSGI_PYFROMSTRINGSIZE(x, y) PyUnicode_FromStringAndSize(x, y) +#define PyInt_FromLong PyLong_FromLong +#define PyInt_AsLong PyLong_AsLong +#define PyInt_Check PyLong_Check +#define PyString_Check PyBytes_Check +#define PyString_FromStringAndSize PyBytes_FromStringAndSize +#define PyString_FromFormat PyBytes_FromFormat +#define PyString_FromString PyBytes_FromString +#define PyString_Size PyBytes_Size +#define PyString_Concat PyBytes_Concat +#define PyString_AsString (char *) PyBytes_AsString +#define PyFile_FromFile(A,B,C,D) PyFile_FromFd(fileno((A)), (B), (C), -1, NULL, NULL, NULL, 0) + +#else +#define UWSGI_PYFROMSTRING(x) PyString_FromString(x) +#define UWSGI_PYFROMSTRINGSIZE(x, y) PyString_FromStringAndSize(x, y) +#endif + +struct uwsgi_python { + + char *home; + int optimize; + + char *argv; + int argc; + + PyObject *wsgi_spitout; + PyObject *wsgi_writeout; + + PyThreadState *main_thread; + + char *test_module; + + char *python_path[MAX_PYTHONPATH]; + int python_path_cnt; + + PyObject *loader_dict; + PyObject* (*loaders[LOADER_MAX]) (void *); + + char *wsgi_config; + char *file_config; + char *paste; + char *eval; + + + char *callable; + + int ignore_script_name; + int catch_exceptions; + + +#ifdef UWSGI_THREADING + pthread_key_t upt_save_key; + pthread_mutex_t lock_pyloaders; + void (*gil_get) (void); + void (*gil_release) (void); +#endif +}; + + + +void init_uwsgi_vars(void); +void init_uwsgi_embedded_module(void); + + +void uwsgi_wsgi_config(char *); +#ifdef UWSGI_PASTE +void uwsgi_paste_config(char *); +#endif +void uwsgi_file_config(char *); +void uwsgi_eval_config(char *); + +int init_uwsgi_app(int, void *, struct wsgi_request *wsgi_req, int); + + +PyObject *py_eventfd_read(PyObject *, PyObject *) ; +PyObject *py_eventfd_write(PyObject *, PyObject *) ; + + +#ifdef UWSGI_STACKLESS +PyObject *py_uwsgi_stackless(PyObject *, PyObject *) ; +#endif + +int manage_python_response(struct wsgi_request *); +int uwsgi_python_call(struct wsgi_request *, PyObject *, PyObject *); +PyObject *python_call(PyObject *, PyObject *, int); + +#ifdef UWSGI_SENDFILE +PyObject *py_uwsgi_sendfile(PyObject *, PyObject *) ; +ssize_t uwsgi_sendfile(struct wsgi_request *); +ssize_t uwsgi_do_sendfile(int, int, size_t, size_t, off_t*, int); +#endif + +PyObject *py_uwsgi_write(PyObject *, PyObject *) ; +PyObject *py_uwsgi_spit(PyObject *, PyObject *) ; + +#ifdef UWSGI_STACKLESS +struct stackless_req { + PyTaskletObject *tasklet; + struct wsgi_request *wsgi_req; + PyChannelObject *channel; +}; +struct wsgi_request *find_request_by_tasklet(PyTaskletObject *); + +void stackless_init(void); +void stackless_loop(void); +#endif + +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 *); +PyObject *uwsgi_file_loader(void *); +PyObject *uwsgi_eval_loader(void *); +PyObject *uwsgi_paste_loader(void *); +PyObject *uwsgi_callable_loader(void *); +PyObject *uwsgi_string_callable_loader(void *); +PyObject *uwsgi_mount_loader(void *); + +char *get_uwsgi_pymodule(char *); +PyObject *get_uwsgi_pydict(char *); + +int uwsgi_request_wsgi(struct wsgi_request *); +void uwsgi_after_request_wsgi(struct wsgi_request *); + +void *uwsgi_request_subhandler_wsgi(struct wsgi_request *, struct uwsgi_app*); +int uwsgi_response_subhandler_wsgi(struct wsgi_request *); + +void gil_real_get(void); +void gil_real_release(void); +void gil_fake_get(void); +void gil_fake_release(void); diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py new file mode 100644 index 00000000..0061fb4b --- /dev/null +++ b/plugins/python/uwsgiplugin.py @@ -0,0 +1,22 @@ +import os,sys + +from distutils import sysconfig + +NAME='python' +GCC_LIST = ['python_plugin', 'pyutils', 'pyloader', 'wsgi_handlers', 'wsgi_headers', 'wsgi_subhandler', 'gil'] + +CFLAGS = ['-I' + sysconfig.get_python_inc(), '-I' + sysconfig.get_python_inc(plat_specific=True) ] +LDFLAGS = [] + +LIBS = sysconfig.get_config_var('LIBS').split() + sysconfig.get_config_var('SYSLIBS').split() +if not sysconfig.get_config_var('Py_ENABLE_SHARED'): + LIBS.append('-L' + sysconfig.get_config_var('LIBPL')) + +version = sys.version_info +uver = "%d.%d" % (version[0], version[1]) + +LIBS.append('-lpython' + uver) + +#if str(PYLIB_PATH) != '': +# libs.insert(0,'-L' + PYLIB_PATH) +# os.environ['LD_RUN_PATH'] = PYLIB_PATH diff --git a/plugins/python/uwsgiplugin.pyc b/plugins/python/uwsgiplugin.pyc new file mode 100644 index 00000000..5e640299 Binary files /dev/null and b/plugins/python/uwsgiplugin.pyc differ diff --git a/plugins/python/web3_subhandler.c b/plugins/python/web3_subhandler.c new file mode 100644 index 00000000..2767f44b --- /dev/null +++ b/plugins/python/web3_subhandler.c @@ -0,0 +1,165 @@ +#include "uwsgi.h" + +extern struct uwsgi_server uwsgi; + +void *uwsgi_request_subhandler_web3(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { + + PyObject *zero, *wsgi_socket; + + + 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); + + 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); + + zero = PyFile_FromFile(stderr, "web3_input", "w", NULL); + PyDict_SetItemString(wsgi_req->async_environ, "web3.errors", zero); + Py_DECREF(zero); + + PyDict_SetItemString(wsgi_req->async_environ, "web3.run_once", Py_False); + + PyDict_SetItemString(wsgi_req->async_environ, "web3.multithread", Py_False); + if (uwsgi.numproc == 1) { + PyDict_SetItemString(wsgi_req->async_environ, "web3.multiprocess", Py_False); + } + else { + PyDict_SetItemString(wsgi_req->async_environ, "web3.multiprocess", Py_True); + } + + if (wsgi_req->scheme_len > 0) { + zero = PyString_FromStringAndSize(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 = PyString_FromString("https"); + } + else { + zero = PyString_FromString("http"); + } + } + else { + zero = PyString_FromString("http"); + } + PyDict_SetItemString(wsgi_req->async_environ, "web3.url_scheme", zero); + Py_DECREF(zero); + + + wsgi_req->async_app = wi->wsgi_callable ; + + PyDict_SetItemString(uwsgi.embedded_dict, "env", wsgi_req->async_environ); + + // TODO: fix this + //PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.version", uwsgi_version); + + + // 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); +} + + +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; + } + + + // ok its a yield + if (!wsgi_req->async_placeholder) { + if (PyTuple_Check((PyObject *)wsgi_req->async_result)) { + if (PyTuple_Size((PyObject *)wsgi_req->async_result) != 3) { + uwsgi_log("invalid Web3 response.\n"); + goto clear; + } + if (py_uwsgi_spit(NULL, (PyObject *)wsgi_req->async_result) == Py_None) { + goto clear; + } + + wsgi_req->async_result = PyTuple_GetItem((PyObject *)wsgi_req->async_result, 0); + + wsgi_req->async_placeholder = PyObject_GetIter( (PyObject *)wsgi_req->async_result ); + + if (!wsgi_req->async_placeholder) { + goto clear2; + } +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + return UWSGI_AGAIN; + } + } + else { + uwsgi_log("invalid Web3 response.\n"); + goto clear; + } +#endif + } + + + + pychunk = PyIter_Next(wsgi_req->async_placeholder) ; + + if (!pychunk) { + if (PyErr_Occurred()) PyErr_Print(); + goto clear; + } + + + + if (PyString_Check(pychunk)) { + if ((wsize = write(wsgi_req->poll.fd, PyString_AsString(pychunk), PyString_Size(pychunk))) < 0) { + uwsgi_error("write()"); + Py_DECREF(pychunk); + goto clear; + } + wsgi_req->response_size += wsize; + } + + + Py_DECREF(pychunk); + return UWSGI_AGAIN ; + +clear: + 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 + return UWSGI_OK; +} + diff --git a/plugins/python/wsgi_handlers.c b/plugins/python/wsgi_handlers.c new file mode 100644 index 00000000..f0bf5a25 --- /dev/null +++ b/plugins/python/wsgi_handlers.c @@ -0,0 +1,451 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +PyObject *py_uwsgi_write(PyObject * self, PyObject * args) { + PyObject *data; + char *content; + int len; + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + data = PyTuple_GetItem(args, 0); + if (PyString_Check(data)) { + content = PyString_AsString(data); + len = PyString_Size(data); + UWSGI_RELEASE_GIL + wsgi_req->response_size = write(wsgi_req->poll.fd, content, len); + UWSGI_GET_GIL + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef UWSGI_ASYNC + +PyObject *py_eventfd_read(PyObject * self, PyObject * args) { + int fd, timeout; + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (!PyArg_ParseTuple(args, "i|i", &fd, &timeout)) { + return NULL; + } + + if (fd >= 0) { + wsgi_req->async_waiting_fd = fd ; + wsgi_req->async_waiting_fd_type = ASYNC_IN ; + wsgi_req->async_waiting_fd_monitored = 0 ; + } + + return PyString_FromString("") ; +} + + +PyObject *py_eventfd_write(PyObject * self, PyObject * args) { + int fd, timeout; + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (!PyArg_ParseTuple(args, "i|i", &fd, &timeout)) { + return NULL; + } + + if (fd >= 0) { + wsgi_req->async_waiting_fd = fd ; + wsgi_req->async_waiting_fd_type = ASYNC_OUT ; + wsgi_req->async_waiting_fd_monitored = 0 ; + } + + return PyString_FromString("") ; +} +#endif + +int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) { + + int i; + + size_t post_remains = wsgi_req->post_cl; + ssize_t post_chunk; + + PyObject *pydictkey, *pydictvalue; + + char *path_info; + struct uwsgi_app *wi ; + + int tmp_stderr; + char *what; + int what_len; + + +#ifdef UWSGI_ASYNC + if (wsgi_req->async_status == UWSGI_AGAIN) { + // get rid of timeout + if (wsgi_req->async_timeout_expired) { + PyDict_SetItemString(wsgi_req->async_environ, "x-wsgiorg.fdevent.timeout", Py_True); + wsgi_req->async_timeout_expired = 0 ; + } + else { + PyDict_SetItemString(wsgi_req->async_environ, "x-wsgiorg.fdevent.timeout", Py_None); + } + return manage_python_response(wsgi_req); + } +#endif + + + /* Standard WSGI request */ + if (!wsgi_req->uh.pktsize) { + uwsgi_log( "Invalid WSGI request. skip.\n"); + return -1; + } + + if (uwsgi_parse_vars(wsgi_req)) { + uwsgi_log("Invalid WSGI request. skip.\n"); + return -1; + } + + uwsgi_log("uno\n"); + + if (uwsgi.limit_post) { + if (wsgi_req->post_cl > uwsgi.limit_post) { + uwsgi_log("Invalid (too big) CONTENT_LENGTH. skip.\n"); + return -1; + } + } + + + if (!up.ignore_script_name) { + + if (!wsgi_req->script_name) + wsgi_req->script_name = ""; + + if (uwsgi.vhost) { + what = uwsgi_concat3n(wsgi_req->host, wsgi_req->host_len, "|",1, wsgi_req->script_name, wsgi_req->script_name_len); + what_len = wsgi_req->host_len + 1 + wsgi_req->script_name_len ; +#ifdef UWSGI_DEBUG + uwsgi_debug("VirtualHost SCRIPT_NAME=%s\n", what); +#endif + } + else { + what = wsgi_req->script_name; + what_len = wsgi_req->script_name_len ; + } + + + if ( (wsgi_req->app_id = uwsgi_get_app_id(what, what_len)) == -1) { + if (wsgi_req->script_name_len > 1 || uwsgi.default_app < 0 || uwsgi.vhost) { + /* unavailable app for this SCRIPT_NAME */ + wsgi_req->app_id = -1; + if (wsgi_req->wsgi_script_len > 0 + || wsgi_req->wsgi_module_len > 0 + || wsgi_req->wsgi_file_len > 0 +#ifdef UWSGI_PASTE + || wsgi_req->wsgi_paste_len > 0 +#endif + ) { + // a bit of magic: 1-1 = 0 / 0-1 = -1 + // this part must be heavy locked in threaded modes + if (uwsgi.threads > 1) { + pthread_mutex_lock(&up.lock_pyloaders); + } + UWSGI_GET_GIL + wsgi_req->app_id = init_uwsgi_app(LOADER_DYN, (void *) wsgi_req, wsgi_req, uwsgi.single_interpreter-1); + UWSGI_RELEASE_GIL + if (uwsgi.threads > 1) { + pthread_mutex_unlock(&up.lock_pyloaders); + } + } + } + } + + if (uwsgi.vhost) { + free(what); + } + + } + else { + wsgi_req->app_id = 0; + } + + + if (wsgi_req->app_id == -1) { + // use default app ? + if (!uwsgi.no_default_app && uwsgi.default_app >= 0) { + wsgi_req->app_id = uwsgi.default_app ; + } + else { + internal_server_error(wsgi_req->poll.fd, "wsgi application not found"); + goto clear2; + } + + } + + wi = &uwsgi.apps[wsgi_req->app_id]; + + if (uwsgi.single_interpreter == 0 && wsgi_req->app_id > 0) { + if (!wi->interpreter) { + internal_server_error(wsgi_req->poll.fd, "wsgi application's %d interpreter not found"); + goto clear2; + } + + // set the interpreter + UWSGI_GET_GIL + if (uwsgi.threads > 1) { + PyThreadState_Swap(uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id]->ts[wsgi_req->app_id]); + } + else { + PyThreadState_Swap(wi->interpreter); + } + UWSGI_RELEASE_GIL + if (wi->chdir) { +#ifdef UWSGI_DEBUG + uwsgi_debug("chdir to %s\n", wi->chdir); +#endif + if (chdir(wi->chdir)) { + uwsgi_error("chdir()"); + } + } + } + + + + + if (wsgi_req->protocol_len < 5) { + uwsgi_log( "INVALID PROTOCOL: %.*s\n", wsgi_req->protocol_len, wsgi_req->protocol); + internal_server_error(wsgi_req->poll.fd, "invalid HTTP protocol !!!"); + goto clear; + + } + if (strncmp(wsgi_req->protocol, "HTTP/", 5)) { + uwsgi_log( "INVALID PROTOCOL: %.*s\n", wsgi_req->protocol_len, wsgi_req->protocol); + internal_server_error(wsgi_req->poll.fd, "invalid HTTP protocol !!!"); + goto clear; + } + + + +#ifdef UWSGI_ASYNC + wsgi_req->async_environ = wi->environ[wsgi_req->async_id]; + wsgi_req->async_args = wi->args[wsgi_req->async_id]; +#else + wsgi_req->async_environ = wi->environ; + wsgi_req->async_args = wi->args; +#endif + + UWSGI_GET_GIL + + // no fear of race conditions for this counter as it is already protected by the GIL + wi->requests++; + + 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, "ignore"); + pydictvalue = PyUnicode_DecodeLatin1(wsgi_req->hvec[i + 1].iov_base, wsgi_req->hvec[i + 1].iov_len, "ignore"); +#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) { + 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))); + } + } + } + } + } + + + + + uwsgi_log("OOOOOPS000\n"); + + if (uwsgi.post_buffering > 0 && wsgi_req->post_cl > (size_t) uwsgi.post_buffering) { + UWSGI_RELEASE_GIL + wsgi_req->async_post = tmpfile(); + if (!wsgi_req->async_post) { + uwsgi_error("tmpfile()"); + goto clear; + } + + + while(post_remains > 0) { + if (uwsgi.shared->options[UWSGI_OPTION_HARAKIRI] > 0) { + inc_harakiri(uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]); + } + if (post_remains > (size_t) uwsgi.post_buffering_bufsize) { + post_chunk = read(wsgi_req->poll.fd, wsgi_req->post_buffering_buf, uwsgi.post_buffering_bufsize); + } + else { + post_chunk = read(wsgi_req->poll.fd, wsgi_req->post_buffering_buf, post_remains); + } + if (post_chunk < 0) { + uwsgi_error("read()"); + goto clear; + } + if (!fwrite(wsgi_req->post_buffering_buf, post_chunk, 1, wsgi_req->async_post)) { + uwsgi_error("fwrite()"); + goto clear; + } + post_remains -= post_chunk; + } + rewind(wsgi_req->async_post); + UWSGI_GET_GIL + } + else { + wsgi_req->async_post = fdopen(wsgi_req->poll.fd, "r"); + } + + + wsgi_req->async_result = wi->request_subhandler(wsgi_req, wi); + + + uwsgi_log("fase2\n"); + + if (wsgi_req->async_result) { + + + UWSGI_RELEASE_GIL + while ( (*wi->response_subhandler)(wsgi_req) != UWSGI_OK) { + wsgi_req->switches++; + uwsgi_log("again...\n"); +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + return UWSGI_AGAIN; + } +#endif + } + + + } + + else if (up.catch_exceptions) { + + // LOCK THIS PART + + wsgi_req->response_size += write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len); + wsgi_req->response_size += write(wsgi_req->poll.fd, " 500 Internal Server Error\r\n", 28 ); + wsgi_req->response_size += write(wsgi_req->poll.fd, "Content-type: text/plain\r\n\r\n", 28 ); + wsgi_req->header_cnt = 1 ; + + /* + sorry that is a hack to avoid the rewrite of PyErr_Print + temporarily map (using dup2) stderr to wsgi_req->poll.fd + */ + tmp_stderr = dup(2); + if (tmp_stderr < 0) { + uwsgi_error("dup()"); + goto clear; + } + // map 2 to wsgi_req + if (dup2(wsgi_req->poll.fd, 2) < 0) { + close(tmp_stderr); + uwsgi_error("dup2()"); + goto clear; + } + // print the error + PyErr_Print(); + // ...resume the original stderr, in case of error we are damaged forever !!! + if (dup2(tmp_stderr, 2) < 0) { + uwsgi_error("dup2()"); + } + close(tmp_stderr); + } + + uwsgi_log("opps\n"); + +clear: + + UWSGI_GET_GIL + + if (uwsgi.single_interpreter == 0 && wsgi_req->app_id > 0) { + // restoring main interpreter + if (uwsgi.threads > 1) { + PyThreadState_Swap((PyThreadState *) pthread_getspecific(up.upt_save_key)); + } + else { + PyThreadState_Swap(up.main_thread); + } + } + + UWSGI_RELEASE_GIL + +clear2: + + + return UWSGI_OK; + +} + +void uwsgi_after_request_wsgi(struct wsgi_request *wsgi_req) { + + uwsgi_log("LOGGING\n"); + + if (uwsgi.shared->options[UWSGI_OPTION_LOGGING]) { + log_request(wsgi_req); + } + else { + if (uwsgi.shared->options[UWSGI_OPTION_LOG_ZERO]) { + if (wsgi_req->response_size == 0) { log_request(wsgi_req); return; } + } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_SLOW]) { + if ((uint32_t) wsgi_req_time >= uwsgi.shared->options[UWSGI_OPTION_LOG_SLOW]) { log_request(wsgi_req); return; } + } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_4xx]) { + if (wsgi_req->status >= 400 && wsgi_req->status <= 499) { log_request(wsgi_req); return; } + } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_5xx]) { + if (wsgi_req->status >= 500 && wsgi_req->status <= 599) { log_request(wsgi_req); return; } + } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_BIG]) { + if (wsgi_req->response_size >= uwsgi.shared->options[UWSGI_OPTION_LOG_BIG]) { log_request(wsgi_req); return; } + } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_SENDFILE]) { + if (wsgi_req->sendfile_fd > -1 && wsgi_req->sendfile_obj == wsgi_req->async_result) { log_request(wsgi_req); return; } + } + } +} + +#ifdef UWSGI_SENDFILE +PyObject *py_uwsgi_sendfile(PyObject * self, PyObject * args) { + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (!PyArg_ParseTuple(args, "O|i:uwsgi_sendfile", &wsgi_req->async_sendfile, &wsgi_req->sendfile_fd_chunk)) { + return NULL; + } + +#ifdef PYTHREE + wsgi_req->sendfile_fd = PyObject_AsFileDescriptor(wsgi_req->async_sendfile); +#else + if (PyFile_Check((PyObject *)wsgi_req->async_sendfile)) { + Py_INCREF((PyObject *)wsgi_req->async_sendfile); + wsgi_req->sendfile_fd = PyObject_AsFileDescriptor(wsgi_req->async_sendfile); + } +#endif + + // PEP 333 hack + wsgi_req->sendfile_obj = wsgi_req->async_sendfile; + //wsgi_req->sendfile_obj = (void *) PyTuple_New(0); + + Py_INCREF((PyObject *) wsgi_req->sendfile_obj); + return (PyObject *) wsgi_req->sendfile_obj; +} +#endif diff --git a/plugins/python/wsgi_headers.c b/plugins/python/wsgi_headers.c new file mode 100644 index 00000000..157b1b01 --- /dev/null +++ b/plugins/python/wsgi_headers.c @@ -0,0 +1,163 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +static char *nl = "\r\n"; +static char *h_sep = ": "; +static const char *http_protocol = "HTTP/1.1"; + +// check here + +PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) { + PyObject *headers, *head; + PyObject *h_key, *h_value; + int i, j; + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + int base = 0; + int shift = 0; + + // use writev() + + // is a Web3 response ? + /* + if (PyTuple_Size(args) == 3) { + shift = 0; + } + */ + + uwsgi_log("HEADERS !!!!\n"); + + head = PyTuple_GetItem(args, 0+shift); + if (!head) { + goto clear; + } + + if (!PyString_Check(head)) { + uwsgi_log( "http status must be a string !\n"); + goto clear; + } + + + if (uwsgi.shared->options[UWSGI_OPTION_CGI_MODE] == 0) { + base = 4; + + if (wsgi_req->protocol_len == 0) { + wsgi_req->hvec[0].iov_base = (char *) http_protocol; + wsgi_req->protocol_len = 8; + } + else { + wsgi_req->hvec[0].iov_base = wsgi_req->protocol; + } + + wsgi_req->hvec[0].iov_len = wsgi_req->protocol_len; + wsgi_req->hvec[1].iov_base = " "; + wsgi_req->hvec[1].iov_len = 1; +#ifdef PYTHREE + wsgi_req->hvec[2].iov_base = PyBytes_AsString(PyUnicode_AsASCIIString(head)); + wsgi_req->hvec[2].iov_len = strlen(wsgi_req->hvec[2].iov_base); +#else + wsgi_req->hvec[2].iov_base = PyString_AsString(head); + wsgi_req->hvec[2].iov_len = PyString_Size(head); +#endif + wsgi_req->status = atoi(wsgi_req->hvec[2].iov_base); + wsgi_req->hvec[3].iov_base = nl; + wsgi_req->hvec[3].iov_len = NL_SIZE; + } + else { + // drop http status on cgi mode + base = 3; + wsgi_req->hvec[0].iov_base = "Status: "; + wsgi_req->hvec[0].iov_len = 8; +#ifdef PYTHREE + wsgi_req->hvec[1].iov_base = PyBytes_AsString(PyUnicode_AsASCIIString(head)); + wsgi_req->hvec[1].iov_len = strlen(wsgi_req->hvec[1].iov_base); +#else + wsgi_req->hvec[1].iov_base = PyString_AsString(head); + wsgi_req->hvec[1].iov_len = PyString_Size(head); +#endif + wsgi_req->status = atoi(wsgi_req->hvec[1].iov_base); + wsgi_req->hvec[2].iov_base = nl; + wsgi_req->hvec[2].iov_len = NL_SIZE; + } + + + headers = PyTuple_GetItem(args, 1+shift); + if (!headers) { + goto clear; + } + if (!PyList_Check(headers)) { + uwsgi_log( "http headers must be in a python list\n"); + goto clear; + } + wsgi_req->header_cnt = PyList_Size(headers); + + + + if (wsgi_req->header_cnt > uwsgi.max_vars) { + wsgi_req->header_cnt = uwsgi.max_vars; + } + for (i = 0; i < wsgi_req->header_cnt; i++) { + j = (i * 4) + base; + head = PyList_GetItem(headers, i); + if (!head) { + goto clear; + } + if (!PyTuple_Check(head)) { + uwsgi_log( "http header must be defined in a tuple !\n"); + goto clear; + } + h_key = PyTuple_GetItem(head, 0); + if (!h_key) { + goto clear; + } + h_value = PyTuple_GetItem(head, 1); + if (!h_value) { + goto clear; + } +#ifdef PYTHREE + wsgi_req->hvec[j].iov_base = PyBytes_AsString(PyUnicode_AsASCIIString(h_key)); + wsgi_req->hvec[j].iov_len = strlen(wsgi_req->hvec[j].iov_base); +#else + wsgi_req->hvec[j].iov_base = PyString_AsString(h_key); + wsgi_req->hvec[j].iov_len = PyString_Size(h_key); +#endif + wsgi_req->hvec[j + 1].iov_base = h_sep; + wsgi_req->hvec[j + 1].iov_len = H_SEP_SIZE; +#ifdef PYTHREE + wsgi_req->hvec[j + 2].iov_base = PyBytes_AsString(PyUnicode_AsASCIIString(h_value)); + wsgi_req->hvec[j + 2].iov_len = strlen(wsgi_req->hvec[j + 2].iov_base); +#else + wsgi_req->hvec[j + 2].iov_base = PyString_AsString(h_value); + wsgi_req->hvec[j + 2].iov_len = PyString_Size(h_value); +#endif + wsgi_req->hvec[j + 3].iov_base = nl; + wsgi_req->hvec[j + 3].iov_len = NL_SIZE; + //uwsgi_log( "%.*s: %.*s\n", wsgi_req->hvec[j].iov_len, (char *)wsgi_req->hvec[j].iov_base, wsgi_req->hvec[j+2].iov_len, (char *) wsgi_req->hvec[j+2].iov_base); + } + + + // \r\n + j = (i * 4) + base; + wsgi_req->hvec[j].iov_base = nl; + wsgi_req->hvec[j].iov_len = NL_SIZE; + + UWSGI_RELEASE_GIL + wsgi_req->headers_size = writev(wsgi_req->poll.fd, wsgi_req->hvec, j + 1); + UWSGI_GET_GIL + if (wsgi_req->headers_size < 0) { + uwsgi_error("writev()"); + } + + Py_INCREF(up.wsgi_writeout); + + + return up.wsgi_writeout; + + clear: + + Py_INCREF(Py_None); + return Py_None; +} diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c new file mode 100644 index 00000000..fd7c143e --- /dev/null +++ b/plugins/python/wsgi_subhandler.c @@ -0,0 +1,223 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + +void *uwsgi_request_subhandler_wsgi(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { + + PyObject *wsgi_socket, *zero; + + //static PyObject *uwsgi_version = NULL; + + /* + if (uwsgi_version == NULL) { + uwsgi_version = PyString_FromString(UWSGI_VERSION); + } + */ + + + wsgi_socket = PyFile_FromFile(wsgi_req->async_post, "wsgi_input", "r", NULL); + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.input", wsgi_socket); + Py_DECREF(wsgi_socket); + +#ifdef UWSGI_SENDFILE + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.file_wrapper", wi->sendfile); +#endif + +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + 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); + } +#endif + + // cache this + zero = PyTuple_New(2); + PyTuple_SetItem(zero, 0, PyInt_FromLong(1)); + PyTuple_SetItem(zero, 1, PyInt_FromLong(0)); + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.version", zero); + Py_DECREF(zero); + + zero = PyFile_FromFile(stderr, "wsgi_input", "w", NULL); + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.errors", zero); + Py_DECREF(zero); + + 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 = PyString_FromStringAndSize(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 = PyString_FromString("https"); + } + else { + zero = PyString_FromString("http"); + } + } + else { + zero = PyString_FromString("http"); + } + PyDict_SetItemString(wsgi_req->async_environ, "wsgi.url_scheme", zero); + Py_DECREF(zero); + + uwsgi_log("subhandler ok\n"); + + wsgi_req->async_app = wi->callable ; + + //PyDict_SetItemString(uwsgi.embedded_dict, "env", wsgi_req->async_environ); + + //PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.version", uwsgi_version); + + PyDict_SetItemString(wsgi_req->async_environ, "uwsgi.core", PyInt_FromLong(wsgi_req->async_id)); + + +#ifdef UWSGI_ROUTING + uwsgi_log("routing %d routes %d\n", uwsgi.routing, uwsgi.nroutes); + if (uwsgi.routing && uwsgi.nroutes > 0) { + check_route(uwsgi, wsgi_req); + } +#endif + + + // call + + + PyTuple_SetItem(wsgi_req->async_args, 0, wsgi_req->async_environ); + uwsgi_log("subhandler2 ok %p %p %p\n", wsgi_req->async_app, wsgi_req->async_args, wsgi_req->async_environ ); + return python_call(wsgi_req->async_app, wsgi_req->async_args, up.catch_exceptions); +} + +int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { + + PyObject *pychunk ; + ssize_t wsize ; +#ifdef UWSGI_SENDFILE + ssize_t sf_len = 0 ; +#endif + + UWSGI_GET_GIL + + // return or yield ? + 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; + } + +#ifdef UWSGI_SENDFILE + if (wsgi_req->sendfile_obj == wsgi_req->async_result && wsgi_req->sendfile_fd != -1) { + sf_len = uwsgi_sendfile(wsgi_req); + if (sf_len < 1) goto clear; + wsgi_req->response_size += sf_len ; +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + if (wsgi_req->response_size < wsgi_req->sendfile_fd_size) { + UWSGI_RELEASE_GIL + return UWSGI_AGAIN; + } + } +#endif + goto clear; + } +#endif + + // 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 clear2; + } +#ifdef UWSGI_ASYNC + if (uwsgi.async > 1) { + UWSGI_RELEASE_GIL + return UWSGI_AGAIN; + } +#endif + } + + + + pychunk = PyIter_Next(wsgi_req->async_placeholder) ; + + if (!pychunk) { + if (PyErr_Occurred()) PyErr_Print(); + goto clear; + } + + + + if (PyString_Check(pychunk)) { + if ((wsize = write(wsgi_req->poll.fd, PyString_AsString(pychunk), PyString_Size(pychunk))) < 0) { + uwsgi_error("write()"); + Py_DECREF(pychunk); + goto clear; + } + wsgi_req->response_size += wsize; + } + +#ifdef UWSGI_SENDFILE + else if (wsgi_req->sendfile_obj == pychunk && wsgi_req->sendfile_fd != -1) { + sf_len = uwsgi_sendfile(wsgi_req); + if (sf_len < 1) goto clear; + wsgi_req->response_size += sf_len ; + } +#endif + + Py_DECREF(pychunk); + UWSGI_RELEASE_GIL + return UWSGI_AGAIN ; + +clear: + if (wsgi_req->sendfile_fd != -1) { + Py_DECREF((PyObject *)wsgi_req->async_sendfile); + } + 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; +} + +