xref: /unit/src/python/nxt_python_wsgi.c (revision 1745)
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