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