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