xref: /unit/src/python/nxt_python_wsgi.c (revision 2594:62b17d26306e)
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