xref: /unit/src/python/nxt_python_wsgi.c (revision 2208:26af8eadc943)
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 
541918Smax.romanov@nginx.com static int nxt_python_wsgi_ctx_data_alloc(void **pdata, int main);
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);
621745Svbart@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
nxt_python_wsgi_init(nxt_unit_init_t * init,nxt_python_proto_t * proto)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
nxt_python_wsgi_ctx_data_alloc(void ** pdata,int main)2131918Smax.romanov@nginx.com nxt_python_wsgi_ctx_data_alloc(void **pdata, int main)
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;
2251745Svbart@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 
2421745Svbart@nginx.com     pctx->environ = nxt_python_copy_environ(NULL);
2431745Svbart@nginx.com     if (nxt_slow_path(pctx->environ == NULL)) {
2441745Svbart@nginx.com         goto fail;
2451745Svbart@nginx.com     }
2461745Svbart@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
nxt_python_wsgi_ctx_data_free(void * data)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);
2681745Svbart@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
nxt_python_wsgi_run(nxt_unit_ctx_t * ctx)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
nxt_python_wsgi_done(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
nxt_python_request_handler(nxt_unit_request_info_t * req)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;
3051872So.canty@f5.com     PyObject          *close, *result, *application;
3061745Svbart@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 
3171745Svbart@nginx.com     if (nxt_slow_path(pctx->environ == NULL)) {
3181745Svbart@nginx.com         pctx->environ = nxt_python_copy_environ(req);
3191745Svbart@nginx.com 
3201745Svbart@nginx.com         if (pctx->environ == NULL) {
3211745Svbart@nginx.com             prepare_environ = 0;
3221745Svbart@nginx.com 
3231745Svbart@nginx.com             rc = NXT_UNIT_ERROR;
3241745Svbart@nginx.com             goto done;
3251745Svbart@nginx.com         }
3261745Svbart@nginx.com     }
3271745Svbart@nginx.com 
3281745Svbart@nginx.com     prepare_environ = 1;
3291745Svbart@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 
3511872So.canty@f5.com     application = nxt_py_targets->target[req->request->app_target].application;
3521872So.canty@f5.com     response = PyObject_CallObject(application, args);
3531591Smax.romanov@nginx.com 
3541591Smax.romanov@nginx.com     Py_DECREF(args);
3551591Smax.romanov@nginx.com 
3561591Smax.romanov@nginx.com     if (nxt_slow_path(response == NULL)) {
3571591Smax.romanov@nginx.com         nxt_unit_req_error(req, "Python failed to call the application");
3581591Smax.romanov@nginx.com         nxt_python_print_exception();
3591591Smax.romanov@nginx.com 
3601591Smax.romanov@nginx.com         rc = NXT_UNIT_ERROR;
3611591Smax.romanov@nginx.com         goto done;
3621591Smax.romanov@nginx.com     }
3631591Smax.romanov@nginx.com 
3641591Smax.romanov@nginx.com     /* Shortcut: avoid iterate over response string symbols. */
3651591Smax.romanov@nginx.com     if (PyBytes_Check(response)) {
3661681Smax.romanov@nginx.com         rc = nxt_python_write(pctx, response);
3671591Smax.romanov@nginx.com 
3681591Smax.romanov@nginx.com     } else {
3691591Smax.romanov@nginx.com         iterator = PyObject_GetIter(response);
3701591Smax.romanov@nginx.com 
3711591Smax.romanov@nginx.com         if (nxt_fast_path(iterator != NULL)) {
3721591Smax.romanov@nginx.com             rc = NXT_UNIT_OK;
3731591Smax.romanov@nginx.com 
3741681Smax.romanov@nginx.com             while (pctx->bytes_sent < pctx->content_length) {
3751591Smax.romanov@nginx.com                 item = PyIter_Next(iterator);
3761591Smax.romanov@nginx.com 
3771591Smax.romanov@nginx.com                 if (item == NULL) {
3781591Smax.romanov@nginx.com                     if (nxt_slow_path(PyErr_Occurred() != NULL)) {
3791591Smax.romanov@nginx.com                         nxt_unit_req_error(req, "Python failed to iterate over "
3801591Smax.romanov@nginx.com                                            "the application response object");
3811591Smax.romanov@nginx.com                         nxt_python_print_exception();
3821591Smax.romanov@nginx.com 
3831591Smax.romanov@nginx.com                         rc = NXT_UNIT_ERROR;
3841591Smax.romanov@nginx.com                     }
3851591Smax.romanov@nginx.com 
3861591Smax.romanov@nginx.com                     break;
3871591Smax.romanov@nginx.com                 }
3881591Smax.romanov@nginx.com 
3891591Smax.romanov@nginx.com                 if (nxt_fast_path(PyBytes_Check(item))) {
3901681Smax.romanov@nginx.com                     rc = nxt_python_write(pctx, item);
3911591Smax.romanov@nginx.com 
3921591Smax.romanov@nginx.com                 } else {
3931591Smax.romanov@nginx.com                     nxt_unit_req_error(req, "the application returned "
3941591Smax.romanov@nginx.com                                             "not a bytestring object");
3951591Smax.romanov@nginx.com                     rc = NXT_UNIT_ERROR;
3961591Smax.romanov@nginx.com                 }
3971591Smax.romanov@nginx.com 
3981591Smax.romanov@nginx.com                 Py_DECREF(item);
3991591Smax.romanov@nginx.com 
4001591Smax.romanov@nginx.com                 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
4011591Smax.romanov@nginx.com                     break;
4021591Smax.romanov@nginx.com                 }
4031591Smax.romanov@nginx.com             }
4041591Smax.romanov@nginx.com 
4051591Smax.romanov@nginx.com             Py_DECREF(iterator);
4061591Smax.romanov@nginx.com 
4071591Smax.romanov@nginx.com         } else {
4081591Smax.romanov@nginx.com             nxt_unit_req_error(req,
4091591Smax.romanov@nginx.com                             "the application returned not an iterable object");
4101591Smax.romanov@nginx.com             nxt_python_print_exception();
4111591Smax.romanov@nginx.com 
4121591Smax.romanov@nginx.com             rc = NXT_UNIT_ERROR;
4131591Smax.romanov@nginx.com         }
4141591Smax.romanov@nginx.com 
4151591Smax.romanov@nginx.com         close = PyObject_GetAttr(response, nxt_py_close_str);
4161591Smax.romanov@nginx.com 
4171591Smax.romanov@nginx.com         if (close != NULL) {
4181591Smax.romanov@nginx.com             result = PyObject_CallFunction(close, NULL);
4191591Smax.romanov@nginx.com             if (nxt_slow_path(result == NULL)) {
4201591Smax.romanov@nginx.com                 nxt_unit_req_error(req, "Python failed to call the close() "
4211591Smax.romanov@nginx.com                                         "method of the application response");
4221591Smax.romanov@nginx.com                 nxt_python_print_exception();
4231591Smax.romanov@nginx.com 
4241591Smax.romanov@nginx.com             } else {
4251591Smax.romanov@nginx.com                 Py_DECREF(result);
4261591Smax.romanov@nginx.com             }
4271591Smax.romanov@nginx.com 
4281591Smax.romanov@nginx.com             Py_DECREF(close);
4291591Smax.romanov@nginx.com 
4301591Smax.romanov@nginx.com         } else {
4311591Smax.romanov@nginx.com             PyErr_Clear();
4321591Smax.romanov@nginx.com         }
4331591Smax.romanov@nginx.com     }
4341591Smax.romanov@nginx.com 
4351591Smax.romanov@nginx.com     Py_DECREF(response);
4361591Smax.romanov@nginx.com 
4371591Smax.romanov@nginx.com done:
4381591Smax.romanov@nginx.com 
4391681Smax.romanov@nginx.com     pctx->thread_state = PyEval_SaveThread();
4401591Smax.romanov@nginx.com 
4411681Smax.romanov@nginx.com     pctx->req = NULL;
4421681Smax.romanov@nginx.com 
4431591Smax.romanov@nginx.com     nxt_unit_request_done(req, rc);
4441745Svbart@nginx.com 
4451745Svbart@nginx.com     if (nxt_fast_path(prepare_environ)) {
4461745Svbart@nginx.com         PyEval_RestoreThread(pctx->thread_state);
4471745Svbart@nginx.com 
4481745Svbart@nginx.com         pctx->environ = nxt_python_copy_environ(NULL);
4491745Svbart@nginx.com 
4501745Svbart@nginx.com         pctx->thread_state = PyEval_SaveThread();
4511745Svbart@nginx.com     }
4521591Smax.romanov@nginx.com }
4531591Smax.romanov@nginx.com 
4541591Smax.romanov@nginx.com 
4551591Smax.romanov@nginx.com static PyObject *
nxt_python_create_environ(nxt_python_app_conf_t * c)4561681Smax.romanov@nginx.com nxt_python_create_environ(nxt_python_app_conf_t *c)
4571591Smax.romanov@nginx.com {
4581591Smax.romanov@nginx.com     PyObject  *obj, *err, *environ;
4591591Smax.romanov@nginx.com 
4601591Smax.romanov@nginx.com     environ = PyDict_New();
4611591Smax.romanov@nginx.com 
4621591Smax.romanov@nginx.com     if (nxt_slow_path(environ == NULL)) {
4631681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
4641681Smax.romanov@nginx.com                        "Python failed to create the \"environ\" dictionary");
4651591Smax.romanov@nginx.com         return NULL;
4661591Smax.romanov@nginx.com     }
4671591Smax.romanov@nginx.com 
4681591Smax.romanov@nginx.com     obj = PyString_FromStringAndSize((char *) nxt_server.start,
4691591Smax.romanov@nginx.com                                      nxt_server.length);
4701591Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
4711681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
4721591Smax.romanov@nginx.com               "Python failed to create the \"SERVER_SOFTWARE\" environ value");
4731591Smax.romanov@nginx.com         goto fail;
4741591Smax.romanov@nginx.com     }
4751591Smax.romanov@nginx.com 
4761591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_SOFTWARE", obj)
4771591Smax.romanov@nginx.com         != 0))
4781591Smax.romanov@nginx.com     {
4791681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
4801591Smax.romanov@nginx.com                   "Python failed to set the \"SERVER_SOFTWARE\" environ value");
4811591Smax.romanov@nginx.com         goto fail;
4821591Smax.romanov@nginx.com     }
4831591Smax.romanov@nginx.com 
4841591Smax.romanov@nginx.com     Py_DECREF(obj);
4851591Smax.romanov@nginx.com 
4861591Smax.romanov@nginx.com     obj = Py_BuildValue("(ii)", 1, 0);
4871591Smax.romanov@nginx.com 
4881591Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
4891681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
4901591Smax.romanov@nginx.com                   "Python failed to build the \"wsgi.version\" environ value");
4911591Smax.romanov@nginx.com         goto fail;
4921591Smax.romanov@nginx.com     }
4931591Smax.romanov@nginx.com 
4941591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.version", obj) != 0))
4951591Smax.romanov@nginx.com     {
4961681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
4971681Smax.romanov@nginx.com                     "Python failed to set the \"wsgi.version\" environ value");
4981591Smax.romanov@nginx.com         goto fail;
4991591Smax.romanov@nginx.com     }
5001591Smax.romanov@nginx.com 
5011591Smax.romanov@nginx.com     Py_DECREF(obj);
5021591Smax.romanov@nginx.com     obj = NULL;
5031591Smax.romanov@nginx.com 
5041591Smax.romanov@nginx.com 
5051591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multithread",
5061681Smax.romanov@nginx.com                                            c->threads > 1 ? Py_True : Py_False)
5071591Smax.romanov@nginx.com         != 0))
5081591Smax.romanov@nginx.com     {
5091681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
5101591Smax.romanov@nginx.com                 "Python failed to set the \"wsgi.multithread\" environ value");
5111591Smax.romanov@nginx.com         goto fail;
5121591Smax.romanov@nginx.com     }
5131591Smax.romanov@nginx.com 
5141591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multiprocess",
5151591Smax.romanov@nginx.com                                            Py_True)
5161591Smax.romanov@nginx.com         != 0))
5171591Smax.romanov@nginx.com     {
5181681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
5191591Smax.romanov@nginx.com                "Python failed to set the \"wsgi.multiprocess\" environ value");
5201591Smax.romanov@nginx.com         goto fail;
5211591Smax.romanov@nginx.com     }
5221591Smax.romanov@nginx.com 
5231591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.run_once",
5241591Smax.romanov@nginx.com                                            Py_False)
5251591Smax.romanov@nginx.com         != 0))
5261591Smax.romanov@nginx.com     {
5271681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
5281591Smax.romanov@nginx.com                   "Python failed to set the \"wsgi.run_once\" environ value");
5291591Smax.romanov@nginx.com         goto fail;
5301591Smax.romanov@nginx.com     }
5311591Smax.romanov@nginx.com 
5321591Smax.romanov@nginx.com 
5331591Smax.romanov@nginx.com     if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) {
5341681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
5351591Smax.romanov@nginx.com                   "Python failed to initialize the \"wsgi.input\" type object");
5361591Smax.romanov@nginx.com         goto fail;
5371591Smax.romanov@nginx.com     }
5381591Smax.romanov@nginx.com 
5391591Smax.romanov@nginx.com 
5401591Smax.romanov@nginx.com     err = PySys_GetObject((char *) "stderr");
5411591Smax.romanov@nginx.com 
5421591Smax.romanov@nginx.com     if (nxt_slow_path(err == NULL)) {
5431681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get \"sys.stderr\" object");
5441591Smax.romanov@nginx.com         goto fail;
5451591Smax.romanov@nginx.com     }
5461591Smax.romanov@nginx.com 
5471591Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.errors", err) != 0))
5481591Smax.romanov@nginx.com     {
5491681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
5501681Smax.romanov@nginx.com                       "Python failed to set the \"wsgi.errors\" environ value");
5511591Smax.romanov@nginx.com         goto fail;
5521591Smax.romanov@nginx.com     }
5531591Smax.romanov@nginx.com 
5541591Smax.romanov@nginx.com     return environ;
5551591Smax.romanov@nginx.com 
5561591Smax.romanov@nginx.com fail:
5571591Smax.romanov@nginx.com 
5581591Smax.romanov@nginx.com     Py_XDECREF(obj);
5591591Smax.romanov@nginx.com     Py_DECREF(environ);
5601591Smax.romanov@nginx.com 
5611591Smax.romanov@nginx.com     return NULL;
5621591Smax.romanov@nginx.com }
5631591Smax.romanov@nginx.com 
5641591Smax.romanov@nginx.com 
5651591Smax.romanov@nginx.com static PyObject *
nxt_python_copy_environ(nxt_unit_request_info_t * req)5661745Svbart@nginx.com nxt_python_copy_environ(nxt_unit_request_info_t *req)
5671745Svbart@nginx.com {
5681745Svbart@nginx.com     PyObject  *environ;
5691745Svbart@nginx.com 
5701745Svbart@nginx.com     environ = PyDict_Copy(nxt_py_environ_ptyp);
5711745Svbart@nginx.com 
5721745Svbart@nginx.com     if (nxt_slow_path(environ == NULL)) {
5731745Svbart@nginx.com         nxt_unit_req_alert(req,
5741745Svbart@nginx.com                            "Python failed to copy the \"environ\" dictionary");
5751745Svbart@nginx.com         nxt_python_print_exception();
5761745Svbart@nginx.com     }
5771745Svbart@nginx.com 
5781745Svbart@nginx.com     return environ;
5791745Svbart@nginx.com }
5801745Svbart@nginx.com 
5811745Svbart@nginx.com 
5821745Svbart@nginx.com static PyObject *
nxt_python_get_environ(nxt_python_ctx_t * pctx)5831681Smax.romanov@nginx.com nxt_python_get_environ(nxt_python_ctx_t *pctx)
5841591Smax.romanov@nginx.com {
5851591Smax.romanov@nginx.com     int                 rc;
5861594Smax.romanov@nginx.com     uint32_t            i, j, vl;
5871591Smax.romanov@nginx.com     PyObject            *environ;
5881594Smax.romanov@nginx.com     nxt_unit_field_t    *f, *f2;
5891591Smax.romanov@nginx.com     nxt_unit_request_t  *r;
5901591Smax.romanov@nginx.com 
5911681Smax.romanov@nginx.com     r = pctx->req->request;
5921591Smax.romanov@nginx.com 
5931591Smax.romanov@nginx.com #define RC(S)                                                                 \
5941591Smax.romanov@nginx.com     do {                                                                      \
5951591Smax.romanov@nginx.com         rc = (S);                                                             \
5961591Smax.romanov@nginx.com         if (nxt_slow_path(rc != NXT_UNIT_OK)) {                               \
5971591Smax.romanov@nginx.com             goto fail;                                                        \
5981591Smax.romanov@nginx.com         }                                                                     \
5991591Smax.romanov@nginx.com     } while(0)
6001591Smax.romanov@nginx.com 
6011681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_request_method_str, &r->method,
6021591Smax.romanov@nginx.com                            r->method_length));
6031681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_request_uri_str, &r->target,
6041591Smax.romanov@nginx.com                            r->target_length));
6051681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query,
6061591Smax.romanov@nginx.com                            r->query_length));
6071681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path,
6081591Smax.romanov@nginx.com                            r->path_length));
6091591Smax.romanov@nginx.com 
6101681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote,
6111591Smax.romanov@nginx.com                            r->remote_length));
612*2208Sa.clayton@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local_addr,
613*2208Sa.clayton@nginx.com                            r->local_addr_length));
6141591Smax.romanov@nginx.com 
6151591Smax.romanov@nginx.com     if (r->tls) {
6161681Smax.romanov@nginx.com         RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
6171591Smax.romanov@nginx.com                               nxt_py_https_str));
6181591Smax.romanov@nginx.com     } else {
6191681Smax.romanov@nginx.com         RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
6201591Smax.romanov@nginx.com                               nxt_py_http_str));
6211591Smax.romanov@nginx.com     }
6221591Smax.romanov@nginx.com 
6231681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_server_protocol_str, &r->version,
6241591Smax.romanov@nginx.com                            r->version_length));
6251591Smax.romanov@nginx.com 
6261681Smax.romanov@nginx.com     RC(nxt_python_add_sptr(pctx, nxt_py_server_name_str, &r->server_name,
6271591Smax.romanov@nginx.com                            r->server_name_length));
6281681Smax.romanov@nginx.com     RC(nxt_python_add_obj(pctx, nxt_py_server_port_str, nxt_py_80_str));
6291591Smax.romanov@nginx.com 
6301681Smax.romanov@nginx.com     nxt_unit_request_group_dup_fields(pctx->req);
6311594Smax.romanov@nginx.com 
6321594Smax.romanov@nginx.com     for (i = 0; i < r->fields_count;) {
6331591Smax.romanov@nginx.com         f = r->fields + i;
6341594Smax.romanov@nginx.com         vl = f->value_length;
6351591Smax.romanov@nginx.com 
6361594Smax.romanov@nginx.com         for (j = i + 1; j < r->fields_count; j++) {
6371594Smax.romanov@nginx.com             f2 = r->fields + j;
6381594Smax.romanov@nginx.com 
6391594Smax.romanov@nginx.com             if (f2->hash != f->hash
6401594Smax.romanov@nginx.com                 || nxt_unit_sptr_get(&f2->name) != nxt_unit_sptr_get(&f->name))
6411594Smax.romanov@nginx.com             {
6421594Smax.romanov@nginx.com                 break;
6431594Smax.romanov@nginx.com             }
6441594Smax.romanov@nginx.com 
6451594Smax.romanov@nginx.com             vl += 2 + f2->value_length;
6461594Smax.romanov@nginx.com         }
6471594Smax.romanov@nginx.com 
6481681Smax.romanov@nginx.com         RC(nxt_python_add_field(pctx, f, j - i, vl));
6491594Smax.romanov@nginx.com 
6501594Smax.romanov@nginx.com         i = j;
6511591Smax.romanov@nginx.com     }
6521591Smax.romanov@nginx.com 
6531591Smax.romanov@nginx.com     if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
6541591Smax.romanov@nginx.com         f = r->fields + r->content_length_field;
6551591Smax.romanov@nginx.com 
6561681Smax.romanov@nginx.com         RC(nxt_python_add_sptr(pctx, nxt_py_content_length_str, &f->value,
6571591Smax.romanov@nginx.com                                f->value_length));
6581591Smax.romanov@nginx.com     }
6591591Smax.romanov@nginx.com 
6601591Smax.romanov@nginx.com     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
6611591Smax.romanov@nginx.com         f = r->fields + r->content_type_field;
6621591Smax.romanov@nginx.com 
6631681Smax.romanov@nginx.com         RC(nxt_python_add_sptr(pctx, nxt_py_content_type_str, &f->value,
6641591Smax.romanov@nginx.com                                f->value_length));
6651591Smax.romanov@nginx.com     }
6661591Smax.romanov@nginx.com 
6671591Smax.romanov@nginx.com #undef RC
6681591Smax.romanov@nginx.com 
6691745Svbart@nginx.com     if (nxt_slow_path(PyDict_SetItem(pctx->environ, nxt_py_wsgi_input_str,
6701681Smax.romanov@nginx.com                                      (PyObject *) pctx) != 0))
6711681Smax.romanov@nginx.com     {
6721681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
6731681Smax.romanov@nginx.com                        "Python failed to set the \"wsgi.input\" environ value");
6741681Smax.romanov@nginx.com         goto fail;
6751681Smax.romanov@nginx.com     }
6761681Smax.romanov@nginx.com 
6771745Svbart@nginx.com     environ = pctx->environ;
6781745Svbart@nginx.com     pctx->environ = NULL;
6791745Svbart@nginx.com 
6801591Smax.romanov@nginx.com     return environ;
6811591Smax.romanov@nginx.com 
6821591Smax.romanov@nginx.com fail:
6831591Smax.romanov@nginx.com 
6841745Svbart@nginx.com     Py_DECREF(pctx->environ);
6851745Svbart@nginx.com     pctx->environ = NULL;
6861591Smax.romanov@nginx.com 
6871591Smax.romanov@nginx.com     return NULL;
6881591Smax.romanov@nginx.com }
6891591Smax.romanov@nginx.com 
6901591Smax.romanov@nginx.com 
6911591Smax.romanov@nginx.com static int
nxt_python_add_sptr(nxt_python_ctx_t * pctx,PyObject * name,nxt_unit_sptr_t * sptr,uint32_t size)6921681Smax.romanov@nginx.com nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
6931591Smax.romanov@nginx.com     nxt_unit_sptr_t *sptr, uint32_t size)
6941591Smax.romanov@nginx.com {
6951591Smax.romanov@nginx.com     char      *src;
6961591Smax.romanov@nginx.com     PyObject  *value;
6971591Smax.romanov@nginx.com 
6981591Smax.romanov@nginx.com     src = nxt_unit_sptr_get(sptr);
6991591Smax.romanov@nginx.com 
7001591Smax.romanov@nginx.com     value = PyString_FromStringAndSize(src, size);
7011591Smax.romanov@nginx.com     if (nxt_slow_path(value == NULL)) {
7021681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
7031591Smax.romanov@nginx.com                            "Python failed to create value string \"%.*s\"",
7041591Smax.romanov@nginx.com                            (int) size, src);
7051591Smax.romanov@nginx.com         nxt_python_print_exception();
7061591Smax.romanov@nginx.com 
7071591Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
7081591Smax.romanov@nginx.com     }
7091591Smax.romanov@nginx.com 
7101681Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
7111681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
7121591Smax.romanov@nginx.com                            "Python failed to set the \"%s\" environ value",
7131591Smax.romanov@nginx.com                            PyUnicode_AsUTF8(name));
7141591Smax.romanov@nginx.com         Py_DECREF(value);
7151591Smax.romanov@nginx.com 
7161591Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
7171591Smax.romanov@nginx.com     }
7181591Smax.romanov@nginx.com 
7191591Smax.romanov@nginx.com     Py_DECREF(value);
7201591Smax.romanov@nginx.com 
7211591Smax.romanov@nginx.com     return NXT_UNIT_OK;
7221591Smax.romanov@nginx.com }
7231591Smax.romanov@nginx.com 
7241591Smax.romanov@nginx.com 
7251591Smax.romanov@nginx.com static int
nxt_python_add_field(nxt_python_ctx_t * pctx,nxt_unit_field_t * field,int n,uint32_t vl)7261681Smax.romanov@nginx.com nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n,
7271594Smax.romanov@nginx.com     uint32_t vl)
7281591Smax.romanov@nginx.com {
7291591Smax.romanov@nginx.com     char      *src;
7301591Smax.romanov@nginx.com     PyObject  *name, *value;
7311591Smax.romanov@nginx.com 
7321591Smax.romanov@nginx.com     src = nxt_unit_sptr_get(&field->name);
7331591Smax.romanov@nginx.com 
7341594Smax.romanov@nginx.com     name = nxt_python_field_name(src, field->name_length);
7351591Smax.romanov@nginx.com     if (nxt_slow_path(name == NULL)) {
7361681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
7371591Smax.romanov@nginx.com                            "Python failed to create name string \"%.*s\"",
7381591Smax.romanov@nginx.com                            (int) field->name_length, src);
7391591Smax.romanov@nginx.com         nxt_python_print_exception();
7401591Smax.romanov@nginx.com 
7411591Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
7421591Smax.romanov@nginx.com     }
7431591Smax.romanov@nginx.com 
7441594Smax.romanov@nginx.com     value = nxt_python_field_value(field, n, vl);
7451591Smax.romanov@nginx.com 
7461591Smax.romanov@nginx.com     if (nxt_slow_path(value == NULL)) {
7471681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
7481591Smax.romanov@nginx.com                            "Python failed to create value string \"%.*s\"",
7491594Smax.romanov@nginx.com                            (int) field->value_length,
7501594Smax.romanov@nginx.com                            (char *) nxt_unit_sptr_get(&field->value));
7511591Smax.romanov@nginx.com         nxt_python_print_exception();
7521591Smax.romanov@nginx.com 
7531591Smax.romanov@nginx.com         goto fail;
7541591Smax.romanov@nginx.com     }
7551591Smax.romanov@nginx.com 
7561681Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
7571681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
7581591Smax.romanov@nginx.com                            "Python failed to set the \"%s\" environ value",
7591591Smax.romanov@nginx.com                            PyUnicode_AsUTF8(name));
7601591Smax.romanov@nginx.com         goto fail;
7611591Smax.romanov@nginx.com     }
7621591Smax.romanov@nginx.com 
7631591Smax.romanov@nginx.com     Py_DECREF(name);
7641591Smax.romanov@nginx.com     Py_DECREF(value);
7651591Smax.romanov@nginx.com 
7661591Smax.romanov@nginx.com     return NXT_UNIT_OK;
7671591Smax.romanov@nginx.com 
7681591Smax.romanov@nginx.com fail:
7691591Smax.romanov@nginx.com 
7701591Smax.romanov@nginx.com     Py_DECREF(name);
7711591Smax.romanov@nginx.com     Py_XDECREF(value);
7721591Smax.romanov@nginx.com 
7731591Smax.romanov@nginx.com     return NXT_UNIT_ERROR;
7741591Smax.romanov@nginx.com }
7751591Smax.romanov@nginx.com 
7761591Smax.romanov@nginx.com 
7771594Smax.romanov@nginx.com static PyObject *
nxt_python_field_name(const char * name,uint8_t len)7781594Smax.romanov@nginx.com nxt_python_field_name(const char *name, uint8_t len)
7791594Smax.romanov@nginx.com {
7801594Smax.romanov@nginx.com     char      *p, c;
7811594Smax.romanov@nginx.com     uint8_t   i;
7821594Smax.romanov@nginx.com     PyObject  *res;
7831594Smax.romanov@nginx.com 
7841594Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
7851594Smax.romanov@nginx.com     res = PyUnicode_New(len + 5, 255);
7861594Smax.romanov@nginx.com #else
7871594Smax.romanov@nginx.com     res = PyString_FromStringAndSize(NULL, len + 5);
7881594Smax.romanov@nginx.com #endif
7891594Smax.romanov@nginx.com 
7901594Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
7911594Smax.romanov@nginx.com         return NULL;
7921594Smax.romanov@nginx.com     }
7931594Smax.romanov@nginx.com 
7941594Smax.romanov@nginx.com     p = PyString_AS_STRING(res);
7951594Smax.romanov@nginx.com 
7961594Smax.romanov@nginx.com     p = nxt_cpymem(p, "HTTP_", 5);
7971594Smax.romanov@nginx.com 
7981594Smax.romanov@nginx.com     for (i = 0; i < len; i++) {
7991594Smax.romanov@nginx.com         c = name[i];
8001594Smax.romanov@nginx.com 
8011594Smax.romanov@nginx.com         if (c >= 'a' && c <= 'z') {
8021594Smax.romanov@nginx.com             *p++ = (c & ~0x20);
8031594Smax.romanov@nginx.com             continue;
8041594Smax.romanov@nginx.com         }
8051594Smax.romanov@nginx.com 
8061594Smax.romanov@nginx.com         if (c == '-') {
8071594Smax.romanov@nginx.com             *p++ = '_';
8081594Smax.romanov@nginx.com             continue;
8091594Smax.romanov@nginx.com         }
8101594Smax.romanov@nginx.com 
8111594Smax.romanov@nginx.com         *p++ = c;
8121594Smax.romanov@nginx.com     }
8131594Smax.romanov@nginx.com 
8141594Smax.romanov@nginx.com     return res;
8151594Smax.romanov@nginx.com }
8161594Smax.romanov@nginx.com 
8171594Smax.romanov@nginx.com 
8181594Smax.romanov@nginx.com static PyObject *
nxt_python_field_value(nxt_unit_field_t * f,int n,uint32_t vl)8191594Smax.romanov@nginx.com nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl)
8201594Smax.romanov@nginx.com {
8211594Smax.romanov@nginx.com     int       i;
8221594Smax.romanov@nginx.com     char      *p, *src;
8231594Smax.romanov@nginx.com     PyObject  *res;
8241594Smax.romanov@nginx.com 
8251594Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
8261594Smax.romanov@nginx.com     res = PyUnicode_New(vl, 255);
8271594Smax.romanov@nginx.com #else
8281594Smax.romanov@nginx.com     res = PyString_FromStringAndSize(NULL, vl);
8291594Smax.romanov@nginx.com #endif
8301594Smax.romanov@nginx.com 
8311594Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
8321594Smax.romanov@nginx.com         return NULL;
8331594Smax.romanov@nginx.com     }
8341594Smax.romanov@nginx.com 
8351594Smax.romanov@nginx.com     p = PyString_AS_STRING(res);
8361594Smax.romanov@nginx.com 
8371594Smax.romanov@nginx.com     src = nxt_unit_sptr_get(&f->value);
8381594Smax.romanov@nginx.com     p = nxt_cpymem(p, src, f->value_length);
8391594Smax.romanov@nginx.com 
8401594Smax.romanov@nginx.com     for (i = 1; i < n; i++) {
8411594Smax.romanov@nginx.com         p = nxt_cpymem(p, ", ", 2);
8421594Smax.romanov@nginx.com 
8431594Smax.romanov@nginx.com         src = nxt_unit_sptr_get(&f[i].value);
8441594Smax.romanov@nginx.com         p = nxt_cpymem(p, src, f[i].value_length);
8451594Smax.romanov@nginx.com     }
8461594Smax.romanov@nginx.com 
8471594Smax.romanov@nginx.com     return res;
8481594Smax.romanov@nginx.com }
8491594Smax.romanov@nginx.com 
8501594Smax.romanov@nginx.com 
8511591Smax.romanov@nginx.com static int
nxt_python_add_obj(nxt_python_ctx_t * pctx,PyObject * name,PyObject * value)8521681Smax.romanov@nginx.com nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value)
8531591Smax.romanov@nginx.com {
8541681Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
8551681Smax.romanov@nginx.com         nxt_unit_req_error(pctx->req,
8561591Smax.romanov@nginx.com                            "Python failed to set the \"%s\" environ value",
8571591Smax.romanov@nginx.com                            PyUnicode_AsUTF8(name));
8581591Smax.romanov@nginx.com 
8591591Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
8601591Smax.romanov@nginx.com     }
8611591Smax.romanov@nginx.com 
8621591Smax.romanov@nginx.com     return NXT_UNIT_OK;
8631591Smax.romanov@nginx.com }
8641591Smax.romanov@nginx.com 
8651591Smax.romanov@nginx.com 
8661591Smax.romanov@nginx.com static PyObject *
nxt_py_start_resp(PyObject * self,PyObject * args)8671591Smax.romanov@nginx.com nxt_py_start_resp(PyObject *self, PyObject *args)
8681591Smax.romanov@nginx.com {
8691681Smax.romanov@nginx.com     int               rc, status;
8701681Smax.romanov@nginx.com     char              *status_str, *space_ptr;
8711681Smax.romanov@nginx.com     uint32_t          status_len;
8721681Smax.romanov@nginx.com     PyObject          *headers, *tuple, *string, *status_bytes;
8731681Smax.romanov@nginx.com     Py_ssize_t        i, n, fields_size, fields_count;
8741681Smax.romanov@nginx.com     nxt_python_ctx_t  *pctx;
8751591Smax.romanov@nginx.com 
8761681Smax.romanov@nginx.com     pctx = (nxt_python_ctx_t *) self;
8771681Smax.romanov@nginx.com     if (nxt_slow_path(pctx->req == NULL)) {
8781591Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
8791591Smax.romanov@nginx.com                             "start_response() is called "
8801591Smax.romanov@nginx.com                             "outside of WSGI request processing");
8811591Smax.romanov@nginx.com     }
8821591Smax.romanov@nginx.com 
8831591Smax.romanov@nginx.com     n = PyTuple_GET_SIZE(args);
8841591Smax.romanov@nginx.com 
8851591Smax.romanov@nginx.com     if (n < 2 || n > 3) {
8861591Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
8871591Smax.romanov@nginx.com     }
8881591Smax.romanov@nginx.com 
8891591Smax.romanov@nginx.com     string = PyTuple_GET_ITEM(args, 0);
8901591Smax.romanov@nginx.com     if (!PyBytes_Check(string) && !PyUnicode_Check(string)) {
8911591Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
8921591Smax.romanov@nginx.com                             "failed to write first argument (not a string?)");
8931591Smax.romanov@nginx.com     }
8941591Smax.romanov@nginx.com 
8951591Smax.romanov@nginx.com     headers = PyTuple_GET_ITEM(args, 1);
8961591Smax.romanov@nginx.com     if (!PyList_Check(headers)) {
8971591Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
8981591Smax.romanov@nginx.com                          "the second argument is not a response headers list");
8991591Smax.romanov@nginx.com     }
9001591Smax.romanov@nginx.com 
9011591Smax.romanov@nginx.com     fields_size = 0;
9021591Smax.romanov@nginx.com     fields_count = PyList_GET_SIZE(headers);
9031591Smax.romanov@nginx.com 
9041591Smax.romanov@nginx.com     for (i = 0; i < fields_count; i++) {
9051591Smax.romanov@nginx.com         tuple = PyList_GET_ITEM(headers, i);
9061591Smax.romanov@nginx.com 
9071591Smax.romanov@nginx.com         if (!PyTuple_Check(tuple)) {
9081591Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
9091591Smax.romanov@nginx.com                               "the response headers must be a list of tuples");
9101591Smax.romanov@nginx.com         }
9111591Smax.romanov@nginx.com 
9121591Smax.romanov@nginx.com         if (PyTuple_GET_SIZE(tuple) != 2) {
9131591Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
9141591Smax.romanov@nginx.com                                 "each header must be a tuple of two items");
9151591Smax.romanov@nginx.com         }
9161591Smax.romanov@nginx.com 
9171591Smax.romanov@nginx.com