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