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