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