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