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