xref: /unit/src/python/nxt_python_asgi.c (revision 1697)
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 
19*1697Smax.romanov@nginx.com static PyObject *nxt_python_asgi_get_func(PyObject *obj);
201681Smax.romanov@nginx.com static int nxt_python_asgi_ctx_data_alloc(void **pdata);
211681Smax.romanov@nginx.com static void nxt_python_asgi_ctx_data_free(void *data);
221681Smax.romanov@nginx.com static int nxt_python_asgi_startup(void *data);
231681Smax.romanov@nginx.com static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
241681Smax.romanov@nginx.com 
251681Smax.romanov@nginx.com static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
261681Smax.romanov@nginx.com     nxt_unit_port_t *port);
271624Smax.romanov@nginx.com static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);
281624Smax.romanov@nginx.com 
291624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req);
301624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
311624Smax.romanov@nginx.com     uint16_t port);
321624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f);
331624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f);
341624Smax.romanov@nginx.com 
351681Smax.romanov@nginx.com static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx);
361681Smax.romanov@nginx.com 
371624Smax.romanov@nginx.com static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
381624Smax.romanov@nginx.com static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port);
391624Smax.romanov@nginx.com static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
401624Smax.romanov@nginx.com static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
411624Smax.romanov@nginx.com 
421624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
431681Smax.romanov@nginx.com static void nxt_python_asgi_done(void);
441624Smax.romanov@nginx.com 
451624Smax.romanov@nginx.com 
46*1697Smax.romanov@nginx.com int                       nxt_py_asgi_legacy;
471681Smax.romanov@nginx.com static PyObject           *nxt_py_port_read;
481681Smax.romanov@nginx.com static nxt_unit_port_t    *nxt_py_shared_port;
491624Smax.romanov@nginx.com 
501624Smax.romanov@nginx.com static PyMethodDef        nxt_py_port_read_method =
511624Smax.romanov@nginx.com     {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
521624Smax.romanov@nginx.com 
531681Smax.romanov@nginx.com static nxt_python_proto_t  nxt_py_asgi_proto = {
541681Smax.romanov@nginx.com     .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,
551681Smax.romanov@nginx.com     .ctx_data_free  = nxt_python_asgi_ctx_data_free,
561681Smax.romanov@nginx.com     .startup        = nxt_python_asgi_startup,
571681Smax.romanov@nginx.com     .run            = nxt_python_asgi_run,
581681Smax.romanov@nginx.com     .ready          = nxt_python_asgi_ready,
591681Smax.romanov@nginx.com     .done           = nxt_python_asgi_done,
601681Smax.romanov@nginx.com };
611681Smax.romanov@nginx.com 
621624Smax.romanov@nginx.com #define NXT_UNIT_HASH_WS_PROTOCOL  0xED0A
631624Smax.romanov@nginx.com 
641624Smax.romanov@nginx.com 
651624Smax.romanov@nginx.com int
661624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj)
671624Smax.romanov@nginx.com {
681624Smax.romanov@nginx.com     int           res;
69*1697Smax.romanov@nginx.com     PyObject      *func;
701624Smax.romanov@nginx.com     PyCodeObject  *code;
711624Smax.romanov@nginx.com 
72*1697Smax.romanov@nginx.com     func = nxt_python_asgi_get_func(obj);
73*1697Smax.romanov@nginx.com 
74*1697Smax.romanov@nginx.com     if (func == NULL) {
75*1697Smax.romanov@nginx.com         return 0;
76*1697Smax.romanov@nginx.com     }
77*1697Smax.romanov@nginx.com 
78*1697Smax.romanov@nginx.com     code = (PyCodeObject *) PyFunction_GET_CODE(func);
79*1697Smax.romanov@nginx.com 
80*1697Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with "
81*1697Smax.romanov@nginx.com                          "%d argument(s)",
82*1697Smax.romanov@nginx.com                    (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ",
83*1697Smax.romanov@nginx.com                    code->co_argcount);
84*1697Smax.romanov@nginx.com 
85*1697Smax.romanov@nginx.com     res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1;
86*1697Smax.romanov@nginx.com 
87*1697Smax.romanov@nginx.com     Py_DECREF(func);
88*1697Smax.romanov@nginx.com 
89*1697Smax.romanov@nginx.com     return res;
90*1697Smax.romanov@nginx.com }
91*1697Smax.romanov@nginx.com 
92*1697Smax.romanov@nginx.com 
93*1697Smax.romanov@nginx.com static PyObject *
94*1697Smax.romanov@nginx.com nxt_python_asgi_get_func(PyObject *obj)
95*1697Smax.romanov@nginx.com {
96*1697Smax.romanov@nginx.com     PyObject  *call;
97*1697Smax.romanov@nginx.com 
981624Smax.romanov@nginx.com     if (PyFunction_Check(obj)) {
99*1697Smax.romanov@nginx.com         Py_INCREF(obj);
100*1697Smax.romanov@nginx.com         return obj;
1011624Smax.romanov@nginx.com     }
1021624Smax.romanov@nginx.com 
1031624Smax.romanov@nginx.com     if (PyMethod_Check(obj)) {
1041624Smax.romanov@nginx.com         obj = PyMethod_GET_FUNCTION(obj);
1051624Smax.romanov@nginx.com 
106*1697Smax.romanov@nginx.com         Py_INCREF(obj);
107*1697Smax.romanov@nginx.com         return obj;
1081624Smax.romanov@nginx.com     }
1091624Smax.romanov@nginx.com 
1101624Smax.romanov@nginx.com     call = PyObject_GetAttrString(obj, "__call__");
1111624Smax.romanov@nginx.com 
1121624Smax.romanov@nginx.com     if (call == NULL) {
113*1697Smax.romanov@nginx.com         return NULL;
1141624Smax.romanov@nginx.com     }
1151624Smax.romanov@nginx.com 
1161624Smax.romanov@nginx.com     if (PyFunction_Check(call)) {
117*1697Smax.romanov@nginx.com         return call;
118*1697Smax.romanov@nginx.com     }
1191624Smax.romanov@nginx.com 
120*1697Smax.romanov@nginx.com     if (PyMethod_Check(call)) {
121*1697Smax.romanov@nginx.com         obj = PyMethod_GET_FUNCTION(call);
1221624Smax.romanov@nginx.com 
123*1697Smax.romanov@nginx.com         Py_INCREF(obj);
124*1697Smax.romanov@nginx.com         Py_DECREF(call);
125*1697Smax.romanov@nginx.com 
126*1697Smax.romanov@nginx.com         return obj;
1271624Smax.romanov@nginx.com     }
1281624Smax.romanov@nginx.com 
1291624Smax.romanov@nginx.com     Py_DECREF(call);
1301624Smax.romanov@nginx.com 
131*1697Smax.romanov@nginx.com     return NULL;
1321624Smax.romanov@nginx.com }
1331624Smax.romanov@nginx.com 
1341624Smax.romanov@nginx.com 
1351681Smax.romanov@nginx.com int
1361681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
1371624Smax.romanov@nginx.com {
138*1697Smax.romanov@nginx.com     PyObject      *func;
139*1697Smax.romanov@nginx.com     PyCodeObject  *code;
140*1697Smax.romanov@nginx.com 
1411681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_init");
1421624Smax.romanov@nginx.com 
1431681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
1441681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to init string objects");
1451681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1461624Smax.romanov@nginx.com     }
1471624Smax.romanov@nginx.com 
1481624Smax.romanov@nginx.com     nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);
1491624Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_port_read == NULL)) {
1501681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
1511681Smax.romanov@nginx.com                        "Python failed to initialize the 'port_read' function");
1521681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1531624Smax.romanov@nginx.com     }
1541624Smax.romanov@nginx.com 
1551681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
1561681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1571624Smax.romanov@nginx.com     }
1581624Smax.romanov@nginx.com 
1591681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
1601681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1611624Smax.romanov@nginx.com     }
1621624Smax.romanov@nginx.com 
163*1697Smax.romanov@nginx.com     func = nxt_python_asgi_get_func(nxt_py_application);
164*1697Smax.romanov@nginx.com     if (nxt_slow_path(func == NULL)) {
165*1697Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python cannot find function for callable");
166*1697Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
167*1697Smax.romanov@nginx.com     }
168*1697Smax.romanov@nginx.com 
169*1697Smax.romanov@nginx.com     code = (PyCodeObject *) PyFunction_GET_CODE(func);
170*1697Smax.romanov@nginx.com 
171*1697Smax.romanov@nginx.com     if ((code->co_flags & CO_COROUTINE) == 0) {
172*1697Smax.romanov@nginx.com         nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
173*1697Smax.romanov@nginx.com                              "switching to legacy mode");
174*1697Smax.romanov@nginx.com         nxt_py_asgi_legacy = 1;
175*1697Smax.romanov@nginx.com     }
176*1697Smax.romanov@nginx.com 
177*1697Smax.romanov@nginx.com     Py_DECREF(func);
178*1697Smax.romanov@nginx.com 
1791624Smax.romanov@nginx.com     init->callbacks.request_handler = nxt_py_asgi_request_handler;
1801624Smax.romanov@nginx.com     init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
1811624Smax.romanov@nginx.com     init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
1821624Smax.romanov@nginx.com     init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler;
1831624Smax.romanov@nginx.com     init->callbacks.quit = nxt_py_asgi_quit;
1841624Smax.romanov@nginx.com     init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
1851624Smax.romanov@nginx.com     init->callbacks.add_port = nxt_py_asgi_add_port;
1861624Smax.romanov@nginx.com     init->callbacks.remove_port = nxt_py_asgi_remove_port;
1871624Smax.romanov@nginx.com 
1881681Smax.romanov@nginx.com     *proto = nxt_py_asgi_proto;
1891681Smax.romanov@nginx.com 
1901681Smax.romanov@nginx.com     return NXT_UNIT_OK;
1911681Smax.romanov@nginx.com }
1921681Smax.romanov@nginx.com 
1931681Smax.romanov@nginx.com 
1941681Smax.romanov@nginx.com static int
1951681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_alloc(void **pdata)
1961681Smax.romanov@nginx.com {
1971681Smax.romanov@nginx.com     uint32_t                i;
1981681Smax.romanov@nginx.com     PyObject                *asyncio, *loop, *new_event_loop, *obj;
1991681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
2001681Smax.romanov@nginx.com 
2011681Smax.romanov@nginx.com     ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
2021681Smax.romanov@nginx.com     if (nxt_slow_path(ctx_data == NULL)) {
2031681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate context data");
2041681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
2051681Smax.romanov@nginx.com     }
2061681Smax.romanov@nginx.com 
2071681Smax.romanov@nginx.com     memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t));
2081681Smax.romanov@nginx.com 
2091681Smax.romanov@nginx.com     nxt_queue_init(&ctx_data->drain_queue);
2101681Smax.romanov@nginx.com 
2111681Smax.romanov@nginx.com     struct {
2121681Smax.romanov@nginx.com         const char  *key;
2131681Smax.romanov@nginx.com         PyObject    **handler;
2141681Smax.romanov@nginx.com 
2151681Smax.romanov@nginx.com     } handlers[] = {
2161681Smax.romanov@nginx.com         { "create_task",        &ctx_data->loop_create_task },
2171681Smax.romanov@nginx.com         { "add_reader",         &ctx_data->loop_add_reader },
2181681Smax.romanov@nginx.com         { "remove_reader",      &ctx_data->loop_remove_reader },
2191681Smax.romanov@nginx.com         { "call_soon",          &ctx_data->loop_call_soon },
2201681Smax.romanov@nginx.com         { "run_until_complete", &ctx_data->loop_run_until_complete },
2211681Smax.romanov@nginx.com         { "create_future",      &ctx_data->loop_create_future },
2221681Smax.romanov@nginx.com     };
2231681Smax.romanov@nginx.com 
2241681Smax.romanov@nginx.com     loop = NULL;
2251681Smax.romanov@nginx.com 
2261681Smax.romanov@nginx.com     asyncio = PyImport_ImportModule("asyncio");
2271681Smax.romanov@nginx.com     if (nxt_slow_path(asyncio == NULL)) {
2281681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to import module 'asyncio'");
2291681Smax.romanov@nginx.com         nxt_python_print_exception();
2301681Smax.romanov@nginx.com         goto fail;
2311681Smax.romanov@nginx.com     }
2321681Smax.romanov@nginx.com 
2331681Smax.romanov@nginx.com     new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
2341681Smax.romanov@nginx.com                                           "new_event_loop");
2351681Smax.romanov@nginx.com     if (nxt_slow_path(new_event_loop == NULL)) {
2361681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
2371681Smax.romanov@nginx.com                  "Python failed to get 'new_event_loop' from module 'asyncio'");
2381681Smax.romanov@nginx.com         goto fail;
2391681Smax.romanov@nginx.com     }
2401681Smax.romanov@nginx.com 
2411681Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) {
2421681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
2431681Smax.romanov@nginx.com                        "'asyncio.new_event_loop' is not a callable object");
2441681Smax.romanov@nginx.com         goto fail;
2451681Smax.romanov@nginx.com     }
2461681Smax.romanov@nginx.com 
2471681Smax.romanov@nginx.com     loop = PyObject_CallObject(new_event_loop, NULL);
2481681Smax.romanov@nginx.com     if (nxt_slow_path(loop == NULL)) {
2491681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'");
2501681Smax.romanov@nginx.com         goto fail;
2511681Smax.romanov@nginx.com     }
2521681Smax.romanov@nginx.com 
2531681Smax.romanov@nginx.com     for (i = 0; i < nxt_nitems(handlers); i++) {
2541681Smax.romanov@nginx.com         obj = PyObject_GetAttrString(loop, handlers[i].key);
2551681Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
2561681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Python failed to get 'loop.%s'",
2571681Smax.romanov@nginx.com                                  handlers[i].key);
2581681Smax.romanov@nginx.com             goto fail;
2591681Smax.romanov@nginx.com         }
2601681Smax.romanov@nginx.com 
2611681Smax.romanov@nginx.com         *handlers[i].handler = obj;
2621681Smax.romanov@nginx.com 
2631681Smax.romanov@nginx.com         if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
2641681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "'loop.%s' is not a callable object",
2651681Smax.romanov@nginx.com                                  handlers[i].key);
2661681Smax.romanov@nginx.com             goto fail;
2671681Smax.romanov@nginx.com         }
2681681Smax.romanov@nginx.com     }
2691681Smax.romanov@nginx.com 
2701681Smax.romanov@nginx.com     obj = PyObject_CallObject(ctx_data->loop_create_future, NULL);
2711681Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
2721681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Future ");
2731681Smax.romanov@nginx.com         nxt_python_print_exception();
2741681Smax.romanov@nginx.com         goto fail;
2751681Smax.romanov@nginx.com     }
2761681Smax.romanov@nginx.com 
2771681Smax.romanov@nginx.com     ctx_data->quit_future = obj;
2781681Smax.romanov@nginx.com 
2791681Smax.romanov@nginx.com     obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result");
2801681Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
2811681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get 'future.set_result'");
2821681Smax.romanov@nginx.com         goto fail;
2831681Smax.romanov@nginx.com     }
2841681Smax.romanov@nginx.com 
2851681Smax.romanov@nginx.com     ctx_data->quit_future_set_result = obj;
2861681Smax.romanov@nginx.com 
2871681Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
2881681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "'future.set_result' is not a callable object");
2891681Smax.romanov@nginx.com         goto fail;
2901681Smax.romanov@nginx.com     }
2911681Smax.romanov@nginx.com 
2921624Smax.romanov@nginx.com     Py_DECREF(loop);
2931624Smax.romanov@nginx.com     Py_DECREF(asyncio);
2941624Smax.romanov@nginx.com 
2951681Smax.romanov@nginx.com     *pdata = ctx_data;
2961681Smax.romanov@nginx.com 
2971681Smax.romanov@nginx.com     return NXT_UNIT_OK;
2981624Smax.romanov@nginx.com 
2991624Smax.romanov@nginx.com fail:
3001624Smax.romanov@nginx.com 
3011681Smax.romanov@nginx.com     nxt_python_asgi_ctx_data_free(ctx_data);
3021681Smax.romanov@nginx.com 
3031624Smax.romanov@nginx.com     Py_XDECREF(loop);
3041681Smax.romanov@nginx.com     Py_XDECREF(asyncio);
3051624Smax.romanov@nginx.com 
3061681Smax.romanov@nginx.com     return NXT_UNIT_ERROR;
3071624Smax.romanov@nginx.com }
3081624Smax.romanov@nginx.com 
3091624Smax.romanov@nginx.com 
3101681Smax.romanov@nginx.com static void
3111681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(void *data)
3121681Smax.romanov@nginx.com {
3131681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3141681Smax.romanov@nginx.com 
3151681Smax.romanov@nginx.com     ctx_data = data;
3161681Smax.romanov@nginx.com 
3171681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_run_until_complete);
3181681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_create_future);
3191681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_create_task);
3201681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_call_soon);
3211681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_add_reader);
3221681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->loop_remove_reader);
3231681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->quit_future);
3241681Smax.romanov@nginx.com     Py_XDECREF(ctx_data->quit_future_set_result);
3251681Smax.romanov@nginx.com 
3261681Smax.romanov@nginx.com     nxt_unit_free(NULL, ctx_data);
3271681Smax.romanov@nginx.com }
3281681Smax.romanov@nginx.com 
3291681Smax.romanov@nginx.com 
3301681Smax.romanov@nginx.com static int
3311681Smax.romanov@nginx.com nxt_python_asgi_startup(void *data)
3321681Smax.romanov@nginx.com {
3331681Smax.romanov@nginx.com     return nxt_py_asgi_lifespan_startup(data);
3341681Smax.romanov@nginx.com }
3351681Smax.romanov@nginx.com 
3361681Smax.romanov@nginx.com 
3371681Smax.romanov@nginx.com static int
3381624Smax.romanov@nginx.com nxt_python_asgi_run(nxt_unit_ctx_t *ctx)
3391624Smax.romanov@nginx.com {
3401681Smax.romanov@nginx.com     PyObject                *res;
3411681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3421624Smax.romanov@nginx.com 
3431681Smax.romanov@nginx.com     ctx_data = ctx->data;
3441681Smax.romanov@nginx.com 
3451681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
3461681Smax.romanov@nginx.com                                        ctx_data->quit_future, NULL);
3471624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
3481624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete");
3491624Smax.romanov@nginx.com         nxt_python_print_exception();
3501624Smax.romanov@nginx.com 
3511681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
3521624Smax.romanov@nginx.com     }
3531624Smax.romanov@nginx.com 
3541624Smax.romanov@nginx.com     Py_DECREF(res);
3551624Smax.romanov@nginx.com 
3561681Smax.romanov@nginx.com     nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port);
3571681Smax.romanov@nginx.com     nxt_py_asgi_remove_reader(ctx, ctx_data->port);
3581681Smax.romanov@nginx.com 
3591681Smax.romanov@nginx.com     if (ctx_data->port != NULL) {
3601681Smax.romanov@nginx.com         ctx_data->port->data = NULL;
3611681Smax.romanov@nginx.com         ctx_data->port = NULL;
3621681Smax.romanov@nginx.com     }
3631681Smax.romanov@nginx.com 
3641681Smax.romanov@nginx.com     nxt_py_asgi_lifespan_shutdown(ctx);
3651681Smax.romanov@nginx.com 
3661681Smax.romanov@nginx.com     return NXT_UNIT_OK;
3671681Smax.romanov@nginx.com }
3681681Smax.romanov@nginx.com 
3691624Smax.romanov@nginx.com 
3701681Smax.romanov@nginx.com static void
3711681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
3721681Smax.romanov@nginx.com {
3731682Smax.romanov@nginx.com     PyObject                *res, *fd;
3741681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3751681Smax.romanov@nginx.com 
3761681Smax.romanov@nginx.com     if (port == NULL || port->in_fd == -1) {
3771681Smax.romanov@nginx.com         return;
3781681Smax.romanov@nginx.com     }
3791681Smax.romanov@nginx.com 
3801681Smax.romanov@nginx.com     ctx_data = ctx->data;
3811681Smax.romanov@nginx.com 
3821681Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port);
3831681Smax.romanov@nginx.com 
3841682Smax.romanov@nginx.com     fd = PyLong_FromLong(port->in_fd);
3851682Smax.romanov@nginx.com     if (nxt_slow_path(fd == NULL)) {
3861682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create Long object");
3871681Smax.romanov@nginx.com         nxt_python_print_exception();
3881681Smax.romanov@nginx.com 
3891681Smax.romanov@nginx.com         return;
3901681Smax.romanov@nginx.com     }
3911681Smax.romanov@nginx.com 
3921682Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL);
3931682Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
3941682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to remove_reader");
3951682Smax.romanov@nginx.com         nxt_python_print_exception();
3961682Smax.romanov@nginx.com 
3971682Smax.romanov@nginx.com     } else {
3981682Smax.romanov@nginx.com         Py_DECREF(res);
3991682Smax.romanov@nginx.com     }
4001682Smax.romanov@nginx.com 
4011682Smax.romanov@nginx.com     Py_DECREF(fd);
4021624Smax.romanov@nginx.com }
4031624Smax.romanov@nginx.com 
4041624Smax.romanov@nginx.com 
4051624Smax.romanov@nginx.com static void
4061624Smax.romanov@nginx.com nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
4071624Smax.romanov@nginx.com {
4081681Smax.romanov@nginx.com     PyObject                *scope, *res, *task, *receive, *send, *done, *asgi;
409*1697Smax.romanov@nginx.com     PyObject                *stage2;
4101681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
4111624Smax.romanov@nginx.com 
4121624Smax.romanov@nginx.com     if (req->request->websocket_handshake) {
4131624Smax.romanov@nginx.com         asgi = nxt_py_asgi_websocket_create(req);
4141624Smax.romanov@nginx.com 
4151624Smax.romanov@nginx.com     } else {
4161624Smax.romanov@nginx.com         asgi = nxt_py_asgi_http_create(req);
4171624Smax.romanov@nginx.com     }
4181624Smax.romanov@nginx.com 
4191624Smax.romanov@nginx.com     if (nxt_slow_path(asgi == NULL)) {
4201624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create asgi object");
4211624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4221624Smax.romanov@nginx.com 
4231624Smax.romanov@nginx.com         return;
4241624Smax.romanov@nginx.com     }
4251624Smax.romanov@nginx.com 
4261624Smax.romanov@nginx.com     receive = PyObject_GetAttrString(asgi, "receive");
4271624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
4281624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get 'receive' method");
4291624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4301624Smax.romanov@nginx.com 
4311624Smax.romanov@nginx.com         goto release_asgi;
4321624Smax.romanov@nginx.com     }
4331624Smax.romanov@nginx.com 
4341624Smax.romanov@nginx.com     send = PyObject_GetAttrString(asgi, "send");
4351624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
4361624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get 'send' method");
4371624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4381624Smax.romanov@nginx.com 
4391624Smax.romanov@nginx.com         goto release_receive;
4401624Smax.romanov@nginx.com     }
4411624Smax.romanov@nginx.com 
4421624Smax.romanov@nginx.com     done = PyObject_GetAttrString(asgi, "_done");
4431624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
4441624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to get '_done' method");
4451624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4461624Smax.romanov@nginx.com 
4471624Smax.romanov@nginx.com         goto release_send;
4481624Smax.romanov@nginx.com     }
4491624Smax.romanov@nginx.com 
4501624Smax.romanov@nginx.com     scope = nxt_py_asgi_create_http_scope(req);
4511624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
4521624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4531624Smax.romanov@nginx.com 
4541624Smax.romanov@nginx.com         goto release_done;
4551624Smax.romanov@nginx.com     }
4561624Smax.romanov@nginx.com 
4571624Smax.romanov@nginx.com     req->data = asgi;
4581624Smax.romanov@nginx.com 
459*1697Smax.romanov@nginx.com     if (!nxt_py_asgi_legacy) {
460*1697Smax.romanov@nginx.com         nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
461*1697Smax.romanov@nginx.com 
462*1697Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(nxt_py_application,
463*1697Smax.romanov@nginx.com                                            scope, receive, send, NULL);
464*1697Smax.romanov@nginx.com 
465*1697Smax.romanov@nginx.com     } else {
466*1697Smax.romanov@nginx.com         nxt_unit_req_debug(req, "Python call legacy application");
467*1697Smax.romanov@nginx.com 
468*1697Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
469*1697Smax.romanov@nginx.com 
470*1697Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
471*1697Smax.romanov@nginx.com             nxt_unit_req_error(req, "Python failed to call legacy app stage1");
472*1697Smax.romanov@nginx.com             nxt_python_print_exception();
473*1697Smax.romanov@nginx.com             nxt_unit_request_done(req, NXT_UNIT_ERROR);
474*1697Smax.romanov@nginx.com 
475*1697Smax.romanov@nginx.com             goto release_scope;
476*1697Smax.romanov@nginx.com         }
477*1697Smax.romanov@nginx.com 
478*1697Smax.romanov@nginx.com         if (nxt_slow_path(PyCallable_Check(res) == 0)) {
479*1697Smax.romanov@nginx.com             nxt_unit_req_error(req,
480*1697Smax.romanov@nginx.com                               "Legacy ASGI application returns not a callable");
481*1697Smax.romanov@nginx.com             nxt_unit_request_done(req, NXT_UNIT_ERROR);
482*1697Smax.romanov@nginx.com 
483*1697Smax.romanov@nginx.com             Py_DECREF(res);
484*1697Smax.romanov@nginx.com 
485*1697Smax.romanov@nginx.com             goto release_scope;
486*1697Smax.romanov@nginx.com         }
487*1697Smax.romanov@nginx.com 
488*1697Smax.romanov@nginx.com         stage2 = res;
489*1697Smax.romanov@nginx.com 
490*1697Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
491*1697Smax.romanov@nginx.com 
492*1697Smax.romanov@nginx.com         Py_DECREF(stage2);
493*1697Smax.romanov@nginx.com     }
494*1697Smax.romanov@nginx.com 
4951624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
4961624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Python failed to call the application");
4971624Smax.romanov@nginx.com         nxt_python_print_exception();
4981624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
4991624Smax.romanov@nginx.com 
5001624Smax.romanov@nginx.com         goto release_scope;
5011624Smax.romanov@nginx.com     }
5021624Smax.romanov@nginx.com 
5031624Smax.romanov@nginx.com     if (nxt_slow_path(!PyCoro_CheckExact(res))) {
5041624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Application result type is not a coroutine");
5051624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5061624Smax.romanov@nginx.com 
5071624Smax.romanov@nginx.com         Py_DECREF(res);
5081624Smax.romanov@nginx.com 
5091624Smax.romanov@nginx.com         goto release_scope;
5101624Smax.romanov@nginx.com     }
5111624Smax.romanov@nginx.com 
5121681Smax.romanov@nginx.com     ctx_data = req->ctx->data;
5131681Smax.romanov@nginx.com 
5141681Smax.romanov@nginx.com     task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
5151624Smax.romanov@nginx.com     if (nxt_slow_path(task == NULL)) {
5161624Smax.romanov@nginx.com         nxt_unit_req_error(req, "Python failed to call the create_task");
5171624Smax.romanov@nginx.com         nxt_python_print_exception();
5181624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5191624Smax.romanov@nginx.com 
5201624Smax.romanov@nginx.com         Py_DECREF(res);
5211624Smax.romanov@nginx.com 
5221624Smax.romanov@nginx.com         goto release_scope;
5231624Smax.romanov@nginx.com     }
5241624Smax.romanov@nginx.com 
5251624Smax.romanov@nginx.com     Py_DECREF(res);
5261624Smax.romanov@nginx.com 
5271624Smax.romanov@nginx.com     res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done,
5281624Smax.romanov@nginx.com                                      NULL);
5291624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
5301624Smax.romanov@nginx.com         nxt_unit_req_error(req,
5311624Smax.romanov@nginx.com                            "Python failed to call 'task.add_done_callback'");
5321624Smax.romanov@nginx.com         nxt_python_print_exception();
5331624Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
5341624Smax.romanov@nginx.com 
5351624Smax.romanov@nginx.com         goto release_task;
5361624Smax.romanov@nginx.com     }
5371624Smax.romanov@nginx.com 
5381624Smax.romanov@nginx.com     Py_DECREF(res);
5391624Smax.romanov@nginx.com release_task:
5401624Smax.romanov@nginx.com     Py_DECREF(task);
5411624Smax.romanov@nginx.com release_scope:
5421624Smax.romanov@nginx.com     Py_DECREF(scope);
5431624Smax.romanov@nginx.com release_done:
5441624Smax.romanov@nginx.com     Py_DECREF(done);
5451624Smax.romanov@nginx.com release_send:
5461624Smax.romanov@nginx.com     Py_DECREF(send);
5471624Smax.romanov@nginx.com release_receive:
5481624Smax.romanov@nginx.com     Py_DECREF(receive);
5491624Smax.romanov@nginx.com release_asgi:
5501624Smax.romanov@nginx.com     Py_DECREF(asgi);
5511624Smax.romanov@nginx.com }
5521624Smax.romanov@nginx.com 
5531624Smax.romanov@nginx.com 
5541624Smax.romanov@nginx.com static PyObject *
5551624Smax.romanov@nginx.com nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req)
5561624Smax.romanov@nginx.com {
5571624Smax.romanov@nginx.com     char                *p, *target, *query;
5581624Smax.romanov@nginx.com     uint32_t            target_length, i;
5591624Smax.romanov@nginx.com     PyObject            *scope, *v, *type, *scheme;
5601624Smax.romanov@nginx.com     PyObject            *headers, *header;
5611624Smax.romanov@nginx.com     nxt_unit_field_t    *f;
5621624Smax.romanov@nginx.com     nxt_unit_request_t  *r;
5631624Smax.romanov@nginx.com 
5641624Smax.romanov@nginx.com     static const nxt_str_t  ws_protocol = nxt_string("sec-websocket-protocol");
5651624Smax.romanov@nginx.com 
5661624Smax.romanov@nginx.com #define SET_ITEM(dict, key, value) \
5671624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value)      \
5681624Smax.romanov@nginx.com                       == -1))                                                  \
5691624Smax.romanov@nginx.com     {                                                                          \
5701624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set '"                       \
5711624Smax.romanov@nginx.com                                 #dict "." #key "' item");                      \
5721624Smax.romanov@nginx.com         goto fail;                                                             \
5731624Smax.romanov@nginx.com     }
5741624Smax.romanov@nginx.com 
5751624Smax.romanov@nginx.com     v = NULL;
5761624Smax.romanov@nginx.com     headers = NULL;
5771624Smax.romanov@nginx.com 
5781624Smax.romanov@nginx.com     r = req->request;
5791624Smax.romanov@nginx.com 
5801624Smax.romanov@nginx.com     if (r->websocket_handshake) {
5811624Smax.romanov@nginx.com         type = nxt_py_websocket_str;
5821624Smax.romanov@nginx.com         scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str;
5831624Smax.romanov@nginx.com 
5841624Smax.romanov@nginx.com     } else {
5851624Smax.romanov@nginx.com         type = nxt_py_http_str;
5861624Smax.romanov@nginx.com         scheme = r->tls ? nxt_py_https_str : nxt_py_http_str;
5871624Smax.romanov@nginx.com     }
5881624Smax.romanov@nginx.com 
5891624Smax.romanov@nginx.com     scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str);
5901624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
5911624Smax.romanov@nginx.com         return NULL;
5921624Smax.romanov@nginx.com     }
5931624Smax.romanov@nginx.com 
5941624Smax.romanov@nginx.com     p = nxt_unit_sptr_get(&r->version);
5951624Smax.romanov@nginx.com     SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str
5961624Smax.romanov@nginx.com                                               : nxt_py_1_0_str)
5971624Smax.romanov@nginx.com     SET_ITEM(scope, scheme, scheme)
5981624Smax.romanov@nginx.com 
5991624Smax.romanov@nginx.com     v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method),
6001624Smax.romanov@nginx.com                                    r->method_length);
6011624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6021624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'method' string");
6031624Smax.romanov@nginx.com         goto fail;
6041624Smax.romanov@nginx.com     }
6051624Smax.romanov@nginx.com 
6061624Smax.romanov@nginx.com     SET_ITEM(scope, method, v)
6071624Smax.romanov@nginx.com     Py_DECREF(v);
6081624Smax.romanov@nginx.com 
6091624Smax.romanov@nginx.com     v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length,
6101624Smax.romanov@nginx.com                              "replace");
6111624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6121624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'path' string");
6131624Smax.romanov@nginx.com         goto fail;
6141624Smax.romanov@nginx.com     }
6151624Smax.romanov@nginx.com 
6161624Smax.romanov@nginx.com     SET_ITEM(scope, path, v)
6171624Smax.romanov@nginx.com     Py_DECREF(v);
6181624Smax.romanov@nginx.com 
6191624Smax.romanov@nginx.com     target = nxt_unit_sptr_get(&r->target);
6201624Smax.romanov@nginx.com     query = nxt_unit_sptr_get(&r->query);
6211624Smax.romanov@nginx.com 
6221624Smax.romanov@nginx.com     if (r->query.offset != 0) {
6231624Smax.romanov@nginx.com         target_length = query - target - 1;
6241624Smax.romanov@nginx.com 
6251624Smax.romanov@nginx.com     } else {
6261624Smax.romanov@nginx.com         target_length = r->target_length;
6271624Smax.romanov@nginx.com     }
6281624Smax.romanov@nginx.com 
6291624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(target, target_length);
6301624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6311624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'raw_path' string");
6321624Smax.romanov@nginx.com         goto fail;
6331624Smax.romanov@nginx.com     }
6341624Smax.romanov@nginx.com 
6351624Smax.romanov@nginx.com     SET_ITEM(scope, raw_path, v)
6361624Smax.romanov@nginx.com     Py_DECREF(v);
6371624Smax.romanov@nginx.com 
6381624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(query, r->query_length);
6391624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6401624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'query' string");
6411624Smax.romanov@nginx.com         goto fail;
6421624Smax.romanov@nginx.com     }
6431624Smax.romanov@nginx.com 
6441624Smax.romanov@nginx.com     SET_ITEM(scope, query_string, v)
6451624Smax.romanov@nginx.com     Py_DECREF(v);
6461624Smax.romanov@nginx.com 
6471624Smax.romanov@nginx.com     v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0);
6481624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6491624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'client' pair");
6501624Smax.romanov@nginx.com         goto fail;
6511624Smax.romanov@nginx.com     }
6521624Smax.romanov@nginx.com 
6531624Smax.romanov@nginx.com     SET_ITEM(scope, client, v)
6541624Smax.romanov@nginx.com     Py_DECREF(v);
6551624Smax.romanov@nginx.com 
6561624Smax.romanov@nginx.com     v = nxt_py_asgi_create_address(&r->local, r->local_length, 80);
6571624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
6581624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'server' pair");
6591624Smax.romanov@nginx.com         goto fail;
6601624Smax.romanov@nginx.com     }
6611624Smax.romanov@nginx.com 
6621624Smax.romanov@nginx.com     SET_ITEM(scope, server, v)
6631624Smax.romanov@nginx.com     Py_DECREF(v);
6641624Smax.romanov@nginx.com 
6651624Smax.romanov@nginx.com     v = NULL;
6661624Smax.romanov@nginx.com 
6671624Smax.romanov@nginx.com     headers = PyTuple_New(r->fields_count);
6681624Smax.romanov@nginx.com     if (nxt_slow_path(headers == NULL)) {
6691624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'headers' object");
6701624Smax.romanov@nginx.com         goto fail;
6711624Smax.romanov@nginx.com     }
6721624Smax.romanov@nginx.com 
6731624Smax.romanov@nginx.com     for (i = 0; i < r->fields_count; i++) {
6741624Smax.romanov@nginx.com         f = r->fields + i;
6751624Smax.romanov@nginx.com 
6761624Smax.romanov@nginx.com         header = nxt_py_asgi_create_header(f);
6771624Smax.romanov@nginx.com         if (nxt_slow_path(header == NULL)) {
6781624Smax.romanov@nginx.com             nxt_unit_req_alert(req, "Python failed to create 'header' pair");
6791624Smax.romanov@nginx.com             goto fail;
6801624Smax.romanov@nginx.com         }
6811624Smax.romanov@nginx.com 
6821624Smax.romanov@nginx.com         PyTuple_SET_ITEM(headers, i, header);
6831624Smax.romanov@nginx.com 
6841624Smax.romanov@nginx.com         if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL
6851624Smax.romanov@nginx.com             && f->name_length == ws_protocol.length
6861624Smax.romanov@nginx.com             && f->value_length > 0
6871624Smax.romanov@nginx.com             && r->websocket_handshake)
6881624Smax.romanov@nginx.com         {
6891624Smax.romanov@nginx.com             v = nxt_py_asgi_create_subprotocols(f);
6901624Smax.romanov@nginx.com             if (nxt_slow_path(v == NULL)) {
6911624Smax.romanov@nginx.com                 nxt_unit_req_alert(req, "Failed to create subprotocols");
6921624Smax.romanov@nginx.com                 goto fail;
6931624Smax.romanov@nginx.com             }
6941624Smax.romanov@nginx.com 
6951624Smax.romanov@nginx.com             SET_ITEM(scope, subprotocols, v);
6961624Smax.romanov@nginx.com             Py_DECREF(v);
6971624Smax.romanov@nginx.com         }
6981624Smax.romanov@nginx.com     }
6991624Smax.romanov@nginx.com 
7001624Smax.romanov@nginx.com     SET_ITEM(scope, headers, headers)
7011624Smax.romanov@nginx.com     Py_DECREF(headers);
7021624Smax.romanov@nginx.com 
7031624Smax.romanov@nginx.com     return scope;
7041624Smax.romanov@nginx.com 
7051624Smax.romanov@nginx.com fail:
7061624Smax.romanov@nginx.com 
7071624Smax.romanov@nginx.com     Py_XDECREF(v);
7081624Smax.romanov@nginx.com     Py_XDECREF(headers);
7091624Smax.romanov@nginx.com     Py_DECREF(scope);
7101624Smax.romanov@nginx.com 
7111624Smax.romanov@nginx.com     return NULL;
7121624Smax.romanov@nginx.com 
7131624Smax.romanov@nginx.com #undef SET_ITEM
7141624Smax.romanov@nginx.com }
7151624Smax.romanov@nginx.com 
7161624Smax.romanov@nginx.com 
7171624Smax.romanov@nginx.com static PyObject *
7181624Smax.romanov@nginx.com nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
7191624Smax.romanov@nginx.com {
7201624Smax.romanov@nginx.com     char      *p, *s;
7211624Smax.romanov@nginx.com     PyObject  *pair, *v;
7221624Smax.romanov@nginx.com 
7231624Smax.romanov@nginx.com     pair = PyTuple_New(2);
7241624Smax.romanov@nginx.com     if (nxt_slow_path(pair == NULL)) {
7251624Smax.romanov@nginx.com         return NULL;
7261624Smax.romanov@nginx.com     }
7271624Smax.romanov@nginx.com 
7281624Smax.romanov@nginx.com     p = nxt_unit_sptr_get(sptr);
7291624Smax.romanov@nginx.com     s = memchr(p, ':', len);
7301624Smax.romanov@nginx.com 
7311624Smax.romanov@nginx.com     v = PyString_FromStringAndSize(p, s == NULL ? len : s - p);
7321624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7331624Smax.romanov@nginx.com         Py_DECREF(pair);
7341624Smax.romanov@nginx.com 
7351624Smax.romanov@nginx.com         return NULL;
7361624Smax.romanov@nginx.com     }
7371624Smax.romanov@nginx.com 
7381624Smax.romanov@nginx.com     PyTuple_SET_ITEM(pair, 0, v);
7391624Smax.romanov@nginx.com 
7401624Smax.romanov@nginx.com     if (s != NULL) {
7411624Smax.romanov@nginx.com         p += len;
7421624Smax.romanov@nginx.com         v = PyLong_FromString(s + 1, &p, 10);
7431624Smax.romanov@nginx.com 
7441624Smax.romanov@nginx.com     } else {
7451624Smax.romanov@nginx.com         v = PyLong_FromLong(port);
7461624Smax.romanov@nginx.com     }
7471624Smax.romanov@nginx.com 
7481624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7491624Smax.romanov@nginx.com         Py_DECREF(pair);
7501624Smax.romanov@nginx.com 
7511624Smax.romanov@nginx.com         return NULL;
7521624Smax.romanov@nginx.com     }
7531624Smax.romanov@nginx.com 
7541624Smax.romanov@nginx.com     PyTuple_SET_ITEM(pair, 1, v);
7551624Smax.romanov@nginx.com 
7561624Smax.romanov@nginx.com     return pair;
7571624Smax.romanov@nginx.com }
7581624Smax.romanov@nginx.com 
7591624Smax.romanov@nginx.com 
7601624Smax.romanov@nginx.com static PyObject *
7611624Smax.romanov@nginx.com nxt_py_asgi_create_header(nxt_unit_field_t *f)
7621624Smax.romanov@nginx.com {
7631624Smax.romanov@nginx.com     char      c, *name;
7641624Smax.romanov@nginx.com     uint8_t   pos;
7651624Smax.romanov@nginx.com     PyObject  *header, *v;
7661624Smax.romanov@nginx.com 
7671624Smax.romanov@nginx.com     header = PyTuple_New(2);
7681624Smax.romanov@nginx.com     if (nxt_slow_path(header == NULL)) {
7691624Smax.romanov@nginx.com         return NULL;
7701624Smax.romanov@nginx.com     }
7711624Smax.romanov@nginx.com 
7721624Smax.romanov@nginx.com     name = nxt_unit_sptr_get(&f->name);
7731624Smax.romanov@nginx.com 
7741624Smax.romanov@nginx.com     for (pos = 0; pos < f->name_length; pos++) {
7751624Smax.romanov@nginx.com         c = name[pos];
7761624Smax.romanov@nginx.com         if (c >= 'A' && c <= 'Z') {
7771624Smax.romanov@nginx.com             name[pos] = (c | 0x20);
7781624Smax.romanov@nginx.com         }
7791624Smax.romanov@nginx.com     }
7801624Smax.romanov@nginx.com 
7811624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(name, f->name_length);
7821624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7831624Smax.romanov@nginx.com         Py_DECREF(header);
7841624Smax.romanov@nginx.com 
7851624Smax.romanov@nginx.com         return NULL;
7861624Smax.romanov@nginx.com     }
7871624Smax.romanov@nginx.com 
7881624Smax.romanov@nginx.com     PyTuple_SET_ITEM(header, 0, v);
7891624Smax.romanov@nginx.com 
7901624Smax.romanov@nginx.com     v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value),
7911624Smax.romanov@nginx.com                                   f->value_length);
7921624Smax.romanov@nginx.com     if (nxt_slow_path(v == NULL)) {
7931624Smax.romanov@nginx.com         Py_DECREF(header);
7941624Smax.romanov@nginx.com 
7951624Smax.romanov@nginx.com         return NULL;
7961624Smax.romanov@nginx.com     }
7971624Smax.romanov@nginx.com 
7981624Smax.romanov@nginx.com     PyTuple_SET_ITEM(header, 1, v);
7991624Smax.romanov@nginx.com 
8001624Smax.romanov@nginx.com     return header;
8011624Smax.romanov@nginx.com }
8021624Smax.romanov@nginx.com 
8031624Smax.romanov@nginx.com 
8041624Smax.romanov@nginx.com static PyObject *
8051624Smax.romanov@nginx.com nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f)
8061624Smax.romanov@nginx.com {
8071624Smax.romanov@nginx.com     char      *v;
8081624Smax.romanov@nginx.com     uint32_t  i, n, start;
8091624Smax.romanov@nginx.com     PyObject  *res, *proto;
8101624Smax.romanov@nginx.com 
8111624Smax.romanov@nginx.com     v = nxt_unit_sptr_get(&f->value);
8121624Smax.romanov@nginx.com     n = 1;
8131624Smax.romanov@nginx.com 
8141624Smax.romanov@nginx.com     for (i = 0; i < f->value_length; i++) {
8151624Smax.romanov@nginx.com         if (v[i] == ',') {
8161624Smax.romanov@nginx.com             n++;
8171624Smax.romanov@nginx.com         }
8181624Smax.romanov@nginx.com     }
8191624Smax.romanov@nginx.com 
8201624Smax.romanov@nginx.com     res = PyTuple_New(n);
8211624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
8221624Smax.romanov@nginx.com         return NULL;
8231624Smax.romanov@nginx.com     }
8241624Smax.romanov@nginx.com 
8251624Smax.romanov@nginx.com     n = 0;
8261624Smax.romanov@nginx.com     start = 0;
8271624Smax.romanov@nginx.com 
8281624Smax.romanov@nginx.com     for (i = 0; i < f->value_length; ) {
8291624Smax.romanov@nginx.com         if (v[i] != ',') {
8301624Smax.romanov@nginx.com             i++;
8311624Smax.romanov@nginx.com 
8321624Smax.romanov@nginx.com             continue;
8331624Smax.romanov@nginx.com         }
8341624Smax.romanov@nginx.com 
8351624Smax.romanov@nginx.com         if (i - start > 0) {
8361624Smax.romanov@nginx.com             proto = PyString_FromStringAndSize(v + start, i - start);
8371624Smax.romanov@nginx.com             if (nxt_slow_path(proto == NULL)) {
8381624Smax.romanov@nginx.com                 goto fail;
8391624Smax.romanov@nginx.com             }
8401624Smax.romanov@nginx.com 
8411624Smax.romanov@nginx.com             PyTuple_SET_ITEM(res, n, proto);
8421624Smax.romanov@nginx.com 
8431624Smax.romanov@nginx.com             n++;
8441624Smax.romanov@nginx.com         }
8451624Smax.romanov@nginx.com 
8461624Smax.romanov@nginx.com         do {
8471624Smax.romanov@nginx.com             i++;
8481624Smax.romanov@nginx.com         } while (i < f->value_length && v[i] == ' ');
8491624Smax.romanov@nginx.com 
8501624Smax.romanov@nginx.com         start = i;
8511624Smax.romanov@nginx.com     }
8521624Smax.romanov@nginx.com 
8531624Smax.romanov@nginx.com     if (i - start > 0) {
8541624Smax.romanov@nginx.com         proto = PyString_FromStringAndSize(v + start, i - start);
8551624Smax.romanov@nginx.com         if (nxt_slow_path(proto == NULL)) {
8561624Smax.romanov@nginx.com             goto fail;
8571624Smax.romanov@nginx.com         }
8581624Smax.romanov@nginx.com 
8591624Smax.romanov@nginx.com         PyTuple_SET_ITEM(res, n, proto);
8601624Smax.romanov@nginx.com     }
8611624Smax.romanov@nginx.com 
8621624Smax.romanov@nginx.com     return res;
8631624Smax.romanov@nginx.com 
8641624Smax.romanov@nginx.com fail:
8651624Smax.romanov@nginx.com 
8661624Smax.romanov@nginx.com     Py_DECREF(res);
8671624Smax.romanov@nginx.com 
8681624Smax.romanov@nginx.com     return NULL;
8691624Smax.romanov@nginx.com }
8701624Smax.romanov@nginx.com 
8711624Smax.romanov@nginx.com 
8721624Smax.romanov@nginx.com static int
8731681Smax.romanov@nginx.com nxt_python_asgi_ready(nxt_unit_ctx_t *ctx)
8741681Smax.romanov@nginx.com {
8751682Smax.romanov@nginx.com     int                     rc;
8761682Smax.romanov@nginx.com     PyObject                *res, *fd, *py_ctx, *py_port;
8771681Smax.romanov@nginx.com     nxt_unit_port_t         *port;
8781681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
8791681Smax.romanov@nginx.com 
8801681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_shared_port == NULL)) {
8811681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
8821681Smax.romanov@nginx.com     }
8831681Smax.romanov@nginx.com 
8841681Smax.romanov@nginx.com     port = nxt_py_shared_port;
8851681Smax.romanov@nginx.com 
8861681Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port);
8871681Smax.romanov@nginx.com 
8881681Smax.romanov@nginx.com     ctx_data = ctx->data;
8891681Smax.romanov@nginx.com 
8901682Smax.romanov@nginx.com     rc = NXT_UNIT_ERROR;
8911682Smax.romanov@nginx.com 
8921682Smax.romanov@nginx.com     fd = PyLong_FromLong(port->in_fd);
8931682Smax.romanov@nginx.com     if (nxt_slow_path(fd == NULL)) {
8941682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create fd");
8951682Smax.romanov@nginx.com         nxt_python_print_exception();
8961682Smax.romanov@nginx.com 
8971682Smax.romanov@nginx.com         return rc;
8981682Smax.romanov@nginx.com     }
8991682Smax.romanov@nginx.com 
9001682Smax.romanov@nginx.com     py_ctx = PyLong_FromVoidPtr(ctx);
9011682Smax.romanov@nginx.com     if (nxt_slow_path(py_ctx == NULL)) {
9021682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_ctx");
9031682Smax.romanov@nginx.com         nxt_python_print_exception();
9041682Smax.romanov@nginx.com 
9051682Smax.romanov@nginx.com         goto clean_fd;
9061682Smax.romanov@nginx.com     }
9071682Smax.romanov@nginx.com 
9081682Smax.romanov@nginx.com     py_port = PyLong_FromVoidPtr(port);
9091682Smax.romanov@nginx.com     if (nxt_slow_path(py_port == NULL)) {
9101682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_port");
9111682Smax.romanov@nginx.com         nxt_python_print_exception();
9121682Smax.romanov@nginx.com 
9131682Smax.romanov@nginx.com         goto clean_py_ctx;
9141682Smax.romanov@nginx.com     }
9151682Smax.romanov@nginx.com 
9161681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
9171682Smax.romanov@nginx.com                                        fd, nxt_py_port_read,
9181682Smax.romanov@nginx.com                                        py_ctx, py_port, NULL);
9191681Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
9201681Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to add_reader");
9211681Smax.romanov@nginx.com         nxt_python_print_exception();
9221681Smax.romanov@nginx.com 
9231682Smax.romanov@nginx.com     } else {
9241682Smax.romanov@nginx.com         Py_DECREF(res);
9251682Smax.romanov@nginx.com 
9261682Smax.romanov@nginx.com         rc = NXT_UNIT_OK;
9271681Smax.romanov@nginx.com     }
9281681Smax.romanov@nginx.com 
9291682Smax.romanov@nginx.com     Py_DECREF(py_port);
9301682Smax.romanov@nginx.com 
9311682Smax.romanov@nginx.com clean_py_ctx:
9321682Smax.romanov@nginx.com 
9331682Smax.romanov@nginx.com     Py_DECREF(py_ctx);
9341681Smax.romanov@nginx.com 
9351682Smax.romanov@nginx.com clean_fd:
9361682Smax.romanov@nginx.com 
9371682Smax.romanov@nginx.com     Py_DECREF(fd);
9381682Smax.romanov@nginx.com 
9391682Smax.romanov@nginx.com     return rc;
9401681Smax.romanov@nginx.com }
9411681Smax.romanov@nginx.com 
9421681Smax.romanov@nginx.com 
9431681Smax.romanov@nginx.com static int
9441624Smax.romanov@nginx.com nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
9451624Smax.romanov@nginx.com {
9461682Smax.romanov@nginx.com     int                     nb, rc;
9471682Smax.romanov@nginx.com     PyObject                *res, *fd, *py_ctx, *py_port;
9481681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
9491624Smax.romanov@nginx.com 
9501624Smax.romanov@nginx.com     if (port->in_fd == -1) {
9511624Smax.romanov@nginx.com         return NXT_UNIT_OK;
9521624Smax.romanov@nginx.com     }
9531624Smax.romanov@nginx.com 
9541624Smax.romanov@nginx.com     nb = 1;
9551624Smax.romanov@nginx.com 
9561624Smax.romanov@nginx.com     if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) {
9571624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)",
9581624Smax.romanov@nginx.com                        port->in_fd, strerror(errno), errno);
9591624Smax.romanov@nginx.com 
9601624Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
9611624Smax.romanov@nginx.com     }
9621624Smax.romanov@nginx.com 
9631624Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port);
9641624Smax.romanov@nginx.com 
9651681Smax.romanov@nginx.com     if (port->id.id == NXT_UNIT_SHARED_PORT_ID) {
9661681Smax.romanov@nginx.com         nxt_py_shared_port = port;
9671681Smax.romanov@nginx.com 
9681681Smax.romanov@nginx.com         return NXT_UNIT_OK;
9691681Smax.romanov@nginx.com     }
9701681Smax.romanov@nginx.com 
9711681Smax.romanov@nginx.com     ctx_data = ctx->data;
9721681Smax.romanov@nginx.com 
9731681Smax.romanov@nginx.com     ctx_data->port = port;
9741681Smax.romanov@nginx.com     port->data = ctx_data;
9751681Smax.romanov@nginx.com 
9761682Smax.romanov@nginx.com     rc = NXT_UNIT_ERROR;
9771682Smax.romanov@nginx.com 
9781682Smax.romanov@nginx.com     fd = PyLong_FromLong(port->in_fd);
9791682Smax.romanov@nginx.com     if (nxt_slow_path(fd == NULL)) {
9801682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create fd");
9811682Smax.romanov@nginx.com         nxt_python_print_exception();
9821682Smax.romanov@nginx.com 
9831682Smax.romanov@nginx.com         return rc;
9841682Smax.romanov@nginx.com     }
9851682Smax.romanov@nginx.com 
9861682Smax.romanov@nginx.com     py_ctx = PyLong_FromVoidPtr(ctx);
9871682Smax.romanov@nginx.com     if (nxt_slow_path(py_ctx == NULL)) {
9881682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_ctx");
9891682Smax.romanov@nginx.com         nxt_python_print_exception();
9901682Smax.romanov@nginx.com 
9911682Smax.romanov@nginx.com         goto clean_fd;
9921682Smax.romanov@nginx.com     }
9931682Smax.romanov@nginx.com 
9941682Smax.romanov@nginx.com     py_port = PyLong_FromVoidPtr(port);
9951682Smax.romanov@nginx.com     if (nxt_slow_path(py_port == NULL)) {
9961682Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to create py_port");
9971682Smax.romanov@nginx.com         nxt_python_print_exception();
9981682Smax.romanov@nginx.com 
9991682Smax.romanov@nginx.com         goto clean_py_ctx;
10001682Smax.romanov@nginx.com     }
10011682Smax.romanov@nginx.com 
10021681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
10031682Smax.romanov@nginx.com                                        fd, nxt_py_port_read,
10041682Smax.romanov@nginx.com                                        py_ctx, py_port, NULL);
10051624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
10061624Smax.romanov@nginx.com         nxt_unit_alert(ctx, "Python failed to add_reader");
10071681Smax.romanov@nginx.com         nxt_python_print_exception();
10081624Smax.romanov@nginx.com 
10091682Smax.romanov@nginx.com     } else {
10101682Smax.romanov@nginx.com         Py_DECREF(res);
10111682Smax.romanov@nginx.com 
10121682Smax.romanov@nginx.com         rc = NXT_UNIT_OK;
10131624Smax.romanov@nginx.com     }
10141624Smax.romanov@nginx.com 
10151682Smax.romanov@nginx.com     Py_DECREF(py_port);
10161682Smax.romanov@nginx.com 
10171682Smax.romanov@nginx.com clean_py_ctx:
10181682Smax.romanov@nginx.com 
10191682Smax.romanov@nginx.com     Py_DECREF(py_ctx);
10201624Smax.romanov@nginx.com 
10211682Smax.romanov@nginx.com clean_fd:
10221682Smax.romanov@nginx.com 
10231682Smax.romanov@nginx.com     Py_DECREF(fd);
10241682Smax.romanov@nginx.com 
10251682Smax.romanov@nginx.com     return rc;
10261624Smax.romanov@nginx.com }
10271624Smax.romanov@nginx.com 
10281624Smax.romanov@nginx.com 
10291624Smax.romanov@nginx.com static void
10301624Smax.romanov@nginx.com nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port)
10311624Smax.romanov@nginx.com {
10321624Smax.romanov@nginx.com     if (port->in_fd == -1) {
10331624Smax.romanov@nginx.com         return;
10341624Smax.romanov@nginx.com     }
10351624Smax.romanov@nginx.com 
10361681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port);
10371681Smax.romanov@nginx.com 
10381681Smax.romanov@nginx.com     if (nxt_py_shared_port == port) {
10391681Smax.romanov@nginx.com         nxt_py_shared_port = NULL;
10401624Smax.romanov@nginx.com     }
10411624Smax.romanov@nginx.com }
10421624Smax.romanov@nginx.com 
10431624Smax.romanov@nginx.com 
10441624Smax.romanov@nginx.com static void
10451624Smax.romanov@nginx.com nxt_py_asgi_quit(nxt_unit_ctx_t *ctx)
10461624Smax.romanov@nginx.com {
10471682Smax.romanov@nginx.com     PyObject                *res, *p;
10481681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
10491624Smax.romanov@nginx.com 
10501624Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_quit %p", ctx);
10511624Smax.romanov@nginx.com 
10521681Smax.romanov@nginx.com     ctx_data = ctx->data;
10531681Smax.romanov@nginx.com 
10541681Smax.romanov@nginx.com     if (nxt_py_shared_port != NULL) {
10551682Smax.romanov@nginx.com         p = PyLong_FromLong(nxt_py_shared_port->in_fd);
10561682Smax.romanov@nginx.com         if (nxt_slow_path(p == NULL)) {
10571682Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Python failed to create Long");
10581682Smax.romanov@nginx.com             nxt_python_print_exception();
10591682Smax.romanov@nginx.com 
10601682Smax.romanov@nginx.com         } else {
10611682Smax.romanov@nginx.com             res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader,
10621682Smax.romanov@nginx.com                                                p, NULL);
10631682Smax.romanov@nginx.com             if (nxt_slow_path(res == NULL)) {
10641682Smax.romanov@nginx.com                 nxt_unit_alert(NULL, "Python failed to remove_reader");
10651682Smax.romanov@nginx.com                 nxt_python_print_exception();
10661682Smax.romanov@nginx.com 
10671682Smax.romanov@nginx.com             } else {
10681682Smax.romanov@nginx.com                 Py_DECREF(res);
10691682Smax.romanov@nginx.com             }
10701682Smax.romanov@nginx.com 
10711682Smax.romanov@nginx.com             Py_DECREF(p);
10721682Smax.romanov@nginx.com         }
10731682Smax.romanov@nginx.com     }
10741682Smax.romanov@nginx.com 
10751682Smax.romanov@nginx.com     p = PyLong_FromLong(0);
10761682Smax.romanov@nginx.com     if (nxt_slow_path(p == NULL)) {
10771682Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Long");
10781682Smax.romanov@nginx.com         nxt_python_print_exception();
10791682Smax.romanov@nginx.com 
10801682Smax.romanov@nginx.com     } else {
10811682Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result,
10821682Smax.romanov@nginx.com                                            p, NULL);
10831681Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
10841682Smax.romanov@nginx.com             nxt_unit_alert(ctx, "Python failed to set_result");
10851681Smax.romanov@nginx.com             nxt_python_print_exception();
10861681Smax.romanov@nginx.com 
10871681Smax.romanov@nginx.com         } else {
10881681Smax.romanov@nginx.com             Py_DECREF(res);
10891681Smax.romanov@nginx.com         }
10901681Smax.romanov@nginx.com 
10911682Smax.romanov@nginx.com         Py_DECREF(p);
10921681Smax.romanov@nginx.com     }
10931624Smax.romanov@nginx.com }
10941624Smax.romanov@nginx.com 
10951624Smax.romanov@nginx.com 
10961624Smax.romanov@nginx.com static void
10971624Smax.romanov@nginx.com nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx)
10981624Smax.romanov@nginx.com {
10991681Smax.romanov@nginx.com     int                     rc;
11001681Smax.romanov@nginx.com     nxt_queue_link_t        *lnk;
11011681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
11021624Smax.romanov@nginx.com 
11031681Smax.romanov@nginx.com     ctx_data = ctx->data;
11041681Smax.romanov@nginx.com 
11051681Smax.romanov@nginx.com     while (!nxt_queue_is_empty(&ctx_data->drain_queue)) {
11061681Smax.romanov@nginx.com         lnk = nxt_queue_first(&ctx_data->drain_queue);
11071624Smax.romanov@nginx.com 
11081624Smax.romanov@nginx.com         rc = nxt_py_asgi_http_drain(lnk);
11091624Smax.romanov@nginx.com         if (rc == NXT_UNIT_AGAIN) {
11101681Smax.romanov@nginx.com             return;
11111624Smax.romanov@nginx.com         }
11121624Smax.romanov@nginx.com 
11131624Smax.romanov@nginx.com         nxt_queue_remove(lnk);
11141624Smax.romanov@nginx.com     }
11151624Smax.romanov@nginx.com }
11161624Smax.romanov@nginx.com 
11171624Smax.romanov@nginx.com 
11181624Smax.romanov@nginx.com static PyObject *
11191624Smax.romanov@nginx.com nxt_py_asgi_port_read(PyObject *self, PyObject *args)
11201624Smax.romanov@nginx.com {
11211624Smax.romanov@nginx.com     int              rc;
11221624Smax.romanov@nginx.com     PyObject         *arg;
11231624Smax.romanov@nginx.com     Py_ssize_t       n;
11241624Smax.romanov@nginx.com     nxt_unit_ctx_t   *ctx;
11251624Smax.romanov@nginx.com     nxt_unit_port_t  *port;
11261624Smax.romanov@nginx.com 
11271624Smax.romanov@nginx.com     n = PyTuple_GET_SIZE(args);
11281624Smax.romanov@nginx.com 
11291624Smax.romanov@nginx.com     if (n != 2) {
11301624Smax.romanov@nginx.com         nxt_unit_alert(NULL,
11311624Smax.romanov@nginx.com                        "nxt_py_asgi_port_read: invalid number of arguments %d",
11321624Smax.romanov@nginx.com                        (int) n);
11331624Smax.romanov@nginx.com 
11341624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
11351624Smax.romanov@nginx.com     }
11361624Smax.romanov@nginx.com 
11371624Smax.romanov@nginx.com     arg = PyTuple_GET_ITEM(args, 0);
11381624Smax.romanov@nginx.com     if (nxt_slow_path(arg == NULL || PyLong_Check(arg) == 0)) {
11391624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
11401624Smax.romanov@nginx.com                             "the first argument is not a long");
11411624Smax.romanov@nginx.com     }
11421624Smax.romanov@nginx.com 
11431624Smax.romanov@nginx.com     ctx = PyLong_AsVoidPtr(arg);
11441624Smax.romanov@nginx.com 
11451624Smax.romanov@nginx.com     arg = PyTuple_GET_ITEM(args, 1);
11461624Smax.romanov@nginx.com     if (nxt_slow_path(arg == NULL || PyLong_Check(arg) == 0)) {
11471624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
11481624Smax.romanov@nginx.com                             "the second argument is not a long");
11491624Smax.romanov@nginx.com     }
11501624Smax.romanov@nginx.com 
11511624Smax.romanov@nginx.com     port = PyLong_AsVoidPtr(arg);
11521624Smax.romanov@nginx.com 
11531624Smax.romanov@nginx.com     nxt_unit_debug(ctx, "asgi_port_read %p %p", ctx, port);
11541624Smax.romanov@nginx.com 
11551624Smax.romanov@nginx.com     rc = nxt_unit_process_port_msg(ctx, port);
11561624Smax.romanov@nginx.com 
11571624Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
11581624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
11591681Smax.romanov@nginx.com                             "error processing port %d message", port->id.id);
11601624Smax.romanov@nginx.com     }
11611624Smax.romanov@nginx.com 
11621624Smax.romanov@nginx.com     Py_RETURN_NONE;
11631624Smax.romanov@nginx.com }
11641624Smax.romanov@nginx.com 
11651624Smax.romanov@nginx.com 
11661624Smax.romanov@nginx.com PyObject *
11671624Smax.romanov@nginx.com nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb,
11681624Smax.romanov@nginx.com     void *data)
11691624Smax.romanov@nginx.com {
11701624Smax.romanov@nginx.com     int       i;
11711624Smax.romanov@nginx.com     PyObject  *iter, *header, *h_iter, *name, *val, *res;
11721624Smax.romanov@nginx.com 
11731624Smax.romanov@nginx.com     iter = PyObject_GetIter(headers);
11741624Smax.romanov@nginx.com     if (nxt_slow_path(iter == NULL)) {
11751624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable");
11761624Smax.romanov@nginx.com     }
11771624Smax.romanov@nginx.com 
11781624Smax.romanov@nginx.com     for (i = 0; /* void */; i++) {
11791624Smax.romanov@nginx.com         header = PyIter_Next(iter);
11801624Smax.romanov@nginx.com         if (header == NULL) {
11811624Smax.romanov@nginx.com             break;
11821624Smax.romanov@nginx.com         }
11831624Smax.romanov@nginx.com 
11841624Smax.romanov@nginx.com         h_iter = PyObject_GetIter(header);
11851624Smax.romanov@nginx.com         if (nxt_slow_path(h_iter == NULL)) {
11861624Smax.romanov@nginx.com             Py_DECREF(header);
11871624Smax.romanov@nginx.com             Py_DECREF(iter);
11881624Smax.romanov@nginx.com 
11891624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
11901624Smax.romanov@nginx.com                                 "'headers' item #%d is not an iterable", i);
11911624Smax.romanov@nginx.com         }
11921624Smax.romanov@nginx.com 
11931624Smax.romanov@nginx.com         name = PyIter_Next(h_iter);
11941624Smax.romanov@nginx.com         if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) {
11951624Smax.romanov@nginx.com             Py_XDECREF(name);
11961624Smax.romanov@nginx.com             Py_DECREF(h_iter);
11971624Smax.romanov@nginx.com             Py_DECREF(header);
11981624Smax.romanov@nginx.com             Py_DECREF(iter);
11991624Smax.romanov@nginx.com 
12001624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
12011624Smax.romanov@nginx.com                           "'headers' item #%d 'name' is not a byte string", i);
12021624Smax.romanov@nginx.com         }
12031624Smax.romanov@nginx.com 
12041624Smax.romanov@nginx.com         val = PyIter_Next(h_iter);
12051624Smax.romanov@nginx.com         if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) {
12061624Smax.romanov@nginx.com             Py_XDECREF(val);
12071624Smax.romanov@nginx.com             Py_DECREF(h_iter);
12081624Smax.romanov@nginx.com             Py_DECREF(header);
12091624Smax.romanov@nginx.com             Py_DECREF(iter);
12101624Smax.romanov@nginx.com 
12111624Smax.romanov@nginx.com             return PyErr_Format(PyExc_TypeError,
12121624Smax.romanov@nginx.com                          "'headers' item #%d 'value' is not a byte string", i);
12131624Smax.romanov@nginx.com         }
12141624Smax.romanov@nginx.com 
12151624Smax.romanov@nginx.com         res = cb(data, i, name, val);
12161624Smax.romanov@nginx.com 
12171624Smax.romanov@nginx.com         Py_DECREF(name);
12181624Smax.romanov@nginx.com         Py_DECREF(val);
12191624Smax.romanov@nginx.com         Py_DECREF(h_iter);
12201624Smax.romanov@nginx.com         Py_DECREF(header);
12211624Smax.romanov@nginx.com 
12221624Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
12231624Smax.romanov@nginx.com             Py_DECREF(iter);
12241624Smax.romanov@nginx.com 
12251624Smax.romanov@nginx.com             return NULL;
12261624Smax.romanov@nginx.com         }
12271624Smax.romanov@nginx.com 
12281624Smax.romanov@nginx.com         Py_DECREF(res);
12291624Smax.romanov@nginx.com     }
12301624Smax.romanov@nginx.com 
12311624Smax.romanov@nginx.com     Py_DECREF(iter);
12321624Smax.romanov@nginx.com 
12331624Smax.romanov@nginx.com     Py_RETURN_NONE;
12341624Smax.romanov@nginx.com }
12351624Smax.romanov@nginx.com 
12361624Smax.romanov@nginx.com 
12371624Smax.romanov@nginx.com PyObject *
12381624Smax.romanov@nginx.com nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val)
12391624Smax.romanov@nginx.com {
12401624Smax.romanov@nginx.com     nxt_py_asgi_calc_size_ctx_t  *ctx;
12411624Smax.romanov@nginx.com 
12421624Smax.romanov@nginx.com     ctx = data;
12431624Smax.romanov@nginx.com 
12441624Smax.romanov@nginx.com     ctx->fields_count++;
12451624Smax.romanov@nginx.com     ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val);
12461624Smax.romanov@nginx.com 
12471624Smax.romanov@nginx.com     Py_RETURN_NONE;
12481624Smax.romanov@nginx.com }
12491624Smax.romanov@nginx.com 
12501624Smax.romanov@nginx.com 
12511624Smax.romanov@nginx.com PyObject *
12521624Smax.romanov@nginx.com nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val)
12531624Smax.romanov@nginx.com {
12541624Smax.romanov@nginx.com     int                          rc;
12551624Smax.romanov@nginx.com     char                         *name_str, *val_str;
12561624Smax.romanov@nginx.com     uint32_t                     name_len, val_len;
12571624Smax.romanov@nginx.com     nxt_off_t                    content_length;
12581624Smax.romanov@nginx.com     nxt_unit_request_info_t      *req;
12591624Smax.romanov@nginx.com     nxt_py_asgi_add_field_ctx_t  *ctx;
12601624Smax.romanov@nginx.com 
12611624Smax.romanov@nginx.com     name_str = PyBytes_AS_STRING(name);
12621624Smax.romanov@nginx.com     name_len = PyBytes_GET_SIZE(name);
12631624Smax.romanov@nginx.com 
12641624Smax.romanov@nginx.com     val_str = PyBytes_AS_STRING(val);
12651624Smax.romanov@nginx.com     val_len = PyBytes_GET_SIZE(val);
12661624Smax.romanov@nginx.com 
12671624Smax.romanov@nginx.com     ctx = data;
12681624Smax.romanov@nginx.com     req = ctx->req;
12691624Smax.romanov@nginx.com 
12701624Smax.romanov@nginx.com     rc = nxt_unit_response_add_field(req, name_str, name_len,
12711624Smax.romanov@nginx.com                                      val_str, val_len);
12721624Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
12731624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
12741624Smax.romanov@nginx.com                             "failed to add header #%d", i);
12751624Smax.romanov@nginx.com     }
12761624Smax.romanov@nginx.com 
12771624Smax.romanov@nginx.com     if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
12781624Smax.romanov@nginx.com         content_length = nxt_off_t_parse((u_char *) val_str, val_len);
12791624Smax.romanov@nginx.com         if (nxt_slow_path(content_length < 0)) {
12801624Smax.romanov@nginx.com             nxt_unit_req_error(req, "failed to parse Content-Length "
12811624Smax.romanov@nginx.com                                "value %.*s", (int) val_len, val_str);
12821624Smax.romanov@nginx.com 
12831624Smax.romanov@nginx.com             return PyErr_Format(PyExc_ValueError,
12841624Smax.romanov@nginx.com                                 "Failed to parse Content-Length: '%.*s'",
12851624Smax.romanov@nginx.com                                 (int) val_len, val_str);
12861624Smax.romanov@nginx.com         }
12871624Smax.romanov@nginx.com 
12881624Smax.romanov@nginx.com         ctx->content_length = content_length;
12891624Smax.romanov@nginx.com     }
12901624Smax.romanov@nginx.com 
12911624Smax.romanov@nginx.com     Py_RETURN_NONE;
12921624Smax.romanov@nginx.com }
12931624Smax.romanov@nginx.com 
12941624Smax.romanov@nginx.com 
12951624Smax.romanov@nginx.com PyObject *
12961681Smax.romanov@nginx.com nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
12971681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result)
12981624Smax.romanov@nginx.com {
12991624Smax.romanov@nginx.com     PyObject  *set_result, *res;
13001624Smax.romanov@nginx.com 
13011624Smax.romanov@nginx.com     if (nxt_slow_path(result == NULL)) {
13021624Smax.romanov@nginx.com         Py_DECREF(future);
13031624Smax.romanov@nginx.com 
13041624Smax.romanov@nginx.com         return NULL;
13051624Smax.romanov@nginx.com     }
13061624Smax.romanov@nginx.com 
13071624Smax.romanov@nginx.com     set_result = PyObject_GetAttrString(future, "set_result");
13081624Smax.romanov@nginx.com     if (nxt_slow_path(set_result == NULL)) {
13091624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "failed to get 'set_result' for future");
13101624Smax.romanov@nginx.com 
13111624Smax.romanov@nginx.com         Py_CLEAR(future);
13121624Smax.romanov@nginx.com 
13131681Smax.romanov@nginx.com         goto cleanup_result;
13141624Smax.romanov@nginx.com     }
13151624Smax.romanov@nginx.com 
13161624Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(set_result) == 0)) {
13171624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "'future.set_result' is not a callable");
13181624Smax.romanov@nginx.com 
13191624Smax.romanov@nginx.com         Py_CLEAR(future);
13201624Smax.romanov@nginx.com 
13211624Smax.romanov@nginx.com         goto cleanup;
13221624Smax.romanov@nginx.com     }
13231624Smax.romanov@nginx.com 
13241681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result,
13251624Smax.romanov@nginx.com                                        result, NULL);
13261624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
13271624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'");
13281624Smax.romanov@nginx.com         nxt_python_print_exception();
13291624Smax.romanov@nginx.com 
13301624Smax.romanov@nginx.com         Py_CLEAR(future);
13311624Smax.romanov@nginx.com     }
13321624Smax.romanov@nginx.com 
13331624Smax.romanov@nginx.com     Py_XDECREF(res);
13341624Smax.romanov@nginx.com 
13351624Smax.romanov@nginx.com cleanup:
13361624Smax.romanov@nginx.com 
13371624Smax.romanov@nginx.com     Py_DECREF(set_result);
13381681Smax.romanov@nginx.com 
13391681Smax.romanov@nginx.com cleanup_result:
13401681Smax.romanov@nginx.com 
13411624Smax.romanov@nginx.com     Py_DECREF(result);
13421624Smax.romanov@nginx.com 
13431624Smax.romanov@nginx.com     return future;
13441624Smax.romanov@nginx.com }
13451624Smax.romanov@nginx.com 
13461624Smax.romanov@nginx.com 
13471624Smax.romanov@nginx.com PyObject *
13481624Smax.romanov@nginx.com nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type)
13491624Smax.romanov@nginx.com {
13501624Smax.romanov@nginx.com     PyObject  *msg;
13511624Smax.romanov@nginx.com 
13521624Smax.romanov@nginx.com     msg = PyDict_New();
13531624Smax.romanov@nginx.com     if (nxt_slow_path(msg == NULL)) {
13541624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create message dict");
13551624Smax.romanov@nginx.com         nxt_python_print_exception();
13561624Smax.romanov@nginx.com 
13571624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
13581624Smax.romanov@nginx.com                             "failed to create message dict");
13591624Smax.romanov@nginx.com     }
13601624Smax.romanov@nginx.com 
13611624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) {
13621624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'msg.type' item");
13631624Smax.romanov@nginx.com 
13641624Smax.romanov@nginx.com         Py_DECREF(msg);
13651624Smax.romanov@nginx.com 
13661624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
13671624Smax.romanov@nginx.com                             "failed to set 'msg.type' item");
13681624Smax.romanov@nginx.com     }
13691624Smax.romanov@nginx.com 
13701624Smax.romanov@nginx.com     return msg;
13711624Smax.romanov@nginx.com }
13721624Smax.romanov@nginx.com 
13731624Smax.romanov@nginx.com 
13741624Smax.romanov@nginx.com PyObject *
13751624Smax.romanov@nginx.com nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
13761624Smax.romanov@nginx.com     PyObject *spec_version)
13771624Smax.romanov@nginx.com {
13781624Smax.romanov@nginx.com     PyObject  *scope, *asgi;
13791624Smax.romanov@nginx.com 
13801624Smax.romanov@nginx.com     scope = PyDict_New();
13811624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
13821624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'scope' dict");
13831624Smax.romanov@nginx.com         nxt_python_print_exception();
13841624Smax.romanov@nginx.com 
13851624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
13861624Smax.romanov@nginx.com                             "failed to create 'scope' dict");
13871624Smax.romanov@nginx.com     }
13881624Smax.romanov@nginx.com 
13891624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) {
13901624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'scope.type' item");
13911624Smax.romanov@nginx.com 
13921624Smax.romanov@nginx.com         Py_DECREF(scope);
13931624Smax.romanov@nginx.com 
13941624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
13951624Smax.romanov@nginx.com                             "failed to set 'scope.type' item");
13961624Smax.romanov@nginx.com     }
13971624Smax.romanov@nginx.com 
13981624Smax.romanov@nginx.com     asgi = PyDict_New();
13991624Smax.romanov@nginx.com     if (nxt_slow_path(asgi == NULL)) {
14001624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to create 'asgi' dict");
14011624Smax.romanov@nginx.com         nxt_python_print_exception();
14021624Smax.romanov@nginx.com 
14031624Smax.romanov@nginx.com         Py_DECREF(scope);
14041624Smax.romanov@nginx.com 
14051624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14061624Smax.romanov@nginx.com                             "failed to create 'asgi' dict");
14071624Smax.romanov@nginx.com     }
14081624Smax.romanov@nginx.com 
14091624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) {
14101624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item");
14111624Smax.romanov@nginx.com 
14121624Smax.romanov@nginx.com         Py_DECREF(asgi);
14131624Smax.romanov@nginx.com         Py_DECREF(scope);
14141624Smax.romanov@nginx.com 
14151624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14161624Smax.romanov@nginx.com                             "failed to set 'scope.asgi' item");
14171624Smax.romanov@nginx.com     }
14181624Smax.romanov@nginx.com 
14191624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str,
14201624Smax.romanov@nginx.com                                      nxt_py_3_0_str) == -1))
14211624Smax.romanov@nginx.com     {
14221624Smax.romanov@nginx.com         nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item");
14231624Smax.romanov@nginx.com 
14241624Smax.romanov@nginx.com         Py_DECREF(asgi);
14251624Smax.romanov@nginx.com         Py_DECREF(scope);
14261624Smax.romanov@nginx.com 
14271624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14281624Smax.romanov@nginx.com                             "failed to set 'asgi.version' item");
14291624Smax.romanov@nginx.com     }
14301624Smax.romanov@nginx.com 
14311624Smax.romanov@nginx.com     if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str,
14321624Smax.romanov@nginx.com                                      spec_version) == -1))
14331624Smax.romanov@nginx.com     {
14341624Smax.romanov@nginx.com         nxt_unit_req_alert(req,
14351624Smax.romanov@nginx.com                            "Python failed to set 'asgi.spec_version' item");
14361624Smax.romanov@nginx.com 
14371624Smax.romanov@nginx.com         Py_DECREF(asgi);
14381624Smax.romanov@nginx.com         Py_DECREF(scope);
14391624Smax.romanov@nginx.com 
14401624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
14411624Smax.romanov@nginx.com                             "failed to set 'asgi.spec_version' item");
14421624Smax.romanov@nginx.com     }
14431624Smax.romanov@nginx.com 
14441624Smax.romanov@nginx.com     Py_DECREF(asgi);
14451624Smax.romanov@nginx.com 
14461624Smax.romanov@nginx.com     return scope;
14471624Smax.romanov@nginx.com }
14481624Smax.romanov@nginx.com 
14491624Smax.romanov@nginx.com 
14501624Smax.romanov@nginx.com void
14511681Smax.romanov@nginx.com nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link)
14521681Smax.romanov@nginx.com {
14531681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
14541681Smax.romanov@nginx.com 
14551681Smax.romanov@nginx.com     ctx_data = req->ctx->data;
14561681Smax.romanov@nginx.com 
14571681Smax.romanov@nginx.com     nxt_queue_insert_tail(&ctx_data->drain_queue, link);
14581681Smax.romanov@nginx.com }
14591681Smax.romanov@nginx.com 
14601681Smax.romanov@nginx.com 
14611681Smax.romanov@nginx.com void
14621624Smax.romanov@nginx.com nxt_py_asgi_dealloc(PyObject *self)
14631624Smax.romanov@nginx.com {
14641624Smax.romanov@nginx.com     PyObject_Del(self);
14651624Smax.romanov@nginx.com }
14661624Smax.romanov@nginx.com 
14671624Smax.romanov@nginx.com 
14681624Smax.romanov@nginx.com PyObject *
14691624Smax.romanov@nginx.com nxt_py_asgi_await(PyObject *self)
14701624Smax.romanov@nginx.com {
14711624Smax.romanov@nginx.com     Py_INCREF(self);
14721624Smax.romanov@nginx.com     return self;
14731624Smax.romanov@nginx.com }
14741624Smax.romanov@nginx.com 
14751624Smax.romanov@nginx.com 
14761624Smax.romanov@nginx.com PyObject *
14771624Smax.romanov@nginx.com nxt_py_asgi_iter(PyObject *self)
14781624Smax.romanov@nginx.com {
14791624Smax.romanov@nginx.com     Py_INCREF(self);
14801624Smax.romanov@nginx.com     return self;
14811624Smax.romanov@nginx.com }
14821624Smax.romanov@nginx.com 
14831624Smax.romanov@nginx.com 
14841624Smax.romanov@nginx.com PyObject *
14851624Smax.romanov@nginx.com nxt_py_asgi_next(PyObject *self)
14861624Smax.romanov@nginx.com {
14871624Smax.romanov@nginx.com     return NULL;
14881624Smax.romanov@nginx.com }
14891624Smax.romanov@nginx.com 
14901624Smax.romanov@nginx.com 
14911681Smax.romanov@nginx.com static void
14921624Smax.romanov@nginx.com nxt_python_asgi_done(void)
14931624Smax.romanov@nginx.com {
14941624Smax.romanov@nginx.com     nxt_py_asgi_str_done();
14951624Smax.romanov@nginx.com 
14961624Smax.romanov@nginx.com     Py_XDECREF(nxt_py_port_read);
14971624Smax.romanov@nginx.com }
14981624Smax.romanov@nginx.com 
14991624Smax.romanov@nginx.com #else /* !(NXT_HAVE_ASGI) */
15001624Smax.romanov@nginx.com 
15011624Smax.romanov@nginx.com 
15021624Smax.romanov@nginx.com int
15031624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj)
15041624Smax.romanov@nginx.com {
15051624Smax.romanov@nginx.com     return 0;
15061624Smax.romanov@nginx.com }
15071624Smax.romanov@nginx.com 
15081624Smax.romanov@nginx.com 
15091681Smax.romanov@nginx.com int
15101681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
15111624Smax.romanov@nginx.com {
15121681Smax.romanov@nginx.com     nxt_unit_alert(NULL, "ASGI not implemented");
15131681Smax.romanov@nginx.com     return NXT_UNIT_ERROR;
15141624Smax.romanov@nginx.com }
15151624Smax.romanov@nginx.com 
15161624Smax.romanov@nginx.com 
15171624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */
1518