xref: /unit/src/python/nxt_python_asgi.c (revision 2484:2df6640b0a60)
11624Smax.romanov@nginx.com 
21624Smax.romanov@nginx.com /*
31624Smax.romanov@nginx.com  * Copyright (C) NGINX, Inc.
41624Smax.romanov@nginx.com  */
51624Smax.romanov@nginx.com 
61624Smax.romanov@nginx.com 
71624Smax.romanov@nginx.com #include <python/nxt_python.h>
81624Smax.romanov@nginx.com 
91624Smax.romanov@nginx.com #if (NXT_HAVE_ASGI)
101624Smax.romanov@nginx.com 
111624Smax.romanov@nginx.com #include <nxt_main.h>
121624Smax.romanov@nginx.com #include <nxt_unit.h>
131624Smax.romanov@nginx.com #include <nxt_unit_request.h>
141624Smax.romanov@nginx.com #include <nxt_unit_response.h>
151624Smax.romanov@nginx.com #include <python/nxt_python_asgi.h>
161624Smax.romanov@nginx.com #include <python/nxt_python_asgi_str.h>
171624Smax.romanov@nginx.com 
181624Smax.romanov@nginx.com 
191697Smax.romanov@nginx.com static PyObject *nxt_python_asgi_get_func(PyObject *obj);
202315Sa.clayton@nginx.com static PyObject *nxt_python_asgi_get_event_loop(PyObject *asyncio,
212315Sa.clayton@nginx.com     const char *event_loop_func);
221918Smax.romanov@nginx.com static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main);
231681Smax.romanov@nginx.com static void nxt_python_asgi_ctx_data_free(void *data);
241681Smax.romanov@nginx.com static int nxt_python_asgi_startup(void *data);
251681Smax.romanov@nginx.com static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
261681Smax.romanov@nginx.com 
271681Smax.romanov@nginx.com static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
281681Smax.romanov@nginx.com     nxt_unit_port_t *port);
291624Smax.romanov@nginx.com static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);
301715Smax.romanov@nginx.com static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req);
311624Smax.romanov@nginx.com 
322273Sjeff.iadarola@gmail.com static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req,
332273Sjeff.iadarola@gmail.com     nxt_python_target_t *app_target);
341624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
351624Smax.romanov@nginx.com     uint16_t port);
362172Salx.manpages@gmail.com static PyObject *nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr,
372172Salx.manpages@gmail.com     uint8_t len, uint16_t port);
381624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f);
391624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f);
401624Smax.romanov@nginx.com 
411624Smax.romanov@nginx.com static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
421979Smax.romanov@nginx.com static int nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
431980Smax.romanov@nginx.com static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx,
441980Smax.romanov@nginx.com     nxt_unit_port_t *port);
451624Smax.romanov@nginx.com static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
461624Smax.romanov@nginx.com static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
471624Smax.romanov@nginx.com 
481624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
491681Smax.romanov@nginx.com static void nxt_python_asgi_done(void);
501624Smax.romanov@nginx.com 
511681Smax.romanov@nginx.com static PyObject           *nxt_py_port_read;
521624Smax.romanov@nginx.com 
531624Smax.romanov@nginx.com static PyMethodDef        nxt_py_port_read_method =
541624Smax.romanov@nginx.com     {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
551624Smax.romanov@nginx.com 
561681Smax.romanov@nginx.com static nxt_python_proto_t  nxt_py_asgi_proto = {
571681Smax.romanov@nginx.com     .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,
581681Smax.romanov@nginx.com     .ctx_data_free  = nxt_python_asgi_ctx_data_free,
591681Smax.romanov@nginx.com     .startup        = nxt_python_asgi_startup,
601681Smax.romanov@nginx.com     .run            = nxt_python_asgi_run,
611681Smax.romanov@nginx.com     .done           = nxt_python_asgi_done,
621681Smax.romanov@nginx.com };
631681Smax.romanov@nginx.com 
641624Smax.romanov@nginx.com #define NXT_UNIT_HASH_WS_PROTOCOL  0xED0A
651624Smax.romanov@nginx.com 
661624Smax.romanov@nginx.com 
671624Smax.romanov@nginx.com int
nxt_python_asgi_check(PyObject * obj)681624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj)
691624Smax.romanov@nginx.com {
701624Smax.romanov@nginx.com     int           res;
711697Smax.romanov@nginx.com     PyObject      *func;
721624Smax.romanov@nginx.com     PyCodeObject  *code;
731624Smax.romanov@nginx.com 
741697Smax.romanov@nginx.com     func = nxt_python_asgi_get_func(obj);
751697Smax.romanov@nginx.com 
761697Smax.romanov@nginx.com     if (func == NULL) {
771697Smax.romanov@nginx.com         return 0;
781697Smax.romanov@nginx.com     }
791697Smax.romanov@nginx.com 
801697Smax.romanov@nginx.com     code = (PyCodeObject *) PyFunction_GET_CODE(func);
811697Smax.romanov@nginx.com 
821697Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with "
831697Smax.romanov@nginx.com                          "%d argument(s)",
841697Smax.romanov@nginx.com                    (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ",
851697Smax.romanov@nginx.com                    code->co_argcount);
861697Smax.romanov@nginx.com 
871697Smax.romanov@nginx.com     res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1;
881697Smax.romanov@nginx.com 
891697Smax.romanov@nginx.com     Py_DECREF(func);
901697Smax.romanov@nginx.com 
911697Smax.romanov@nginx.com     return res;
921697Smax.romanov@nginx.com }
931697Smax.romanov@nginx.com 
941697Smax.romanov@nginx.com 
951697Smax.romanov@nginx.com static PyObject *
nxt_python_asgi_get_func(PyObject * obj)961697Smax.romanov@nginx.com nxt_python_asgi_get_func(PyObject *obj)
971697Smax.romanov@nginx.com {
981697Smax.romanov@nginx.com     PyObject  *call;
991697Smax.romanov@nginx.com 
1001624Smax.romanov@nginx.com     if (PyFunction_Check(obj)) {
1011697Smax.romanov@nginx.com         Py_INCREF(obj);
1021697Smax.romanov@nginx.com         return obj;
1031624Smax.romanov@nginx.com     }
1041624Smax.romanov@nginx.com 
1051624Smax.romanov@nginx.com     if (PyMethod_Check(obj)) {
1061624Smax.romanov@nginx.com         obj = PyMethod_GET_FUNCTION(obj);
1071624Smax.romanov@nginx.com 
1081697Smax.romanov@nginx.com         Py_INCREF(obj);
1091697Smax.romanov@nginx.com         return obj;
1101624Smax.romanov@nginx.com     }
1111624Smax.romanov@nginx.com 
1121624Smax.romanov@nginx.com     call = PyObject_GetAttrString(obj, "__call__");
1131624Smax.romanov@nginx.com 
1141624Smax.romanov@nginx.com     if (call == NULL) {
1151697Smax.romanov@nginx.com         return NULL;
1161624Smax.romanov@nginx.com     }
1171624Smax.romanov@nginx.com 
1181624Smax.romanov@nginx.com     if (PyFunction_Check(call)) {
1191697Smax.romanov@nginx.com         return call;
1201697Smax.romanov@nginx.com     }
1211624Smax.romanov@nginx.com 
1221697Smax.romanov@nginx.com     if (PyMethod_Check(call)) {
1231697Smax.romanov@nginx.com         obj = PyMethod_GET_FUNCTION(call);
1241624Smax.romanov@nginx.com 
1252067Smax.romanov@nginx.com         if (PyFunction_Check(obj)) {
1262067Smax.romanov@nginx.com             Py_INCREF(obj);
1271697Smax.romanov@nginx.com 
1282067Smax.romanov@nginx.com         } else {
1292067Smax.romanov@nginx.com             obj = NULL;
1302067Smax.romanov@nginx.com         }
1312067Smax.romanov@nginx.com 
1322067Smax.romanov@nginx.com     } else {
1332067Smax.romanov@nginx.com         obj = NULL;
1341624Smax.romanov@nginx.com     }
1351624Smax.romanov@nginx.com 
1361624Smax.romanov@nginx.com     Py_DECREF(call);
1371624Smax.romanov@nginx.com 
1382067Smax.romanov@nginx.com     return obj;
1391624Smax.romanov@nginx.com }
1401624Smax.romanov@nginx.com 
1411624Smax.romanov@nginx.com 
1421681Smax.romanov@nginx.com int
nxt_python_asgi_init(nxt_unit_init_t * init,nxt_python_proto_t * proto)1431681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
1441624Smax.romanov@nginx.com {
1451697Smax.romanov@nginx.com     PyObject      *func;
1461872So.canty@f5.com     nxt_int_t     i;
1471697Smax.romanov@nginx.com     PyCodeObject  *code;
1481697Smax.romanov@nginx.com 
1491681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_init");
1501624Smax.romanov@nginx.com 
1511681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
1521681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to init string objects");
1531681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1541624Smax.romanov@nginx.com     }
1551624Smax.romanov@nginx.com 
1561624Smax.romanov@nginx.com     nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);
1571624Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_port_read == NULL)) {
1581681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
1591681Smax.romanov@nginx.com                        "Python failed to initialize the 'port_read' function");
1601681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1611624Smax.romanov@nginx.com     }
1621624Smax.romanov@nginx.com 
1631681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
1641681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1651624Smax.romanov@nginx.com     }
1661624Smax.romanov@nginx.com 
1671681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
1681681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1691624Smax.romanov@nginx.com     }
1701624Smax.romanov@nginx.com 
1711872So.canty@f5.com     for (i = 0; i < nxt_py_targets->count; i++) {
1721872So.canty@f5.com         func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application);
1731872So.canty@f5.com         if (nxt_slow_path(func == NULL)) {
1742067Smax.romanov@nginx.com             nxt_unit_debug(NULL, "asgi: cannot find function for callable, "
1752068Smax.romanov@nginx.com                                  "unable to check for legacy mode (#%d)",
1762068Smax.romanov@nginx.com                                  (int) i);
1772067Smax.romanov@nginx.com             continue;
1781872So.canty@f5.com         }
1791697Smax.romanov@nginx.com 
1801872So.canty@f5.com         code = (PyCodeObject *) PyFunction_GET_CODE(func);
1811697Smax.romanov@nginx.com 
1821872So.canty@f5.com         if ((code->co_flags & CO_COROUTINE) == 0) {
1831872So.canty@f5.com             nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
1841872So.canty@f5.com                                  "switching to legacy mode");
1851872So.canty@f5.com             nxt_py_targets->target[i].asgi_legacy = 1;
1861872So.canty@f5.com         }
1871872So.canty@f5.com 
1881872So.canty@f5.com         Py_DECREF(func);
1891697Smax.romanov@nginx.com     }
1901697Smax.romanov@nginx.com 
1911624Smax.romanov@nginx.com     init->callbacks.request_handler = nxt_py_asgi_request_handler;
1921624Smax.romanov@nginx.com     init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
1931624Smax.romanov@nginx.com     init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
1941715Smax.romanov@nginx.com     init->callbacks.close_handler = nxt_py_asgi_close_handler;
1951624Smax.romanov@nginx.com     init->callbacks.quit = nxt_py_asgi_quit;
1961624Smax.romanov@nginx.com     init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
1971624Smax.romanov@nginx.com     init->callbacks.add_port = nxt_py_asgi_add_port;
1981624Smax.romanov@nginx.com     init->callbacks.remove_port = nxt_py_asgi_remove_port;
1991624Smax.romanov@nginx.com 
2001681Smax.romanov@nginx.com     *proto = nxt_py_asgi_proto;
2011681Smax.romanov@nginx.com 
2021681Smax.romanov@nginx.com     return NXT_UNIT_OK;
2031681Smax.romanov@nginx.com }
2041681Smax.romanov@nginx.com 
2051681Smax.romanov@nginx.com 
2062315Sa.clayton@nginx.com static PyObject *
nxt_python_asgi_get_event_loop(PyObject * asyncio,const char * event_loop_func)2072315Sa.clayton@nginx.com nxt_python_asgi_get_event_loop(PyObject *asyncio, const char *event_loop_func)
2082315Sa.clayton@nginx.com {
2092315Sa.clayton@nginx.com     PyObject  *event_loop, *loop;
2102315Sa.clayton@nginx.com 
2112315Sa.clayton@nginx.com     event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
2122315Sa.clayton@nginx.com                                       event_loop_func);
2132315Sa.clayton@nginx.com     if (nxt_slow_path(event_loop == NULL)) {
2142315Sa.clayton@nginx.com         nxt_unit_alert(NULL, "Python failed to get '%s' from module 'asyncio'",
2152315Sa.clayton@nginx.com                        event_loop_func);
2162315Sa.clayton@nginx.com         return NULL;
2172315Sa.clayton@nginx.com     }
2182315Sa.clayton@nginx.com 
2192315Sa.clayton@nginx.com     if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) {
2202315Sa.clayton@nginx.com         nxt_unit_alert(NULL, "'asyncio.%s' is not a callable object",
2212315Sa.clayton@nginx.com                        event_loop_func);
2222315Sa.clayton@nginx.com         return NULL;
2232315Sa.clayton@nginx.com     }
2242315Sa.clayton@nginx.com 
2252315Sa.clayton@nginx.com     loop = PyObject_CallObject(event_loop, NULL);
2262315Sa.clayton@nginx.com     if (nxt_slow_path(loop == NULL)) {
2272317Sa.clayton@nginx.com         if (strcmp(event_loop_func, "get_running_loop") != 0) {
2282317Sa.clayton@nginx.com             nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'",
2292317Sa.clayton@nginx.com                            event_loop_func);
2302317Sa.clayton@nginx.com         }
2312317Sa.clayton@nginx.com 
2322315Sa.clayton@nginx.com         return NULL;
2332315Sa.clayton@nginx.com     }
2342315Sa.clayton@nginx.com 
2352315Sa.clayton@nginx.com     return loop;
2362315Sa.clayton@nginx.com }
2372315Sa.clayton@nginx.com 
2382315Sa.clayton@nginx.com 
2391681Smax.romanov@nginx.com static int
nxt_python_asgi_ctx_data_alloc(void ** pdata,int main)2401918Smax.romanov@nginx.com nxt_python_asgi_ctx_data_alloc(void **pdata, int main)
2411681Smax.romanov@nginx.com {
2421681Smax.romanov@nginx.com     uint32_t                i;
2432315Sa.clayton@nginx.com     PyObject                *asyncio, *loop, *obj;
2441918Smax.romanov@nginx.com     const char              *event_loop_func;
2451681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
2461681Smax.romanov@nginx.com 
2472316Sa.clayton@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
2482316Sa.clayton@nginx.com     static const char       *main_event_loop_func = "get_event_loop";
2492316Sa.clayton@nginx.com #else
2502316Sa.clayton@nginx.com     static const char       *main_event_loop_func = "get_running_loop";
2512316Sa.clayton@nginx.com #endif
2522316Sa.clayton@nginx.com 
2531681Smax.romanov@nginx.com     ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
2541681Smax.romanov@nginx.com     if (nxt_slow_path(ctx_data == NULL)) {
2551681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate context data");
2561681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
2571681Smax.romanov@nginx.com     }
2581681Smax.romanov@nginx.com 
2591681Smax.romanov@nginx.com     memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t));
2601681Smax.romanov@nginx.com 
2611681Smax.romanov@nginx.com     nxt_queue_init(&ctx_data->drain_queue);
2621681Smax.romanov@nginx.com 
2631681Smax.romanov@nginx.com     struct {
2641681Smax.romanov@nginx.com         const char  *key;
2651681Smax.romanov@nginx.com         PyObject    **handler;
2661681Smax.romanov@nginx.com 
2671681Smax.romanov@nginx.com     } handlers[] = {
2681681Smax.romanov@nginx.com         { "create_task",        &ctx_data->loop_create_task },
2691681Smax.romanov@nginx.com         { "add_reader",         &ctx_data->loop_add_reader },
2701681Smax.romanov@nginx.com         { "remove_reader",      &ctx_data->loop_remove_reader },
2711681Smax.romanov@nginx.com         { "call_soon",          &ctx_data->loop_call_soon },
2721681Smax.romanov@nginx.com         { "run_until_complete", &ctx_data->loop_run_until_complete },
2731681Smax.romanov@nginx.com         { "create_future",      &ctx_data->loop_create_future },
2741681Smax.romanov@nginx.com     };
2751681Smax.romanov@nginx.com 
2761681Smax.romanov@nginx.com     loop = NULL;
2771681Smax.romanov@nginx.com 
2781681Smax.romanov@nginx.com     asyncio = PyImport_ImportModule("asyncio");
2791681Smax.romanov@nginx.com     if (nxt_slow_path(asyncio == NULL)) {
2801681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to import module 'asyncio'");
2811681Smax.romanov@nginx.com         nxt_python_print_exception();
2821681Smax.romanov@nginx.com         goto fail;
2831681Smax.romanov@nginx.com     }
2841681Smax.romanov@nginx.com 
2852316Sa.clayton@nginx.com     event_loop_func = main ? main_event_loop_func : "new_event_loop";
2861918Smax.romanov@nginx.com 
2872315Sa.clayton@nginx.com     loop = nxt_python_asgi_get_event_loop(asyncio, event_loop_func);
2882315Sa.clayton@nginx.com     if (loop == NULL) {
2892316Sa.clayton@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
2901681Smax.romanov@nginx.com         goto fail;
2912316Sa.clayton@nginx.com #else
2922316Sa.clayton@nginx.com         if (!main) {
2932316Sa.clayton@nginx.com             goto fail;
2942316Sa.clayton@nginx.com         }
2952316Sa.clayton@nginx.com 
2962316Sa.clayton@nginx.com         PyErr_Clear();
2972316Sa.clayton@nginx.com 
2982316Sa.clayton@nginx.com         loop = nxt_python_asgi_get_event_loop(asyncio, "new_event_loop");
2992316Sa.clayton@nginx.com         if (nxt_slow_path(loop == NULL)) {
3002316Sa.clayton@nginx.com             goto fail;
3012316Sa.clayton@nginx.com         }
3022316Sa.clayton@nginx.com #endif
3031681Smax.romanov@nginx.com     }
3041681Smax.romanov@nginx.com 
3051681Smax.romanov@nginx.com     for (i = 0; i < nxt_nitems(handlers); i++) {
3061681Smax.romanov@nginx.com         obj = PyObject_GetAttrString(loop, handlers[i].key);
3071681Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
3081681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Python failed to get 'loop.%s'",
3091681Smax.romanov@nginx.com                                  handlers[i].key);
3101681Smax.romanov@nginx.com             goto fail;
3111681Smax.romanov@nginx.com         }
3121681Smax.romanov@nginx.com 
3131681Smax.romanov@nginx.com         *handlers[i].handler = obj;
3141681Smax.romanov@nginx.com 
3151681Smax.romanov@nginx.com         if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
3161681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "'loop.%s' is not a callable object",
3171681Smax.romanov@nginx.com                                  handlers[i].key);
3181681Smax.romanov@nginx.com             goto fail;
3191681Smax.romanov@nginx.com         }
3201681Smax.romanov@nginx.com     }
3211681Smax.romanov@nginx.com 
3221681Smax.romanov@nginx.com     obj = PyObject_CallObject(ctx_data->loop_create_future, NULL);
3231681Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
3241681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Future ");
3251681Smax.romanov@nginx.com         nxt_python_print_exception();
3261681Smax.romanov@nginx.com         goto fail;
3271681Smax.romanov@nginx.com     }
3281681Smax.romanov@nginx.com 
3291681Smax.romanov@nginx.com     ctx_data->quit_future = obj;
3301681Smax.romanov@nginx.com 
3311681Smax.romanov@nginx.com     obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result");
3321681Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
3331681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get 'future.set_result'");
3341681Smax.romanov@nginx.com         goto fail;
3351681Smax.romanov@nginx.com     }
3361681Smax.romanov@nginx.com 
3371681Smax.romanov@nginx.com     ctx_data->quit_future_set_result = obj;
3381681Smax.romanov@nginx.com 
3391681Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
3401681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "'future.set_result' is not a callable object");
3411681Smax.romanov@nginx.com         goto fail;
3421681Smax.romanov@nginx.com     }
3431681Smax.romanov@nginx.com 
3441624Smax.romanov@nginx.com     Py_DECREF(loop);
3451624Smax.romanov@nginx.com     Py_DECREF(asyncio);
3461624Smax.romanov@nginx.com 
3471681Smax.romanov@nginx.com     *pdata = ctx_data;
3481681Smax.romanov@nginx.com 
3491681Smax.romanov@nginx.com     return NXT_UNIT_OK;
3501624Smax.romanov@nginx.com 
3511624Smax.romanov@nginx.com fail:
3521624Smax.romanov@nginx.com 
3531681Smax.romanov@nginx.com     nxt_python_asgi_ctx_data_free(ctx_data);
3541681Smax.romanov@nginx.com 
3551624Smax.romanov@nginx.com     Py_XDECREF(loop);
3561681Smax.romanov@nginx.com     Py_XDECREF(asyncio);
3571624Smax.romanov@nginx.com 
3581681Smax.romanov@nginx.com     return NXT_UNIT_ERROR;
3591624Smax.romanov@nginx.com }
3601624Smax.romanov@nginx.com 
3611624Smax.romanov@nginx.com 
3621681Smax.romanov@nginx.com static void
nxt_python_asgi_ctx_data_free(void * data)3631681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(void *data)
3641681Smax.romanov@nginx.com {
3651681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3661681Smax.romanov@nginx.com 
3671681Smax.romanov@nginx.com     ctx_data = data;
3681681Smax.romanov@nginx.com 
3691681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_run_until_complete);
3701681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_create_future);
3711681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_create_task);
3721681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_call_soon);
3731681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_add_reader);
3741681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_remove_reader);
3751681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->quit_future);
3761681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->quit_future_set_result);
3771681Smax.romanov@nginx.com 
3781681Smax.romanov@nginx.com     nxt_unit_free(NULL, ctx_data);
3791681Smax.romanov@nginx.com }
3801681Smax.romanov@nginx.com 
3811681Smax.romanov@nginx.com 
3821681Smax.romanov@nginx.com static int
nxt_python_asgi_startup(void * data)3831681Smax.romanov@nginx.com nxt_python_asgi_startup(void *data)
3841681Smax.romanov@nginx.com {
3851681Smax.romanov@nginx.com     return nxt_py_asgi_lifespan_startup(data);
3861681Smax.romanov@nginx.com }
3871681Smax.romanov@nginx.com 
3881681Smax.romanov@nginx.com 
3891681Smax.romanov@nginx.com static int
nxt_python_asgi_run(nxt_unit_ctx_t * ctx)3901624Smax.romanov@nginx.com nxt_python_asgi_run(nxt_unit_ctx_t *ctx)
3911624Smax.romanov@nginx.com {
3921681Smax.romanov@nginx.com     PyObject                *res;
3931681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3941624Smax.romanov@nginx.com 
3951681Smax.romanov@nginx.com     ctx_data = ctx->data;
3961681Smax.romanov@nginx.com 
3971681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
3981681Smax.romanov@nginx.com                                        ctx_data->quit_future, NULL);
3991624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
4001624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete");
4011624Smax.romanov@nginx.com         nxt_python_print_exception();
4021624Smax.romanov@nginx.com 
4031681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
4041624Smax.romanov@nginx.com     }
4051624Smax.romanov@nginx.com 
4061624Smax.romanov@nginx.com     Py_DECREF(res);
4071624Smax.romanov@nginx.com 
4081681Smax.romanov@nginx.com     nxt_py_asgi_lifespan_shutdown(ctx);
4091681Smax.romanov@nginx.com 
4101681Smax.romanov@nginx.com     return NXT_UNIT_OK;
4111681Smax.romanov@nginx.com }
4121681Smax.romanov@nginx.com 
4131624Smax.romanov@nginx.com 
4141681Smax.romanov@nginx.com static void
nxt_py_asgi_remove_reader(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)4151681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
4161681Smax.romanov@nginx.com {
4171682Smax.romanov@nginx.com     PyObject                *res, *fd;
4181681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
4191681Smax.romanov@nginx.com 
4201681Smax.romanov@nginx.com     if (port == NULL || port->in_fd == -1) {
4211681Smax.romanov@nginx.com         return;
4221681Smax.romanov@nginx.com     }
4231681Smax.romanov@nginx.com 
4241681Smax.romanov@nginx.com     ctx_data = ctx->data;
4251681Smax.romanov@nginx.com 
4261681Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port);
4271681Smax.romanov@nginx.com 
4281682Smax.romanov@nginx.com     fd = PyLong_FromLong(port->in_fd);
4291682Smax.romanov@nginx.com     if (nxt_slow_path(fd == NULL)) {
4301682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create Long object");
4311681Smax.romanov@nginx.com         nxt_python_print_exception();
4321681Smax.romanov@nginx.com 
4331681Smax.romanov@nginx.com         return;
4341681Smax.romanov@nginx.com     }
4351681Smax.romanov@nginx.com 
4361682Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL);
4371682Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
4381682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to remove_reader");
4391682Smax.romanov@nginx.com         nxt_python_print_exception();
4401682Smax.romanov@nginx.com 
4411682Smax.romanov@nginx.com     } else {
4421682Smax.romanov@nginx.com         Py_DECREF(res);
4431682Smax.romanov@nginx.com     }
4441682Smax.romanov@nginx.com 
4451682Smax.romanov@nginx.com     Py_DECREF(fd);
4461624Smax.romanov@nginx.com }
4471624Smax.romanov@nginx.com 
4481624Smax.romanov@nginx.com 
4491624Smax.romanov@nginx.com static void
nxt_py_asgi_request_handler(nxt_unit_request_info_t * req)4501624Smax.romanov@nginx.com nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
4511624Smax.romanov@nginx.com {
4521681Smax.romanov@nginx.com     PyObject                *scope, *res, *task, *receive, *send, *done, *asgi;
4532483Sdiguohuangjiajinweijun@gmail.com     PyObject                *state, *newstate, *lifespan;
4541697Smax.romanov@nginx.com     PyObject                *stage2;
4551872So.canty@f5.com     nxt_python_target_t     *target;
4561681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
4571624Smax.romanov@nginx.com 
4581624Smax.romanov@nginx.com     if (req->request->websocket_handshake) {
4591624Smax.romanov@nginx.com         asgi = nxt_py_asgi_websocket_create(req);
4601624Smax.romanov@nginx.com 
4611624Smax.romanov@nginx.com     } else {
4621624Smax.romanov@nginx.com         asgi = nxt_py_asgi_http_create(req);
4631624Smax.romanov@nginx.com     }
4641624Smax.romanov@nginx.com 
4651624Smax.romanov@nginx.com     if (nxt_slow_path(asgi == NULL)) {
4661624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create asgi object");
4671624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4681624Smax.romanov@nginx.com 
4691624Smax.romanov@nginx.com         return;
4701624Smax.romanov@nginx.com     }
4711624Smax.romanov@nginx.com 
4721624Smax.romanov@nginx.com     receive = PyObject_GetAttrString(asgi, "receive");
4731624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
4741624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get 'receive' method");
4751624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4761624Smax.romanov@nginx.com 
4771624Smax.romanov@nginx.com         goto release_asgi;
4781624Smax.romanov@nginx.com     }
4791624Smax.romanov@nginx.com 
4801624Smax.romanov@nginx.com     send = PyObject_GetAttrString(asgi, "send");
481*2484Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(send == NULL)) {
4821624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get 'send' method");
4831624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4841624Smax.romanov@nginx.com 
4851624Smax.romanov@nginx.com         goto release_receive;
4861624Smax.romanov@nginx.com     }
4871624Smax.romanov@nginx.com 
4881624Smax.romanov@nginx.com     done = PyObject_GetAttrString(asgi, "_done");
489*2484Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(done == NULL)) {
4901624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get '_done' method");
4911624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4921624Smax.romanov@nginx.com 
4931624Smax.romanov@nginx.com         goto release_send;
4941624Smax.romanov@nginx.com     }
4951624Smax.romanov@nginx.com 
4962273Sjeff.iadarola@gmail.com     req->data = asgi;
4972483Sdiguohuangjiajinweijun@gmail.com     ctx_data = req->ctx->data;
4982273Sjeff.iadarola@gmail.com     target = &nxt_py_targets->target[req->request->app_target];
4992483Sdiguohuangjiajinweijun@gmail.com     lifespan = ctx_data->target_lifespans[req->request->app_target];
5002483Sdiguohuangjiajinweijun@gmail.com     state = PyObject_GetAttr(lifespan, nxt_py_state_str);
5012483Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(state == NULL)) {
5022483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_req_alert(req, "Python failed to get 'state' attribute");
5032483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5042483Sdiguohuangjiajinweijun@gmail.com 
5052483Sdiguohuangjiajinweijun@gmail.com         goto release_done;
5062483Sdiguohuangjiajinweijun@gmail.com     }
5072483Sdiguohuangjiajinweijun@gmail.com 
5082483Sdiguohuangjiajinweijun@gmail.com     newstate = PyDict_Copy(state);
5092483Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(newstate == NULL)) {
5102483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_req_alert(req, "Python failed to call state.copy()");
5112483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5122483Sdiguohuangjiajinweijun@gmail.com         Py_DECREF(state);
5132483Sdiguohuangjiajinweijun@gmail.com         goto release_done;
5142483Sdiguohuangjiajinweijun@gmail.com     }
5152483Sdiguohuangjiajinweijun@gmail.com     Py_DECREF(state);
5162273Sjeff.iadarola@gmail.com 
5172273Sjeff.iadarola@gmail.com     scope = nxt_py_asgi_create_http_scope(req, target);
5181624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
5191624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5202483Sdiguohuangjiajinweijun@gmail.com         Py_DECREF(newstate);
5211624Smax.romanov@nginx.com         goto release_done;
5221624Smax.romanov@nginx.com     }
5231624Smax.romanov@nginx.com 
5242483Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str, newstate)
5252483Sdiguohuangjiajinweijun@gmail.com                       == -1))
5262483Sdiguohuangjiajinweijun@gmail.com     {
5272483Sdiguohuangjiajinweijun@gmail.com         Py_DECREF(newstate);
5282483Sdiguohuangjiajinweijun@gmail.com         goto release_scope;
5292483Sdiguohuangjiajinweijun@gmail.com     }
5302483Sdiguohuangjiajinweijun@gmail.com     Py_DECREF(newstate);
5312483Sdiguohuangjiajinweijun@gmail.com 
5321872So.canty@f5.com     if (!target->asgi_legacy) {
5331697Smax.romanov@nginx.com         nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
5341697Smax.romanov@nginx.com 
5351872So.canty@f5.com         res = PyObject_CallFunctionObjArgs(target->application,
5361697Smax.romanov@nginx.com                                            scope, receive, send, NULL);
5371697Smax.romanov@nginx.com 
5381697Smax.romanov@nginx.com     } else {
5391697Smax.romanov@nginx.com         nxt_unit_req_debug(req, "Python call legacy application");
5401697Smax.romanov@nginx.com 
5411872So.canty@f5.com         res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
5421697Smax.romanov@nginx.com 
5431697Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
5441697Smax.romanov@nginx.com             nxt_unit_req_error(req, "Python failed to call legacy app stage1");
5451697Smax.romanov@nginx.com             nxt_python_print_exception();
5461697Smax.romanov@nginx.com             nxt_unit_request_done(req, NXT_UNIT_ERROR);
5471697Smax.romanov@nginx.com 
5481697Smax.romanov@nginx.com             goto release_scope;
5491697Smax.romanov@nginx.com         }
5501697Smax.romanov@nginx.com 
5511697Smax.romanov@nginx.com         if (nxt_slow_path(PyCallable_Check(res) == 0)) {
5521697Smax.romanov@nginx.com             nxt_unit_req_error(req,
5531697Smax.romanov@nginx.com                               "Legacy ASGI application returns not a callable");
5541697Smax.romanov@nginx.com             nxt_unit_request_done(req, NXT_UNIT_ERROR);
5551697Smax.romanov@nginx.com 
5561697Smax.romanov@nginx.com             Py_DECREF(res);
5571697Smax.romanov@nginx.com 
5581697Smax.romanov@nginx.com             goto release_scope;
5591697Smax.romanov@nginx.com         }
5601697Smax.romanov@nginx.com 
5611697Smax.romanov@nginx.com         stage2 = res;
5621697Smax.romanov@nginx.com 
5631697Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
5641697Smax.romanov@nginx.com 
5651697Smax.romanov@nginx.com         Py_DECREF(stage2);
5661697Smax.romanov@nginx.com     }
5671697Smax.romanov@nginx.com 
5681624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
5691624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Python failed to call the application");
5701624Smax.romanov@nginx.com         nxt_python_print_exception();
5711624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5721624Smax.romanov@nginx.com 
5731624Smax.romanov@nginx.com         goto release_scope;
5741624Smax.romanov@nginx.com     }
5751624Smax.romanov@nginx.com 
5761624Smax.romanov@nginx.com     if (nxt_slow_path(!PyCoro_CheckExact(res))) {
5771624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Application result type is not a coroutine");
5781624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5791624Smax.romanov@nginx.com 
5801624Smax.romanov@nginx.com         Py_DECREF(res);
5811624Smax.romanov@nginx.com 
5821624Smax.romanov@nginx.com         goto release_scope;
5831624Smax.romanov@nginx.com     }
5841624Smax.romanov@nginx.com 
5851681Smax.romanov@nginx.com 
5861681Smax.romanov@nginx.com     task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
5871624Smax.romanov@nginx.com     if (nxt_slow_path(task == NULL)) {
5881624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Python failed to call the create_task");
5891624Smax.romanov@nginx.com         nxt_python_print_exception();
5901624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5911624Smax.romanov@nginx.com 
5921624Smax.romanov@nginx.com         Py_DECREF(res);
5931624Smax.romanov@nginx.com 
5941624Smax.romanov@nginx.com         goto release_scope;
5951624Smax.romanov@nginx.com     }
5961624Smax.romanov@nginx.com 
5971624Smax.romanov@nginx.com     Py_DECREF(res);
5981624Smax.romanov@nginx.com 
5991624Smax.romanov@nginx.com     res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done,
6001624Smax.romanov@nginx.com                                      NULL);
6011624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
6021624Smax.romanov@nginx.com         nxt_unit_req_error(req,
6031624Smax.romanov@nginx.com                            "Python failed to call 'task.add_done_callback'");
6041624Smax.romanov@nginx.com         nxt_python_print_exception();
6051624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
6061624Smax.romanov@nginx.com 
6071624Smax.romanov@nginx.com         goto release_task;
6081624Smax.romanov@nginx.com     }
6091624Smax.romanov@nginx.com 
6101624Smax.romanov@nginx.com     Py_DECREF(res);
6111624Smax.romanov@nginx.com release_task:
6121624Smax.romanov@nginx.com     Py_DECREF(task);
6131624Smax.romanov@nginx.com release_scope:
6141624Smax.romanov@nginx.com     Py_DECREF(scope);
6151624Smax.romanov@nginx.com release_done:
6161624Smax.romanov@nginx.com     Py_DECREF(done);
6171624Smax.romanov@nginx.com release_send:
6181624Smax.romanov@nginx.com     Py_DECREF(send);
6191624Smax.romanov@nginx.com release_receive:
6201624Smax.romanov@nginx.com     Py_DECREF(receive);
6211624Smax.romanov@nginx.com release_asgi:
6221624Smax.romanov@nginx.com     Py_DECREF(asgi);
6231624Smax.romanov@nginx.com }
6241624Smax.romanov@nginx.com 
6251624Smax.romanov@nginx.com 
6261715Smax.romanov@nginx.com static void
nxt_py_asgi_close_handler(nxt_unit_request_info_t * req)6271715Smax.romanov@nginx.com nxt_py_asgi_close_handler(nxt_unit_request_info_t *req)
6281715Smax.romanov@nginx.com {
6291715Smax.romanov@nginx.com     if (req->request->websocket_handshake) {
6301715Smax.romanov@nginx.com         nxt_py_asgi_websocket_close_handler(req);
6311715Smax.romanov@nginx.com 
6321715Smax.romanov@nginx.com     } else {
6331715Smax.romanov@nginx.com         nxt_py_asgi_http_close_handler(req);
6341715Smax.romanov@nginx.com     }
6351715Smax.romanov@nginx.com }
6361715Smax.romanov@nginx.com 
6371715Smax.romanov@nginx.com 
6381624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_create_http_scope(nxt_unit_request_info_t * req,nxt_python_target_t * app_target)6392273Sjeff.iadarola@gmail.com nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req,
6402273Sjeff.iadarola@gmail.com     nxt_python_target_t *app_target)
6411624Smax.romanov@nginx.com {
6421624Smax.romanov@nginx.com     char                *p, *target, *query;
6432273Sjeff.iadarola@gmail.com     uint32_t            target_length, i, path_length;
6441624Smax.romanov@nginx.com     PyObject            *scope, *v, *type, *scheme;
6451624Smax.romanov@nginx.com     PyObject            *headers, *header;
6462273Sjeff.iadarola@gmail.com     nxt_str_t           prefix;
6471624Smax.romanov@nginx.com     nxt_unit_field_t    *f;
6481624Smax.romanov@nginx.com     nxt_unit_request_t  *r;
6491624Smax.romanov@nginx.com 
6501624Smax.romanov@nginx.com     static const nxt_str_t  ws_protocol = nxt_string("sec-websocket-protocol");
6511624Smax.romanov@nginx.com 
6521624Smax.romanov@nginx.com #define SET_ITEM(dict, key, value) \
6531624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value)      \
6541624Smax.romanov@nginx.com                       == -1))                                                  \
6551624Smax.romanov@nginx.com     {                                                                          \
6561624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set '"                       \
6571624Smax.romanov@nginx.com                                 #dict "." #key "' item");                      \
6581624Smax.romanov@nginx.com         goto fail;                                                             \
6591624Smax.romanov@nginx.com     }
6601624Smax.romanov@nginx.com 
6611624Smax.romanov@nginx.com     v = NULL;
6621624Smax.romanov@nginx.com     headers = NULL;
6631624Smax.romanov@nginx.com 
6641624Smax.romanov@nginx.com     r = req->request;
6651624Smax.romanov@nginx.com 
6661624Smax.romanov@nginx.com     if (r->websocket_handshake) {
6671624Smax.romanov@nginx.com         type = nxt_py_websocket_str;
6681624Smax.romanov@nginx.com         scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str;
6691624Smax.romanov@nginx.com 
6701624Smax.romanov@nginx.com     } else {
6711624Smax.romanov@nginx.com         type = nxt_py_http_str;
6721624Smax.romanov@nginx.com         scheme = r->tls ? nxt_py_https_str : nxt_py_http_str;
6731624Smax.romanov@nginx.com     }
6741624Smax.romanov@nginx.com 
6751624Smax.romanov@nginx.com     scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str);
6761624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
6771624Smax.romanov@nginx.com         return NULL;
6781624Smax.romanov@nginx.com     }
6791624Smax.romanov@nginx.com 
6802273Sjeff.iadarola@gmail.com     prefix = app_target->prefix;
6812273Sjeff.iadarola@gmail.com     path_length = r->path_length;
6822273Sjeff.iadarola@gmail.com     p = nxt_unit_sptr_get(&r->path);
6832273Sjeff.iadarola@gmail.com     if (prefix.length > 0
6842273Sjeff.iadarola@gmail.com         && ((path_length > prefix.length && p[prefix.length] == '/')
6852273Sjeff.iadarola@gmail.com             || path_length == prefix.length)
6862273Sjeff.iadarola@gmail.com         && memcmp(prefix.start, p, prefix.length) == 0)
6872273Sjeff.iadarola@gmail.com     {
6882273Sjeff.iadarola@gmail.com         SET_ITEM(scope, root_path, app_target->py_prefix);
6892273Sjeff.iadarola@gmail.com     }
6902273Sjeff.iadarola@gmail.com 
6911624Smax.romanov@nginx.com     p = nxt_unit_sptr_get(&r->version);
6921624Smax.romanov@nginx.com     SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str
6931624Smax.romanov@nginx.com                                               : nxt_py_1_0_str)
6941624Smax.romanov@nginx.com     SET_ITEM(scope, scheme, scheme)
6951624Smax.romanov@nginx.com 
6961624Smax.romanov@nginx.com     v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method),
6971624Smax.romanov@nginx.com                                    r->method_length);
6981624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6991624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'method' string");
7001624Smax.romanov@nginx.com         goto fail;
7011624Smax.romanov@nginx.com     }
7021624Smax.romanov@nginx.com 
7031624Smax.romanov@nginx.com     SET_ITEM(scope, method, v)
7041624Smax.romanov@nginx.com     Py_DECREF(v);
7051624Smax.romanov@nginx.com 
7061624Smax.romanov@nginx.com     v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length,
7071624Smax.romanov@nginx.com                              "replace");
7081624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7091624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'path' string");
7101624Smax.romanov@nginx.com         goto fail;
7111624Smax.romanov@nginx.com     }
7121624Smax.romanov@nginx.com 
7131624Smax.romanov@nginx.com     SET_ITEM(scope, path, v)
7141624Smax.romanov@nginx.com     Py_DECREF(v);
7151624Smax.romanov@nginx.com 
7161624Smax.romanov@nginx.com     target = nxt_unit_sptr_get(&r->target);
7171624Smax.romanov@nginx.com     query = nxt_unit_sptr_get(&r->query);
7181624Smax.romanov@nginx.com 
7191624Smax.romanov@nginx.com     if (r->query.offset != 0) {
7201624Smax.romanov@nginx.com         target_length = query - target - 1;
7211624Smax.romanov@nginx.com 
7221624Smax.romanov@nginx.com     } else {
7231624Smax.romanov@nginx.com         target_length = r->target_length;
7241624Smax.romanov@nginx.com     }
7251624Smax.romanov@nginx.com 
7261624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(target, target_length);
7271624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7281624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'raw_path' string");
7291624Smax.romanov@nginx.com         goto fail;
7301624Smax.romanov@nginx.com     }
7311624Smax.romanov@nginx.com 
7321624Smax.romanov@nginx.com     SET_ITEM(scope, raw_path, v)
7331624Smax.romanov@nginx.com     Py_DECREF(v);
7341624Smax.romanov@nginx.com 
7351624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(query, r->query_length);
7361624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7371624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'query' string");
7381624Smax.romanov@nginx.com         goto fail;
7391624Smax.romanov@nginx.com     }
7401624Smax.romanov@nginx.com 
7411624Smax.romanov@nginx.com     SET_ITEM(scope, query_string, v)
7421624Smax.romanov@nginx.com     Py_DECREF(v);
7431624Smax.romanov@nginx.com 
7442176Szelenkov@nginx.com     v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0);
7451624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7461624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'client' pair");
7471624Smax.romanov@nginx.com         goto fail;
7481624Smax.romanov@nginx.com     }
7491624Smax.romanov@nginx.com 
7501624Smax.romanov@nginx.com     SET_ITEM(scope, client, v)
7511624Smax.romanov@nginx.com     Py_DECREF(v);
7521624Smax.romanov@nginx.com 
7532208Sa.clayton@nginx.com     v = nxt_py_asgi_create_address(&r->local_addr, r->local_addr_length, 80);
7541624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7551624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'server' pair");
7561624Smax.romanov@nginx.com         goto fail;
7571624Smax.romanov@nginx.com     }
7581624Smax.romanov@nginx.com 
7591624Smax.romanov@nginx.com     SET_ITEM(scope, server, v)
7601624Smax.romanov@nginx.com     Py_DECREF(v);
7611624Smax.romanov@nginx.com 
7621624Smax.romanov@nginx.com     v = NULL;
7631624Smax.romanov@nginx.com 
7641624Smax.romanov@nginx.com     headers = PyTuple_New(r->fields_count);
7651624Smax.romanov@nginx.com     if (nxt_slow_path(headers == NULL)) {
7661624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'headers' object");
7671624Smax.romanov@nginx.com         goto fail;
7681624Smax.romanov@nginx.com     }
7691624Smax.romanov@nginx.com 
7701624Smax.romanov@nginx.com     for (i = 0; i < r->fields_count; i++) {
7711624Smax.romanov@nginx.com         f = r->fields + i;
7721624Smax.romanov@nginx.com 
7731624Smax.romanov@nginx.com         header = nxt_py_asgi_create_header(f);
7741624Smax.romanov@nginx.com         if (nxt_slow_path(header == NULL)) {
7751624Smax.romanov@nginx.com             nxt_unit_req_alert(req, "Python failed to create 'header' pair");
7761624Smax.romanov@nginx.com             goto fail;
7771624Smax.romanov@nginx.com         }
7781624Smax.romanov@nginx.com 
7791624Smax.romanov@nginx.com         PyTuple_SET_ITEM(headers, i, header);
7801624Smax.romanov@nginx.com 
7811624Smax.romanov@nginx.com         if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL
7821624Smax.romanov@nginx.com             && f->name_length == ws_protocol.length
7831624Smax.romanov@nginx.com             && f->value_length > 0
7841624Smax.romanov@nginx.com             && r->websocket_handshake)
7851624Smax.romanov@nginx.com         {
7861624Smax.romanov@nginx.com             v = nxt_py_asgi_create_subprotocols(f);
7871624Smax.romanov@nginx.com             if (nxt_slow_path(v == NULL)) {
7881624Smax.romanov@nginx.com                 nxt_unit_req_alert(req, "Failed to create subprotocols");
7891624Smax.romanov@nginx.com                 goto fail;
7901624Smax.romanov@nginx.com             }
7911624Smax.romanov@nginx.com 
7921624Smax.romanov@nginx.com             SET_ITEM(scope, subprotocols, v);
7931624Smax.romanov@nginx.com             Py_DECREF(v);
7941624Smax.romanov@nginx.com         }
7951624Smax.romanov@nginx.com     }
7961624Smax.romanov@nginx.com 
7971624Smax.romanov@nginx.com     SET_ITEM(scope, headers, headers)
7981624Smax.romanov@nginx.com     Py_DECREF(headers);
7991624Smax.romanov@nginx.com 
8001624Smax.romanov@nginx.com     return scope;
8011624Smax.romanov@nginx.com 
8021624Smax.romanov@nginx.com fail:
8031624Smax.romanov@nginx.com 
8041624Smax.romanov@nginx.com     Py_XDECREF(v);
8051624Smax.romanov@nginx.com     Py_XDECREF(headers);
8061624Smax.romanov@nginx.com     Py_DECREF(scope);
8071624Smax.romanov@nginx.com 
8081624Smax.romanov@nginx.com     return NULL;
8091624Smax.romanov@nginx.com 
8101624Smax.romanov@nginx.com #undef SET_ITEM
8111624Smax.romanov@nginx.com }
8121624Smax.romanov@nginx.com 
8131624Smax.romanov@nginx.com 
8141624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_create_address(nxt_unit_sptr_t * sptr,uint8_t len,uint16_t port)8151624Smax.romanov@nginx.com nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
8161624Smax.romanov@nginx.com {
8172234Szelenkov@nginx.com #if (NXT_HAVE_UNIX_DOMAIN)
8182172Salx.manpages@gmail.com     size_t     prefix_len;
8192234Szelenkov@nginx.com     PyObject   *pair, *v;
8202172Salx.manpages@gmail.com     nxt_str_t  addr;
8212172Salx.manpages@gmail.com 
8222172Salx.manpages@gmail.com     addr.length = len;
8232172Salx.manpages@gmail.com     addr.start = nxt_unit_sptr_get(sptr);
8242172Salx.manpages@gmail.com 
8252172Salx.manpages@gmail.com     prefix_len = nxt_length("unix:");
8262234Szelenkov@nginx.com     if (nxt_str_start(&addr, "unix:", prefix_len)) {
8272234Szelenkov@nginx.com 
8282234Szelenkov@nginx.com         pair = PyTuple_New(2);
8292234Szelenkov@nginx.com         if (nxt_slow_path(pair == NULL)) {
8302234Szelenkov@nginx.com             return NULL;
8312234Szelenkov@nginx.com         }
8322234Szelenkov@nginx.com 
8332234Szelenkov@nginx.com         addr.start += prefix_len;
8342234Szelenkov@nginx.com         addr.length -= prefix_len;
8352172Salx.manpages@gmail.com 
8362234Szelenkov@nginx.com         v = PyString_FromStringAndSize((const char *) addr.start, addr.length);
8372234Szelenkov@nginx.com         if (nxt_slow_path(v == NULL)) {
8382234Szelenkov@nginx.com             Py_DECREF(pair);
8392234Szelenkov@nginx.com 
8402234Szelenkov@nginx.com             return NULL;
8412234Szelenkov@nginx.com         }
8422234Szelenkov@nginx.com 
8432234Szelenkov@nginx.com         PyTuple_SET_ITEM(pair, 0, v);
8442234Szelenkov@nginx.com         PyTuple_SET_ITEM(pair, 1, Py_None);
8452234Szelenkov@nginx.com 
8462234Szelenkov@nginx.com         return pair;
8472172Salx.manpages@gmail.com     }
8482172Salx.manpages@gmail.com 
8492172Salx.manpages@gmail.com #endif
8502234Szelenkov@nginx.com     return nxt_py_asgi_create_ip_address(sptr, len, port);
8512172Salx.manpages@gmail.com }
8522172Salx.manpages@gmail.com 
8532172Salx.manpages@gmail.com 
8542172Salx.manpages@gmail.com static PyObject *
nxt_py_asgi_create_ip_address(nxt_unit_sptr_t * sptr,uint8_t len,uint16_t port)8552172Salx.manpages@gmail.com nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
8562172Salx.manpages@gmail.com {
8572468Sa.clayton@nginx.com     char      *p;
8581624Smax.romanov@nginx.com     PyObject  *pair, *v;
8591624Smax.romanov@nginx.com 
8601624Smax.romanov@nginx.com     pair = PyTuple_New(2);
8611624Smax.romanov@nginx.com     if (nxt_slow_path(pair == NULL)) {
8621624Smax.romanov@nginx.com         return NULL;
8631624Smax.romanov@nginx.com     }
8641624Smax.romanov@nginx.com 
8651624Smax.romanov@nginx.com     p = nxt_unit_sptr_get(sptr);
8661624Smax.romanov@nginx.com 
8672468Sa.clayton@nginx.com     v = PyString_FromStringAndSize(p, len);
8681624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
8691624Smax.romanov@nginx.com         Py_DECREF(pair);
8701624Smax.romanov@nginx.com 
8711624Smax.romanov@nginx.com         return NULL;
8721624Smax.romanov@nginx.com     }
8731624Smax.romanov@nginx.com 
8741624Smax.romanov@nginx.com     PyTuple_SET_ITEM(pair, 0, v);
8751624Smax.romanov@nginx.com 
8762468Sa.clayton@nginx.com     v = PyLong_FromLong(port);
8771624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
8781624Smax.romanov@nginx.com         Py_DECREF(pair);
8791624Smax.romanov@nginx.com 
8801624Smax.romanov@nginx.com         return NULL;
8811624Smax.romanov@nginx.com     }
8821624Smax.romanov@nginx.com 
8831624Smax.romanov@nginx.com     PyTuple_SET_ITEM(pair, 1, v);
8841624Smax.romanov@nginx.com 
8851624Smax.romanov@nginx.com     return pair;
8861624Smax.romanov@nginx.com }
8871624Smax.romanov@nginx.com 
8881624Smax.romanov@nginx.com 
8891624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_create_header(nxt_unit_field_t * f)8901624Smax.romanov@nginx.com nxt_py_asgi_create_header(nxt_unit_field_t *f)
8911624Smax.romanov@nginx.com {
8921624Smax.romanov@nginx.com     char      c, *name;
8931624Smax.romanov@nginx.com     uint8_t   pos;
8941624Smax.romanov@nginx.com     PyObject  *header, *v;
8951624Smax.romanov@nginx.com 
8961624Smax.romanov@nginx.com     header = PyTuple_New(2);
8971624Smax.romanov@nginx.com     if (nxt_slow_path(header == NULL)) {
8981624Smax.romanov@nginx.com         return NULL;
8991624Smax.romanov@nginx.com     }
9001624Smax.romanov@nginx.com 
9011624Smax.romanov@nginx.com     name = nxt_unit_sptr_get(&f->name);
9021624Smax.romanov@nginx.com 
9031624Smax.romanov@nginx.com     for (pos = 0; pos < f->name_length; pos++) {
9041624Smax.romanov@nginx.com         c = name[pos];
9051624Smax.romanov@nginx.com         if (c >= 'A' && c <= 'Z') {
9061624Smax.romanov@nginx.com             name[pos] = (c | 0x20);
9071624Smax.romanov@nginx.com         }
9081624Smax.romanov@nginx.com     }
9091624Smax.romanov@nginx.com 
9101624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(name, f->name_length);
9111624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
9121624Smax.romanov@nginx.com         Py_DECREF(header);
9131624Smax.romanov@nginx.com 
9141624Smax.romanov@nginx.com         return NULL;
9151624Smax.romanov@nginx.com     }
9161624Smax.romanov@nginx.com 
9171624Smax.romanov@nginx.com     PyTuple_SET_ITEM(header, 0, v);
9181624Smax.romanov@nginx.com 
9191624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value),
9201624Smax.romanov@nginx.com                                   f->value_length);
9211624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
9221624Smax.romanov@nginx.com         Py_DECREF(header);
9231624Smax.romanov@nginx.com 
9241624Smax.romanov@nginx.com         return NULL;
9251624Smax.romanov@nginx.com     }
9261624Smax.romanov@nginx.com 
9271624Smax.romanov@nginx.com     PyTuple_SET_ITEM(header, 1, v);
9281624Smax.romanov@nginx.com 
9291624Smax.romanov@nginx.com     return header;
9301624Smax.romanov@nginx.com }
9311624Smax.romanov@nginx.com 
9321624Smax.romanov@nginx.com 
9331624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_create_subprotocols(nxt_unit_field_t * f)9341624Smax.romanov@nginx.com nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f)
9351624Smax.romanov@nginx.com {
9361624Smax.romanov@nginx.com     char      *v;
9371624Smax.romanov@nginx.com     uint32_t  i, n, start;
9381624Smax.romanov@nginx.com     PyObject  *res, *proto;
9391624Smax.romanov@nginx.com 
9401624Smax.romanov@nginx.com     v = nxt_unit_sptr_get(&f->value);
9411624Smax.romanov@nginx.com     n = 1;
9421624Smax.romanov@nginx.com 
9431624Smax.romanov@nginx.com     for (i = 0; i < f->value_length; i++) {
9441624Smax.romanov@nginx.com         if (v[i] == ',') {
9451624Smax.romanov@nginx.com             n++;
9461624Smax.romanov@nginx.com         }
9471624Smax.romanov@nginx.com     }
9481624Smax.romanov@nginx.com 
9491624Smax.romanov@nginx.com     res = PyTuple_New(n);
9501624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
9511624Smax.romanov@nginx.com         return NULL;
9521624Smax.romanov@nginx.com     }
9531624Smax.romanov@nginx.com 
9541624Smax.romanov@nginx.com     n = 0;
9551624Smax.romanov@nginx.com     start = 0;
9561624Smax.romanov@nginx.com 
9571624Smax.romanov@nginx.com     for (i = 0; i < f->value_length; ) {
9581624Smax.romanov@nginx.com         if (v[i] != ',') {
9591624Smax.romanov@nginx.com             i++;
9601624Smax.romanov@nginx.com 
9611624Smax.romanov@nginx.com             continue;
9621624Smax.romanov@nginx.com         }
9631624Smax.romanov@nginx.com 
9641624Smax.romanov@nginx.com         if (i - start > 0) {
9651624Smax.romanov@nginx.com             proto = PyString_FromStringAndSize(v + start, i - start);
9661624Smax.romanov@nginx.com             if (nxt_slow_path(proto == NULL)) {
9671624Smax.romanov@nginx.com                 goto fail;
9681624Smax.romanov@nginx.com             }
9691624Smax.romanov@nginx.com 
9701624Smax.romanov@nginx.com             PyTuple_SET_ITEM(res, n, proto);
9711624Smax.romanov@nginx.com 
9721624Smax.romanov@nginx.com             n++;
9731624Smax.romanov@nginx.com         }
9741624Smax.romanov@nginx.com 
9751624Smax.romanov@nginx.com         do {
9761624Smax.romanov@nginx.com             i++;
9771624Smax.romanov@nginx.com         } while (i < f->value_length && v[i] == ' ');
9781624Smax.romanov@nginx.com 
9791624Smax.romanov@nginx.com         start = i;
9801624Smax.romanov@nginx.com     }
9811624Smax.romanov@nginx.com 
9821624Smax.romanov@nginx.com     if (i - start > 0) {
9831624Smax.romanov@nginx.com         proto = PyString_FromStringAndSize(v + start, i - start);
9841624Smax.romanov@nginx.com         if (nxt_slow_path(proto == NULL)) {
9851624Smax.romanov@nginx.com             goto fail;
9861624Smax.romanov@nginx.com         }
9871624Smax.romanov@nginx.com 
9881624Smax.romanov@nginx.com         PyTuple_SET_ITEM(res, n, proto);
9891624Smax.romanov@nginx.com     }
9901624Smax.romanov@nginx.com 
9911624Smax.romanov@nginx.com     return res;
9921624Smax.romanov@nginx.com 
9931624Smax.romanov@nginx.com fail:
9941624Smax.romanov@nginx.com 
9951624Smax.romanov@nginx.com     Py_DECREF(res);
9961624Smax.romanov@nginx.com 
9971624Smax.romanov@nginx.com     return NULL;
9981624Smax.romanov@nginx.com }
9991624Smax.romanov@nginx.com 
10001624Smax.romanov@nginx.com 
10011624Smax.romanov@nginx.com static int
nxt_py_asgi_add_port(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)10021624Smax.romanov@nginx.com nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
10031624Smax.romanov@nginx.com {
10041980Smax.romanov@nginx.com     int  nb;
10051624Smax.romanov@nginx.com 
10061624Smax.romanov@nginx.com     if (port->in_fd == -1) {
10071624Smax.romanov@nginx.com         return NXT_UNIT_OK;
10081624Smax.romanov@nginx.com     }
10091624Smax.romanov@nginx.com 
10101624Smax.romanov@nginx.com     nb = 1;
10111624Smax.romanov@nginx.com 
10121624Smax.romanov@nginx.com     if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) {
10131624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)",
10141624Smax.romanov@nginx.com                        port->in_fd, strerror(errno), errno);
10151624Smax.romanov@nginx.com 
10161624Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
10171624Smax.romanov@nginx.com     }
10181624Smax.romanov@nginx.com 
10191624Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port);
10201624Smax.romanov@nginx.com 
10211979Smax.romanov@nginx.com     return nxt_py_asgi_add_reader(ctx, port);
10221979Smax.romanov@nginx.com }
10231979Smax.romanov@nginx.com 
10241681Smax.romanov@nginx.com 
10251979Smax.romanov@nginx.com static int
nxt_py_asgi_add_reader(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)10261979Smax.romanov@nginx.com nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
10271979Smax.romanov@nginx.com {
10281979Smax.romanov@nginx.com     int                     rc;
10291979Smax.romanov@nginx.com     PyObject                *res, *fd, *py_ctx, *py_port;
10301979Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
10311979Smax.romanov@nginx.com 
10321979Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_add_reader %d %p %p", port->in_fd, ctx, port);
10331979Smax.romanov@nginx.com 
10341979Smax.romanov@nginx.com     ctx_data = ctx->data;
10351682Smax.romanov@nginx.com 
10361682Smax.romanov@nginx.com     fd = PyLong_FromLong(port->in_fd);
10371682Smax.romanov@nginx.com     if (nxt_slow_path(fd == NULL)) {
10381682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create fd");
10391682Smax.romanov@nginx.com         nxt_python_print_exception();
10401682Smax.romanov@nginx.com 
10411979Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
10421682Smax.romanov@nginx.com     }
10431682Smax.romanov@nginx.com 
10441979Smax.romanov@nginx.com     rc = NXT_UNIT_ERROR;
10451979Smax.romanov@nginx.com 
10461682Smax.romanov@nginx.com     py_ctx = PyLong_FromVoidPtr(ctx);
10471682Smax.romanov@nginx.com     if (nxt_slow_path(py_ctx == NULL)) {
10481682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_ctx");
10491682Smax.romanov@nginx.com         nxt_python_print_exception();
10501682Smax.romanov@nginx.com 
10511682Smax.romanov@nginx.com         goto clean_fd;
10521682Smax.romanov@nginx.com     }
10531682Smax.romanov@nginx.com 
10541682Smax.romanov@nginx.com     py_port = PyLong_FromVoidPtr(port);
10551682Smax.romanov@nginx.com     if (nxt_slow_path(py_port == NULL)) {
10561682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_port");
10571682Smax.romanov@nginx.com         nxt_python_print_exception();
10581682Smax.romanov@nginx.com 
10591682Smax.romanov@nginx.com         goto clean_py_ctx;
10601682Smax.romanov@nginx.com     }
10611682Smax.romanov@nginx.com 
10621681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
10631682Smax.romanov@nginx.com                                        fd, nxt_py_port_read,
10641682Smax.romanov@nginx.com                                        py_ctx, py_port, NULL);
10651624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
10661624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to add_reader");
10671681Smax.romanov@nginx.com         nxt_python_print_exception();
10681624Smax.romanov@nginx.com 
10691682Smax.romanov@nginx.com     } else {
10701682Smax.romanov@nginx.com         Py_DECREF(res);
10711682Smax.romanov@nginx.com 
10721682Smax.romanov@nginx.com         rc = NXT_UNIT_OK;
10731624Smax.romanov@nginx.com     }
10741624Smax.romanov@nginx.com 
10751682Smax.romanov@nginx.com     Py_DECREF(py_port);
10761682Smax.romanov@nginx.com 
10771682Smax.romanov@nginx.com clean_py_ctx:
10781682Smax.romanov@nginx.com 
10791682Smax.romanov@nginx.com     Py_DECREF(py_ctx);
10801624Smax.romanov@nginx.com 
10811682Smax.romanov@nginx.com clean_fd:
10821682Smax.romanov@nginx.com 
10831682Smax.romanov@nginx.com     Py_DECREF(fd);
10841682Smax.romanov@nginx.com 
10851682Smax.romanov@nginx.com     return rc;
10861624Smax.romanov@nginx.com }
10871624Smax.romanov@nginx.com 
10881624Smax.romanov@nginx.com 
10891624Smax.romanov@nginx.com static void
nxt_py_asgi_remove_port(nxt_unit_t * lib,nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)10901980Smax.romanov@nginx.com nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx,
10911980Smax.romanov@nginx.com     nxt_unit_port_t *port)
10921624Smax.romanov@nginx.com {
10931980Smax.romanov@nginx.com     if (port->in_fd == -1 || ctx == NULL) {
10941624Smax.romanov@nginx.com         return;
10951624Smax.romanov@nginx.com     }
10961624Smax.romanov@nginx.com 
10971681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port);
10981681Smax.romanov@nginx.com 
10991980Smax.romanov@nginx.com     nxt_py_asgi_remove_reader(ctx, port);
11001624Smax.romanov@nginx.com }
11011624Smax.romanov@nginx.com 
11021624Smax.romanov@nginx.com 
11031624Smax.romanov@nginx.com static void
nxt_py_asgi_quit(nxt_unit_ctx_t * ctx)11041624Smax.romanov@nginx.com nxt_py_asgi_quit(nxt_unit_ctx_t *ctx)
11051624Smax.romanov@nginx.com {
11061682Smax.romanov@nginx.com     PyObject                *res, *p;
11071681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
11081624Smax.romanov@nginx.com 
11091624Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_quit %p", ctx);
11101624Smax.romanov@nginx.com 
11111681Smax.romanov@nginx.com     ctx_data = ctx->data;
11121681Smax.romanov@nginx.com 
11131682Smax.romanov@nginx.com     p = PyLong_FromLong(0);
11141682Smax.romanov@nginx.com     if (nxt_slow_path(p == NULL)) {
11151682Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Long");
11161682Smax.romanov@nginx.com         nxt_python_print_exception();
11171682Smax.romanov@nginx.com 
11181682Smax.romanov@nginx.com     } else {
11191682Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result,
11201682Smax.romanov@nginx.com                                            p, NULL);
11211681Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
11221682Smax.romanov@nginx.com             nxt_unit_alert(ctx, "Python failed to set_result");
11231681Smax.romanov@nginx.com             nxt_python_print_exception();
11241681Smax.romanov@nginx.com 
11251681Smax.romanov@nginx.com         } else {
11261681Smax.romanov@nginx.com             Py_DECREF(res);
11271681Smax.romanov@nginx.com         }
11281681Smax.romanov@nginx.com 
11291682Smax.romanov@nginx.com         Py_DECREF(p);
11301681Smax.romanov@nginx.com     }
11311624Smax.romanov@nginx.com }
11321624Smax.romanov@nginx.com 
11331624Smax.romanov@nginx.com 
11341624Smax.romanov@nginx.com static void
nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t * ctx)11351624Smax.romanov@nginx.com nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx)
11361624Smax.romanov@nginx.com {
11371681Smax.romanov@nginx.com     int                     rc;
11381681Smax.romanov@nginx.com     nxt_queue_link_t        *lnk;
11391681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
11401624Smax.romanov@nginx.com 
11411681Smax.romanov@nginx.com     ctx_data = ctx->data;
11421681Smax.romanov@nginx.com 
11431681Smax.romanov@nginx.com     while (!nxt_queue_is_empty(&ctx_data->drain_queue)) {
11441681Smax.romanov@nginx.com         lnk = nxt_queue_first(&ctx_data->drain_queue);
11451624Smax.romanov@nginx.com 
11461624Smax.romanov@nginx.com         rc = nxt_py_asgi_http_drain(lnk);
11471624Smax.romanov@nginx.com         if (rc == NXT_UNIT_AGAIN) {
11481681Smax.romanov@nginx.com             return;
11491624Smax.romanov@nginx.com         }
11501624Smax.romanov@nginx.com 
11511624Smax.romanov@nginx.com         nxt_queue_remove(lnk);
11521624Smax.romanov@nginx.com     }
11531624Smax.romanov@nginx.com }
11541624Smax.romanov@nginx.com 
11551624Smax.romanov@nginx.com 
11561624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_port_read(PyObject * self,PyObject * args)11571624Smax.romanov@nginx.com nxt_py_asgi_port_read(PyObject *self, PyObject *args)
11581624Smax.romanov@nginx.com {
11591767Smax.romanov@nginx.com     int                     rc;
11601767Smax.romanov@nginx.com     PyObject                *arg0, *arg1, *res;
11611767Smax.romanov@nginx.com     Py_ssize_t              n;
11621767Smax.romanov@nginx.com     nxt_unit_ctx_t          *ctx;
11631767Smax.romanov@nginx.com     nxt_unit_port_t         *port;
11641767Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
11651624Smax.romanov@nginx.com 
11661624Smax.romanov@nginx.com     n = PyTuple_GET_SIZE(args);
11671624Smax.romanov@nginx.com 
11681624Smax.romanov@nginx.com     if (n != 2) {
11691624Smax.romanov@nginx.com         nxt_unit_alert(NULL,
11701624Smax.romanov@nginx.com                        "nxt_py_asgi_port_read: invalid number of arguments %d",
11711624Smax.romanov@nginx.com                        (int) n);
11721624Smax.romanov@nginx.com 
11731624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
11741624Smax.romanov@nginx.com     }
11751624Smax.romanov@nginx.com 
11761767Smax.romanov@nginx.com     arg0 = PyTuple_GET_ITEM(args, 0);
11771767Smax.romanov@nginx.com     if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) {
11781624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
11791624Smax.romanov@nginx.com                             "the first argument is not a long");
11801624Smax.romanov@nginx.com     }
11811624Smax.romanov@nginx.com 
11821767Smax.romanov@nginx.com     ctx = PyLong_AsVoidPtr(arg0);
11831624Smax.romanov@nginx.com 
11841767Smax.romanov@nginx.com     arg1 = PyTuple_GET_ITEM(args, 1);
11851767Smax.romanov@nginx.com     if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) {
11861624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
11871624Smax.romanov@nginx.com                             "the second argument is not a long");
11881624Smax.romanov@nginx.com     }
11891624Smax.romanov@nginx.com 
11901767Smax.romanov@nginx.com     port = PyLong_AsVoidPtr(arg1);
11911624Smax.romanov@nginx.com 
11921624Smax.romanov@nginx.com     rc = nxt_unit_process_port_msg(ctx, port);
11931624Smax.romanov@nginx.com 
11941767Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc);
11951767Smax.romanov@nginx.com 
11961624Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
11971624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
11981681Smax.romanov@nginx.com                             "error processing port %d message", port->id.id);
11991624Smax.romanov@nginx.com     }
12001624Smax.romanov@nginx.com 
12011767Smax.romanov@nginx.com     if (rc == NXT_UNIT_OK) {
12021767Smax.romanov@nginx.com         ctx_data = ctx->data;
12031767Smax.romanov@nginx.com 
12041767Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon,
12051767Smax.romanov@nginx.com                                            nxt_py_port_read,
12061767Smax.romanov@nginx.com                                            arg0, arg1, NULL);
12071767Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
12081767Smax.romanov@nginx.com             nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'");
12091767Smax.romanov@nginx.com             nxt_python_print_exception();
12101767Smax.romanov@nginx.com         }
12111767Smax.romanov@nginx.com 
12121767Smax.romanov@nginx.com         Py_XDECREF(res);
12131767Smax.romanov@nginx.com     }
12141767Smax.romanov@nginx.com 
12151624Smax.romanov@nginx.com     Py_RETURN_NONE;
12161624Smax.romanov@nginx.com }
12171624Smax.romanov@nginx.com 
12181624Smax.romanov@nginx.com 
12191624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_enum_headers(PyObject * headers,nxt_py_asgi_enum_header_cb cb,void * data)12201624Smax.romanov@nginx.com nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb,
12211624Smax.romanov@nginx.com     void *data)
12221624Smax.romanov@nginx.com {
12231624Smax.romanov@nginx.com     int       i;
12241624Smax.romanov@nginx.com     PyObject  *iter, *header, *h_iter, *name, *val, *res;
12251624Smax.romanov@nginx.com 
12261624Smax.romanov@nginx.com     iter = PyObject_GetIter(headers);
12271624Smax.romanov@nginx.com     if (nxt_slow_path(iter == NULL)) {
12281624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable");
12291624Smax.romanov@nginx.com     }
12301624Smax.romanov@nginx.com 
12311624Smax.romanov@nginx.com     for (i = 0; /* void */; i++) {
12321624Smax.romanov@nginx.com         header = PyIter_Next(iter);
12331624Smax.romanov@nginx.com         if (header == NULL) {
12341624Smax.romanov@nginx.com             break;
12351624Smax.romanov@nginx.com         }
12361624Smax.romanov@nginx.com 
12371624Smax.romanov@nginx.com         h_iter = PyObject_GetIter(header);
12381624Smax.romanov@nginx.com         if (nxt_slow_path(h_iter == NULL)) {
12391624Smax.romanov@nginx.com             Py_DECREF(header);
12401624Smax.romanov@nginx.com             Py_DECREF(iter);
12411624Smax.romanov@nginx.com 
12421624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
12431624Smax.romanov@nginx.com                                 "'headers' item #%d is not an iterable", i);
12441624Smax.romanov@nginx.com         }
12451624Smax.romanov@nginx.com 
12461624Smax.romanov@nginx.com         name = PyIter_Next(h_iter);
12471624Smax.romanov@nginx.com         if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) {
12481624Smax.romanov@nginx.com             Py_XDECREF(name);
12491624Smax.romanov@nginx.com             Py_DECREF(h_iter);
12501624Smax.romanov@nginx.com             Py_DECREF(header);
12511624Smax.romanov@nginx.com             Py_DECREF(iter);
12521624Smax.romanov@nginx.com 
12531624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
12541624Smax.romanov@nginx.com                           "'headers' item #%d 'name' is not a byte string", i);
12551624Smax.romanov@nginx.com         }
12561624Smax.romanov@nginx.com 
12571624Smax.romanov@nginx.com         val = PyIter_Next(h_iter);
12581624Smax.romanov@nginx.com         if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) {
12591624Smax.romanov@nginx.com             Py_XDECREF(val);
12601624Smax.romanov@nginx.com             Py_DECREF(h_iter);
12611624Smax.romanov@nginx.com             Py_DECREF(header);
12621624Smax.romanov@nginx.com             Py_DECREF(iter);
12631624Smax.romanov@nginx.com 
12641624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
12651624Smax.romanov@nginx.com                          "'headers' item #%d 'value' is not a byte string", i);
12661624Smax.romanov@nginx.com         }
12671624Smax.romanov@nginx.com 
12681624Smax.romanov@nginx.com         res = cb(data, i, name, val);
12691624Smax.romanov@nginx.com 
12701624Smax.romanov@nginx.com         Py_DECREF(name);
12711624Smax.romanov@nginx.com         Py_DECREF(val);
12721624Smax.romanov@nginx.com         Py_DECREF(h_iter);
12731624Smax.romanov@nginx.com         Py_DECREF(header);
12741624Smax.romanov@nginx.com 
12751624Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
12761624Smax.romanov@nginx.com             Py_DECREF(iter);
12771624Smax.romanov@nginx.com 
12781624Smax.romanov@nginx.com             return NULL;
12791624Smax.romanov@nginx.com         }
12801624Smax.romanov@nginx.com 
12811624Smax.romanov@nginx.com         Py_DECREF(res);
12821624Smax.romanov@nginx.com     }
12831624Smax.romanov@nginx.com 
12841624Smax.romanov@nginx.com     Py_DECREF(iter);
12851624Smax.romanov@nginx.com 
12861624Smax.romanov@nginx.com     Py_RETURN_NONE;
12871624Smax.romanov@nginx.com }
12881624Smax.romanov@nginx.com 
12891624Smax.romanov@nginx.com 
12901624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_calc_size(void * data,int i,PyObject * name,PyObject * val)12911624Smax.romanov@nginx.com nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val)
12921624Smax.romanov@nginx.com {
12931624Smax.romanov@nginx.com     nxt_py_asgi_calc_size_ctx_t  *ctx;
12941624Smax.romanov@nginx.com 
12951624Smax.romanov@nginx.com     ctx = data;
12961624Smax.romanov@nginx.com 
12971624Smax.romanov@nginx.com     ctx->fields_count++;
12981624Smax.romanov@nginx.com     ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val);
12991624Smax.romanov@nginx.com 
13001624Smax.romanov@nginx.com     Py_RETURN_NONE;
13011624Smax.romanov@nginx.com }
13021624Smax.romanov@nginx.com 
13031624Smax.romanov@nginx.com 
13041624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_add_field(void * data,int i,PyObject * name,PyObject * val)13051624Smax.romanov@nginx.com nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val)
13061624Smax.romanov@nginx.com {
13071624Smax.romanov@nginx.com     int                          rc;
13081624Smax.romanov@nginx.com     char                         *name_str, *val_str;
13091624Smax.romanov@nginx.com     uint32_t                     name_len, val_len;
13101624Smax.romanov@nginx.com     nxt_off_t                    content_length;
13111624Smax.romanov@nginx.com     nxt_unit_request_info_t      *req;
13121624Smax.romanov@nginx.com     nxt_py_asgi_add_field_ctx_t  *ctx;
13131624Smax.romanov@nginx.com 
13141624Smax.romanov@nginx.com     name_str = PyBytes_AS_STRING(name);
13151624Smax.romanov@nginx.com     name_len = PyBytes_GET_SIZE(name);
13161624Smax.romanov@nginx.com 
13171624Smax.romanov@nginx.com     val_str = PyBytes_AS_STRING(val);
13181624Smax.romanov@nginx.com     val_len = PyBytes_GET_SIZE(val);
13191624Smax.romanov@nginx.com 
13201624Smax.romanov@nginx.com     ctx = data;
13211624Smax.romanov@nginx.com     req = ctx->req;
13221624Smax.romanov@nginx.com 
13231624Smax.romanov@nginx.com     rc = nxt_unit_response_add_field(req, name_str, name_len,
13241624Smax.romanov@nginx.com                                      val_str, val_len);
13251624Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
13261624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
13271624Smax.romanov@nginx.com                             "failed to add header #%d", i);
13281624Smax.romanov@nginx.com     }
13291624Smax.romanov@nginx.com 
13301624Smax.romanov@nginx.com     if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
13311624Smax.romanov@nginx.com         content_length = nxt_off_t_parse((u_char *) val_str, val_len);
13321624Smax.romanov@nginx.com         if (nxt_slow_path(content_length < 0)) {
13331624Smax.romanov@nginx.com             nxt_unit_req_error(req, "failed to parse Content-Length "
13341624Smax.romanov@nginx.com                                "value %.*s", (int) val_len, val_str);
13351624Smax.romanov@nginx.com 
13361624Smax.romanov@nginx.com             return PyErr_Format(PyExc_ValueError,
13371624Smax.romanov@nginx.com                                 "Failed to parse Content-Length: '%.*s'",
13381624Smax.romanov@nginx.com                                 (int) val_len, val_str);
13391624Smax.romanov@nginx.com         }
13401624Smax.romanov@nginx.com 
13411624Smax.romanov@nginx.com         ctx->content_length = content_length;
13421624Smax.romanov@nginx.com     }
13431624Smax.romanov@nginx.com 
13441624Smax.romanov@nginx.com     Py_RETURN_NONE;
13451624Smax.romanov@nginx.com }
13461624Smax.romanov@nginx.com 
13471624Smax.romanov@nginx.com 
13481624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_set_result_soon(nxt_unit_request_info_t * req,nxt_py_asgi_ctx_data_t * ctx_data,PyObject * future,PyObject * result)13491681Smax.romanov@nginx.com nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
13501681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result)
13511624Smax.romanov@nginx.com {
13521624Smax.romanov@nginx.com     PyObject  *set_result, *res;
13531624Smax.romanov@nginx.com 
13541624Smax.romanov@nginx.com     if (nxt_slow_path(result == NULL)) {
13551624Smax.romanov@nginx.com         Py_DECREF(future);
13561624Smax.romanov@nginx.com 
13571624Smax.romanov@nginx.com         return NULL;
13581624Smax.romanov@nginx.com     }
13591624Smax.romanov@nginx.com 
13601624Smax.romanov@nginx.com     set_result = PyObject_GetAttrString(future, "set_result");
13611624Smax.romanov@nginx.com     if (nxt_slow_path(set_result == NULL)) {
13621624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "failed to get 'set_result' for future");
13631624Smax.romanov@nginx.com 
13641624Smax.romanov@nginx.com         Py_CLEAR(future);
13651624Smax.romanov@nginx.com 
13661681Smax.romanov@nginx.com         goto cleanup_result;
13671624Smax.romanov@nginx.com     }
13681624Smax.romanov@nginx.com 
13691624Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(set_result) == 0)) {
13701624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "'future.set_result' is not a callable");
13711624Smax.romanov@nginx.com 
13721624Smax.romanov@nginx.com         Py_CLEAR(future);
13731624Smax.romanov@nginx.com 
13741624Smax.romanov@nginx.com         goto cleanup;
13751624Smax.romanov@nginx.com     }
13761624Smax.romanov@nginx.com 
13771681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result,
13781624Smax.romanov@nginx.com                                        result, NULL);
13791624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
13801624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'");
13811624Smax.romanov@nginx.com         nxt_python_print_exception();
13821624Smax.romanov@nginx.com 
13831624Smax.romanov@nginx.com         Py_CLEAR(future);
13841624Smax.romanov@nginx.com     }
13851624Smax.romanov@nginx.com 
13861624Smax.romanov@nginx.com     Py_XDECREF(res);
13871624Smax.romanov@nginx.com 
13881624Smax.romanov@nginx.com cleanup:
13891624Smax.romanov@nginx.com 
13901624Smax.romanov@nginx.com     Py_DECREF(set_result);
13911681Smax.romanov@nginx.com 
13921681Smax.romanov@nginx.com cleanup_result:
13931681Smax.romanov@nginx.com 
13941624Smax.romanov@nginx.com     Py_DECREF(result);
13951624Smax.romanov@nginx.com 
13961624Smax.romanov@nginx.com     return future;
13971624Smax.romanov@nginx.com }
13981624Smax.romanov@nginx.com 
13991624Smax.romanov@nginx.com 
14001624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_new_msg(nxt_unit_request_info_t * req,PyObject * type)14011624Smax.romanov@nginx.com nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type)
14021624Smax.romanov@nginx.com {
14031624Smax.romanov@nginx.com     PyObject  *msg;
14041624Smax.romanov@nginx.com 
14051624Smax.romanov@nginx.com     msg = PyDict_New();
14061624Smax.romanov@nginx.com     if (nxt_slow_path(msg == NULL)) {
14071624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create message dict");
14081624Smax.romanov@nginx.com         nxt_python_print_exception();
14091624Smax.romanov@nginx.com 
14101624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14111624Smax.romanov@nginx.com                             "failed to create message dict");
14121624Smax.romanov@nginx.com     }
14131624Smax.romanov@nginx.com 
14141624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) {
14151624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'msg.type' item");
14161624Smax.romanov@nginx.com 
14171624Smax.romanov@nginx.com         Py_DECREF(msg);
14181624Smax.romanov@nginx.com 
14191624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14201624Smax.romanov@nginx.com                             "failed to set 'msg.type' item");
14211624Smax.romanov@nginx.com     }
14221624Smax.romanov@nginx.com 
14231624Smax.romanov@nginx.com     return msg;
14241624Smax.romanov@nginx.com }
14251624Smax.romanov@nginx.com 
14261624Smax.romanov@nginx.com 
14271624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_new_scope(nxt_unit_request_info_t * req,PyObject * type,PyObject * spec_version)14281624Smax.romanov@nginx.com nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
14291624Smax.romanov@nginx.com     PyObject *spec_version)
14301624Smax.romanov@nginx.com {
14311624Smax.romanov@nginx.com     PyObject  *scope, *asgi;
14321624Smax.romanov@nginx.com 
14331624Smax.romanov@nginx.com     scope = PyDict_New();
14341624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
14351624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'scope' dict");
14361624Smax.romanov@nginx.com         nxt_python_print_exception();
14371624Smax.romanov@nginx.com 
14381624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14391624Smax.romanov@nginx.com                             "failed to create 'scope' dict");
14401624Smax.romanov@nginx.com     }
14411624Smax.romanov@nginx.com 
14421624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) {
14431624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'scope.type' item");
14441624Smax.romanov@nginx.com 
14451624Smax.romanov@nginx.com         Py_DECREF(scope);
14461624Smax.romanov@nginx.com 
14471624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14481624Smax.romanov@nginx.com                             "failed to set 'scope.type' item");
14491624Smax.romanov@nginx.com     }
14501624Smax.romanov@nginx.com 
14511624Smax.romanov@nginx.com     asgi = PyDict_New();
14521624Smax.romanov@nginx.com     if (nxt_slow_path(asgi == NULL)) {
14531624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'asgi' dict");
14541624Smax.romanov@nginx.com         nxt_python_print_exception();
14551624Smax.romanov@nginx.com 
14561624Smax.romanov@nginx.com         Py_DECREF(scope);
14571624Smax.romanov@nginx.com 
14581624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14591624Smax.romanov@nginx.com                             "failed to create 'asgi' dict");
14601624Smax.romanov@nginx.com     }
14611624Smax.romanov@nginx.com 
14621624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) {
14631624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item");
14641624Smax.romanov@nginx.com 
14651624Smax.romanov@nginx.com         Py_DECREF(asgi);
14661624Smax.romanov@nginx.com         Py_DECREF(scope);
14671624Smax.romanov@nginx.com 
14681624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14691624Smax.romanov@nginx.com                             "failed to set 'scope.asgi' item");
14701624Smax.romanov@nginx.com     }
14711624Smax.romanov@nginx.com 
14721624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str,
14731624Smax.romanov@nginx.com                                      nxt_py_3_0_str) == -1))
14741624Smax.romanov@nginx.com     {
14751624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item");
14761624Smax.romanov@nginx.com 
14771624Smax.romanov@nginx.com         Py_DECREF(asgi);
14781624Smax.romanov@nginx.com         Py_DECREF(scope);
14791624Smax.romanov@nginx.com 
14801624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14811624Smax.romanov@nginx.com                             "failed to set 'asgi.version' item");
14821624Smax.romanov@nginx.com     }
14831624Smax.romanov@nginx.com 
14841624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str,
14851624Smax.romanov@nginx.com                                      spec_version) == -1))
14861624Smax.romanov@nginx.com     {
14871624Smax.romanov@nginx.com         nxt_unit_req_alert(req,
14881624Smax.romanov@nginx.com                            "Python failed to set 'asgi.spec_version' item");
14891624Smax.romanov@nginx.com 
14901624Smax.romanov@nginx.com         Py_DECREF(asgi);
14911624Smax.romanov@nginx.com         Py_DECREF(scope);
14921624Smax.romanov@nginx.com 
14931624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14941624Smax.romanov@nginx.com                             "failed to set 'asgi.spec_version' item");
14951624Smax.romanov@nginx.com     }
14961624Smax.romanov@nginx.com 
14971624Smax.romanov@nginx.com     Py_DECREF(asgi);
14981624Smax.romanov@nginx.com 
14991624Smax.romanov@nginx.com     return scope;
15001624Smax.romanov@nginx.com }
15011624Smax.romanov@nginx.com 
15021624Smax.romanov@nginx.com 
15031624Smax.romanov@nginx.com void
nxt_py_asgi_drain_wait(nxt_unit_request_info_t * req,nxt_queue_link_t * link)15041681Smax.romanov@nginx.com nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link)
15051681Smax.romanov@nginx.com {
15061681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
15071681Smax.romanov@nginx.com 
15081681Smax.romanov@nginx.com     ctx_data = req->ctx->data;
15091681Smax.romanov@nginx.com 
15101681Smax.romanov@nginx.com     nxt_queue_insert_tail(&ctx_data->drain_queue, link);
15111681Smax.romanov@nginx.com }
15121681Smax.romanov@nginx.com 
15131681Smax.romanov@nginx.com 
15141681Smax.romanov@nginx.com void
nxt_py_asgi_dealloc(PyObject * self)15151624Smax.romanov@nginx.com nxt_py_asgi_dealloc(PyObject *self)
15161624Smax.romanov@nginx.com {
15171624Smax.romanov@nginx.com     PyObject_Del(self);
15181624Smax.romanov@nginx.com }
15191624Smax.romanov@nginx.com 
15201624Smax.romanov@nginx.com 
15211624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_await(PyObject * self)15221624Smax.romanov@nginx.com nxt_py_asgi_await(PyObject *self)
15231624Smax.romanov@nginx.com {
15241624Smax.romanov@nginx.com     Py_INCREF(self);
15251624Smax.romanov@nginx.com     return self;
15261624Smax.romanov@nginx.com }
15271624Smax.romanov@nginx.com 
15281624Smax.romanov@nginx.com 
15291624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_iter(PyObject * self)15301624Smax.romanov@nginx.com nxt_py_asgi_iter(PyObject *self)
15311624Smax.romanov@nginx.com {
15321624Smax.romanov@nginx.com     Py_INCREF(self);
15331624Smax.romanov@nginx.com     return self;
15341624Smax.romanov@nginx.com }
15351624Smax.romanov@nginx.com 
15361624Smax.romanov@nginx.com 
15371624Smax.romanov@nginx.com PyObject *
nxt_py_asgi_next(PyObject * self)15381624Smax.romanov@nginx.com nxt_py_asgi_next(PyObject *self)
15391624Smax.romanov@nginx.com {
15401624Smax.romanov@nginx.com     return NULL;
15411624Smax.romanov@nginx.com }
15421624Smax.romanov@nginx.com 
15431624Smax.romanov@nginx.com 
15441681Smax.romanov@nginx.com static void
nxt_python_asgi_done(void)15451624Smax.romanov@nginx.com nxt_python_asgi_done(void)
15461624Smax.romanov@nginx.com {
15471624Smax.romanov@nginx.com     nxt_py_asgi_str_done();
15481624Smax.romanov@nginx.com 
15491624Smax.romanov@nginx.com     Py_XDECREF(nxt_py_port_read);
15501624Smax.romanov@nginx.com }
15511624Smax.romanov@nginx.com 
15521624Smax.romanov@nginx.com #else /* !(NXT_HAVE_ASGI) */
15531624Smax.romanov@nginx.com 
15541624Smax.romanov@nginx.com 
15551624Smax.romanov@nginx.com int
nxt_python_asgi_check(PyObject * obj)15561624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj)
15571624Smax.romanov@nginx.com {
15581624Smax.romanov@nginx.com     return 0;
15591624Smax.romanov@nginx.com }
15601624Smax.romanov@nginx.com 
15611624Smax.romanov@nginx.com 
15621681Smax.romanov@nginx.com int
nxt_python_asgi_init(nxt_unit_init_t * init,nxt_python_proto_t * proto)15631681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
15641624Smax.romanov@nginx.com {
15651681Smax.romanov@nginx.com     nxt_unit_alert(NULL, "ASGI not implemented");
15661681Smax.romanov@nginx.com     return NXT_UNIT_ERROR;
15671624Smax.romanov@nginx.com }
15681624Smax.romanov@nginx.com 
15691624Smax.romanov@nginx.com 
15701624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */
1571