11591Smax.romanov@nginx.com 21591Smax.romanov@nginx.com /* 31591Smax.romanov@nginx.com * Copyright (C) Max Romanov 41591Smax.romanov@nginx.com * Copyright (C) Valentin V. Bartenev 51591Smax.romanov@nginx.com * Copyright (C) NGINX, Inc. 61591Smax.romanov@nginx.com */ 71591Smax.romanov@nginx.com 81591Smax.romanov@nginx.com 91591Smax.romanov@nginx.com #include <Python.h> 101591Smax.romanov@nginx.com 111591Smax.romanov@nginx.com #include <nxt_main.h> 121591Smax.romanov@nginx.com #include <nxt_router.h> 131591Smax.romanov@nginx.com #include <nxt_unit.h> 141591Smax.romanov@nginx.com #include <nxt_unit_field.h> 151591Smax.romanov@nginx.com #include <nxt_unit_request.h> 161591Smax.romanov@nginx.com #include <nxt_unit_response.h> 171591Smax.romanov@nginx.com 181592Smax.romanov@nginx.com #include <python/nxt_python.h> 191592Smax.romanov@nginx.com 201591Smax.romanov@nginx.com #include NXT_PYTHON_MOUNTS_H 211591Smax.romanov@nginx.com 221591Smax.romanov@nginx.com /* 231591Smax.romanov@nginx.com * According to "PEP 3333 / A Note On String Types" 241591Smax.romanov@nginx.com * [https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types] 251591Smax.romanov@nginx.com * 261591Smax.romanov@nginx.com * WSGI therefore defines two kinds of "string": 271591Smax.romanov@nginx.com * 281591Smax.romanov@nginx.com * - "Native" strings (which are always implemented using the type named str ) 291591Smax.romanov@nginx.com * that are used for request/response headers and metadata 301591Smax.romanov@nginx.com * 311591Smax.romanov@nginx.com * will use PyString_* or corresponding PyUnicode_* functions 321591Smax.romanov@nginx.com * 331591Smax.romanov@nginx.com * - "Bytestrings" (which are implemented using the bytes type in Python 3, and 341591Smax.romanov@nginx.com * str elsewhere), that are used for the bodies of requests and responses 351591Smax.romanov@nginx.com * (e.g. POST/PUT input data and HTML page outputs). 361591Smax.romanov@nginx.com * 371591Smax.romanov@nginx.com * will use PyString_* or corresponding PyBytes_* functions 381591Smax.romanov@nginx.com */ 391591Smax.romanov@nginx.com 401591Smax.romanov@nginx.com 411591Smax.romanov@nginx.com typedef struct { 421591Smax.romanov@nginx.com PyObject_HEAD 431681Smax.romanov@nginx.com 441681Smax.romanov@nginx.com uint64_t content_length; 451681Smax.romanov@nginx.com uint64_t bytes_sent; 461681Smax.romanov@nginx.com PyObject *environ; 471681Smax.romanov@nginx.com PyObject *start_resp; 481681Smax.romanov@nginx.com PyObject *write; 491681Smax.romanov@nginx.com nxt_unit_request_info_t *req; 501681Smax.romanov@nginx.com PyThreadState *thread_state; 511681Smax.romanov@nginx.com } nxt_python_ctx_t; 521591Smax.romanov@nginx.com 531591Smax.romanov@nginx.com 541681Smax.romanov@nginx.com static int nxt_python_wsgi_ctx_data_alloc(void **pdata); 551681Smax.romanov@nginx.com static void nxt_python_wsgi_ctx_data_free(void *data); 561681Smax.romanov@nginx.com static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx); 571681Smax.romanov@nginx.com static void nxt_python_wsgi_done(void); 581591Smax.romanov@nginx.com 591591Smax.romanov@nginx.com static void nxt_python_request_handler(nxt_unit_request_info_t *req); 601591Smax.romanov@nginx.com 611681Smax.romanov@nginx.com static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c); 62*1745Svbart@nginx.com static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req); 631681Smax.romanov@nginx.com static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx); 641681Smax.romanov@nginx.com static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, 651591Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t size); 661681Smax.romanov@nginx.com static int nxt_python_add_field(nxt_python_ctx_t *pctx, 671594Smax.romanov@nginx.com nxt_unit_field_t *field, int n, uint32_t vl); 681594Smax.romanov@nginx.com static PyObject *nxt_python_field_name(const char *name, uint8_t len); 691594Smax.romanov@nginx.com static PyObject *nxt_python_field_value(nxt_unit_field_t *f, int n, 701594Smax.romanov@nginx.com uint32_t vl); 711681Smax.romanov@nginx.com static int nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, 721591Smax.romanov@nginx.com PyObject *value); 731591Smax.romanov@nginx.com 741591Smax.romanov@nginx.com static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args); 751681Smax.romanov@nginx.com static int nxt_python_response_add_field(nxt_python_ctx_t *pctx, 761591Smax.romanov@nginx.com PyObject *name, PyObject *value, int i); 771591Smax.romanov@nginx.com static int nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, 781591Smax.romanov@nginx.com PyObject **bytes); 791591Smax.romanov@nginx.com static PyObject *nxt_py_write(PyObject *self, PyObject *args); 801591Smax.romanov@nginx.com 811681Smax.romanov@nginx.com static void nxt_py_input_dealloc(nxt_python_ctx_t *pctx); 821681Smax.romanov@nginx.com static PyObject *nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args); 831681Smax.romanov@nginx.com static PyObject *nxt_py_input_readline(nxt_python_ctx_t *pctx, 841681Smax.romanov@nginx.com PyObject *args); 851681Smax.romanov@nginx.com static PyObject *nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size); 861681Smax.romanov@nginx.com static PyObject *nxt_py_input_readlines(nxt_python_ctx_t *self, 871681Smax.romanov@nginx.com PyObject *args); 881591Smax.romanov@nginx.com 891681Smax.romanov@nginx.com static PyObject *nxt_py_input_iter(PyObject *pctx); 901681Smax.romanov@nginx.com static PyObject *nxt_py_input_next(PyObject *pctx); 911591Smax.romanov@nginx.com 921681Smax.romanov@nginx.com static int nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes); 931591Smax.romanov@nginx.com 941591Smax.romanov@nginx.com 951591Smax.romanov@nginx.com static PyMethodDef nxt_py_start_resp_method[] = { 961591Smax.romanov@nginx.com {"unit_start_response", nxt_py_start_resp, METH_VARARGS, ""} 971591Smax.romanov@nginx.com }; 981591Smax.romanov@nginx.com 991591Smax.romanov@nginx.com 1001591Smax.romanov@nginx.com static PyMethodDef nxt_py_write_method[] = { 1011591Smax.romanov@nginx.com {"unit_write", nxt_py_write, METH_O, ""} 1021591Smax.romanov@nginx.com }; 1031591Smax.romanov@nginx.com 1041591Smax.romanov@nginx.com 1051591Smax.romanov@nginx.com static PyMethodDef nxt_py_input_methods[] = { 1061591Smax.romanov@nginx.com { "read", (PyCFunction) nxt_py_input_read, METH_VARARGS, 0 }, 1071591Smax.romanov@nginx.com { "readline", (PyCFunction) nxt_py_input_readline, METH_VARARGS, 0 }, 1081591Smax.romanov@nginx.com { "readlines", (PyCFunction) nxt_py_input_readlines, METH_VARARGS, 0 }, 1091591Smax.romanov@nginx.com { NULL, NULL, 0, 0 } 1101591Smax.romanov@nginx.com }; 1111591Smax.romanov@nginx.com 1121591Smax.romanov@nginx.com 1131591Smax.romanov@nginx.com static PyTypeObject nxt_py_input_type = { 1141591Smax.romanov@nginx.com PyVarObject_HEAD_INIT(NULL, 0) 1151591Smax.romanov@nginx.com 1161591Smax.romanov@nginx.com .tp_name = "unit._input", 1171681Smax.romanov@nginx.com .tp_basicsize = sizeof(nxt_python_ctx_t), 1181591Smax.romanov@nginx.com .tp_dealloc = (destructor) nxt_py_input_dealloc, 1191591Smax.romanov@nginx.com .tp_flags = Py_TPFLAGS_DEFAULT, 1201591Smax.romanov@nginx.com .tp_doc = "unit input object.", 1211591Smax.romanov@nginx.com .tp_iter = nxt_py_input_iter, 1221591Smax.romanov@nginx.com .tp_iternext = nxt_py_input_next, 1231591Smax.romanov@nginx.com .tp_methods = nxt_py_input_methods, 1241591Smax.romanov@nginx.com }; 1251591Smax.romanov@nginx.com 1261591Smax.romanov@nginx.com 1271681Smax.romanov@nginx.com static PyObject *nxt_py_environ_ptyp; 1281591Smax.romanov@nginx.com 1291591Smax.romanov@nginx.com static PyObject *nxt_py_80_str; 1301591Smax.romanov@nginx.com static PyObject *nxt_py_close_str; 1311591Smax.romanov@nginx.com static PyObject *nxt_py_content_length_str; 1321591Smax.romanov@nginx.com static PyObject *nxt_py_content_type_str; 1331591Smax.romanov@nginx.com static PyObject *nxt_py_http_str; 1341591Smax.romanov@nginx.com static PyObject *nxt_py_https_str; 1351591Smax.romanov@nginx.com static PyObject *nxt_py_path_info_str; 1361591Smax.romanov@nginx.com static PyObject *nxt_py_query_string_str; 1371591Smax.romanov@nginx.com static PyObject *nxt_py_remote_addr_str; 1381591Smax.romanov@nginx.com static PyObject *nxt_py_request_method_str; 1391591Smax.romanov@nginx.com static PyObject *nxt_py_request_uri_str; 1401591Smax.romanov@nginx.com static PyObject *nxt_py_server_addr_str; 1411591Smax.romanov@nginx.com static PyObject *nxt_py_server_name_str; 1421591Smax.romanov@nginx.com static PyObject *nxt_py_server_port_str; 1431591Smax.romanov@nginx.com static PyObject *nxt_py_server_protocol_str; 1441681Smax.romanov@nginx.com static PyObject *nxt_py_wsgi_input_str; 1451591Smax.romanov@nginx.com static PyObject *nxt_py_wsgi_uri_scheme_str; 1461591Smax.romanov@nginx.com 1471591Smax.romanov@nginx.com static nxt_python_string_t nxt_python_strings[] = { 1481591Smax.romanov@nginx.com { nxt_string("80"), &nxt_py_80_str }, 1491591Smax.romanov@nginx.com { nxt_string("close"), &nxt_py_close_str }, 1501591Smax.romanov@nginx.com { nxt_string("CONTENT_LENGTH"), &nxt_py_content_length_str }, 1511591Smax.romanov@nginx.com { nxt_string("CONTENT_TYPE"), &nxt_py_content_type_str }, 1521591Smax.romanov@nginx.com { nxt_string("http"), &nxt_py_http_str }, 1531591Smax.romanov@nginx.com { nxt_string("https"), &nxt_py_https_str }, 1541591Smax.romanov@nginx.com { nxt_string("PATH_INFO"), &nxt_py_path_info_str }, 1551591Smax.romanov@nginx.com { nxt_string("QUERY_STRING"), &nxt_py_query_string_str }, 1561591Smax.romanov@nginx.com { nxt_string("REMOTE_ADDR"), &nxt_py_remote_addr_str }, 1571591Smax.romanov@nginx.com { nxt_string("REQUEST_METHOD"), &nxt_py_request_method_str }, 1581591Smax.romanov@nginx.com { nxt_string("REQUEST_URI"), &nxt_py_request_uri_str }, 1591591Smax.romanov@nginx.com { nxt_string("SERVER_ADDR"), &nxt_py_server_addr_str }, 1601591Smax.romanov@nginx.com { nxt_string("SERVER_NAME"), &nxt_py_server_name_str }, 1611591Smax.romanov@nginx.com { nxt_string("SERVER_PORT"), &nxt_py_server_port_str }, 1621591Smax.romanov@nginx.com { nxt_string("SERVER_PROTOCOL"), &nxt_py_server_protocol_str }, 1631681Smax.romanov@nginx.com { nxt_string("wsgi.input"), &nxt_py_wsgi_input_str }, 1641591Smax.romanov@nginx.com { nxt_string("wsgi.url_scheme"), &nxt_py_wsgi_uri_scheme_str }, 1651592Smax.romanov@nginx.com { nxt_null_string, NULL }, 1661591Smax.romanov@nginx.com }; 1671591Smax.romanov@nginx.com 1681681Smax.romanov@nginx.com static nxt_python_proto_t nxt_py_wsgi_proto = { 1691681Smax.romanov@nginx.com .ctx_data_alloc = nxt_python_wsgi_ctx_data_alloc, 1701681Smax.romanov@nginx.com .ctx_data_free = nxt_python_wsgi_ctx_data_free, 1711681Smax.romanov@nginx.com .run = nxt_python_wsgi_run, 1721681Smax.romanov@nginx.com .done = nxt_python_wsgi_done, 1731681Smax.romanov@nginx.com }; 1741591Smax.romanov@nginx.com 1751681Smax.romanov@nginx.com 1761681Smax.romanov@nginx.com int 1771681Smax.romanov@nginx.com nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 1781591Smax.romanov@nginx.com { 1791592Smax.romanov@nginx.com PyObject *obj; 1801591Smax.romanov@nginx.com 1811591Smax.romanov@nginx.com obj = NULL; 1821591Smax.romanov@nginx.com 1831681Smax.romanov@nginx.com if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings) 1841681Smax.romanov@nginx.com != NXT_UNIT_OK)) 1851681Smax.romanov@nginx.com { 1861681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to init string objects"); 1871591Smax.romanov@nginx.com goto fail; 1881591Smax.romanov@nginx.com } 1891591Smax.romanov@nginx.com 1901681Smax.romanov@nginx.com obj = nxt_python_create_environ(init->data); 1911591Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 1921591Smax.romanov@nginx.com goto fail; 1931591Smax.romanov@nginx.com } 1941591Smax.romanov@nginx.com 1951591Smax.romanov@nginx.com nxt_py_environ_ptyp = obj; 1961591Smax.romanov@nginx.com obj = NULL; 1971591Smax.romanov@nginx.com 1981592Smax.romanov@nginx.com init->callbacks.request_handler = nxt_python_request_handler; 1991591Smax.romanov@nginx.com 2001681Smax.romanov@nginx.com *proto = nxt_py_wsgi_proto; 2011681Smax.romanov@nginx.com 2021681Smax.romanov@nginx.com return NXT_UNIT_OK; 2031591Smax.romanov@nginx.com 2041591Smax.romanov@nginx.com fail: 2051591Smax.romanov@nginx.com 2061591Smax.romanov@nginx.com Py_XDECREF(obj); 2071591Smax.romanov@nginx.com 2081681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2091591Smax.romanov@nginx.com } 2101591Smax.romanov@nginx.com 2111591Smax.romanov@nginx.com 2121681Smax.romanov@nginx.com static int 2131681Smax.romanov@nginx.com nxt_python_wsgi_ctx_data_alloc(void **pdata) 2141681Smax.romanov@nginx.com { 2151681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 2161681Smax.romanov@nginx.com 2171681Smax.romanov@nginx.com pctx = PyObject_New(nxt_python_ctx_t, &nxt_py_input_type); 2181681Smax.romanov@nginx.com if (nxt_slow_path(pctx == NULL)) { 2191681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2201681Smax.romanov@nginx.com "Python failed to create the \"wsgi.input\" object"); 2211681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2221681Smax.romanov@nginx.com } 2231681Smax.romanov@nginx.com 2241681Smax.romanov@nginx.com pctx->write = NULL; 225*1745Svbart@nginx.com pctx->environ = NULL; 2261681Smax.romanov@nginx.com 2271681Smax.romanov@nginx.com pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method, 2281681Smax.romanov@nginx.com (PyObject *) pctx); 2291681Smax.romanov@nginx.com if (nxt_slow_path(pctx->start_resp == NULL)) { 2301681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2311681Smax.romanov@nginx.com "Python failed to initialize the \"start_response\" function"); 2321681Smax.romanov@nginx.com goto fail; 2331681Smax.romanov@nginx.com } 2341681Smax.romanov@nginx.com 2351681Smax.romanov@nginx.com pctx->write = PyCFunction_New(nxt_py_write_method, (PyObject *) pctx); 2361681Smax.romanov@nginx.com if (nxt_slow_path(pctx->write == NULL)) { 2371681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2381681Smax.romanov@nginx.com "Python failed to initialize the \"write\" function"); 2391681Smax.romanov@nginx.com goto fail; 2401681Smax.romanov@nginx.com } 2411681Smax.romanov@nginx.com 242*1745Svbart@nginx.com pctx->environ = nxt_python_copy_environ(NULL); 243*1745Svbart@nginx.com if (nxt_slow_path(pctx->environ == NULL)) { 244*1745Svbart@nginx.com goto fail; 245*1745Svbart@nginx.com } 246*1745Svbart@nginx.com 2471681Smax.romanov@nginx.com *pdata = pctx; 2481681Smax.romanov@nginx.com 2491681Smax.romanov@nginx.com return NXT_UNIT_OK; 2501681Smax.romanov@nginx.com 2511681Smax.romanov@nginx.com fail: 2521681Smax.romanov@nginx.com 2531681Smax.romanov@nginx.com nxt_python_wsgi_ctx_data_free(pctx); 2541681Smax.romanov@nginx.com 2551681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2561681Smax.romanov@nginx.com } 2571681Smax.romanov@nginx.com 2581681Smax.romanov@nginx.com 2591681Smax.romanov@nginx.com static void 2601681Smax.romanov@nginx.com nxt_python_wsgi_ctx_data_free(void *data) 2611681Smax.romanov@nginx.com { 2621681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 2631681Smax.romanov@nginx.com 2641681Smax.romanov@nginx.com pctx = data; 2651681Smax.romanov@nginx.com 2661681Smax.romanov@nginx.com Py_XDECREF(pctx->start_resp); 2671681Smax.romanov@nginx.com Py_XDECREF(pctx->write); 268*1745Svbart@nginx.com Py_XDECREF(pctx->environ); 2691681Smax.romanov@nginx.com Py_XDECREF(pctx); 2701681Smax.romanov@nginx.com } 2711681Smax.romanov@nginx.com 2721681Smax.romanov@nginx.com 2731681Smax.romanov@nginx.com static int 2741592Smax.romanov@nginx.com nxt_python_wsgi_run(nxt_unit_ctx_t *ctx) 2751591Smax.romanov@nginx.com { 2761681Smax.romanov@nginx.com int rc; 2771681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 2781592Smax.romanov@nginx.com 2791681Smax.romanov@nginx.com pctx = ctx->data; 2801681Smax.romanov@nginx.com 2811681Smax.romanov@nginx.com pctx->thread_state = PyEval_SaveThread(); 2821591Smax.romanov@nginx.com 2831592Smax.romanov@nginx.com rc = nxt_unit_run(ctx); 2841592Smax.romanov@nginx.com 2851681Smax.romanov@nginx.com PyEval_RestoreThread(pctx->thread_state); 2861591Smax.romanov@nginx.com 2871592Smax.romanov@nginx.com return rc; 2881592Smax.romanov@nginx.com } 2891592Smax.romanov@nginx.com 2901591Smax.romanov@nginx.com 2911681Smax.romanov@nginx.com static void 2921592Smax.romanov@nginx.com nxt_python_wsgi_done(void) 2931592Smax.romanov@nginx.com { 2941592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_strings); 2951591Smax.romanov@nginx.com 2961592Smax.romanov@nginx.com Py_XDECREF(nxt_py_environ_ptyp); 2971591Smax.romanov@nginx.com } 2981591Smax.romanov@nginx.com 2991591Smax.romanov@nginx.com 3001591Smax.romanov@nginx.com static void 3011591Smax.romanov@nginx.com nxt_python_request_handler(nxt_unit_request_info_t *req) 3021591Smax.romanov@nginx.com { 3031681Smax.romanov@nginx.com int rc; 3041681Smax.romanov@nginx.com PyObject *environ, *args, *response, *iterator, *item; 3051681Smax.romanov@nginx.com PyObject *close, *result; 306*1745Svbart@nginx.com nxt_bool_t prepare_environ; 3071681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 3081681Smax.romanov@nginx.com 3091681Smax.romanov@nginx.com pctx = req->ctx->data; 3101591Smax.romanov@nginx.com 3111681Smax.romanov@nginx.com pctx->content_length = -1; 3121681Smax.romanov@nginx.com pctx->bytes_sent = 0; 3131681Smax.romanov@nginx.com pctx->req = req; 3141591Smax.romanov@nginx.com 3151681Smax.romanov@nginx.com PyEval_RestoreThread(pctx->thread_state); 3161681Smax.romanov@nginx.com 317*1745Svbart@nginx.com if (nxt_slow_path(pctx->environ == NULL)) { 318*1745Svbart@nginx.com pctx->environ = nxt_python_copy_environ(req); 319*1745Svbart@nginx.com 320*1745Svbart@nginx.com if (pctx->environ == NULL) { 321*1745Svbart@nginx.com prepare_environ = 0; 322*1745Svbart@nginx.com 323*1745Svbart@nginx.com rc = NXT_UNIT_ERROR; 324*1745Svbart@nginx.com goto done; 325*1745Svbart@nginx.com } 326*1745Svbart@nginx.com } 327*1745Svbart@nginx.com 328*1745Svbart@nginx.com prepare_environ = 1; 329*1745Svbart@nginx.com 3301681Smax.romanov@nginx.com environ = nxt_python_get_environ(pctx); 3311591Smax.romanov@nginx.com if (nxt_slow_path(environ == NULL)) { 3321591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 3331591Smax.romanov@nginx.com goto done; 3341591Smax.romanov@nginx.com } 3351591Smax.romanov@nginx.com 3361591Smax.romanov@nginx.com args = PyTuple_New(2); 3371591Smax.romanov@nginx.com if (nxt_slow_path(args == NULL)) { 3381591Smax.romanov@nginx.com Py_DECREF(environ); 3391591Smax.romanov@nginx.com 3401591Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to create arguments tuple"); 3411591Smax.romanov@nginx.com 3421591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 3431591Smax.romanov@nginx.com goto done; 3441591Smax.romanov@nginx.com } 3451591Smax.romanov@nginx.com 3461591Smax.romanov@nginx.com PyTuple_SET_ITEM(args, 0, environ); 3471591Smax.romanov@nginx.com 3481681Smax.romanov@nginx.com Py_INCREF(pctx->start_resp); 3491681Smax.romanov@nginx.com PyTuple_SET_ITEM(args, 1, pctx->start_resp); 3501591Smax.romanov@nginx.com 3511591Smax.romanov@nginx.com response = PyObject_CallObject(nxt_py_application, args); 3521591Smax.romanov@nginx.com 3531591Smax.romanov@nginx.com Py_DECREF(args); 3541591Smax.romanov@nginx.com 3551591Smax.romanov@nginx.com if (nxt_slow_path(response == NULL)) { 3561591Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the application"); 3571591Smax.romanov@nginx.com nxt_python_print_exception(); 3581591Smax.romanov@nginx.com 3591591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 3601591Smax.romanov@nginx.com goto done; 3611591Smax.romanov@nginx.com } 3621591Smax.romanov@nginx.com 3631591Smax.romanov@nginx.com /* Shortcut: avoid iterate over response string symbols. */ 3641591Smax.romanov@nginx.com if (PyBytes_Check(response)) { 3651681Smax.romanov@nginx.com rc = nxt_python_write(pctx, response); 3661591Smax.romanov@nginx.com 3671591Smax.romanov@nginx.com } else { 3681591Smax.romanov@nginx.com iterator = PyObject_GetIter(response); 3691591Smax.romanov@nginx.com 3701591Smax.romanov@nginx.com if (nxt_fast_path(iterator != NULL)) { 3711591Smax.romanov@nginx.com rc = NXT_UNIT_OK; 3721591Smax.romanov@nginx.com 3731681Smax.romanov@nginx.com while (pctx->bytes_sent < pctx->content_length) { 3741591Smax.romanov@nginx.com item = PyIter_Next(iterator); 3751591Smax.romanov@nginx.com 3761591Smax.romanov@nginx.com if (item == NULL) { 3771591Smax.romanov@nginx.com if (nxt_slow_path(PyErr_Occurred() != NULL)) { 3781591Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to iterate over " 3791591Smax.romanov@nginx.com "the application response object"); 3801591Smax.romanov@nginx.com nxt_python_print_exception(); 3811591Smax.romanov@nginx.com 3821591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 3831591Smax.romanov@nginx.com } 3841591Smax.romanov@nginx.com 3851591Smax.romanov@nginx.com break; 3861591Smax.romanov@nginx.com } 3871591Smax.romanov@nginx.com 3881591Smax.romanov@nginx.com if (nxt_fast_path(PyBytes_Check(item))) { 3891681Smax.romanov@nginx.com rc = nxt_python_write(pctx, item); 3901591Smax.romanov@nginx.com 3911591Smax.romanov@nginx.com } else { 3921591Smax.romanov@nginx.com nxt_unit_req_error(req, "the application returned " 3931591Smax.romanov@nginx.com "not a bytestring object"); 3941591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 3951591Smax.romanov@nginx.com } 3961591Smax.romanov@nginx.com 3971591Smax.romanov@nginx.com Py_DECREF(item); 3981591Smax.romanov@nginx.com 3991591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 4001591Smax.romanov@nginx.com break; 4011591Smax.romanov@nginx.com } 4021591Smax.romanov@nginx.com } 4031591Smax.romanov@nginx.com 4041591Smax.romanov@nginx.com Py_DECREF(iterator); 4051591Smax.romanov@nginx.com 4061591Smax.romanov@nginx.com } else { 4071591Smax.romanov@nginx.com nxt_unit_req_error(req, 4081591Smax.romanov@nginx.com "the application returned not an iterable object"); 4091591Smax.romanov@nginx.com nxt_python_print_exception(); 4101591Smax.romanov@nginx.com 4111591Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 4121591Smax.romanov@nginx.com } 4131591Smax.romanov@nginx.com 4141591Smax.romanov@nginx.com close = PyObject_GetAttr(response, nxt_py_close_str); 4151591Smax.romanov@nginx.com 4161591Smax.romanov@nginx.com if (close != NULL) { 4171591Smax.romanov@nginx.com result = PyObject_CallFunction(close, NULL); 4181591Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 4191591Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the close() " 4201591Smax.romanov@nginx.com "method of the application response"); 4211591Smax.romanov@nginx.com nxt_python_print_exception(); 4221591Smax.romanov@nginx.com 4231591Smax.romanov@nginx.com } else { 4241591Smax.romanov@nginx.com Py_DECREF(result); 4251591Smax.romanov@nginx.com } 4261591Smax.romanov@nginx.com 4271591Smax.romanov@nginx.com Py_DECREF(close); 4281591Smax.romanov@nginx.com 4291591Smax.romanov@nginx.com } else { 4301591Smax.romanov@nginx.com PyErr_Clear(); 4311591Smax.romanov@nginx.com } 4321591Smax.romanov@nginx.com } 4331591Smax.romanov@nginx.com 4341591Smax.romanov@nginx.com Py_DECREF(response); 4351591Smax.romanov@nginx.com 4361591Smax.romanov@nginx.com done: 4371591Smax.romanov@nginx.com 4381681Smax.romanov@nginx.com pctx->thread_state = PyEval_SaveThread(); 4391591Smax.romanov@nginx.com 4401681Smax.romanov@nginx.com pctx->req = NULL; 4411681Smax.romanov@nginx.com 4421591Smax.romanov@nginx.com nxt_unit_request_done(req, rc); 443*1745Svbart@nginx.com 444*1745Svbart@nginx.com if (nxt_fast_path(prepare_environ)) { 445*1745Svbart@nginx.com PyEval_RestoreThread(pctx->thread_state); 446*1745Svbart@nginx.com 447*1745Svbart@nginx.com pctx->environ = nxt_python_copy_environ(NULL); 448*1745Svbart@nginx.com 449*1745Svbart@nginx.com pctx->thread_state = PyEval_SaveThread(); 450*1745Svbart@nginx.com } 4511591Smax.romanov@nginx.com } 4521591Smax.romanov@nginx.com 4531591Smax.romanov@nginx.com 4541591Smax.romanov@nginx.com static PyObject * 4551681Smax.romanov@nginx.com nxt_python_create_environ(nxt_python_app_conf_t *c) 4561591Smax.romanov@nginx.com { 4571591Smax.romanov@nginx.com PyObject *obj, *err, *environ; 4581591Smax.romanov@nginx.com 4591591Smax.romanov@nginx.com environ = PyDict_New(); 4601591Smax.romanov@nginx.com 4611591Smax.romanov@nginx.com if (nxt_slow_path(environ == NULL)) { 4621681Smax.romanov@nginx.com nxt_unit_alert(NULL, 4631681Smax.romanov@nginx.com "Python failed to create the \"environ\" dictionary"); 4641591Smax.romanov@nginx.com return NULL; 4651591Smax.romanov@nginx.com } 4661591Smax.romanov@nginx.com 4671591Smax.romanov@nginx.com obj = PyString_FromStringAndSize((char *) nxt_server.start, 4681591Smax.romanov@nginx.com nxt_server.length); 4691591Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 4701681Smax.romanov@nginx.com nxt_unit_alert(NULL, 4711591Smax.romanov@nginx.com "Python failed to create the \"SERVER_SOFTWARE\" environ value"); 4721591Smax.romanov@nginx.com goto fail; 4731591Smax.romanov@nginx.com } 4741591Smax.romanov@nginx.com 4751591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_SOFTWARE", obj) 4761591Smax.romanov@nginx.com != 0)) 4771591Smax.romanov@nginx.com { 4781681Smax.romanov@nginx.com nxt_unit_alert(NULL, 4791591Smax.romanov@nginx.com "Python failed to set the \"SERVER_SOFTWARE\" environ value"); 4801591Smax.romanov@nginx.com goto fail; 4811591Smax.romanov@nginx.com } 4821591Smax.romanov@nginx.com 4831591Smax.romanov@nginx.com Py_DECREF(obj); 4841591Smax.romanov@nginx.com 4851591Smax.romanov@nginx.com obj = Py_BuildValue("(ii)", 1, 0); 4861591Smax.romanov@nginx.com 4871591Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 4881681Smax.romanov@nginx.com nxt_unit_alert(NULL, 4891591Smax.romanov@nginx.com "Python failed to build the \"wsgi.version\" environ value"); 4901591Smax.romanov@nginx.com goto fail; 4911591Smax.romanov@nginx.com } 4921591Smax.romanov@nginx.com 4931591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.version", obj) != 0)) 4941591Smax.romanov@nginx.com { 4951681Smax.romanov@nginx.com nxt_unit_alert(NULL, 4961681Smax.romanov@nginx.com "Python failed to set the \"wsgi.version\" environ value"); 4971591Smax.romanov@nginx.com goto fail; 4981591Smax.romanov@nginx.com } 4991591Smax.romanov@nginx.com 5001591Smax.romanov@nginx.com Py_DECREF(obj); 5011591Smax.romanov@nginx.com obj = NULL; 5021591Smax.romanov@nginx.com 5031591Smax.romanov@nginx.com 5041591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multithread", 5051681Smax.romanov@nginx.com c->threads > 1 ? Py_True : Py_False) 5061591Smax.romanov@nginx.com != 0)) 5071591Smax.romanov@nginx.com { 5081681Smax.romanov@nginx.com nxt_unit_alert(NULL, 5091591Smax.romanov@nginx.com "Python failed to set the \"wsgi.multithread\" environ value"); 5101591Smax.romanov@nginx.com goto fail; 5111591Smax.romanov@nginx.com } 5121591Smax.romanov@nginx.com 5131591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multiprocess", 5141591Smax.romanov@nginx.com Py_True) 5151591Smax.romanov@nginx.com != 0)) 5161591Smax.romanov@nginx.com { 5171681Smax.romanov@nginx.com nxt_unit_alert(NULL, 5181591Smax.romanov@nginx.com "Python failed to set the \"wsgi.multiprocess\" environ value"); 5191591Smax.romanov@nginx.com goto fail; 5201591Smax.romanov@nginx.com } 5211591Smax.romanov@nginx.com 5221591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.run_once", 5231591Smax.romanov@nginx.com Py_False) 5241591Smax.romanov@nginx.com != 0)) 5251591Smax.romanov@nginx.com { 5261681Smax.romanov@nginx.com nxt_unit_alert(NULL, 5271591Smax.romanov@nginx.com "Python failed to set the \"wsgi.run_once\" environ value"); 5281591Smax.romanov@nginx.com goto fail; 5291591Smax.romanov@nginx.com } 5301591Smax.romanov@nginx.com 5311591Smax.romanov@nginx.com 5321591Smax.romanov@nginx.com if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) { 5331681Smax.romanov@nginx.com nxt_unit_alert(NULL, 5341591Smax.romanov@nginx.com "Python failed to initialize the \"wsgi.input\" type object"); 5351591Smax.romanov@nginx.com goto fail; 5361591Smax.romanov@nginx.com } 5371591Smax.romanov@nginx.com 5381591Smax.romanov@nginx.com 5391591Smax.romanov@nginx.com err = PySys_GetObject((char *) "stderr"); 5401591Smax.romanov@nginx.com 5411591Smax.romanov@nginx.com if (nxt_slow_path(err == NULL)) { 5421681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get \"sys.stderr\" object"); 5431591Smax.romanov@nginx.com goto fail; 5441591Smax.romanov@nginx.com } 5451591Smax.romanov@nginx.com 5461591Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.errors", err) != 0)) 5471591Smax.romanov@nginx.com { 5481681Smax.romanov@nginx.com nxt_unit_alert(NULL, 5491681Smax.romanov@nginx.com "Python failed to set the \"wsgi.errors\" environ value"); 5501591Smax.romanov@nginx.com goto fail; 5511591Smax.romanov@nginx.com } 5521591Smax.romanov@nginx.com 5531591Smax.romanov@nginx.com return environ; 5541591Smax.romanov@nginx.com 5551591Smax.romanov@nginx.com fail: 5561591Smax.romanov@nginx.com 5571591Smax.romanov@nginx.com Py_XDECREF(obj); 5581591Smax.romanov@nginx.com Py_DECREF(environ); 5591591Smax.romanov@nginx.com 5601591Smax.romanov@nginx.com return NULL; 5611591Smax.romanov@nginx.com } 5621591Smax.romanov@nginx.com 5631591Smax.romanov@nginx.com 5641591Smax.romanov@nginx.com static PyObject * 565*1745Svbart@nginx.com nxt_python_copy_environ(nxt_unit_request_info_t *req) 566*1745Svbart@nginx.com { 567*1745Svbart@nginx.com PyObject *environ; 568*1745Svbart@nginx.com 569*1745Svbart@nginx.com environ = PyDict_Copy(nxt_py_environ_ptyp); 570*1745Svbart@nginx.com 571*1745Svbart@nginx.com if (nxt_slow_path(environ == NULL)) { 572*1745Svbart@nginx.com nxt_unit_req_alert(req, 573*1745Svbart@nginx.com "Python failed to copy the \"environ\" dictionary"); 574*1745Svbart@nginx.com nxt_python_print_exception(); 575*1745Svbart@nginx.com } 576*1745Svbart@nginx.com 577*1745Svbart@nginx.com return environ; 578*1745Svbart@nginx.com } 579*1745Svbart@nginx.com 580*1745Svbart@nginx.com 581*1745Svbart@nginx.com static PyObject * 5821681Smax.romanov@nginx.com nxt_python_get_environ(nxt_python_ctx_t *pctx) 5831591Smax.romanov@nginx.com { 5841591Smax.romanov@nginx.com int rc; 5851594Smax.romanov@nginx.com uint32_t i, j, vl; 5861591Smax.romanov@nginx.com PyObject *environ; 5871594Smax.romanov@nginx.com nxt_unit_field_t *f, *f2; 5881591Smax.romanov@nginx.com nxt_unit_request_t *r; 5891591Smax.romanov@nginx.com 5901681Smax.romanov@nginx.com r = pctx->req->request; 5911591Smax.romanov@nginx.com 5921591Smax.romanov@nginx.com #define RC(S) \ 5931591Smax.romanov@nginx.com do { \ 5941591Smax.romanov@nginx.com rc = (S); \ 5951591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { \ 5961591Smax.romanov@nginx.com goto fail; \ 5971591Smax.romanov@nginx.com } \ 5981591Smax.romanov@nginx.com } while(0) 5991591Smax.romanov@nginx.com 6001681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_request_method_str, &r->method, 6011591Smax.romanov@nginx.com r->method_length)); 6021681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_request_uri_str, &r->target, 6031591Smax.romanov@nginx.com r->target_length)); 6041681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query, 6051591Smax.romanov@nginx.com r->query_length)); 6061681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path, 6071591Smax.romanov@nginx.com r->path_length)); 6081591Smax.romanov@nginx.com 6091681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote, 6101591Smax.romanov@nginx.com r->remote_length)); 6111681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local, 6121591Smax.romanov@nginx.com r->local_length)); 6131591Smax.romanov@nginx.com 6141591Smax.romanov@nginx.com if (r->tls) { 6151681Smax.romanov@nginx.com RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str, 6161591Smax.romanov@nginx.com nxt_py_https_str)); 6171591Smax.romanov@nginx.com } else { 6181681Smax.romanov@nginx.com RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str, 6191591Smax.romanov@nginx.com nxt_py_http_str)); 6201591Smax.romanov@nginx.com } 6211591Smax.romanov@nginx.com 6221681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_server_protocol_str, &r->version, 6231591Smax.romanov@nginx.com r->version_length)); 6241591Smax.romanov@nginx.com 6251681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_server_name_str, &r->server_name, 6261591Smax.romanov@nginx.com r->server_name_length)); 6271681Smax.romanov@nginx.com RC(nxt_python_add_obj(pctx, nxt_py_server_port_str, nxt_py_80_str)); 6281591Smax.romanov@nginx.com 6291681Smax.romanov@nginx.com nxt_unit_request_group_dup_fields(pctx->req); 6301594Smax.romanov@nginx.com 6311594Smax.romanov@nginx.com for (i = 0; i < r->fields_count;) { 6321591Smax.romanov@nginx.com f = r->fields + i; 6331594Smax.romanov@nginx.com vl = f->value_length; 6341591Smax.romanov@nginx.com 6351594Smax.romanov@nginx.com for (j = i + 1; j < r->fields_count; j++) { 6361594Smax.romanov@nginx.com f2 = r->fields + j; 6371594Smax.romanov@nginx.com 6381594Smax.romanov@nginx.com if (f2->hash != f->hash 6391594Smax.romanov@nginx.com || nxt_unit_sptr_get(&f2->name) != nxt_unit_sptr_get(&f->name)) 6401594Smax.romanov@nginx.com { 6411594Smax.romanov@nginx.com break; 6421594Smax.romanov@nginx.com } 6431594Smax.romanov@nginx.com 6441594Smax.romanov@nginx.com vl += 2 + f2->value_length; 6451594Smax.romanov@nginx.com } 6461594Smax.romanov@nginx.com 6471681Smax.romanov@nginx.com RC(nxt_python_add_field(pctx, f, j - i, vl)); 6481594Smax.romanov@nginx.com 6491594Smax.romanov@nginx.com i = j; 6501591Smax.romanov@nginx.com } 6511591Smax.romanov@nginx.com 6521591Smax.romanov@nginx.com if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 6531591Smax.romanov@nginx.com f = r->fields + r->content_length_field; 6541591Smax.romanov@nginx.com 6551681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_content_length_str, &f->value, 6561591Smax.romanov@nginx.com f->value_length)); 6571591Smax.romanov@nginx.com } 6581591Smax.romanov@nginx.com 6591591Smax.romanov@nginx.com if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 6601591Smax.romanov@nginx.com f = r->fields + r->content_type_field; 6611591Smax.romanov@nginx.com 6621681Smax.romanov@nginx.com RC(nxt_python_add_sptr(pctx, nxt_py_content_type_str, &f->value, 6631591Smax.romanov@nginx.com f->value_length)); 6641591Smax.romanov@nginx.com } 6651591Smax.romanov@nginx.com 6661591Smax.romanov@nginx.com #undef RC 6671591Smax.romanov@nginx.com 668*1745Svbart@nginx.com if (nxt_slow_path(PyDict_SetItem(pctx->environ, nxt_py_wsgi_input_str, 6691681Smax.romanov@nginx.com (PyObject *) pctx) != 0)) 6701681Smax.romanov@nginx.com { 6711681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 6721681Smax.romanov@nginx.com "Python failed to set the \"wsgi.input\" environ value"); 6731681Smax.romanov@nginx.com goto fail; 6741681Smax.romanov@nginx.com } 6751681Smax.romanov@nginx.com 676*1745Svbart@nginx.com environ = pctx->environ; 677*1745Svbart@nginx.com pctx->environ = NULL; 678*1745Svbart@nginx.com 6791591Smax.romanov@nginx.com return environ; 6801591Smax.romanov@nginx.com 6811591Smax.romanov@nginx.com fail: 6821591Smax.romanov@nginx.com 683*1745Svbart@nginx.com Py_DECREF(pctx->environ); 684*1745Svbart@nginx.com pctx->environ = NULL; 6851591Smax.romanov@nginx.com 6861591Smax.romanov@nginx.com return NULL; 6871591Smax.romanov@nginx.com } 6881591Smax.romanov@nginx.com 6891591Smax.romanov@nginx.com 6901591Smax.romanov@nginx.com static int 6911681Smax.romanov@nginx.com nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, 6921591Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t size) 6931591Smax.romanov@nginx.com { 6941591Smax.romanov@nginx.com char *src; 6951591Smax.romanov@nginx.com PyObject *value; 6961591Smax.romanov@nginx.com 6971591Smax.romanov@nginx.com src = nxt_unit_sptr_get(sptr); 6981591Smax.romanov@nginx.com 6991591Smax.romanov@nginx.com value = PyString_FromStringAndSize(src, size); 7001591Smax.romanov@nginx.com if (nxt_slow_path(value == NULL)) { 7011681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 7021591Smax.romanov@nginx.com "Python failed to create value string \"%.*s\"", 7031591Smax.romanov@nginx.com (int) size, src); 7041591Smax.romanov@nginx.com nxt_python_print_exception(); 7051591Smax.romanov@nginx.com 7061591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 7071591Smax.romanov@nginx.com } 7081591Smax.romanov@nginx.com 7091681Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { 7101681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 7111591Smax.romanov@nginx.com "Python failed to set the \"%s\" environ value", 7121591Smax.romanov@nginx.com PyUnicode_AsUTF8(name)); 7131591Smax.romanov@nginx.com Py_DECREF(value); 7141591Smax.romanov@nginx.com 7151591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 7161591Smax.romanov@nginx.com } 7171591Smax.romanov@nginx.com 7181591Smax.romanov@nginx.com Py_DECREF(value); 7191591Smax.romanov@nginx.com 7201591Smax.romanov@nginx.com return NXT_UNIT_OK; 7211591Smax.romanov@nginx.com } 7221591Smax.romanov@nginx.com 7231591Smax.romanov@nginx.com 7241591Smax.romanov@nginx.com static int 7251681Smax.romanov@nginx.com nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n, 7261594Smax.romanov@nginx.com uint32_t vl) 7271591Smax.romanov@nginx.com { 7281591Smax.romanov@nginx.com char *src; 7291591Smax.romanov@nginx.com PyObject *name, *value; 7301591Smax.romanov@nginx.com 7311591Smax.romanov@nginx.com src = nxt_unit_sptr_get(&field->name); 7321591Smax.romanov@nginx.com 7331594Smax.romanov@nginx.com name = nxt_python_field_name(src, field->name_length); 7341591Smax.romanov@nginx.com if (nxt_slow_path(name == NULL)) { 7351681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 7361591Smax.romanov@nginx.com "Python failed to create name string \"%.*s\"", 7371591Smax.romanov@nginx.com (int) field->name_length, src); 7381591Smax.romanov@nginx.com nxt_python_print_exception(); 7391591Smax.romanov@nginx.com 7401591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 7411591Smax.romanov@nginx.com } 7421591Smax.romanov@nginx.com 7431594Smax.romanov@nginx.com value = nxt_python_field_value(field, n, vl); 7441591Smax.romanov@nginx.com 7451591Smax.romanov@nginx.com if (nxt_slow_path(value == NULL)) { 7461681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 7471591Smax.romanov@nginx.com "Python failed to create value string \"%.*s\"", 7481594Smax.romanov@nginx.com (int) field->value_length, 7491594Smax.romanov@nginx.com (char *) nxt_unit_sptr_get(&field->value)); 7501591Smax.romanov@nginx.com nxt_python_print_exception(); 7511591Smax.romanov@nginx.com 7521591Smax.romanov@nginx.com goto fail; 7531591Smax.romanov@nginx.com } 7541591Smax.romanov@nginx.com 7551681Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { 7561681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 7571591Smax.romanov@nginx.com "Python failed to set the \"%s\" environ value", 7581591Smax.romanov@nginx.com PyUnicode_AsUTF8(name)); 7591591Smax.romanov@nginx.com goto fail; 7601591Smax.romanov@nginx.com } 7611591Smax.romanov@nginx.com 7621591Smax.romanov@nginx.com Py_DECREF(name); 7631591Smax.romanov@nginx.com Py_DECREF(value); 7641591Smax.romanov@nginx.com 7651591Smax.romanov@nginx.com return NXT_UNIT_OK; 7661591Smax.romanov@nginx.com 7671591Smax.romanov@nginx.com fail: 7681591Smax.romanov@nginx.com 7691591Smax.romanov@nginx.com Py_DECREF(name); 7701591Smax.romanov@nginx.com Py_XDECREF(value); 7711591Smax.romanov@nginx.com 7721591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 7731591Smax.romanov@nginx.com } 7741591Smax.romanov@nginx.com 7751591Smax.romanov@nginx.com 7761594Smax.romanov@nginx.com static PyObject * 7771594Smax.romanov@nginx.com nxt_python_field_name(const char *name, uint8_t len) 7781594Smax.romanov@nginx.com { 7791594Smax.romanov@nginx.com char *p, c; 7801594Smax.romanov@nginx.com uint8_t i; 7811594Smax.romanov@nginx.com PyObject *res; 7821594Smax.romanov@nginx.com 7831594Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 7841594Smax.romanov@nginx.com res = PyUnicode_New(len + 5, 255); 7851594Smax.romanov@nginx.com #else 7861594Smax.romanov@nginx.com res = PyString_FromStringAndSize(NULL, len + 5); 7871594Smax.romanov@nginx.com #endif 7881594Smax.romanov@nginx.com 7891594Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 7901594Smax.romanov@nginx.com return NULL; 7911594Smax.romanov@nginx.com } 7921594Smax.romanov@nginx.com 7931594Smax.romanov@nginx.com p = PyString_AS_STRING(res); 7941594Smax.romanov@nginx.com 7951594Smax.romanov@nginx.com p = nxt_cpymem(p, "HTTP_", 5); 7961594Smax.romanov@nginx.com 7971594Smax.romanov@nginx.com for (i = 0; i < len; i++) { 7981594Smax.romanov@nginx.com c = name[i]; 7991594Smax.romanov@nginx.com 8001594Smax.romanov@nginx.com if (c >= 'a' && c <= 'z') { 8011594Smax.romanov@nginx.com *p++ = (c & ~0x20); 8021594Smax.romanov@nginx.com continue; 8031594Smax.romanov@nginx.com } 8041594Smax.romanov@nginx.com 8051594Smax.romanov@nginx.com if (c == '-') { 8061594Smax.romanov@nginx.com *p++ = '_'; 8071594Smax.romanov@nginx.com continue; 8081594Smax.romanov@nginx.com } 8091594Smax.romanov@nginx.com 8101594Smax.romanov@nginx.com *p++ = c; 8111594Smax.romanov@nginx.com } 8121594Smax.romanov@nginx.com 8131594Smax.romanov@nginx.com return res; 8141594Smax.romanov@nginx.com } 8151594Smax.romanov@nginx.com 8161594Smax.romanov@nginx.com 8171594Smax.romanov@nginx.com static PyObject * 8181594Smax.romanov@nginx.com nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) 8191594Smax.romanov@nginx.com { 8201594Smax.romanov@nginx.com int i; 8211594Smax.romanov@nginx.com char *p, *src; 8221594Smax.romanov@nginx.com PyObject *res; 8231594Smax.romanov@nginx.com 8241594Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 8251594Smax.romanov@nginx.com res = PyUnicode_New(vl, 255); 8261594Smax.romanov@nginx.com #else 8271594Smax.romanov@nginx.com res = PyString_FromStringAndSize(NULL, vl); 8281594Smax.romanov@nginx.com #endif 8291594Smax.romanov@nginx.com 8301594Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 8311594Smax.romanov@nginx.com return NULL; 8321594Smax.romanov@nginx.com } 8331594Smax.romanov@nginx.com 8341594Smax.romanov@nginx.com p = PyString_AS_STRING(res); 8351594Smax.romanov@nginx.com 8361594Smax.romanov@nginx.com src = nxt_unit_sptr_get(&f->value); 8371594Smax.romanov@nginx.com p = nxt_cpymem(p, src, f->value_length); 8381594Smax.romanov@nginx.com 8391594Smax.romanov@nginx.com for (i = 1; i < n; i++) { 8401594Smax.romanov@nginx.com p = nxt_cpymem(p, ", ", 2); 8411594Smax.romanov@nginx.com 8421594Smax.romanov@nginx.com src = nxt_unit_sptr_get(&f[i].value); 8431594Smax.romanov@nginx.com p = nxt_cpymem(p, src, f[i].value_length); 8441594Smax.romanov@nginx.com } 8451594Smax.romanov@nginx.com 8461594Smax.romanov@nginx.com return res; 8471594Smax.romanov@nginx.com } 8481594Smax.romanov@nginx.com 8491594Smax.romanov@nginx.com 8501591Smax.romanov@nginx.com static int 8511681Smax.romanov@nginx.com nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value) 8521591Smax.romanov@nginx.com { 8531681Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { 8541681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, 8551591Smax.romanov@nginx.com "Python failed to set the \"%s\" environ value", 8561591Smax.romanov@nginx.com PyUnicode_AsUTF8(name)); 8571591Smax.romanov@nginx.com 8581591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 8591591Smax.romanov@nginx.com } 8601591Smax.romanov@nginx.com 8611591Smax.romanov@nginx.com return NXT_UNIT_OK; 8621591Smax.romanov@nginx.com } 8631591Smax.romanov@nginx.com 8641591Smax.romanov@nginx.com 8651591Smax.romanov@nginx.com static PyObject * 8661591Smax.romanov@nginx.com nxt_py_start_resp(PyObject *self, PyObject *args) 8671591Smax.romanov@nginx.com { 8681681Smax.romanov@nginx.com int rc, status; 8691681Smax.romanov@nginx.com char *status_str, *space_ptr; 8701681Smax.romanov@nginx.com uint32_t status_len; 8711681Smax.romanov@nginx.com PyObject *headers, *tuple, *string, *status_bytes; 8721681Smax.romanov@nginx.com Py_ssize_t i, n, fields_size, fields_count; 8731681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 8741591Smax.romanov@nginx.com 8751681Smax.romanov@nginx.com pctx = (nxt_python_ctx_t *) self; 8761681Smax.romanov@nginx.com if (nxt_slow_path(pctx->req == NULL)) { 8771591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 8781591Smax.romanov@nginx.com "start_response() is called " 8791591Smax.romanov@nginx.com "outside of WSGI request processing"); 8801591Smax.romanov@nginx.com } 8811591Smax.romanov@nginx.com 8821591Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 8831591Smax.romanov@nginx.com 8841591Smax.romanov@nginx.com if (n < 2 || n > 3) { 8851591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 8861591Smax.romanov@nginx.com } 8871591Smax.romanov@nginx.com 8881591Smax.romanov@nginx.com string = PyTuple_GET_ITEM(args, 0); 8891591Smax.romanov@nginx.com if (!PyBytes_Check(string) && !PyUnicode_Check(string)) { 8901591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 8911591Smax.romanov@nginx.com "failed to write first argument (not a string?)"); 8921591Smax.romanov@nginx.com } 8931591Smax.romanov@nginx.com 8941591Smax.romanov@nginx.com headers = PyTuple_GET_ITEM(args, 1); 8951591Smax.romanov@nginx.com if (!PyList_Check(headers)) { 8961591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 8971591Smax.romanov@nginx.com "the second argument is not a response headers list"); 8981591Smax.romanov@nginx.com } 8991591Smax.romanov@nginx.com 9001591Smax.romanov@nginx.com fields_size = 0; 9011591Smax.romanov@nginx.com fields_count = PyList_GET_SIZE(headers); 9021591Smax.romanov@nginx.com 9031591Smax.romanov@nginx.com for (i = 0; i < fields_count; i++) { 9041591Smax.romanov@nginx.com tuple = PyList_GET_ITEM(headers, i); 9051591Smax.romanov@nginx.com 9061591Smax.romanov@nginx.com if (!PyTuple_Check(tuple)) { 9071591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 9081591Smax.romanov@nginx.com "the response headers must be a list of tuples"); 9091591Smax.romanov@nginx.com } 9101591Smax.romanov@nginx.com 9111591Smax.romanov@nginx.com if (PyTuple_GET_SIZE(tuple) != 2) { 9121591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 9131591Smax.romanov@nginx.com "each header must be a tuple of two items"); 9141591Smax.romanov@nginx.com } 9151591Smax.romanov@nginx.com 9161591Smax.romanov@nginx.com string = PyTuple_GET_ITEM(tuple, 0); 9171591Smax.romanov@nginx.com if (PyBytes_Check(string)) { 9181591Smax.romanov@nginx.com fields_size += PyBytes_GET_SIZE(string); 9191591Smax.romanov@nginx.com 9201591Smax.romanov@nginx.com } else if (PyUnicode_Check(string)) { 9211648Svbart@nginx.com fields_size += PyUnicode_GET_LENGTH(string); 9221591Smax.romanov@nginx.com 9231591Smax.romanov@nginx.com } else { 9241591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 9251591Smax.romanov@nginx.com "header #%d name is not a string", (int) i); 9261591Smax.romanov@nginx.com } 9271591Smax.romanov@nginx.com 9281591Smax.romanov@nginx.com string = PyTuple_GET_ITEM(tuple, 1); 9291591Smax.romanov@nginx.com if (PyBytes_Check(string)) { 9301591Smax.romanov@nginx.com fields_size += PyBytes_GET_SIZE(string); 9311591Smax.romanov@nginx.com 9321591Smax.romanov@nginx.com } else if (PyUnicode_Check(string)) { 9331648Svbart@nginx.com fields_size += PyUnicode_GET_LENGTH(string); 9341591Smax.romanov@nginx.com 9351591Smax.romanov@nginx.com } else { 9361591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 9371591Smax.romanov@nginx.com "header #%d value is not a string", (int) i); 9381591Smax.romanov@nginx.com } 9391591Smax.romanov@nginx.com } 9401591Smax.romanov@nginx.com 9411681Smax.romanov@nginx.com pctx->content_length = -1; 9421591Smax.romanov@nginx.com 9431591Smax.romanov@nginx.com string = PyTuple_GET_ITEM(args, 0); 9441591Smax.romanov@nginx.com rc = nxt_python_str_buf(string, &status_str, &status_len, &status_bytes); 9451591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 9461591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "status is not a string"); 9471591Smax.romanov@nginx.com } 9481591Smax.romanov@nginx.com 9491591Smax.romanov@nginx.com space_ptr = memchr(status_str, ' ', status_len); 9501591Smax.romanov@nginx.com if (space_ptr != NULL) { 9511591Smax.romanov@nginx.com status_len = space_ptr - status_str; 9521591Smax.romanov@nginx.com } 9531591Smax.romanov@nginx.com 9541591Smax.romanov@nginx.com status = nxt_int_parse((u_char *) status_str, status_len); 9551591Smax.romanov@nginx.com if (nxt_slow_path(status < 0)) { 9561591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "failed to parse status code"); 9571591Smax.romanov@nginx.com } 9581591Smax.romanov@nginx.com 9591591Smax.romanov@nginx.com Py_XDECREF(status_bytes); 9601591Smax.romanov@nginx.com 9611591Smax.romanov@nginx.com /* 9621591Smax.romanov@nginx.com * PEP 3333: 9631591Smax.romanov@nginx.com * 9641591Smax.romanov@nginx.com * ... applications can replace their originally intended output with error 9651591Smax.romanov@nginx.com * output, up until the last possible moment. 9661591Smax.romanov@nginx.com */ 9671681Smax.romanov@nginx.com rc = nxt_unit_response_init(pctx->req, status, fields_count, fields_size); 9681591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 9691591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 9701591Smax.romanov@nginx.com "failed to allocate response object"); 9711591Smax.romanov@nginx.com } 9721591Smax.romanov@nginx.com 9731591Smax.romanov@nginx.com for (i = 0; i < fields_count; i++) { 9741591Smax.romanov@nginx.com tuple = PyList_GET_ITEM(headers, i); 9751591Smax.romanov@nginx.com 9761681Smax.romanov@nginx.com rc = nxt_python_response_add_field(pctx, PyTuple_GET_ITEM(tuple, 0), 9771591Smax.romanov@nginx.com PyTuple_GET_ITEM(tuple, 1), i); 9781591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 9791591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 9801591Smax.romanov@nginx.com "failed to add header #%d", (int) i); 9811591Smax.romanov@nginx.com } 9821591Smax.romanov@nginx.com } 9831591Smax.romanov@nginx.com 9841591Smax.romanov@nginx.com /* 9851591Smax.romanov@nginx.com * PEP 3333: 9861591Smax.romanov@nginx.com * 9871591Smax.romanov@nginx.com * However, the start_response callable must not actually transmit the 9881591Smax.romanov@nginx.com * response headers. Instead, it must store them for the server or gateway 9891591Smax.romanov@nginx.com * to transmit only after the first iteration of the application return 9901591Smax.romanov@nginx.com * value that yields a non-empty bytestring, or upon the application's 9911591Smax.romanov@nginx.com * first invocation of the write() callable. In other words, response 9921591Smax.romanov@nginx.com * headers must not be sent until there is actual body data available, or 9931591Smax.romanov@nginx.com * until the application's returned iterable is exhausted. (The only 9941591Smax.romanov@nginx.com * possible exception to this rule is if the response headers explicitly 9951591Smax.romanov@nginx.com * include a Content-Length of zero.) 9961591Smax.romanov@nginx.com */ 9971681Smax.romanov@nginx.com if (pctx->content_length == 0) { 9981681Smax.romanov@nginx.com rc = nxt_unit_response_send(pctx->req); 9991591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10001591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 10011591Smax.romanov@nginx.com "failed to send response headers"); 10021591Smax.romanov@nginx.com } 10031591Smax.romanov@nginx.com } 10041591Smax.romanov@nginx.com 10051681Smax.romanov@nginx.com Py_INCREF(pctx->write); 10061681Smax.romanov@nginx.com return pctx->write; 10071591Smax.romanov@nginx.com } 10081591Smax.romanov@nginx.com 10091591Smax.romanov@nginx.com 10101591Smax.romanov@nginx.com static int 10111681Smax.romanov@nginx.com nxt_python_response_add_field(nxt_python_ctx_t *pctx, PyObject *name, 10121591Smax.romanov@nginx.com PyObject *value, int i) 10131591Smax.romanov@nginx.com { 10141591Smax.romanov@nginx.com int rc; 10151591Smax.romanov@nginx.com char *name_str, *value_str; 10161591Smax.romanov@nginx.com uint32_t name_length, value_length; 10171591Smax.romanov@nginx.com PyObject *name_bytes, *value_bytes; 10181591Smax.romanov@nginx.com nxt_off_t content_length; 10191591Smax.romanov@nginx.com 10201591Smax.romanov@nginx.com name_bytes = NULL; 10211591Smax.romanov@nginx.com value_bytes = NULL; 10221591Smax.romanov@nginx.com 10231591Smax.romanov@nginx.com rc = nxt_python_str_buf(name, &name_str, &name_length, &name_bytes); 10241591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10251591Smax.romanov@nginx.com goto fail; 10261591Smax.romanov@nginx.com } 10271591Smax.romanov@nginx.com 10281591Smax.romanov@nginx.com rc = nxt_python_str_buf(value, &value_str, &value_length, &value_bytes); 10291591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10301591Smax.romanov@nginx.com goto fail; 10311591Smax.romanov@nginx.com } 10321591Smax.romanov@nginx.com 10331681Smax.romanov@nginx.com rc = nxt_unit_response_add_field(pctx->req, name_str, name_length, 10341591Smax.romanov@nginx.com value_str, value_length); 10351591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10361591Smax.romanov@nginx.com goto fail; 10371591Smax.romanov@nginx.com } 10381591Smax.romanov@nginx.com 10391681Smax.romanov@nginx.com if (pctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { 10401591Smax.romanov@nginx.com content_length = nxt_off_t_parse((u_char *) value_str, value_length); 10411591Smax.romanov@nginx.com if (nxt_slow_path(content_length < 0)) { 10421681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, "failed to parse Content-Length " 10431591Smax.romanov@nginx.com "value %.*s", (int) value_length, value_str); 10441591Smax.romanov@nginx.com 10451591Smax.romanov@nginx.com } else { 10461681Smax.romanov@nginx.com pctx->content_length = content_length; 10471591Smax.romanov@nginx.com } 10481591Smax.romanov@nginx.com } 10491591Smax.romanov@nginx.com 10501591Smax.romanov@nginx.com fail: 10511591Smax.romanov@nginx.com 10521591Smax.romanov@nginx.com Py_XDECREF(name_bytes); 10531591Smax.romanov@nginx.com Py_XDECREF(value_bytes); 10541591Smax.romanov@nginx.com 10551591Smax.romanov@nginx.com return rc; 10561591Smax.romanov@nginx.com } 10571591Smax.romanov@nginx.com 10581591Smax.romanov@nginx.com 10591591Smax.romanov@nginx.com static int 10601591Smax.romanov@nginx.com nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, PyObject **bytes) 10611591Smax.romanov@nginx.com { 10621591Smax.romanov@nginx.com if (PyBytes_Check(str)) { 10631591Smax.romanov@nginx.com *buf = PyBytes_AS_STRING(str); 10641591Smax.romanov@nginx.com *len = PyBytes_GET_SIZE(str); 10651591Smax.romanov@nginx.com *bytes = NULL; 10661591Smax.romanov@nginx.com 10671591Smax.romanov@nginx.com } else { 10681591Smax.romanov@nginx.com *bytes = PyUnicode_AsLatin1String(str); 10691591Smax.romanov@nginx.com if (nxt_slow_path(*bytes == NULL)) { 10701591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 10711591Smax.romanov@nginx.com } 10721591Smax.romanov@nginx.com 10731591Smax.romanov@nginx.com *buf = PyBytes_AS_STRING(*bytes); 10741591Smax.romanov@nginx.com *len = PyBytes_GET_SIZE(*bytes); 10751591Smax.romanov@nginx.com } 10761591Smax.romanov@nginx.com 10771591Smax.romanov@nginx.com return NXT_UNIT_OK; 10781591Smax.romanov@nginx.com } 10791591Smax.romanov@nginx.com 10801591Smax.romanov@nginx.com 10811591Smax.romanov@nginx.com static PyObject * 10821591Smax.romanov@nginx.com nxt_py_write(PyObject *self, PyObject *str) 10831591Smax.romanov@nginx.com { 10841591Smax.romanov@nginx.com int rc; 10851591Smax.romanov@nginx.com 10861591Smax.romanov@nginx.com if (nxt_fast_path(!PyBytes_Check(str))) { 10871591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "the argument is not a %s", 10881591Smax.romanov@nginx.com NXT_PYTHON_BYTES_TYPE); 10891591Smax.romanov@nginx.com } 10901591Smax.romanov@nginx.com 10911681Smax.romanov@nginx.com rc = nxt_python_write((nxt_python_ctx_t *) self, str); 10921591Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10931591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 10941591Smax.romanov@nginx.com "failed to write response value"); 10951591Smax.romanov@nginx.com } 10961591Smax.romanov@nginx.com 10971591Smax.romanov@nginx.com Py_RETURN_NONE; 10981591Smax.romanov@nginx.com } 10991591Smax.romanov@nginx.com 11001591Smax.romanov@nginx.com 11011591Smax.romanov@nginx.com static void 11021681Smax.romanov@nginx.com nxt_py_input_dealloc(nxt_python_ctx_t *self) 11031591Smax.romanov@nginx.com { 11041591Smax.romanov@nginx.com PyObject_Del(self); 11051591Smax.romanov@nginx.com } 11061591Smax.romanov@nginx.com 11071591Smax.romanov@nginx.com 11081591Smax.romanov@nginx.com static PyObject * 11091681Smax.romanov@nginx.com nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args) 11101591Smax.romanov@nginx.com { 11111681Smax.romanov@nginx.com char *buf; 11121681Smax.romanov@nginx.com PyObject *content, *obj; 11131681Smax.romanov@nginx.com Py_ssize_t size, n; 11141591Smax.romanov@nginx.com 11151681Smax.romanov@nginx.com if (nxt_slow_path(pctx->req == NULL)) { 11161591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 11171591Smax.romanov@nginx.com "wsgi.input.read() is called " 11181591Smax.romanov@nginx.com "outside of WSGI request processing"); 11191591Smax.romanov@nginx.com } 11201591Smax.romanov@nginx.com 11211681Smax.romanov@nginx.com size = pctx->req->content_length; 11221591Smax.romanov@nginx.com 11231591Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 11241591Smax.romanov@nginx.com 11251591Smax.romanov@nginx.com if (n > 0) { 11261591Smax.romanov@nginx.com if (n != 1) { 11271591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 11281591Smax.romanov@nginx.com } 11291591Smax.romanov@nginx.com 11301591Smax.romanov@nginx.com obj = PyTuple_GET_ITEM(args, 0); 11311591Smax.romanov@nginx.com 11321591Smax.romanov@nginx.com size = PyNumber_AsSsize_t(obj, PyExc_OverflowError); 11331591Smax.romanov@nginx.com 11341591Smax.romanov@nginx.com if (nxt_slow_path(size < 0)) { 11351591Smax.romanov@nginx.com if (size == -1 && PyErr_Occurred()) { 11361591Smax.romanov@nginx.com return NULL; 11371591Smax.romanov@nginx.com } 11381591Smax.romanov@nginx.com 11391591Smax.romanov@nginx.com if (size != -1) { 11401591Smax.romanov@nginx.com return PyErr_Format(PyExc_ValueError, 11411591Smax.romanov@nginx.com "the read body size cannot be zero or less"); 11421591Smax.romanov@nginx.com } 11431591Smax.romanov@nginx.com } 11441591Smax.romanov@nginx.com 11451681Smax.romanov@nginx.com if (size == -1 || size > (Py_ssize_t) pctx->req->content_length) { 11461681Smax.romanov@nginx.com size = pctx->req->content_length; 11471591Smax.romanov@nginx.com } 11481591Smax.romanov@nginx.com } 11491591Smax.romanov@nginx.com 11501591Smax.romanov@nginx.com content = PyBytes_FromStringAndSize(NULL, size); 11511591Smax.romanov@nginx.com if (nxt_slow_path(content == NULL)) { 11521591Smax.romanov@nginx.com return NULL; 11531591Smax.romanov@nginx.com } 11541591Smax.romanov@nginx.com 11551591Smax.romanov@nginx.com buf = PyBytes_AS_STRING(content); 11561591Smax.romanov@nginx.com 11571681Smax.romanov@nginx.com size = nxt_unit_request_read(pctx->req, buf, size); 11581591Smax.romanov@nginx.com 11591591Smax.romanov@nginx.com return content; 11601591Smax.romanov@nginx.com } 11611591Smax.romanov@nginx.com 11621591Smax.romanov@nginx.com 11631591Smax.romanov@nginx.com static PyObject * 11641681Smax.romanov@nginx.com nxt_py_input_readline(nxt_python_ctx_t *pctx, PyObject *args) 11651591Smax.romanov@nginx.com { 11661681Smax.romanov@nginx.com ssize_t ssize; 11671681Smax.romanov@nginx.com PyObject *obj; 11681681Smax.romanov@nginx.com Py_ssize_t n; 11691591Smax.romanov@nginx.com 11701681Smax.romanov@nginx.com if (nxt_slow_path(pctx->req == NULL)) { 11711591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 11721591Smax.romanov@nginx.com "wsgi.input.readline() is called " 11731591Smax.romanov@nginx.com "outside of WSGI request processing"); 11741591Smax.romanov@nginx.com } 11751591Smax.romanov@nginx.com 11761591Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 11771591Smax.romanov@nginx.com 11781591Smax.romanov@nginx.com if (n > 0) { 11791591Smax.romanov@nginx.com if (n != 1) { 11801591Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 11811591Smax.romanov@nginx.com } 11821591Smax.romanov@nginx.com 11831591Smax.romanov@nginx.com obj = PyTuple_GET_ITEM(args, 0); 11841591Smax.romanov@nginx.com 11851591Smax.romanov@nginx.com ssize = PyNumber_AsSsize_t(obj, PyExc_OverflowError); 11861591Smax.romanov@nginx.com 11871591Smax.romanov@nginx.com if (nxt_fast_path(ssize > 0)) { 11881681Smax.romanov@nginx.com return nxt_py_input_getline(pctx, ssize); 11891591Smax.romanov@nginx.com } 11901591Smax.romanov@nginx.com 11911591Smax.romanov@nginx.com if (ssize == 0) { 11921591Smax.romanov@nginx.com return PyBytes_FromStringAndSize("", 0); 11931591Smax.romanov@nginx.com } 11941591Smax.romanov@nginx.com 11951591Smax.romanov@nginx.com if (ssize != -1) { 11961591Smax.romanov@nginx.com return PyErr_Format(PyExc_ValueError, 11971591Smax.romanov@nginx.com "the read line size cannot be zero or less"); 11981591Smax.romanov@nginx.com } 11991591Smax.romanov@nginx.com 12001591Smax.romanov@nginx.com if (PyErr_Occurred()) { 12011591Smax.romanov@nginx.com return NULL; 12021591Smax.romanov@nginx.com } 12031591Smax.romanov@nginx.com } 12041591Smax.romanov@nginx.com 12051681Smax.romanov@nginx.com return nxt_py_input_getline(pctx, SSIZE_MAX); 12061591Smax.romanov@nginx.com } 12071591Smax.romanov@nginx.com 12081591Smax.romanov@nginx.com 12091591Smax.romanov@nginx.com static PyObject * 12101681Smax.romanov@nginx.com nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size) 12111591Smax.romanov@nginx.com { 12121591Smax.romanov@nginx.com void *buf; 12131591Smax.romanov@nginx.com ssize_t res; 12141591Smax.romanov@nginx.com PyObject *content; 12151591Smax.romanov@nginx.com 12161681Smax.romanov@nginx.com res = nxt_unit_request_readline_size(pctx->req, size); 12171591Smax.romanov@nginx.com if (nxt_slow_path(res < 0)) { 12181591Smax.romanov@nginx.com return NULL; 12191591Smax.romanov@nginx.com } 12201591Smax.romanov@nginx.com 12211591Smax.romanov@nginx.com if (res == 0) { 12221591Smax.romanov@nginx.com return PyBytes_FromStringAndSize("", 0); 12231591Smax.romanov@nginx.com } 12241591Smax.romanov@nginx.com 12251591Smax.romanov@nginx.com content = PyBytes_FromStringAndSize(NULL, res); 12261591Smax.romanov@nginx.com if (nxt_slow_path(content == NULL)) { 12271591Smax.romanov@nginx.com return NULL; 12281591Smax.romanov@nginx.com } 12291591Smax.romanov@nginx.com 12301591Smax.romanov@nginx.com buf = PyBytes_AS_STRING(content); 12311591Smax.romanov@nginx.com 12321681Smax.romanov@nginx.com res = nxt_unit_request_read(pctx->req, buf, res); 12331591Smax.romanov@nginx.com 12341591Smax.romanov@nginx.com return content; 12351591Smax.romanov@nginx.com } 12361591Smax.romanov@nginx.com 12371591Smax.romanov@nginx.com 12381591Smax.romanov@nginx.com static PyObject * 12391681Smax.romanov@nginx.com nxt_py_input_readlines(nxt_python_ctx_t *pctx, PyObject *args) 12401591Smax.romanov@nginx.com { 12411681Smax.romanov@nginx.com PyObject *res; 12421591Smax.romanov@nginx.com 12431681Smax.romanov@nginx.com if (nxt_slow_path(pctx->req == NULL)) { 12441591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 12451591Smax.romanov@nginx.com "wsgi.input.readlines() is called " 12461591Smax.romanov@nginx.com "outside of WSGI request processing"); 12471591Smax.romanov@nginx.com } 12481591Smax.romanov@nginx.com 12491591Smax.romanov@nginx.com res = PyList_New(0); 12501591Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 12511591Smax.romanov@nginx.com return NULL; 12521591Smax.romanov@nginx.com } 12531591Smax.romanov@nginx.com 12541591Smax.romanov@nginx.com for ( ;; ) { 12551681Smax.romanov@nginx.com PyObject *line = nxt_py_input_getline(pctx, SSIZE_MAX); 12561591Smax.romanov@nginx.com if (nxt_slow_path(line == NULL)) { 12571591Smax.romanov@nginx.com Py_DECREF(res); 12581591Smax.romanov@nginx.com return NULL; 12591591Smax.romanov@nginx.com } 12601591Smax.romanov@nginx.com 12611591Smax.romanov@nginx.com if (PyBytes_GET_SIZE(line) == 0) { 12621591Smax.romanov@nginx.com Py_DECREF(line); 12631591Smax.romanov@nginx.com return res; 12641591Smax.romanov@nginx.com } 12651591Smax.romanov@nginx.com 12661591Smax.romanov@nginx.com PyList_Append(res, line); 12671591Smax.romanov@nginx.com Py_DECREF(line); 12681591Smax.romanov@nginx.com } 12691591Smax.romanov@nginx.com 12701591Smax.romanov@nginx.com return res; 12711591Smax.romanov@nginx.com } 12721591Smax.romanov@nginx.com 12731591Smax.romanov@nginx.com 12741591Smax.romanov@nginx.com static PyObject * 12751591Smax.romanov@nginx.com nxt_py_input_iter(PyObject *self) 12761591Smax.romanov@nginx.com { 12771591Smax.romanov@nginx.com Py_INCREF(self); 12781591Smax.romanov@nginx.com return self; 12791591Smax.romanov@nginx.com } 12801591Smax.romanov@nginx.com 12811591Smax.romanov@nginx.com 12821591Smax.romanov@nginx.com static PyObject * 12831591Smax.romanov@nginx.com nxt_py_input_next(PyObject *self) 12841591Smax.romanov@nginx.com { 12851681Smax.romanov@nginx.com PyObject *line; 12861681Smax.romanov@nginx.com nxt_python_ctx_t *pctx; 12871591Smax.romanov@nginx.com 12881681Smax.romanov@nginx.com pctx = (nxt_python_ctx_t *) self; 12891681Smax.romanov@nginx.com if (nxt_slow_path(pctx->req == NULL)) { 12901591Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 12911591Smax.romanov@nginx.com "wsgi.input.next() is called " 12921591Smax.romanov@nginx.com "outside of WSGI request processing"); 12931591Smax.romanov@nginx.com } 12941591Smax.romanov@nginx.com 12951681Smax.romanov@nginx.com line = nxt_py_input_getline(pctx, SSIZE_MAX); 12961591Smax.romanov@nginx.com if (nxt_slow_path(line == NULL)) { 12971591Smax.romanov@nginx.com return NULL; 12981591Smax.romanov@nginx.com } 12991591Smax.romanov@nginx.com 13001591Smax.romanov@nginx.com if (PyBytes_GET_SIZE(line) == 0) { 13011591Smax.romanov@nginx.com Py_DECREF(line); 13021591Smax.romanov@nginx.com PyErr_SetNone(PyExc_StopIteration); 13031591Smax.romanov@nginx.com return NULL; 13041591Smax.romanov@nginx.com } 13051591Smax.romanov@nginx.com 13061591Smax.romanov@nginx.com return line; 13071591Smax.romanov@nginx.com } 13081591Smax.romanov@nginx.com 13091591Smax.romanov@nginx.com 13101591Smax.romanov@nginx.com static int 13111681Smax.romanov@nginx.com nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes) 13121591Smax.romanov@nginx.com { 13131591Smax.romanov@nginx.com int rc; 13141591Smax.romanov@nginx.com char *str_buf; 13151591Smax.romanov@nginx.com uint32_t str_length; 13161591Smax.romanov@nginx.com 13171591Smax.romanov@nginx.com str_buf = PyBytes_AS_STRING(bytes); 13181591Smax.romanov@nginx.com str_length = PyBytes_GET_SIZE(bytes); 13191591Smax.romanov@nginx.com 13201591Smax.romanov@nginx.com if (nxt_slow_path(str_length == 0)) { 13211591Smax.romanov@nginx.com return NXT_UNIT_OK; 13221591Smax.romanov@nginx.com } 13231591Smax.romanov@nginx.com 13241591Smax.romanov@nginx.com /* 13251591Smax.romanov@nginx.com * PEP 3333: 13261591Smax.romanov@nginx.com * 13271591Smax.romanov@nginx.com * If the application supplies a Content-Length header, the server should 13281591Smax.romanov@nginx.com * not transmit more bytes to the client than the header allows, and should 13291591Smax.romanov@nginx.com * stop iterating over the response when enough data has been sent, or raise 13301591Smax.romanov@nginx.com * an error if the application tries to write() past that point. 13311591Smax.romanov@nginx.com */ 13321681Smax.romanov@nginx.com if (nxt_slow_path(str_length > pctx->content_length - pctx->bytes_sent)) { 13331681Smax.romanov@nginx.com nxt_unit_req_error(pctx->req, "content length %"PRIu64" exceeded", 13341681Smax.romanov@nginx.com pctx->content_length); 13351591Smax.romanov@nginx.com 13361591Smax.romanov@nginx.com return NXT_UNIT_ERROR; 13371591Smax.romanov@nginx.com } 13381591Smax.romanov@nginx.com 13391681Smax.romanov@nginx.com rc = nxt_unit_response_write(pctx->req, str_buf, str_length); 13401591Smax.romanov@nginx.com if (nxt_fast_path(rc == NXT_UNIT_OK)) { 13411681Smax.romanov@nginx.com pctx->bytes_sent += str_length; 13421591Smax.romanov@nginx.com } 13431591Smax.romanov@nginx.com 13441591Smax.romanov@nginx.com return rc; 13451591Smax.romanov@nginx.com } 1346