xref: /unit/src/python/nxt_python_wsgi.c (revision 1745:a775a9a82b13)
1 
2 /*
3  * Copyright (C) Max Romanov
4  * Copyright (C) Valentin V. Bartenev
5  * Copyright (C) NGINX, Inc.
6  */
7 
8 
9 #include <Python.h>
10 
11 #include <nxt_main.h>
12 #include <nxt_router.h>
13 #include <nxt_unit.h>
14 #include <nxt_unit_field.h>
15 #include <nxt_unit_request.h>
16 #include <nxt_unit_response.h>
17 
18 #include <python/nxt_python.h>
19 
20 #include NXT_PYTHON_MOUNTS_H
21 
22 /*
23  * According to "PEP 3333 / A Note On String Types"
24  * [https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types]
25  *
26  * WSGI therefore defines two kinds of "string":
27  *
28  * - "Native" strings (which are always implemented using the type named str )
29  *   that are used for request/response headers and metadata
30  *
31  *   will use PyString_* or corresponding PyUnicode_* functions
32  *
33  * - "Bytestrings" (which are implemented using the bytes type in Python 3, and
34  *   str elsewhere), that are used for the bodies of requests and responses
35  *   (e.g. POST/PUT input data and HTML page outputs).
36  *
37  *   will use PyString_* or corresponding PyBytes_* functions
38  */
39 
40 
41 typedef struct {
42     PyObject_HEAD
43 
44     uint64_t                 content_length;
45     uint64_t                 bytes_sent;
46     PyObject                 *environ;
47     PyObject                 *start_resp;
48     PyObject                 *write;
49     nxt_unit_request_info_t  *req;
50     PyThreadState            *thread_state;
51 }  nxt_python_ctx_t;
52 
53 
54 static int nxt_python_wsgi_ctx_data_alloc(void **pdata);
55 static void nxt_python_wsgi_ctx_data_free(void *data);
56 static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
57 static void nxt_python_wsgi_done(void);
58 
59 static void nxt_python_request_handler(nxt_unit_request_info_t *req);
60 
61 static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c);
62 static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req);
63 static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx);
64 static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
65     nxt_unit_sptr_t *sptr, uint32_t size);
66 static int nxt_python_add_field(nxt_python_ctx_t *pctx,
67     nxt_unit_field_t *field, int n, uint32_t vl);
68 static PyObject *nxt_python_field_name(const char *name, uint8_t len);
69 static PyObject *nxt_python_field_value(nxt_unit_field_t *f, int n,
70     uint32_t vl);
71 static int nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name,
72     PyObject *value);
73 
74 static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args);
75 static int nxt_python_response_add_field(nxt_python_ctx_t *pctx,
76     PyObject *name, PyObject *value, int i);
77 static int nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len,
78     PyObject **bytes);
79 static PyObject *nxt_py_write(PyObject *self, PyObject *args);
80 
81 static void nxt_py_input_dealloc(nxt_python_ctx_t *pctx);
82 static PyObject *nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args);
83 static PyObject *nxt_py_input_readline(nxt_python_ctx_t *pctx,
84     PyObject *args);
85 static PyObject *nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size);
86 static PyObject *nxt_py_input_readlines(nxt_python_ctx_t *self,
87     PyObject *args);
88 
89 static PyObject *nxt_py_input_iter(PyObject *pctx);
90 static PyObject *nxt_py_input_next(PyObject *pctx);
91 
92 static int nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes);
93 
94 
95 static PyMethodDef nxt_py_start_resp_method[] = {
96     {"unit_start_response", nxt_py_start_resp, METH_VARARGS, ""}
97 };
98 
99 
100 static PyMethodDef nxt_py_write_method[] = {
101     {"unit_write", nxt_py_write, METH_O, ""}
102 };
103 
104 
105 static PyMethodDef nxt_py_input_methods[] = {
106     { "read",      (PyCFunction) nxt_py_input_read,      METH_VARARGS, 0 },
107     { "readline",  (PyCFunction) nxt_py_input_readline,  METH_VARARGS, 0 },
108     { "readlines", (PyCFunction) nxt_py_input_readlines, METH_VARARGS, 0 },
109     { NULL, NULL, 0, 0 }
110 };
111 
112 
113 static PyTypeObject nxt_py_input_type = {
114     PyVarObject_HEAD_INIT(NULL, 0)
115 
116     .tp_name      = "unit._input",
117     .tp_basicsize = sizeof(nxt_python_ctx_t),
118     .tp_dealloc   = (destructor) nxt_py_input_dealloc,
119     .tp_flags     = Py_TPFLAGS_DEFAULT,
120     .tp_doc       = "unit input object.",
121     .tp_iter      = nxt_py_input_iter,
122     .tp_iternext  = nxt_py_input_next,
123     .tp_methods   = nxt_py_input_methods,
124 };
125 
126 
127 static PyObject  *nxt_py_environ_ptyp;
128 
129 static PyObject  *nxt_py_80_str;
130 static PyObject  *nxt_py_close_str;
131 static PyObject  *nxt_py_content_length_str;
132 static PyObject  *nxt_py_content_type_str;
133 static PyObject  *nxt_py_http_str;
134 static PyObject  *nxt_py_https_str;
135 static PyObject  *nxt_py_path_info_str;
136 static PyObject  *nxt_py_query_string_str;
137 static PyObject  *nxt_py_remote_addr_str;
138 static PyObject  *nxt_py_request_method_str;
139 static PyObject  *nxt_py_request_uri_str;
140 static PyObject  *nxt_py_server_addr_str;
141 static PyObject  *nxt_py_server_name_str;
142 static PyObject  *nxt_py_server_port_str;
143 static PyObject  *nxt_py_server_protocol_str;
144 static PyObject  *nxt_py_wsgi_input_str;
145 static PyObject  *nxt_py_wsgi_uri_scheme_str;
146 
147 static nxt_python_string_t nxt_python_strings[] = {
148     { nxt_string("80"), &nxt_py_80_str },
149     { nxt_string("close"), &nxt_py_close_str },
150     { nxt_string("CONTENT_LENGTH"), &nxt_py_content_length_str },
151     { nxt_string("CONTENT_TYPE"), &nxt_py_content_type_str },
152     { nxt_string("http"), &nxt_py_http_str },
153     { nxt_string("https"), &nxt_py_https_str },
154     { nxt_string("PATH_INFO"), &nxt_py_path_info_str },
155     { nxt_string("QUERY_STRING"), &nxt_py_query_string_str },
156     { nxt_string("REMOTE_ADDR"), &nxt_py_remote_addr_str },
157     { nxt_string("REQUEST_METHOD"), &nxt_py_request_method_str },
158     { nxt_string("REQUEST_URI"), &nxt_py_request_uri_str },
159     { nxt_string("SERVER_ADDR"), &nxt_py_server_addr_str },
160     { nxt_string("SERVER_NAME"), &nxt_py_server_name_str },
161     { nxt_string("SERVER_PORT"), &nxt_py_server_port_str },
162     { nxt_string("SERVER_PROTOCOL"), &nxt_py_server_protocol_str },
163     { nxt_string("wsgi.input"), &nxt_py_wsgi_input_str },
164     { nxt_string("wsgi.url_scheme"), &nxt_py_wsgi_uri_scheme_str },
165     { nxt_null_string, NULL },
166 };
167 
168 static nxt_python_proto_t  nxt_py_wsgi_proto = {
169     .ctx_data_alloc = nxt_python_wsgi_ctx_data_alloc,
170     .ctx_data_free  = nxt_python_wsgi_ctx_data_free,
171     .run            = nxt_python_wsgi_run,
172     .done           = nxt_python_wsgi_done,
173 };
174 
175 
176 int
177 nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
178 {
179     PyObject  *obj;
180 
181     obj = NULL;
182 
183     if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings)
184                       != NXT_UNIT_OK))
185     {
186         nxt_unit_alert(NULL, "Python failed to init string objects");
187         goto fail;
188     }
189 
190     obj = nxt_python_create_environ(init->data);
191     if (nxt_slow_path(obj == NULL)) {
192         goto fail;
193     }
194 
195     nxt_py_environ_ptyp = obj;
196     obj = NULL;
197 
198     init->callbacks.request_handler = nxt_python_request_handler;
199 
200     *proto = nxt_py_wsgi_proto;
201 
202     return NXT_UNIT_OK;
203 
204 fail:
205 
206     Py_XDECREF(obj);
207 
208     return NXT_UNIT_ERROR;
209 }
210 
211 
212 static int
213 nxt_python_wsgi_ctx_data_alloc(void **pdata)
214 {
215     nxt_python_ctx_t  *pctx;
216 
217     pctx = PyObject_New(nxt_python_ctx_t, &nxt_py_input_type);
218     if (nxt_slow_path(pctx == NULL)) {
219         nxt_unit_alert(NULL,
220                        "Python failed to create the \"wsgi.input\" object");
221         return NXT_UNIT_ERROR;
222     }
223 
224     pctx->write = NULL;
225     pctx->environ = NULL;
226 
227     pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method,
228                                        (PyObject *) pctx);
229     if (nxt_slow_path(pctx->start_resp == NULL)) {
230         nxt_unit_alert(NULL,
231                 "Python failed to initialize the \"start_response\" function");
232         goto fail;
233     }
234 
235     pctx->write = PyCFunction_New(nxt_py_write_method, (PyObject *) pctx);
236     if (nxt_slow_path(pctx->write == NULL)) {
237         nxt_unit_alert(NULL,
238                        "Python failed to initialize the \"write\" function");
239         goto fail;
240     }
241 
242     pctx->environ = nxt_python_copy_environ(NULL);
243     if (nxt_slow_path(pctx->environ == NULL)) {
244         goto fail;
245     }
246 
247     *pdata = pctx;
248 
249     return NXT_UNIT_OK;
250 
251 fail:
252 
253     nxt_python_wsgi_ctx_data_free(pctx);
254 
255     return NXT_UNIT_ERROR;
256 }
257 
258 
259 static void
260 nxt_python_wsgi_ctx_data_free(void *data)
261 {
262     nxt_python_ctx_t  *pctx;
263 
264     pctx = data;
265 
266     Py_XDECREF(pctx->start_resp);
267     Py_XDECREF(pctx->write);
268     Py_XDECREF(pctx->environ);
269     Py_XDECREF(pctx);
270 }
271 
272 
273 static int
274 nxt_python_wsgi_run(nxt_unit_ctx_t *ctx)
275 {
276     int               rc;
277     nxt_python_ctx_t  *pctx;
278 
279     pctx = ctx->data;
280 
281     pctx->thread_state = PyEval_SaveThread();
282 
283     rc = nxt_unit_run(ctx);
284 
285     PyEval_RestoreThread(pctx->thread_state);
286 
287     return rc;
288 }
289 
290 
291 static void
292 nxt_python_wsgi_done(void)
293 {
294     nxt_python_done_strings(nxt_python_strings);
295 
296     Py_XDECREF(nxt_py_environ_ptyp);
297 }
298 
299 
300 static void
301 nxt_python_request_handler(nxt_unit_request_info_t *req)
302 {
303     int               rc;
304     PyObject          *environ, *args, *response, *iterator, *item;
305     PyObject          *close, *result;
306     nxt_bool_t        prepare_environ;
307     nxt_python_ctx_t  *pctx;
308 
309     pctx = req->ctx->data;
310 
311     pctx->content_length = -1;
312     pctx->bytes_sent = 0;
313     pctx->req = req;
314 
315     PyEval_RestoreThread(pctx->thread_state);
316 
317     if (nxt_slow_path(pctx->environ == NULL)) {
318         pctx->environ = nxt_python_copy_environ(req);
319 
320         if (pctx->environ == NULL) {
321             prepare_environ = 0;
322 
323             rc = NXT_UNIT_ERROR;
324             goto done;
325         }
326     }
327 
328     prepare_environ = 1;
329 
330     environ = nxt_python_get_environ(pctx);
331     if (nxt_slow_path(environ == NULL)) {
332         rc = NXT_UNIT_ERROR;
333         goto done;
334     }
335 
336     args = PyTuple_New(2);
337     if (nxt_slow_path(args == NULL)) {
338         Py_DECREF(environ);
339 
340         nxt_unit_req_error(req, "Python failed to create arguments tuple");
341 
342         rc = NXT_UNIT_ERROR;
343         goto done;
344     }
345 
346     PyTuple_SET_ITEM(args, 0, environ);
347 
348     Py_INCREF(pctx->start_resp);
349     PyTuple_SET_ITEM(args, 1, pctx->start_resp);
350 
351     response = PyObject_CallObject(nxt_py_application, args);
352 
353     Py_DECREF(args);
354 
355     if (nxt_slow_path(response == NULL)) {
356         nxt_unit_req_error(req, "Python failed to call the application");
357         nxt_python_print_exception();
358 
359         rc = NXT_UNIT_ERROR;
360         goto done;
361     }
362 
363     /* Shortcut: avoid iterate over response string symbols. */
364     if (PyBytes_Check(response)) {
365         rc = nxt_python_write(pctx, response);
366 
367     } else {
368         iterator = PyObject_GetIter(response);
369 
370         if (nxt_fast_path(iterator != NULL)) {
371             rc = NXT_UNIT_OK;
372 
373             while (pctx->bytes_sent < pctx->content_length) {
374                 item = PyIter_Next(iterator);
375 
376                 if (item == NULL) {
377                     if (nxt_slow_path(PyErr_Occurred() != NULL)) {
378                         nxt_unit_req_error(req, "Python failed to iterate over "
379                                            "the application response object");
380                         nxt_python_print_exception();
381 
382                         rc = NXT_UNIT_ERROR;
383                     }
384 
385                     break;
386                 }
387 
388                 if (nxt_fast_path(PyBytes_Check(item))) {
389                     rc = nxt_python_write(pctx, item);
390 
391                 } else {
392                     nxt_unit_req_error(req, "the application returned "
393                                             "not a bytestring object");
394                     rc = NXT_UNIT_ERROR;
395                 }
396 
397                 Py_DECREF(item);
398 
399                 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
400                     break;
401                 }
402             }
403 
404             Py_DECREF(iterator);
405 
406         } else {
407             nxt_unit_req_error(req,
408                             "the application returned not an iterable object");
409             nxt_python_print_exception();
410 
411             rc = NXT_UNIT_ERROR;
412         }
413 
414         close = PyObject_GetAttr(response, nxt_py_close_str);
415 
416         if (close != NULL) {
417             result = PyObject_CallFunction(close, NULL);
418             if (nxt_slow_path(result == NULL)) {
419                 nxt_unit_req_error(req, "Python failed to call the close() "
420                                         "method of the application response");
421                 nxt_python_print_exception();
422 
423             } else {
424                 Py_DECREF(result);
425             }
426 
427             Py_DECREF(close);
428 
429         } else {
430             PyErr_Clear();
431         }
432     }
433 
434     Py_DECREF(response);
435 
436 done:
437 
438     pctx->thread_state = PyEval_SaveThread();
439 
440     pctx->req = NULL;
441 
442     nxt_unit_request_done(req, rc);
443 
444     if (nxt_fast_path(prepare_environ)) {
445         PyEval_RestoreThread(pctx->thread_state);
446 
447         pctx->environ = nxt_python_copy_environ(NULL);
448 
449         pctx->thread_state = PyEval_SaveThread();
450     }
451 }
452 
453 
454 static PyObject *
455 nxt_python_create_environ(nxt_python_app_conf_t *c)
456 {
457     PyObject  *obj, *err, *environ;
458 
459     environ = PyDict_New();
460 
461     if (nxt_slow_path(environ == NULL)) {
462         nxt_unit_alert(NULL,
463                        "Python failed to create the \"environ\" dictionary");
464         return NULL;
465     }
466 
467     obj = PyString_FromStringAndSize((char *) nxt_server.start,
468                                      nxt_server.length);
469     if (nxt_slow_path(obj == NULL)) {
470         nxt_unit_alert(NULL,
471               "Python failed to create the \"SERVER_SOFTWARE\" environ value");
472         goto fail;
473     }
474 
475     if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_SOFTWARE", obj)
476         != 0))
477     {
478         nxt_unit_alert(NULL,
479                   "Python failed to set the \"SERVER_SOFTWARE\" environ value");
480         goto fail;
481     }
482 
483     Py_DECREF(obj);
484 
485     obj = Py_BuildValue("(ii)", 1, 0);
486 
487     if (nxt_slow_path(obj == NULL)) {
488         nxt_unit_alert(NULL,
489                   "Python failed to build the \"wsgi.version\" environ value");
490         goto fail;
491     }
492 
493     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.version", obj) != 0))
494     {
495         nxt_unit_alert(NULL,
496                     "Python failed to set the \"wsgi.version\" environ value");
497         goto fail;
498     }
499 
500     Py_DECREF(obj);
501     obj = NULL;
502 
503 
504     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multithread",
505                                            c->threads > 1 ? Py_True : Py_False)
506         != 0))
507     {
508         nxt_unit_alert(NULL,
509                 "Python failed to set the \"wsgi.multithread\" environ value");
510         goto fail;
511     }
512 
513     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multiprocess",
514                                            Py_True)
515         != 0))
516     {
517         nxt_unit_alert(NULL,
518                "Python failed to set the \"wsgi.multiprocess\" environ value");
519         goto fail;
520     }
521 
522     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.run_once",
523                                            Py_False)
524         != 0))
525     {
526         nxt_unit_alert(NULL,
527                   "Python failed to set the \"wsgi.run_once\" environ value");
528         goto fail;
529     }
530 
531 
532     if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) {
533         nxt_unit_alert(NULL,
534                   "Python failed to initialize the \"wsgi.input\" type object");
535         goto fail;
536     }
537 
538 
539     err = PySys_GetObject((char *) "stderr");
540 
541     if (nxt_slow_path(err == NULL)) {
542         nxt_unit_alert(NULL, "Python failed to get \"sys.stderr\" object");
543         goto fail;
544     }
545 
546     if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.errors", err) != 0))
547     {
548         nxt_unit_alert(NULL,
549                       "Python failed to set the \"wsgi.errors\" environ value");
550         goto fail;
551     }
552 
553     return environ;
554 
555 fail:
556 
557     Py_XDECREF(obj);
558     Py_DECREF(environ);
559 
560     return NULL;
561 }
562 
563 
564 static PyObject *
565 nxt_python_copy_environ(nxt_unit_request_info_t *req)
566 {
567     PyObject  *environ;
568 
569     environ = PyDict_Copy(nxt_py_environ_ptyp);
570 
571     if (nxt_slow_path(environ == NULL)) {
572         nxt_unit_req_alert(req,
573                            "Python failed to copy the \"environ\" dictionary");
574         nxt_python_print_exception();
575     }
576 
577     return environ;
578 }
579 
580 
581 static PyObject *
582 nxt_python_get_environ(nxt_python_ctx_t *pctx)
583 {
584     int                 rc;
585     uint32_t            i, j, vl;
586     PyObject            *environ;
587     nxt_unit_field_t    *f, *f2;
588     nxt_unit_request_t  *r;
589 
590     r = pctx->req->request;
591 
592 #define RC(S)                                                                 \
593     do {                                                                      \
594         rc = (S);                                                             \
595         if (nxt_slow_path(rc != NXT_UNIT_OK)) {                               \
596             goto fail;                                                        \
597         }                                                                     \
598     } while(0)
599 
600     RC(nxt_python_add_sptr(pctx, nxt_py_request_method_str, &r->method,
601                            r->method_length));
602     RC(nxt_python_add_sptr(pctx, nxt_py_request_uri_str, &r->target,
603                            r->target_length));
604     RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query,
605                            r->query_length));
606     RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path,
607                            r->path_length));
608 
609     RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote,
610                            r->remote_length));
611     RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local,
612                            r->local_length));
613 
614     if (r->tls) {
615         RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
616                               nxt_py_https_str));
617     } else {
618         RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
619                               nxt_py_http_str));
620     }
621 
622     RC(nxt_python_add_sptr(pctx, nxt_py_server_protocol_str, &r->version,
623                            r->version_length));
624 
625     RC(nxt_python_add_sptr(pctx, nxt_py_server_name_str, &r->server_name,
626                            r->server_name_length));
627     RC(nxt_python_add_obj(pctx, nxt_py_server_port_str, nxt_py_80_str));
628 
629     nxt_unit_request_group_dup_fields(pctx->req);
630 
631     for (i = 0; i < r->fields_count;) {
632         f = r->fields + i;
633         vl = f->value_length;
634 
635         for (j = i + 1; j < r->fields_count; j++) {
636             f2 = r->fields + j;
637 
638             if (f2->hash != f->hash
639                 || nxt_unit_sptr_get(&f2->name) != nxt_unit_sptr_get(&f->name))
640             {
641                 break;
642             }
643 
644             vl += 2 + f2->value_length;
645         }
646 
647         RC(nxt_python_add_field(pctx, f, j - i, vl));
648 
649         i = j;
650     }
651 
652     if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
653         f = r->fields + r->content_length_field;
654 
655         RC(nxt_python_add_sptr(pctx, nxt_py_content_length_str, &f->value,
656                                f->value_length));
657     }
658 
659     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
660         f = r->fields + r->content_type_field;
661 
662         RC(nxt_python_add_sptr(pctx, nxt_py_content_type_str, &f->value,
663                                f->value_length));
664     }
665 
666 #undef RC
667 
668     if (nxt_slow_path(PyDict_SetItem(pctx->environ, nxt_py_wsgi_input_str,
669                                      (PyObject *) pctx) != 0))
670     {
671         nxt_unit_req_error(pctx->req,
672                        "Python failed to set the \"wsgi.input\" environ value");
673         goto fail;
674     }
675 
676     environ = pctx->environ;
677     pctx->environ = NULL;
678 
679     return environ;
680 
681 fail:
682 
683     Py_DECREF(pctx->environ);
684     pctx->environ = NULL;
685 
686     return NULL;
687 }
688 
689 
690 static int
691 nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
692     nxt_unit_sptr_t *sptr, uint32_t size)
693 {
694     char      *src;
695     PyObject  *value;
696 
697     src = nxt_unit_sptr_get(sptr);
698 
699     value = PyString_FromStringAndSize(src, size);
700     if (nxt_slow_path(value == NULL)) {
701         nxt_unit_req_error(pctx->req,
702                            "Python failed to create value string \"%.*s\"",
703                            (int) size, src);
704         nxt_python_print_exception();
705 
706         return NXT_UNIT_ERROR;
707     }
708 
709     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
710         nxt_unit_req_error(pctx->req,
711                            "Python failed to set the \"%s\" environ value",
712                            PyUnicode_AsUTF8(name));
713         Py_DECREF(value);
714 
715         return NXT_UNIT_ERROR;
716     }
717 
718     Py_DECREF(value);
719 
720     return NXT_UNIT_OK;
721 }
722 
723 
724 static int
725 nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n,
726     uint32_t vl)
727 {
728     char      *src;
729     PyObject  *name, *value;
730 
731     src = nxt_unit_sptr_get(&field->name);
732 
733     name = nxt_python_field_name(src, field->name_length);
734     if (nxt_slow_path(name == NULL)) {
735         nxt_unit_req_error(pctx->req,
736                            "Python failed to create name string \"%.*s\"",
737                            (int) field->name_length, src);
738         nxt_python_print_exception();
739 
740         return NXT_UNIT_ERROR;
741     }
742 
743     value = nxt_python_field_value(field, n, vl);
744 
745     if (nxt_slow_path(value == NULL)) {
746         nxt_unit_req_error(pctx->req,
747                            "Python failed to create value string \"%.*s\"",
748                            (int) field->value_length,
749                            (char *) nxt_unit_sptr_get(&field->value));
750         nxt_python_print_exception();
751 
752         goto fail;
753     }
754 
755     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
756         nxt_unit_req_error(pctx->req,
757                            "Python failed to set the \"%s\" environ value",
758                            PyUnicode_AsUTF8(name));
759         goto fail;
760     }
761 
762     Py_DECREF(name);
763     Py_DECREF(value);
764 
765     return NXT_UNIT_OK;
766 
767 fail:
768 
769     Py_DECREF(name);
770     Py_XDECREF(value);
771 
772     return NXT_UNIT_ERROR;
773 }
774 
775 
776 static PyObject *
777 nxt_python_field_name(const char *name, uint8_t len)
778 {
779     char      *p, c;
780     uint8_t   i;
781     PyObject  *res;
782 
783 #if PY_MAJOR_VERSION == 3
784     res = PyUnicode_New(len + 5, 255);
785 #else
786     res = PyString_FromStringAndSize(NULL, len + 5);
787 #endif
788 
789     if (nxt_slow_path(res == NULL)) {
790         return NULL;
791     }
792 
793     p = PyString_AS_STRING(res);
794 
795     p = nxt_cpymem(p, "HTTP_", 5);
796 
797     for (i = 0; i < len; i++) {
798         c = name[i];
799 
800         if (c >= 'a' && c <= 'z') {
801             *p++ = (c & ~0x20);
802             continue;
803         }
804 
805         if (c == '-') {
806             *p++ = '_';
807             continue;
808         }
809 
810         *p++ = c;
811     }
812 
813     return res;
814 }
815 
816 
817 static PyObject *
818 nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl)
819 {
820     int       i;
821     char      *p, *src;
822     PyObject  *res;
823 
824 #if PY_MAJOR_VERSION == 3
825     res = PyUnicode_New(vl, 255);
826 #else
827     res = PyString_FromStringAndSize(NULL, vl);
828 #endif
829 
830     if (nxt_slow_path(res == NULL)) {
831         return NULL;
832     }
833 
834     p = PyString_AS_STRING(res);
835 
836     src = nxt_unit_sptr_get(&f->value);
837     p = nxt_cpymem(p, src, f->value_length);
838 
839     for (i = 1; i < n; i++) {
840         p = nxt_cpymem(p, ", ", 2);
841 
842         src = nxt_unit_sptr_get(&f[i].value);
843         p = nxt_cpymem(p, src, f[i].value_length);
844     }
845 
846     return res;
847 }
848 
849 
850 static int
851 nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value)
852 {
853     if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
854         nxt_unit_req_error(pctx->req,
855                            "Python failed to set the \"%s\" environ value",
856                            PyUnicode_AsUTF8(name));
857 
858         return NXT_UNIT_ERROR;
859     }
860 
861     return NXT_UNIT_OK;
862 }
863 
864 
865 static PyObject *
866 nxt_py_start_resp(PyObject *self, PyObject *args)
867 {
868     int               rc, status;
869     char              *status_str, *space_ptr;
870     uint32_t          status_len;
871     PyObject          *headers, *tuple, *string, *status_bytes;
872     Py_ssize_t        i, n, fields_size, fields_count;
873     nxt_python_ctx_t  *pctx;
874 
875     pctx = (nxt_python_ctx_t *) self;
876     if (nxt_slow_path(pctx->req == NULL)) {
877         return PyErr_Format(PyExc_RuntimeError,
878                             "start_response() is called "
879                             "outside of WSGI request processing");
880     }
881 
882     n = PyTuple_GET_SIZE(args);
883 
884     if (n < 2 || n > 3) {
885         return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
886     }
887 
888     string = PyTuple_GET_ITEM(args, 0);
889     if (!PyBytes_Check(string) && !PyUnicode_Check(string)) {
890         return PyErr_Format(PyExc_TypeError,
891                             "failed to write first argument (not a string?)");
892     }
893 
894     headers = PyTuple_GET_ITEM(args, 1);
895     if (!PyList_Check(headers)) {
896         return PyErr_Format(PyExc_TypeError,
897                          "the second argument is not a response headers list");
898     }
899 
900     fields_size = 0;
901     fields_count = PyList_GET_SIZE(headers);
902 
903     for (i = 0; i < fields_count; i++) {
904         tuple = PyList_GET_ITEM(headers, i);
905 
906         if (!PyTuple_Check(tuple)) {
907             return PyErr_Format(PyExc_TypeError,
908                               "the response headers must be a list of tuples");
909         }
910 
911         if (PyTuple_GET_SIZE(tuple) != 2) {
912             return PyErr_Format(PyExc_TypeError,
913                                 "each header must be a tuple of two items");
914         }
915 
916         string = PyTuple_GET_ITEM(tuple, 0);
917         if (PyBytes_Check(string)) {
918             fields_size += PyBytes_GET_SIZE(string);
919 
920         } else if (PyUnicode_Check(string)) {
921             fields_size += PyUnicode_GET_LENGTH(string);
922 
923         } else {
924             return PyErr_Format(PyExc_TypeError,
925                                 "header #%d name is not a string", (int) i);
926         }
927 
928         string = PyTuple_GET_ITEM(tuple, 1);
929         if (PyBytes_Check(string)) {
930             fields_size += PyBytes_GET_SIZE(string);
931 
932         } else if (PyUnicode_Check(string)) {
933             fields_size += PyUnicode_GET_LENGTH(string);
934 
935         } else {
936             return PyErr_Format(PyExc_TypeError,
937                                 "header #%d value is not a string", (int) i);
938         }
939     }
940 
941     pctx->content_length = -1;
942 
943     string = PyTuple_GET_ITEM(args, 0);
944     rc = nxt_python_str_buf(string, &status_str, &status_len, &status_bytes);
945     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
946         return PyErr_Format(PyExc_TypeError, "status is not a string");
947     }
948 
949     space_ptr = memchr(status_str, ' ', status_len);
950     if (space_ptr != NULL) {
951         status_len = space_ptr - status_str;
952     }
953 
954     status = nxt_int_parse((u_char *) status_str, status_len);
955     if (nxt_slow_path(status < 0)) {
956         return PyErr_Format(PyExc_TypeError, "failed to parse status code");
957     }
958 
959     Py_XDECREF(status_bytes);
960 
961     /*
962      * PEP 3333:
963      *
964      * ... applications can replace their originally intended output with error
965      * output, up until the last possible moment.
966      */
967     rc = nxt_unit_response_init(pctx->req, status, fields_count, fields_size);
968     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
969         return PyErr_Format(PyExc_RuntimeError,
970                             "failed to allocate response object");
971     }
972 
973     for (i = 0; i < fields_count; i++) {
974         tuple = PyList_GET_ITEM(headers, i);
975 
976         rc = nxt_python_response_add_field(pctx, PyTuple_GET_ITEM(tuple, 0),
977                                            PyTuple_GET_ITEM(tuple, 1), i);
978         if (nxt_slow_path(rc != NXT_UNIT_OK)) {
979             return PyErr_Format(PyExc_RuntimeError,
980                                 "failed to add header #%d", (int) i);
981         }
982     }
983 
984     /*
985      * PEP 3333:
986      *
987      * However, the start_response callable must not actually transmit the
988      * response headers. Instead, it must store them for the server or gateway
989      * to transmit only after the first iteration of the application return
990      * value that yields a non-empty bytestring, or upon the application's
991      * first invocation of the write() callable. In other words, response
992      * headers must not be sent until there is actual body data available, or
993      * until the application's returned iterable is exhausted. (The only
994      * possible exception to this rule is if the response headers explicitly
995      * include a Content-Length of zero.)
996      */
997     if (pctx->content_length == 0) {
998         rc = nxt_unit_response_send(pctx->req);
999         if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1000             return PyErr_Format(PyExc_RuntimeError,
1001                                 "failed to send response headers");
1002         }
1003     }
1004 
1005     Py_INCREF(pctx->write);
1006     return pctx->write;
1007 }
1008 
1009 
1010 static int
1011 nxt_python_response_add_field(nxt_python_ctx_t *pctx, PyObject *name,
1012     PyObject *value, int i)
1013 {
1014     int        rc;
1015     char       *name_str, *value_str;
1016     uint32_t   name_length, value_length;
1017     PyObject   *name_bytes, *value_bytes;
1018     nxt_off_t  content_length;
1019 
1020     name_bytes = NULL;
1021     value_bytes = NULL;
1022 
1023     rc = nxt_python_str_buf(name, &name_str, &name_length, &name_bytes);
1024     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1025         goto fail;
1026     }
1027 
1028     rc = nxt_python_str_buf(value, &value_str, &value_length, &value_bytes);
1029     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1030         goto fail;
1031     }
1032 
1033     rc = nxt_unit_response_add_field(pctx->req, name_str, name_length,
1034                                      value_str, value_length);
1035     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1036         goto fail;
1037     }
1038 
1039     if (pctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
1040         content_length = nxt_off_t_parse((u_char *) value_str, value_length);
1041         if (nxt_slow_path(content_length < 0)) {
1042             nxt_unit_req_error(pctx->req, "failed to parse Content-Length "
1043                                "value %.*s", (int) value_length, value_str);
1044 
1045         } else {
1046             pctx->content_length = content_length;
1047         }
1048     }
1049 
1050 fail:
1051 
1052     Py_XDECREF(name_bytes);
1053     Py_XDECREF(value_bytes);
1054 
1055     return rc;
1056 }
1057 
1058 
1059 static int
1060 nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, PyObject **bytes)
1061 {
1062     if (PyBytes_Check(str)) {
1063         *buf = PyBytes_AS_STRING(str);
1064         *len = PyBytes_GET_SIZE(str);
1065         *bytes = NULL;
1066 
1067     } else {
1068         *bytes = PyUnicode_AsLatin1String(str);
1069         if (nxt_slow_path(*bytes == NULL)) {
1070             return NXT_UNIT_ERROR;
1071         }
1072 
1073         *buf = PyBytes_AS_STRING(*bytes);
1074         *len = PyBytes_GET_SIZE(*bytes);
1075     }
1076 
1077     return NXT_UNIT_OK;
1078 }
1079 
1080 
1081 static PyObject *
1082 nxt_py_write(PyObject *self, PyObject *str)
1083 {
1084     int  rc;
1085 
1086     if (nxt_fast_path(!PyBytes_Check(str))) {
1087         return PyErr_Format(PyExc_TypeError, "the argument is not a %s",
1088                             NXT_PYTHON_BYTES_TYPE);
1089     }
1090 
1091     rc = nxt_python_write((nxt_python_ctx_t *) self, str);
1092     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1093         return PyErr_Format(PyExc_RuntimeError,
1094                             "failed to write response value");
1095     }
1096 
1097     Py_RETURN_NONE;
1098 }
1099 
1100 
1101 static void
1102 nxt_py_input_dealloc(nxt_python_ctx_t *self)
1103 {
1104     PyObject_Del(self);
1105 }
1106 
1107 
1108 static PyObject *
1109 nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args)
1110 {
1111     char        *buf;
1112     PyObject    *content, *obj;
1113     Py_ssize_t  size, n;
1114 
1115     if (nxt_slow_path(pctx->req == NULL)) {
1116         return PyErr_Format(PyExc_RuntimeError,
1117                             "wsgi.input.read() is called "
1118                             "outside of WSGI request processing");
1119     }
1120 
1121     size = pctx->req->content_length;
1122 
1123     n = PyTuple_GET_SIZE(args);
1124 
1125     if (n > 0) {
1126         if (n != 1) {
1127             return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
1128         }
1129 
1130         obj = PyTuple_GET_ITEM(args, 0);
1131 
1132         size = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
1133 
1134         if (nxt_slow_path(size < 0)) {
1135             if (size == -1 && PyErr_Occurred()) {
1136                 return NULL;
1137             }
1138 
1139             if (size != -1) {
1140                 return PyErr_Format(PyExc_ValueError,
1141                                   "the read body size cannot be zero or less");
1142             }
1143         }
1144 
1145         if (size == -1 || size > (Py_ssize_t) pctx->req->content_length) {
1146             size = pctx->req->content_length;
1147         }
1148     }
1149 
1150     content = PyBytes_FromStringAndSize(NULL, size);
1151     if (nxt_slow_path(content == NULL)) {
1152         return NULL;
1153     }
1154 
1155     buf = PyBytes_AS_STRING(content);
1156 
1157     size = nxt_unit_request_read(pctx->req, buf, size);
1158 
1159     return content;
1160 }
1161 
1162 
1163 static PyObject *
1164 nxt_py_input_readline(nxt_python_ctx_t *pctx, PyObject *args)
1165 {
1166     ssize_t     ssize;
1167     PyObject    *obj;
1168     Py_ssize_t  n;
1169 
1170     if (nxt_slow_path(pctx->req == NULL)) {
1171         return PyErr_Format(PyExc_RuntimeError,
1172                             "wsgi.input.readline() is called "
1173                             "outside of WSGI request processing");
1174     }
1175 
1176     n = PyTuple_GET_SIZE(args);
1177 
1178     if (n > 0) {
1179         if (n != 1) {
1180             return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
1181         }
1182 
1183         obj = PyTuple_GET_ITEM(args, 0);
1184 
1185         ssize = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
1186 
1187         if (nxt_fast_path(ssize > 0)) {
1188             return nxt_py_input_getline(pctx, ssize);
1189         }
1190 
1191         if (ssize == 0) {
1192             return PyBytes_FromStringAndSize("", 0);
1193         }
1194 
1195         if (ssize != -1) {
1196             return PyErr_Format(PyExc_ValueError,
1197                                 "the read line size cannot be zero or less");
1198         }
1199 
1200         if (PyErr_Occurred()) {
1201             return NULL;
1202         }
1203     }
1204 
1205     return nxt_py_input_getline(pctx, SSIZE_MAX);
1206 }
1207 
1208 
1209 static PyObject *
1210 nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size)
1211 {
1212     void      *buf;
1213     ssize_t   res;
1214     PyObject  *content;
1215 
1216     res = nxt_unit_request_readline_size(pctx->req, size);
1217     if (nxt_slow_path(res < 0)) {
1218         return NULL;
1219     }
1220 
1221     if (res == 0) {
1222         return PyBytes_FromStringAndSize("", 0);
1223     }
1224 
1225     content = PyBytes_FromStringAndSize(NULL, res);
1226     if (nxt_slow_path(content == NULL)) {
1227         return NULL;
1228     }
1229 
1230     buf = PyBytes_AS_STRING(content);
1231 
1232     res = nxt_unit_request_read(pctx->req, buf, res);
1233 
1234     return content;
1235 }
1236 
1237 
1238 static PyObject *
1239 nxt_py_input_readlines(nxt_python_ctx_t *pctx, PyObject *args)
1240 {
1241     PyObject  *res;
1242 
1243     if (nxt_slow_path(pctx->req == NULL)) {
1244         return PyErr_Format(PyExc_RuntimeError,
1245                             "wsgi.input.readlines() is called "
1246                             "outside of WSGI request processing");
1247     }
1248 
1249     res = PyList_New(0);
1250     if (nxt_slow_path(res == NULL)) {
1251         return NULL;
1252     }
1253 
1254     for ( ;; ) {
1255         PyObject *line = nxt_py_input_getline(pctx, SSIZE_MAX);
1256         if (nxt_slow_path(line == NULL)) {
1257             Py_DECREF(res);
1258             return NULL;
1259         }
1260 
1261         if (PyBytes_GET_SIZE(line) == 0) {
1262             Py_DECREF(line);
1263             return res;
1264         }
1265 
1266         PyList_Append(res, line);
1267         Py_DECREF(line);
1268     }
1269 
1270     return res;
1271 }
1272 
1273 
1274 static PyObject *
1275 nxt_py_input_iter(PyObject *self)
1276 {
1277     Py_INCREF(self);
1278     return self;
1279 }
1280 
1281 
1282 static PyObject *
1283 nxt_py_input_next(PyObject *self)
1284 {
1285     PyObject          *line;
1286     nxt_python_ctx_t  *pctx;
1287 
1288     pctx = (nxt_python_ctx_t *) self;
1289     if (nxt_slow_path(pctx->req == NULL)) {
1290         return PyErr_Format(PyExc_RuntimeError,
1291                             "wsgi.input.next() is called "
1292                             "outside of WSGI request processing");
1293     }
1294 
1295     line = nxt_py_input_getline(pctx, SSIZE_MAX);
1296     if (nxt_slow_path(line == NULL)) {
1297         return NULL;
1298     }
1299 
1300     if (PyBytes_GET_SIZE(line) == 0) {
1301         Py_DECREF(line);
1302         PyErr_SetNone(PyExc_StopIteration);
1303         return NULL;
1304     }
1305 
1306     return line;
1307 }
1308 
1309 
1310 static int
1311 nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes)
1312 {
1313     int       rc;
1314     char      *str_buf;
1315     uint32_t  str_length;
1316 
1317     str_buf = PyBytes_AS_STRING(bytes);
1318     str_length = PyBytes_GET_SIZE(bytes);
1319 
1320     if (nxt_slow_path(str_length == 0)) {
1321         return NXT_UNIT_OK;
1322     }
1323 
1324     /*
1325      * PEP 3333:
1326      *
1327      * If the application supplies a Content-Length header, the server should
1328      * not transmit more bytes to the client than the header allows, and should
1329      * stop iterating over the response when enough data has been sent, or raise
1330      * an error if the application tries to write() past that point.
1331      */
1332     if (nxt_slow_path(str_length > pctx->content_length - pctx->bytes_sent)) {
1333         nxt_unit_req_error(pctx->req, "content length %"PRIu64" exceeded",
1334                            pctx->content_length);
1335 
1336         return NXT_UNIT_ERROR;
1337     }
1338 
1339     rc = nxt_unit_response_write(pctx->req, str_buf, str_length);
1340     if (nxt_fast_path(rc == NXT_UNIT_OK)) {
1341         pctx->bytes_sent += str_length;
1342     }
1343 
1344     return rc;
1345 }
1346