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