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); 20*1918Smax.romanov@nginx.com static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main); 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); 281715Smax.romanov@nginx.com static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req); 291624Smax.romanov@nginx.com 301624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req); 311624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, 321624Smax.romanov@nginx.com uint16_t port); 331624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f); 341624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f); 351624Smax.romanov@nginx.com 361681Smax.romanov@nginx.com static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx); 371681Smax.romanov@nginx.com 381624Smax.romanov@nginx.com static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); 391624Smax.romanov@nginx.com static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port); 401624Smax.romanov@nginx.com static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); 411624Smax.romanov@nginx.com static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); 421624Smax.romanov@nginx.com 431624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); 441681Smax.romanov@nginx.com static void nxt_python_asgi_done(void); 451624Smax.romanov@nginx.com 461681Smax.romanov@nginx.com static PyObject *nxt_py_port_read; 471681Smax.romanov@nginx.com static nxt_unit_port_t *nxt_py_shared_port; 481624Smax.romanov@nginx.com 491624Smax.romanov@nginx.com static PyMethodDef nxt_py_port_read_method = 501624Smax.romanov@nginx.com {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""}; 511624Smax.romanov@nginx.com 521681Smax.romanov@nginx.com static nxt_python_proto_t nxt_py_asgi_proto = { 531681Smax.romanov@nginx.com .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc, 541681Smax.romanov@nginx.com .ctx_data_free = nxt_python_asgi_ctx_data_free, 551681Smax.romanov@nginx.com .startup = nxt_python_asgi_startup, 561681Smax.romanov@nginx.com .run = nxt_python_asgi_run, 571681Smax.romanov@nginx.com .ready = nxt_python_asgi_ready, 581681Smax.romanov@nginx.com .done = nxt_python_asgi_done, 591681Smax.romanov@nginx.com }; 601681Smax.romanov@nginx.com 611624Smax.romanov@nginx.com #define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A 621624Smax.romanov@nginx.com 631624Smax.romanov@nginx.com 641624Smax.romanov@nginx.com int 651624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj) 661624Smax.romanov@nginx.com { 671624Smax.romanov@nginx.com int res; 681697Smax.romanov@nginx.com PyObject *func; 691624Smax.romanov@nginx.com PyCodeObject *code; 701624Smax.romanov@nginx.com 711697Smax.romanov@nginx.com func = nxt_python_asgi_get_func(obj); 721697Smax.romanov@nginx.com 731697Smax.romanov@nginx.com if (func == NULL) { 741697Smax.romanov@nginx.com return 0; 751697Smax.romanov@nginx.com } 761697Smax.romanov@nginx.com 771697Smax.romanov@nginx.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 781697Smax.romanov@nginx.com 791697Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with " 801697Smax.romanov@nginx.com "%d argument(s)", 811697Smax.romanov@nginx.com (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ", 821697Smax.romanov@nginx.com code->co_argcount); 831697Smax.romanov@nginx.com 841697Smax.romanov@nginx.com res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1; 851697Smax.romanov@nginx.com 861697Smax.romanov@nginx.com Py_DECREF(func); 871697Smax.romanov@nginx.com 881697Smax.romanov@nginx.com return res; 891697Smax.romanov@nginx.com } 901697Smax.romanov@nginx.com 911697Smax.romanov@nginx.com 921697Smax.romanov@nginx.com static PyObject * 931697Smax.romanov@nginx.com nxt_python_asgi_get_func(PyObject *obj) 941697Smax.romanov@nginx.com { 951697Smax.romanov@nginx.com PyObject *call; 961697Smax.romanov@nginx.com 971624Smax.romanov@nginx.com if (PyFunction_Check(obj)) { 981697Smax.romanov@nginx.com Py_INCREF(obj); 991697Smax.romanov@nginx.com return obj; 1001624Smax.romanov@nginx.com } 1011624Smax.romanov@nginx.com 1021624Smax.romanov@nginx.com if (PyMethod_Check(obj)) { 1031624Smax.romanov@nginx.com obj = PyMethod_GET_FUNCTION(obj); 1041624Smax.romanov@nginx.com 1051697Smax.romanov@nginx.com Py_INCREF(obj); 1061697Smax.romanov@nginx.com return obj; 1071624Smax.romanov@nginx.com } 1081624Smax.romanov@nginx.com 1091624Smax.romanov@nginx.com call = PyObject_GetAttrString(obj, "__call__"); 1101624Smax.romanov@nginx.com 1111624Smax.romanov@nginx.com if (call == NULL) { 1121697Smax.romanov@nginx.com return NULL; 1131624Smax.romanov@nginx.com } 1141624Smax.romanov@nginx.com 1151624Smax.romanov@nginx.com if (PyFunction_Check(call)) { 1161697Smax.romanov@nginx.com return call; 1171697Smax.romanov@nginx.com } 1181624Smax.romanov@nginx.com 1191697Smax.romanov@nginx.com if (PyMethod_Check(call)) { 1201697Smax.romanov@nginx.com obj = PyMethod_GET_FUNCTION(call); 1211624Smax.romanov@nginx.com 1221697Smax.romanov@nginx.com Py_INCREF(obj); 1231697Smax.romanov@nginx.com Py_DECREF(call); 1241697Smax.romanov@nginx.com 1251697Smax.romanov@nginx.com return obj; 1261624Smax.romanov@nginx.com } 1271624Smax.romanov@nginx.com 1281624Smax.romanov@nginx.com Py_DECREF(call); 1291624Smax.romanov@nginx.com 1301697Smax.romanov@nginx.com return NULL; 1311624Smax.romanov@nginx.com } 1321624Smax.romanov@nginx.com 1331624Smax.romanov@nginx.com 1341681Smax.romanov@nginx.com int 1351681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 1361624Smax.romanov@nginx.com { 1371697Smax.romanov@nginx.com PyObject *func; 1381872So.canty@f5.com nxt_int_t i; 1391697Smax.romanov@nginx.com PyCodeObject *code; 1401697Smax.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 1631872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) { 1641872So.canty@f5.com func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application); 1651872So.canty@f5.com if (nxt_slow_path(func == NULL)) { 1661872So.canty@f5.com nxt_unit_alert(NULL, "Python cannot find function for callable"); 1671872So.canty@f5.com return NXT_UNIT_ERROR; 1681872So.canty@f5.com } 1691697Smax.romanov@nginx.com 1701872So.canty@f5.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 1711697Smax.romanov@nginx.com 1721872So.canty@f5.com if ((code->co_flags & CO_COROUTINE) == 0) { 1731872So.canty@f5.com nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " 1741872So.canty@f5.com "switching to legacy mode"); 1751872So.canty@f5.com nxt_py_targets->target[i].asgi_legacy = 1; 1761872So.canty@f5.com } 1771872So.canty@f5.com 1781872So.canty@f5.com Py_DECREF(func); 1791697Smax.romanov@nginx.com } 1801697Smax.romanov@nginx.com 1811624Smax.romanov@nginx.com init->callbacks.request_handler = nxt_py_asgi_request_handler; 1821624Smax.romanov@nginx.com init->callbacks.data_handler = nxt_py_asgi_http_data_handler; 1831624Smax.romanov@nginx.com init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler; 1841715Smax.romanov@nginx.com init->callbacks.close_handler = nxt_py_asgi_close_handler; 1851624Smax.romanov@nginx.com init->callbacks.quit = nxt_py_asgi_quit; 1861624Smax.romanov@nginx.com init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; 1871624Smax.romanov@nginx.com init->callbacks.add_port = nxt_py_asgi_add_port; 1881624Smax.romanov@nginx.com init->callbacks.remove_port = nxt_py_asgi_remove_port; 1891624Smax.romanov@nginx.com 1901681Smax.romanov@nginx.com *proto = nxt_py_asgi_proto; 1911681Smax.romanov@nginx.com 1921681Smax.romanov@nginx.com return NXT_UNIT_OK; 1931681Smax.romanov@nginx.com } 1941681Smax.romanov@nginx.com 1951681Smax.romanov@nginx.com 1961681Smax.romanov@nginx.com static int 197*1918Smax.romanov@nginx.com nxt_python_asgi_ctx_data_alloc(void **pdata, int main) 1981681Smax.romanov@nginx.com { 1991681Smax.romanov@nginx.com uint32_t i; 200*1918Smax.romanov@nginx.com PyObject *asyncio, *loop, *event_loop, *obj; 201*1918Smax.romanov@nginx.com const char *event_loop_func; 2021681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 2031681Smax.romanov@nginx.com 2041681Smax.romanov@nginx.com ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); 2051681Smax.romanov@nginx.com if (nxt_slow_path(ctx_data == NULL)) { 2061681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate context data"); 2071681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2081681Smax.romanov@nginx.com } 2091681Smax.romanov@nginx.com 2101681Smax.romanov@nginx.com memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t)); 2111681Smax.romanov@nginx.com 2121681Smax.romanov@nginx.com nxt_queue_init(&ctx_data->drain_queue); 2131681Smax.romanov@nginx.com 2141681Smax.romanov@nginx.com struct { 2151681Smax.romanov@nginx.com const char *key; 2161681Smax.romanov@nginx.com PyObject **handler; 2171681Smax.romanov@nginx.com 2181681Smax.romanov@nginx.com } handlers[] = { 2191681Smax.romanov@nginx.com { "create_task", &ctx_data->loop_create_task }, 2201681Smax.romanov@nginx.com { "add_reader", &ctx_data->loop_add_reader }, 2211681Smax.romanov@nginx.com { "remove_reader", &ctx_data->loop_remove_reader }, 2221681Smax.romanov@nginx.com { "call_soon", &ctx_data->loop_call_soon }, 2231681Smax.romanov@nginx.com { "run_until_complete", &ctx_data->loop_run_until_complete }, 2241681Smax.romanov@nginx.com { "create_future", &ctx_data->loop_create_future }, 2251681Smax.romanov@nginx.com }; 2261681Smax.romanov@nginx.com 2271681Smax.romanov@nginx.com loop = NULL; 2281681Smax.romanov@nginx.com 2291681Smax.romanov@nginx.com asyncio = PyImport_ImportModule("asyncio"); 2301681Smax.romanov@nginx.com if (nxt_slow_path(asyncio == NULL)) { 2311681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to import module 'asyncio'"); 2321681Smax.romanov@nginx.com nxt_python_print_exception(); 2331681Smax.romanov@nginx.com goto fail; 2341681Smax.romanov@nginx.com } 2351681Smax.romanov@nginx.com 236*1918Smax.romanov@nginx.com event_loop_func = main ? "get_event_loop" : "new_event_loop"; 237*1918Smax.romanov@nginx.com 238*1918Smax.romanov@nginx.com event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), 239*1918Smax.romanov@nginx.com event_loop_func); 240*1918Smax.romanov@nginx.com if (nxt_slow_path(event_loop == NULL)) { 2411681Smax.romanov@nginx.com nxt_unit_alert(NULL, 242*1918Smax.romanov@nginx.com "Python failed to get '%s' from module 'asyncio'", 243*1918Smax.romanov@nginx.com event_loop_func); 2441681Smax.romanov@nginx.com goto fail; 2451681Smax.romanov@nginx.com } 2461681Smax.romanov@nginx.com 247*1918Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { 2481681Smax.romanov@nginx.com nxt_unit_alert(NULL, 249*1918Smax.romanov@nginx.com "'asyncio.%s' is not a callable object", 250*1918Smax.romanov@nginx.com event_loop_func); 2511681Smax.romanov@nginx.com goto fail; 2521681Smax.romanov@nginx.com } 2531681Smax.romanov@nginx.com 254*1918Smax.romanov@nginx.com loop = PyObject_CallObject(event_loop, NULL); 2551681Smax.romanov@nginx.com if (nxt_slow_path(loop == NULL)) { 256*1918Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", 257*1918Smax.romanov@nginx.com event_loop_func); 2581681Smax.romanov@nginx.com goto fail; 2591681Smax.romanov@nginx.com } 2601681Smax.romanov@nginx.com 2611681Smax.romanov@nginx.com for (i = 0; i < nxt_nitems(handlers); i++) { 2621681Smax.romanov@nginx.com obj = PyObject_GetAttrString(loop, handlers[i].key); 2631681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2641681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'loop.%s'", 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 *handlers[i].handler = obj; 2701681Smax.romanov@nginx.com 2711681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 2721681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'loop.%s' is not a callable object", 2731681Smax.romanov@nginx.com handlers[i].key); 2741681Smax.romanov@nginx.com goto fail; 2751681Smax.romanov@nginx.com } 2761681Smax.romanov@nginx.com } 2771681Smax.romanov@nginx.com 2781681Smax.romanov@nginx.com obj = PyObject_CallObject(ctx_data->loop_create_future, NULL); 2791681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2801681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future "); 2811681Smax.romanov@nginx.com nxt_python_print_exception(); 2821681Smax.romanov@nginx.com goto fail; 2831681Smax.romanov@nginx.com } 2841681Smax.romanov@nginx.com 2851681Smax.romanov@nginx.com ctx_data->quit_future = obj; 2861681Smax.romanov@nginx.com 2871681Smax.romanov@nginx.com obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result"); 2881681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2891681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'future.set_result'"); 2901681Smax.romanov@nginx.com goto fail; 2911681Smax.romanov@nginx.com } 2921681Smax.romanov@nginx.com 2931681Smax.romanov@nginx.com ctx_data->quit_future_set_result = obj; 2941681Smax.romanov@nginx.com 2951681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 2961681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'future.set_result' is not a callable object"); 2971681Smax.romanov@nginx.com goto fail; 2981681Smax.romanov@nginx.com } 2991681Smax.romanov@nginx.com 3001624Smax.romanov@nginx.com Py_DECREF(loop); 3011624Smax.romanov@nginx.com Py_DECREF(asyncio); 3021624Smax.romanov@nginx.com 3031681Smax.romanov@nginx.com *pdata = ctx_data; 3041681Smax.romanov@nginx.com 3051681Smax.romanov@nginx.com return NXT_UNIT_OK; 3061624Smax.romanov@nginx.com 3071624Smax.romanov@nginx.com fail: 3081624Smax.romanov@nginx.com 3091681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(ctx_data); 3101681Smax.romanov@nginx.com 3111624Smax.romanov@nginx.com Py_XDECREF(loop); 3121681Smax.romanov@nginx.com Py_XDECREF(asyncio); 3131624Smax.romanov@nginx.com 3141681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3151624Smax.romanov@nginx.com } 3161624Smax.romanov@nginx.com 3171624Smax.romanov@nginx.com 3181681Smax.romanov@nginx.com static void 3191681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(void *data) 3201681Smax.romanov@nginx.com { 3211681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3221681Smax.romanov@nginx.com 3231681Smax.romanov@nginx.com ctx_data = data; 3241681Smax.romanov@nginx.com 3251681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_run_until_complete); 3261681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_future); 3271681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_task); 3281681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_call_soon); 3291681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_add_reader); 3301681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_remove_reader); 3311681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future); 3321681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future_set_result); 3331681Smax.romanov@nginx.com 3341681Smax.romanov@nginx.com nxt_unit_free(NULL, ctx_data); 3351681Smax.romanov@nginx.com } 3361681Smax.romanov@nginx.com 3371681Smax.romanov@nginx.com 3381681Smax.romanov@nginx.com static int 3391681Smax.romanov@nginx.com nxt_python_asgi_startup(void *data) 3401681Smax.romanov@nginx.com { 3411681Smax.romanov@nginx.com return nxt_py_asgi_lifespan_startup(data); 3421681Smax.romanov@nginx.com } 3431681Smax.romanov@nginx.com 3441681Smax.romanov@nginx.com 3451681Smax.romanov@nginx.com static int 3461624Smax.romanov@nginx.com nxt_python_asgi_run(nxt_unit_ctx_t *ctx) 3471624Smax.romanov@nginx.com { 3481681Smax.romanov@nginx.com PyObject *res; 3491681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3501624Smax.romanov@nginx.com 3511681Smax.romanov@nginx.com ctx_data = ctx->data; 3521681Smax.romanov@nginx.com 3531681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 3541681Smax.romanov@nginx.com ctx_data->quit_future, NULL); 3551624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 3561624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete"); 3571624Smax.romanov@nginx.com nxt_python_print_exception(); 3581624Smax.romanov@nginx.com 3591681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3601624Smax.romanov@nginx.com } 3611624Smax.romanov@nginx.com 3621624Smax.romanov@nginx.com Py_DECREF(res); 3631624Smax.romanov@nginx.com 3641681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port); 3651681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(ctx, ctx_data->port); 3661681Smax.romanov@nginx.com 3671681Smax.romanov@nginx.com if (ctx_data->port != NULL) { 3681681Smax.romanov@nginx.com ctx_data->port->data = NULL; 3691681Smax.romanov@nginx.com ctx_data->port = NULL; 3701681Smax.romanov@nginx.com } 3711681Smax.romanov@nginx.com 3721681Smax.romanov@nginx.com nxt_py_asgi_lifespan_shutdown(ctx); 3731681Smax.romanov@nginx.com 3741681Smax.romanov@nginx.com return NXT_UNIT_OK; 3751681Smax.romanov@nginx.com } 3761681Smax.romanov@nginx.com 3771624Smax.romanov@nginx.com 3781681Smax.romanov@nginx.com static void 3791681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 3801681Smax.romanov@nginx.com { 3811682Smax.romanov@nginx.com PyObject *res, *fd; 3821681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3831681Smax.romanov@nginx.com 3841681Smax.romanov@nginx.com if (port == NULL || port->in_fd == -1) { 3851681Smax.romanov@nginx.com return; 3861681Smax.romanov@nginx.com } 3871681Smax.romanov@nginx.com 3881681Smax.romanov@nginx.com ctx_data = ctx->data; 3891681Smax.romanov@nginx.com 3901681Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port); 3911681Smax.romanov@nginx.com 3921682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 3931682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 3941682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create Long object"); 3951681Smax.romanov@nginx.com nxt_python_print_exception(); 3961681Smax.romanov@nginx.com 3971681Smax.romanov@nginx.com return; 3981681Smax.romanov@nginx.com } 3991681Smax.romanov@nginx.com 4001682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL); 4011682Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 4021682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to remove_reader"); 4031682Smax.romanov@nginx.com nxt_python_print_exception(); 4041682Smax.romanov@nginx.com 4051682Smax.romanov@nginx.com } else { 4061682Smax.romanov@nginx.com Py_DECREF(res); 4071682Smax.romanov@nginx.com } 4081682Smax.romanov@nginx.com 4091682Smax.romanov@nginx.com Py_DECREF(fd); 4101624Smax.romanov@nginx.com } 4111624Smax.romanov@nginx.com 4121624Smax.romanov@nginx.com 4131624Smax.romanov@nginx.com static void 4141624Smax.romanov@nginx.com nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) 4151624Smax.romanov@nginx.com { 4161681Smax.romanov@nginx.com PyObject *scope, *res, *task, *receive, *send, *done, *asgi; 4171697Smax.romanov@nginx.com PyObject *stage2; 4181872So.canty@f5.com nxt_python_target_t *target; 4191681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 4201624Smax.romanov@nginx.com 4211624Smax.romanov@nginx.com if (req->request->websocket_handshake) { 4221624Smax.romanov@nginx.com asgi = nxt_py_asgi_websocket_create(req); 4231624Smax.romanov@nginx.com 4241624Smax.romanov@nginx.com } else { 4251624Smax.romanov@nginx.com asgi = nxt_py_asgi_http_create(req); 4261624Smax.romanov@nginx.com } 4271624Smax.romanov@nginx.com 4281624Smax.romanov@nginx.com if (nxt_slow_path(asgi == NULL)) { 4291624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create asgi object"); 4301624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4311624Smax.romanov@nginx.com 4321624Smax.romanov@nginx.com return; 4331624Smax.romanov@nginx.com } 4341624Smax.romanov@nginx.com 4351624Smax.romanov@nginx.com receive = PyObject_GetAttrString(asgi, "receive"); 4361624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4371624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get 'receive' method"); 4381624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4391624Smax.romanov@nginx.com 4401624Smax.romanov@nginx.com goto release_asgi; 4411624Smax.romanov@nginx.com } 4421624Smax.romanov@nginx.com 4431624Smax.romanov@nginx.com send = PyObject_GetAttrString(asgi, "send"); 4441624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4451624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get 'send' method"); 4461624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4471624Smax.romanov@nginx.com 4481624Smax.romanov@nginx.com goto release_receive; 4491624Smax.romanov@nginx.com } 4501624Smax.romanov@nginx.com 4511624Smax.romanov@nginx.com done = PyObject_GetAttrString(asgi, "_done"); 4521624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4531624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get '_done' method"); 4541624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4551624Smax.romanov@nginx.com 4561624Smax.romanov@nginx.com goto release_send; 4571624Smax.romanov@nginx.com } 4581624Smax.romanov@nginx.com 4591624Smax.romanov@nginx.com scope = nxt_py_asgi_create_http_scope(req); 4601624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 4611624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4621624Smax.romanov@nginx.com 4631624Smax.romanov@nginx.com goto release_done; 4641624Smax.romanov@nginx.com } 4651624Smax.romanov@nginx.com 4661624Smax.romanov@nginx.com req->data = asgi; 4671872So.canty@f5.com target = &nxt_py_targets->target[req->request->app_target]; 4681624Smax.romanov@nginx.com 4691872So.canty@f5.com if (!target->asgi_legacy) { 4701697Smax.romanov@nginx.com nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); 4711697Smax.romanov@nginx.com 4721872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application, 4731697Smax.romanov@nginx.com scope, receive, send, NULL); 4741697Smax.romanov@nginx.com 4751697Smax.romanov@nginx.com } else { 4761697Smax.romanov@nginx.com nxt_unit_req_debug(req, "Python call legacy application"); 4771697Smax.romanov@nginx.com 4781872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application, scope, NULL); 4791697Smax.romanov@nginx.com 4801697Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 4811697Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call legacy app stage1"); 4821697Smax.romanov@nginx.com nxt_python_print_exception(); 4831697Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4841697Smax.romanov@nginx.com 4851697Smax.romanov@nginx.com goto release_scope; 4861697Smax.romanov@nginx.com } 4871697Smax.romanov@nginx.com 4881697Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(res) == 0)) { 4891697Smax.romanov@nginx.com nxt_unit_req_error(req, 4901697Smax.romanov@nginx.com "Legacy ASGI application returns not a callable"); 4911697Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4921697Smax.romanov@nginx.com 4931697Smax.romanov@nginx.com Py_DECREF(res); 4941697Smax.romanov@nginx.com 4951697Smax.romanov@nginx.com goto release_scope; 4961697Smax.romanov@nginx.com } 4971697Smax.romanov@nginx.com 4981697Smax.romanov@nginx.com stage2 = res; 4991697Smax.romanov@nginx.com 5001697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); 5011697Smax.romanov@nginx.com 5021697Smax.romanov@nginx.com Py_DECREF(stage2); 5031697Smax.romanov@nginx.com } 5041697Smax.romanov@nginx.com 5051624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5061624Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the application"); 5071624Smax.romanov@nginx.com nxt_python_print_exception(); 5081624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5091624Smax.romanov@nginx.com 5101624Smax.romanov@nginx.com goto release_scope; 5111624Smax.romanov@nginx.com } 5121624Smax.romanov@nginx.com 5131624Smax.romanov@nginx.com if (nxt_slow_path(!PyCoro_CheckExact(res))) { 5141624Smax.romanov@nginx.com nxt_unit_req_error(req, "Application result type is not a coroutine"); 5151624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5161624Smax.romanov@nginx.com 5171624Smax.romanov@nginx.com Py_DECREF(res); 5181624Smax.romanov@nginx.com 5191624Smax.romanov@nginx.com goto release_scope; 5201624Smax.romanov@nginx.com } 5211624Smax.romanov@nginx.com 5221681Smax.romanov@nginx.com ctx_data = req->ctx->data; 5231681Smax.romanov@nginx.com 5241681Smax.romanov@nginx.com task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); 5251624Smax.romanov@nginx.com if (nxt_slow_path(task == NULL)) { 5261624Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the create_task"); 5271624Smax.romanov@nginx.com nxt_python_print_exception(); 5281624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5291624Smax.romanov@nginx.com 5301624Smax.romanov@nginx.com Py_DECREF(res); 5311624Smax.romanov@nginx.com 5321624Smax.romanov@nginx.com goto release_scope; 5331624Smax.romanov@nginx.com } 5341624Smax.romanov@nginx.com 5351624Smax.romanov@nginx.com Py_DECREF(res); 5361624Smax.romanov@nginx.com 5371624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done, 5381624Smax.romanov@nginx.com NULL); 5391624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5401624Smax.romanov@nginx.com nxt_unit_req_error(req, 5411624Smax.romanov@nginx.com "Python failed to call 'task.add_done_callback'"); 5421624Smax.romanov@nginx.com nxt_python_print_exception(); 5431624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5441624Smax.romanov@nginx.com 5451624Smax.romanov@nginx.com goto release_task; 5461624Smax.romanov@nginx.com } 5471624Smax.romanov@nginx.com 5481624Smax.romanov@nginx.com Py_DECREF(res); 5491624Smax.romanov@nginx.com release_task: 5501624Smax.romanov@nginx.com Py_DECREF(task); 5511624Smax.romanov@nginx.com release_scope: 5521624Smax.romanov@nginx.com Py_DECREF(scope); 5531624Smax.romanov@nginx.com release_done: 5541624Smax.romanov@nginx.com Py_DECREF(done); 5551624Smax.romanov@nginx.com release_send: 5561624Smax.romanov@nginx.com Py_DECREF(send); 5571624Smax.romanov@nginx.com release_receive: 5581624Smax.romanov@nginx.com Py_DECREF(receive); 5591624Smax.romanov@nginx.com release_asgi: 5601624Smax.romanov@nginx.com Py_DECREF(asgi); 5611624Smax.romanov@nginx.com } 5621624Smax.romanov@nginx.com 5631624Smax.romanov@nginx.com 5641715Smax.romanov@nginx.com static void 5651715Smax.romanov@nginx.com nxt_py_asgi_close_handler(nxt_unit_request_info_t *req) 5661715Smax.romanov@nginx.com { 5671715Smax.romanov@nginx.com if (req->request->websocket_handshake) { 5681715Smax.romanov@nginx.com nxt_py_asgi_websocket_close_handler(req); 5691715Smax.romanov@nginx.com 5701715Smax.romanov@nginx.com } else { 5711715Smax.romanov@nginx.com nxt_py_asgi_http_close_handler(req); 5721715Smax.romanov@nginx.com } 5731715Smax.romanov@nginx.com } 5741715Smax.romanov@nginx.com 5751715Smax.romanov@nginx.com 5761624Smax.romanov@nginx.com static PyObject * 5771624Smax.romanov@nginx.com nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) 5781624Smax.romanov@nginx.com { 5791624Smax.romanov@nginx.com char *p, *target, *query; 5801624Smax.romanov@nginx.com uint32_t target_length, i; 5811624Smax.romanov@nginx.com PyObject *scope, *v, *type, *scheme; 5821624Smax.romanov@nginx.com PyObject *headers, *header; 5831624Smax.romanov@nginx.com nxt_unit_field_t *f; 5841624Smax.romanov@nginx.com nxt_unit_request_t *r; 5851624Smax.romanov@nginx.com 5861624Smax.romanov@nginx.com static const nxt_str_t ws_protocol = nxt_string("sec-websocket-protocol"); 5871624Smax.romanov@nginx.com 5881624Smax.romanov@nginx.com #define SET_ITEM(dict, key, value) \ 5891624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \ 5901624Smax.romanov@nginx.com == -1)) \ 5911624Smax.romanov@nginx.com { \ 5921624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set '" \ 5931624Smax.romanov@nginx.com #dict "." #key "' item"); \ 5941624Smax.romanov@nginx.com goto fail; \ 5951624Smax.romanov@nginx.com } 5961624Smax.romanov@nginx.com 5971624Smax.romanov@nginx.com v = NULL; 5981624Smax.romanov@nginx.com headers = NULL; 5991624Smax.romanov@nginx.com 6001624Smax.romanov@nginx.com r = req->request; 6011624Smax.romanov@nginx.com 6021624Smax.romanov@nginx.com if (r->websocket_handshake) { 6031624Smax.romanov@nginx.com type = nxt_py_websocket_str; 6041624Smax.romanov@nginx.com scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str; 6051624Smax.romanov@nginx.com 6061624Smax.romanov@nginx.com } else { 6071624Smax.romanov@nginx.com type = nxt_py_http_str; 6081624Smax.romanov@nginx.com scheme = r->tls ? nxt_py_https_str : nxt_py_http_str; 6091624Smax.romanov@nginx.com } 6101624Smax.romanov@nginx.com 6111624Smax.romanov@nginx.com scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str); 6121624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 6131624Smax.romanov@nginx.com return NULL; 6141624Smax.romanov@nginx.com } 6151624Smax.romanov@nginx.com 6161624Smax.romanov@nginx.com p = nxt_unit_sptr_get(&r->version); 6171624Smax.romanov@nginx.com SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str 6181624Smax.romanov@nginx.com : nxt_py_1_0_str) 6191624Smax.romanov@nginx.com SET_ITEM(scope, scheme, scheme) 6201624Smax.romanov@nginx.com 6211624Smax.romanov@nginx.com v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method), 6221624Smax.romanov@nginx.com r->method_length); 6231624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6241624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'method' string"); 6251624Smax.romanov@nginx.com goto fail; 6261624Smax.romanov@nginx.com } 6271624Smax.romanov@nginx.com 6281624Smax.romanov@nginx.com SET_ITEM(scope, method, v) 6291624Smax.romanov@nginx.com Py_DECREF(v); 6301624Smax.romanov@nginx.com 6311624Smax.romanov@nginx.com v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length, 6321624Smax.romanov@nginx.com "replace"); 6331624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6341624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'path' string"); 6351624Smax.romanov@nginx.com goto fail; 6361624Smax.romanov@nginx.com } 6371624Smax.romanov@nginx.com 6381624Smax.romanov@nginx.com SET_ITEM(scope, path, v) 6391624Smax.romanov@nginx.com Py_DECREF(v); 6401624Smax.romanov@nginx.com 6411624Smax.romanov@nginx.com target = nxt_unit_sptr_get(&r->target); 6421624Smax.romanov@nginx.com query = nxt_unit_sptr_get(&r->query); 6431624Smax.romanov@nginx.com 6441624Smax.romanov@nginx.com if (r->query.offset != 0) { 6451624Smax.romanov@nginx.com target_length = query - target - 1; 6461624Smax.romanov@nginx.com 6471624Smax.romanov@nginx.com } else { 6481624Smax.romanov@nginx.com target_length = r->target_length; 6491624Smax.romanov@nginx.com } 6501624Smax.romanov@nginx.com 6511624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(target, target_length); 6521624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6531624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'raw_path' string"); 6541624Smax.romanov@nginx.com goto fail; 6551624Smax.romanov@nginx.com } 6561624Smax.romanov@nginx.com 6571624Smax.romanov@nginx.com SET_ITEM(scope, raw_path, v) 6581624Smax.romanov@nginx.com Py_DECREF(v); 6591624Smax.romanov@nginx.com 6601624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(query, r->query_length); 6611624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6621624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'query' string"); 6631624Smax.romanov@nginx.com goto fail; 6641624Smax.romanov@nginx.com } 6651624Smax.romanov@nginx.com 6661624Smax.romanov@nginx.com SET_ITEM(scope, query_string, v) 6671624Smax.romanov@nginx.com Py_DECREF(v); 6681624Smax.romanov@nginx.com 6691624Smax.romanov@nginx.com v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0); 6701624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6711624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'client' pair"); 6721624Smax.romanov@nginx.com goto fail; 6731624Smax.romanov@nginx.com } 6741624Smax.romanov@nginx.com 6751624Smax.romanov@nginx.com SET_ITEM(scope, client, v) 6761624Smax.romanov@nginx.com Py_DECREF(v); 6771624Smax.romanov@nginx.com 6781624Smax.romanov@nginx.com v = nxt_py_asgi_create_address(&r->local, r->local_length, 80); 6791624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6801624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'server' pair"); 6811624Smax.romanov@nginx.com goto fail; 6821624Smax.romanov@nginx.com } 6831624Smax.romanov@nginx.com 6841624Smax.romanov@nginx.com SET_ITEM(scope, server, v) 6851624Smax.romanov@nginx.com Py_DECREF(v); 6861624Smax.romanov@nginx.com 6871624Smax.romanov@nginx.com v = NULL; 6881624Smax.romanov@nginx.com 6891624Smax.romanov@nginx.com headers = PyTuple_New(r->fields_count); 6901624Smax.romanov@nginx.com if (nxt_slow_path(headers == NULL)) { 6911624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'headers' object"); 6921624Smax.romanov@nginx.com goto fail; 6931624Smax.romanov@nginx.com } 6941624Smax.romanov@nginx.com 6951624Smax.romanov@nginx.com for (i = 0; i < r->fields_count; i++) { 6961624Smax.romanov@nginx.com f = r->fields + i; 6971624Smax.romanov@nginx.com 6981624Smax.romanov@nginx.com header = nxt_py_asgi_create_header(f); 6991624Smax.romanov@nginx.com if (nxt_slow_path(header == NULL)) { 7001624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'header' pair"); 7011624Smax.romanov@nginx.com goto fail; 7021624Smax.romanov@nginx.com } 7031624Smax.romanov@nginx.com 7041624Smax.romanov@nginx.com PyTuple_SET_ITEM(headers, i, header); 7051624Smax.romanov@nginx.com 7061624Smax.romanov@nginx.com if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL 7071624Smax.romanov@nginx.com && f->name_length == ws_protocol.length 7081624Smax.romanov@nginx.com && f->value_length > 0 7091624Smax.romanov@nginx.com && r->websocket_handshake) 7101624Smax.romanov@nginx.com { 7111624Smax.romanov@nginx.com v = nxt_py_asgi_create_subprotocols(f); 7121624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7131624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Failed to create subprotocols"); 7141624Smax.romanov@nginx.com goto fail; 7151624Smax.romanov@nginx.com } 7161624Smax.romanov@nginx.com 7171624Smax.romanov@nginx.com SET_ITEM(scope, subprotocols, v); 7181624Smax.romanov@nginx.com Py_DECREF(v); 7191624Smax.romanov@nginx.com } 7201624Smax.romanov@nginx.com } 7211624Smax.romanov@nginx.com 7221624Smax.romanov@nginx.com SET_ITEM(scope, headers, headers) 7231624Smax.romanov@nginx.com Py_DECREF(headers); 7241624Smax.romanov@nginx.com 7251624Smax.romanov@nginx.com return scope; 7261624Smax.romanov@nginx.com 7271624Smax.romanov@nginx.com fail: 7281624Smax.romanov@nginx.com 7291624Smax.romanov@nginx.com Py_XDECREF(v); 7301624Smax.romanov@nginx.com Py_XDECREF(headers); 7311624Smax.romanov@nginx.com Py_DECREF(scope); 7321624Smax.romanov@nginx.com 7331624Smax.romanov@nginx.com return NULL; 7341624Smax.romanov@nginx.com 7351624Smax.romanov@nginx.com #undef SET_ITEM 7361624Smax.romanov@nginx.com } 7371624Smax.romanov@nginx.com 7381624Smax.romanov@nginx.com 7391624Smax.romanov@nginx.com static PyObject * 7401624Smax.romanov@nginx.com nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port) 7411624Smax.romanov@nginx.com { 7421624Smax.romanov@nginx.com char *p, *s; 7431624Smax.romanov@nginx.com PyObject *pair, *v; 7441624Smax.romanov@nginx.com 7451624Smax.romanov@nginx.com pair = PyTuple_New(2); 7461624Smax.romanov@nginx.com if (nxt_slow_path(pair == NULL)) { 7471624Smax.romanov@nginx.com return NULL; 7481624Smax.romanov@nginx.com } 7491624Smax.romanov@nginx.com 7501624Smax.romanov@nginx.com p = nxt_unit_sptr_get(sptr); 7511624Smax.romanov@nginx.com s = memchr(p, ':', len); 7521624Smax.romanov@nginx.com 7531624Smax.romanov@nginx.com v = PyString_FromStringAndSize(p, s == NULL ? len : s - p); 7541624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7551624Smax.romanov@nginx.com Py_DECREF(pair); 7561624Smax.romanov@nginx.com 7571624Smax.romanov@nginx.com return NULL; 7581624Smax.romanov@nginx.com } 7591624Smax.romanov@nginx.com 7601624Smax.romanov@nginx.com PyTuple_SET_ITEM(pair, 0, v); 7611624Smax.romanov@nginx.com 7621624Smax.romanov@nginx.com if (s != NULL) { 7631624Smax.romanov@nginx.com p += len; 7641624Smax.romanov@nginx.com v = PyLong_FromString(s + 1, &p, 10); 7651624Smax.romanov@nginx.com 7661624Smax.romanov@nginx.com } else { 7671624Smax.romanov@nginx.com v = PyLong_FromLong(port); 7681624Smax.romanov@nginx.com } 7691624Smax.romanov@nginx.com 7701624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7711624Smax.romanov@nginx.com Py_DECREF(pair); 7721624Smax.romanov@nginx.com 7731624Smax.romanov@nginx.com return NULL; 7741624Smax.romanov@nginx.com } 7751624Smax.romanov@nginx.com 7761624Smax.romanov@nginx.com PyTuple_SET_ITEM(pair, 1, v); 7771624Smax.romanov@nginx.com 7781624Smax.romanov@nginx.com return pair; 7791624Smax.romanov@nginx.com } 7801624Smax.romanov@nginx.com 7811624Smax.romanov@nginx.com 7821624Smax.romanov@nginx.com static PyObject * 7831624Smax.romanov@nginx.com nxt_py_asgi_create_header(nxt_unit_field_t *f) 7841624Smax.romanov@nginx.com { 7851624Smax.romanov@nginx.com char c, *name; 7861624Smax.romanov@nginx.com uint8_t pos; 7871624Smax.romanov@nginx.com PyObject *header, *v; 7881624Smax.romanov@nginx.com 7891624Smax.romanov@nginx.com header = PyTuple_New(2); 7901624Smax.romanov@nginx.com if (nxt_slow_path(header == NULL)) { 7911624Smax.romanov@nginx.com return NULL; 7921624Smax.romanov@nginx.com } 7931624Smax.romanov@nginx.com 7941624Smax.romanov@nginx.com name = nxt_unit_sptr_get(&f->name); 7951624Smax.romanov@nginx.com 7961624Smax.romanov@nginx.com for (pos = 0; pos < f->name_length; pos++) { 7971624Smax.romanov@nginx.com c = name[pos]; 7981624Smax.romanov@nginx.com if (c >= 'A' && c <= 'Z') { 7991624Smax.romanov@nginx.com name[pos] = (c | 0x20); 8001624Smax.romanov@nginx.com } 8011624Smax.romanov@nginx.com } 8021624Smax.romanov@nginx.com 8031624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(name, f->name_length); 8041624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 8051624Smax.romanov@nginx.com Py_DECREF(header); 8061624Smax.romanov@nginx.com 8071624Smax.romanov@nginx.com return NULL; 8081624Smax.romanov@nginx.com } 8091624Smax.romanov@nginx.com 8101624Smax.romanov@nginx.com PyTuple_SET_ITEM(header, 0, v); 8111624Smax.romanov@nginx.com 8121624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value), 8131624Smax.romanov@nginx.com f->value_length); 8141624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 8151624Smax.romanov@nginx.com Py_DECREF(header); 8161624Smax.romanov@nginx.com 8171624Smax.romanov@nginx.com return NULL; 8181624Smax.romanov@nginx.com } 8191624Smax.romanov@nginx.com 8201624Smax.romanov@nginx.com PyTuple_SET_ITEM(header, 1, v); 8211624Smax.romanov@nginx.com 8221624Smax.romanov@nginx.com return header; 8231624Smax.romanov@nginx.com } 8241624Smax.romanov@nginx.com 8251624Smax.romanov@nginx.com 8261624Smax.romanov@nginx.com static PyObject * 8271624Smax.romanov@nginx.com nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f) 8281624Smax.romanov@nginx.com { 8291624Smax.romanov@nginx.com char *v; 8301624Smax.romanov@nginx.com uint32_t i, n, start; 8311624Smax.romanov@nginx.com PyObject *res, *proto; 8321624Smax.romanov@nginx.com 8331624Smax.romanov@nginx.com v = nxt_unit_sptr_get(&f->value); 8341624Smax.romanov@nginx.com n = 1; 8351624Smax.romanov@nginx.com 8361624Smax.romanov@nginx.com for (i = 0; i < f->value_length; i++) { 8371624Smax.romanov@nginx.com if (v[i] == ',') { 8381624Smax.romanov@nginx.com n++; 8391624Smax.romanov@nginx.com } 8401624Smax.romanov@nginx.com } 8411624Smax.romanov@nginx.com 8421624Smax.romanov@nginx.com res = PyTuple_New(n); 8431624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 8441624Smax.romanov@nginx.com return NULL; 8451624Smax.romanov@nginx.com } 8461624Smax.romanov@nginx.com 8471624Smax.romanov@nginx.com n = 0; 8481624Smax.romanov@nginx.com start = 0; 8491624Smax.romanov@nginx.com 8501624Smax.romanov@nginx.com for (i = 0; i < f->value_length; ) { 8511624Smax.romanov@nginx.com if (v[i] != ',') { 8521624Smax.romanov@nginx.com i++; 8531624Smax.romanov@nginx.com 8541624Smax.romanov@nginx.com continue; 8551624Smax.romanov@nginx.com } 8561624Smax.romanov@nginx.com 8571624Smax.romanov@nginx.com if (i - start > 0) { 8581624Smax.romanov@nginx.com proto = PyString_FromStringAndSize(v + start, i - start); 8591624Smax.romanov@nginx.com if (nxt_slow_path(proto == NULL)) { 8601624Smax.romanov@nginx.com goto fail; 8611624Smax.romanov@nginx.com } 8621624Smax.romanov@nginx.com 8631624Smax.romanov@nginx.com PyTuple_SET_ITEM(res, n, proto); 8641624Smax.romanov@nginx.com 8651624Smax.romanov@nginx.com n++; 8661624Smax.romanov@nginx.com } 8671624Smax.romanov@nginx.com 8681624Smax.romanov@nginx.com do { 8691624Smax.romanov@nginx.com i++; 8701624Smax.romanov@nginx.com } while (i < f->value_length && v[i] == ' '); 8711624Smax.romanov@nginx.com 8721624Smax.romanov@nginx.com start = i; 8731624Smax.romanov@nginx.com } 8741624Smax.romanov@nginx.com 8751624Smax.romanov@nginx.com if (i - start > 0) { 8761624Smax.romanov@nginx.com proto = PyString_FromStringAndSize(v + start, i - start); 8771624Smax.romanov@nginx.com if (nxt_slow_path(proto == NULL)) { 8781624Smax.romanov@nginx.com goto fail; 8791624Smax.romanov@nginx.com } 8801624Smax.romanov@nginx.com 8811624Smax.romanov@nginx.com PyTuple_SET_ITEM(res, n, proto); 8821624Smax.romanov@nginx.com } 8831624Smax.romanov@nginx.com 8841624Smax.romanov@nginx.com return res; 8851624Smax.romanov@nginx.com 8861624Smax.romanov@nginx.com fail: 8871624Smax.romanov@nginx.com 8881624Smax.romanov@nginx.com Py_DECREF(res); 8891624Smax.romanov@nginx.com 8901624Smax.romanov@nginx.com return NULL; 8911624Smax.romanov@nginx.com } 8921624Smax.romanov@nginx.com 8931624Smax.romanov@nginx.com 8941624Smax.romanov@nginx.com static int 8951681Smax.romanov@nginx.com nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) 8961681Smax.romanov@nginx.com { 8971682Smax.romanov@nginx.com int rc; 8981682Smax.romanov@nginx.com PyObject *res, *fd, *py_ctx, *py_port; 8991681Smax.romanov@nginx.com nxt_unit_port_t *port; 9001681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 9011681Smax.romanov@nginx.com 9021681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_shared_port == NULL)) { 9031681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9041681Smax.romanov@nginx.com } 9051681Smax.romanov@nginx.com 9061681Smax.romanov@nginx.com port = nxt_py_shared_port; 9071681Smax.romanov@nginx.com 9081681Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port); 9091681Smax.romanov@nginx.com 9101681Smax.romanov@nginx.com ctx_data = ctx->data; 9111681Smax.romanov@nginx.com 9121682Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 9131682Smax.romanov@nginx.com 9141682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 9151682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 9161682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create fd"); 9171682Smax.romanov@nginx.com nxt_python_print_exception(); 9181682Smax.romanov@nginx.com 9191682Smax.romanov@nginx.com return rc; 9201682Smax.romanov@nginx.com } 9211682Smax.romanov@nginx.com 9221682Smax.romanov@nginx.com py_ctx = PyLong_FromVoidPtr(ctx); 9231682Smax.romanov@nginx.com if (nxt_slow_path(py_ctx == NULL)) { 9241682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_ctx"); 9251682Smax.romanov@nginx.com nxt_python_print_exception(); 9261682Smax.romanov@nginx.com 9271682Smax.romanov@nginx.com goto clean_fd; 9281682Smax.romanov@nginx.com } 9291682Smax.romanov@nginx.com 9301682Smax.romanov@nginx.com py_port = PyLong_FromVoidPtr(port); 9311682Smax.romanov@nginx.com if (nxt_slow_path(py_port == NULL)) { 9321682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_port"); 9331682Smax.romanov@nginx.com nxt_python_print_exception(); 9341682Smax.romanov@nginx.com 9351682Smax.romanov@nginx.com goto clean_py_ctx; 9361682Smax.romanov@nginx.com } 9371682Smax.romanov@nginx.com 9381681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 9391682Smax.romanov@nginx.com fd, nxt_py_port_read, 9401682Smax.romanov@nginx.com py_ctx, py_port, NULL); 9411681Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 9421681Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to add_reader"); 9431681Smax.romanov@nginx.com nxt_python_print_exception(); 9441681Smax.romanov@nginx.com 9451682Smax.romanov@nginx.com } else { 9461682Smax.romanov@nginx.com Py_DECREF(res); 9471682Smax.romanov@nginx.com 9481682Smax.romanov@nginx.com rc = NXT_UNIT_OK; 9491681Smax.romanov@nginx.com } 9501681Smax.romanov@nginx.com 9511682Smax.romanov@nginx.com Py_DECREF(py_port); 9521682Smax.romanov@nginx.com 9531682Smax.romanov@nginx.com clean_py_ctx: 9541682Smax.romanov@nginx.com 9551682Smax.romanov@nginx.com Py_DECREF(py_ctx); 9561681Smax.romanov@nginx.com 9571682Smax.romanov@nginx.com clean_fd: 9581682Smax.romanov@nginx.com 9591682Smax.romanov@nginx.com Py_DECREF(fd); 9601682Smax.romanov@nginx.com 9611682Smax.romanov@nginx.com return rc; 9621681Smax.romanov@nginx.com } 9631681Smax.romanov@nginx.com 9641681Smax.romanov@nginx.com 9651681Smax.romanov@nginx.com static int 9661624Smax.romanov@nginx.com nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 9671624Smax.romanov@nginx.com { 9681682Smax.romanov@nginx.com int nb, rc; 9691682Smax.romanov@nginx.com PyObject *res, *fd, *py_ctx, *py_port; 9701681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 9711624Smax.romanov@nginx.com 9721624Smax.romanov@nginx.com if (port->in_fd == -1) { 9731624Smax.romanov@nginx.com return NXT_UNIT_OK; 9741624Smax.romanov@nginx.com } 9751624Smax.romanov@nginx.com 9761624Smax.romanov@nginx.com nb = 1; 9771624Smax.romanov@nginx.com 9781624Smax.romanov@nginx.com if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) { 9791624Smax.romanov@nginx.com nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)", 9801624Smax.romanov@nginx.com port->in_fd, strerror(errno), errno); 9811624Smax.romanov@nginx.com 9821624Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9831624Smax.romanov@nginx.com } 9841624Smax.romanov@nginx.com 9851624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); 9861624Smax.romanov@nginx.com 9871681Smax.romanov@nginx.com if (port->id.id == NXT_UNIT_SHARED_PORT_ID) { 9881681Smax.romanov@nginx.com nxt_py_shared_port = port; 9891681Smax.romanov@nginx.com 9901681Smax.romanov@nginx.com return NXT_UNIT_OK; 9911681Smax.romanov@nginx.com } 9921681Smax.romanov@nginx.com 9931681Smax.romanov@nginx.com ctx_data = ctx->data; 9941681Smax.romanov@nginx.com 9951681Smax.romanov@nginx.com ctx_data->port = port; 9961681Smax.romanov@nginx.com port->data = ctx_data; 9971681Smax.romanov@nginx.com 9981682Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 9991682Smax.romanov@nginx.com 10001682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 10011682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 10021682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create fd"); 10031682Smax.romanov@nginx.com nxt_python_print_exception(); 10041682Smax.romanov@nginx.com 10051682Smax.romanov@nginx.com return rc; 10061682Smax.romanov@nginx.com } 10071682Smax.romanov@nginx.com 10081682Smax.romanov@nginx.com py_ctx = PyLong_FromVoidPtr(ctx); 10091682Smax.romanov@nginx.com if (nxt_slow_path(py_ctx == NULL)) { 10101682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_ctx"); 10111682Smax.romanov@nginx.com nxt_python_print_exception(); 10121682Smax.romanov@nginx.com 10131682Smax.romanov@nginx.com goto clean_fd; 10141682Smax.romanov@nginx.com } 10151682Smax.romanov@nginx.com 10161682Smax.romanov@nginx.com py_port = PyLong_FromVoidPtr(port); 10171682Smax.romanov@nginx.com if (nxt_slow_path(py_port == NULL)) { 10181682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_port"); 10191682Smax.romanov@nginx.com nxt_python_print_exception(); 10201682Smax.romanov@nginx.com 10211682Smax.romanov@nginx.com goto clean_py_ctx; 10221682Smax.romanov@nginx.com } 10231682Smax.romanov@nginx.com 10241681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 10251682Smax.romanov@nginx.com fd, nxt_py_port_read, 10261682Smax.romanov@nginx.com py_ctx, py_port, NULL); 10271624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10281624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to add_reader"); 10291681Smax.romanov@nginx.com nxt_python_print_exception(); 10301624Smax.romanov@nginx.com 10311682Smax.romanov@nginx.com } else { 10321682Smax.romanov@nginx.com Py_DECREF(res); 10331682Smax.romanov@nginx.com 10341682Smax.romanov@nginx.com rc = NXT_UNIT_OK; 10351624Smax.romanov@nginx.com } 10361624Smax.romanov@nginx.com 10371682Smax.romanov@nginx.com Py_DECREF(py_port); 10381682Smax.romanov@nginx.com 10391682Smax.romanov@nginx.com clean_py_ctx: 10401682Smax.romanov@nginx.com 10411682Smax.romanov@nginx.com Py_DECREF(py_ctx); 10421624Smax.romanov@nginx.com 10431682Smax.romanov@nginx.com clean_fd: 10441682Smax.romanov@nginx.com 10451682Smax.romanov@nginx.com Py_DECREF(fd); 10461682Smax.romanov@nginx.com 10471682Smax.romanov@nginx.com return rc; 10481624Smax.romanov@nginx.com } 10491624Smax.romanov@nginx.com 10501624Smax.romanov@nginx.com 10511624Smax.romanov@nginx.com static void 10521624Smax.romanov@nginx.com nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) 10531624Smax.romanov@nginx.com { 10541624Smax.romanov@nginx.com if (port->in_fd == -1) { 10551624Smax.romanov@nginx.com return; 10561624Smax.romanov@nginx.com } 10571624Smax.romanov@nginx.com 10581681Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); 10591681Smax.romanov@nginx.com 10601681Smax.romanov@nginx.com if (nxt_py_shared_port == port) { 10611681Smax.romanov@nginx.com nxt_py_shared_port = NULL; 10621624Smax.romanov@nginx.com } 10631624Smax.romanov@nginx.com } 10641624Smax.romanov@nginx.com 10651624Smax.romanov@nginx.com 10661624Smax.romanov@nginx.com static void 10671624Smax.romanov@nginx.com nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) 10681624Smax.romanov@nginx.com { 10691682Smax.romanov@nginx.com PyObject *res, *p; 10701681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 10711624Smax.romanov@nginx.com 10721624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_quit %p", ctx); 10731624Smax.romanov@nginx.com 10741681Smax.romanov@nginx.com ctx_data = ctx->data; 10751681Smax.romanov@nginx.com 10761681Smax.romanov@nginx.com if (nxt_py_shared_port != NULL) { 10771682Smax.romanov@nginx.com p = PyLong_FromLong(nxt_py_shared_port->in_fd); 10781682Smax.romanov@nginx.com if (nxt_slow_path(p == NULL)) { 10791682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Long"); 10801682Smax.romanov@nginx.com nxt_python_print_exception(); 10811682Smax.romanov@nginx.com 10821682Smax.romanov@nginx.com } else { 10831682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, 10841682Smax.romanov@nginx.com p, NULL); 10851682Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10861682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to remove_reader"); 10871682Smax.romanov@nginx.com nxt_python_print_exception(); 10881682Smax.romanov@nginx.com 10891682Smax.romanov@nginx.com } else { 10901682Smax.romanov@nginx.com Py_DECREF(res); 10911682Smax.romanov@nginx.com } 10921682Smax.romanov@nginx.com 10931682Smax.romanov@nginx.com Py_DECREF(p); 10941682Smax.romanov@nginx.com } 10951682Smax.romanov@nginx.com } 10961682Smax.romanov@nginx.com 10971682Smax.romanov@nginx.com p = PyLong_FromLong(0); 10981682Smax.romanov@nginx.com if (nxt_slow_path(p == NULL)) { 10991682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Long"); 11001682Smax.romanov@nginx.com nxt_python_print_exception(); 11011682Smax.romanov@nginx.com 11021682Smax.romanov@nginx.com } else { 11031682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, 11041682Smax.romanov@nginx.com p, NULL); 11051681Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 11061682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to set_result"); 11071681Smax.romanov@nginx.com nxt_python_print_exception(); 11081681Smax.romanov@nginx.com 11091681Smax.romanov@nginx.com } else { 11101681Smax.romanov@nginx.com Py_DECREF(res); 11111681Smax.romanov@nginx.com } 11121681Smax.romanov@nginx.com 11131682Smax.romanov@nginx.com Py_DECREF(p); 11141681Smax.romanov@nginx.com } 11151624Smax.romanov@nginx.com } 11161624Smax.romanov@nginx.com 11171624Smax.romanov@nginx.com 11181624Smax.romanov@nginx.com static void 11191624Smax.romanov@nginx.com nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx) 11201624Smax.romanov@nginx.com { 11211681Smax.romanov@nginx.com int rc; 11221681Smax.romanov@nginx.com nxt_queue_link_t *lnk; 11231681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 11241624Smax.romanov@nginx.com 11251681Smax.romanov@nginx.com ctx_data = ctx->data; 11261681Smax.romanov@nginx.com 11271681Smax.romanov@nginx.com while (!nxt_queue_is_empty(&ctx_data->drain_queue)) { 11281681Smax.romanov@nginx.com lnk = nxt_queue_first(&ctx_data->drain_queue); 11291624Smax.romanov@nginx.com 11301624Smax.romanov@nginx.com rc = nxt_py_asgi_http_drain(lnk); 11311624Smax.romanov@nginx.com if (rc == NXT_UNIT_AGAIN) { 11321681Smax.romanov@nginx.com return; 11331624Smax.romanov@nginx.com } 11341624Smax.romanov@nginx.com 11351624Smax.romanov@nginx.com nxt_queue_remove(lnk); 11361624Smax.romanov@nginx.com } 11371624Smax.romanov@nginx.com } 11381624Smax.romanov@nginx.com 11391624Smax.romanov@nginx.com 11401624Smax.romanov@nginx.com static PyObject * 11411624Smax.romanov@nginx.com nxt_py_asgi_port_read(PyObject *self, PyObject *args) 11421624Smax.romanov@nginx.com { 11431767Smax.romanov@nginx.com int rc; 11441767Smax.romanov@nginx.com PyObject *arg0, *arg1, *res; 11451767Smax.romanov@nginx.com Py_ssize_t n; 11461767Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 11471767Smax.romanov@nginx.com nxt_unit_port_t *port; 11481767Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 11491624Smax.romanov@nginx.com 11501624Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 11511624Smax.romanov@nginx.com 11521624Smax.romanov@nginx.com if (n != 2) { 11531624Smax.romanov@nginx.com nxt_unit_alert(NULL, 11541624Smax.romanov@nginx.com "nxt_py_asgi_port_read: invalid number of arguments %d", 11551624Smax.romanov@nginx.com (int) n); 11561624Smax.romanov@nginx.com 11571624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 11581624Smax.romanov@nginx.com } 11591624Smax.romanov@nginx.com 11601767Smax.romanov@nginx.com arg0 = PyTuple_GET_ITEM(args, 0); 11611767Smax.romanov@nginx.com if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) { 11621624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11631624Smax.romanov@nginx.com "the first argument is not a long"); 11641624Smax.romanov@nginx.com } 11651624Smax.romanov@nginx.com 11661767Smax.romanov@nginx.com ctx = PyLong_AsVoidPtr(arg0); 11671624Smax.romanov@nginx.com 11681767Smax.romanov@nginx.com arg1 = PyTuple_GET_ITEM(args, 1); 11691767Smax.romanov@nginx.com if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) { 11701624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11711624Smax.romanov@nginx.com "the second argument is not a long"); 11721624Smax.romanov@nginx.com } 11731624Smax.romanov@nginx.com 11741767Smax.romanov@nginx.com port = PyLong_AsVoidPtr(arg1); 11751624Smax.romanov@nginx.com 11761624Smax.romanov@nginx.com rc = nxt_unit_process_port_msg(ctx, port); 11771624Smax.romanov@nginx.com 11781767Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc); 11791767Smax.romanov@nginx.com 11801624Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 11811624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 11821681Smax.romanov@nginx.com "error processing port %d message", port->id.id); 11831624Smax.romanov@nginx.com } 11841624Smax.romanov@nginx.com 11851767Smax.romanov@nginx.com if (rc == NXT_UNIT_OK) { 11861767Smax.romanov@nginx.com ctx_data = ctx->data; 11871767Smax.romanov@nginx.com 11881767Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, 11891767Smax.romanov@nginx.com nxt_py_port_read, 11901767Smax.romanov@nginx.com arg0, arg1, NULL); 11911767Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 11921767Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'"); 11931767Smax.romanov@nginx.com nxt_python_print_exception(); 11941767Smax.romanov@nginx.com } 11951767Smax.romanov@nginx.com 11961767Smax.romanov@nginx.com Py_XDECREF(res); 11971767Smax.romanov@nginx.com } 11981767Smax.romanov@nginx.com 11991624Smax.romanov@nginx.com Py_RETURN_NONE; 12001624Smax.romanov@nginx.com } 12011624Smax.romanov@nginx.com 12021624Smax.romanov@nginx.com 12031624Smax.romanov@nginx.com PyObject * 12041624Smax.romanov@nginx.com nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb, 12051624Smax.romanov@nginx.com void *data) 12061624Smax.romanov@nginx.com { 12071624Smax.romanov@nginx.com int i; 12081624Smax.romanov@nginx.com PyObject *iter, *header, *h_iter, *name, *val, *res; 12091624Smax.romanov@nginx.com 12101624Smax.romanov@nginx.com iter = PyObject_GetIter(headers); 12111624Smax.romanov@nginx.com if (nxt_slow_path(iter == NULL)) { 12121624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable"); 12131624Smax.romanov@nginx.com } 12141624Smax.romanov@nginx.com 12151624Smax.romanov@nginx.com for (i = 0; /* void */; i++) { 12161624Smax.romanov@nginx.com header = PyIter_Next(iter); 12171624Smax.romanov@nginx.com if (header == NULL) { 12181624Smax.romanov@nginx.com break; 12191624Smax.romanov@nginx.com } 12201624Smax.romanov@nginx.com 12211624Smax.romanov@nginx.com h_iter = PyObject_GetIter(header); 12221624Smax.romanov@nginx.com if (nxt_slow_path(h_iter == NULL)) { 12231624Smax.romanov@nginx.com Py_DECREF(header); 12241624Smax.romanov@nginx.com Py_DECREF(iter); 12251624Smax.romanov@nginx.com 12261624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 12271624Smax.romanov@nginx.com "'headers' item #%d is not an iterable", i); 12281624Smax.romanov@nginx.com } 12291624Smax.romanov@nginx.com 12301624Smax.romanov@nginx.com name = PyIter_Next(h_iter); 12311624Smax.romanov@nginx.com if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) { 12321624Smax.romanov@nginx.com Py_XDECREF(name); 12331624Smax.romanov@nginx.com Py_DECREF(h_iter); 12341624Smax.romanov@nginx.com Py_DECREF(header); 12351624Smax.romanov@nginx.com Py_DECREF(iter); 12361624Smax.romanov@nginx.com 12371624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 12381624Smax.romanov@nginx.com "'headers' item #%d 'name' is not a byte string", i); 12391624Smax.romanov@nginx.com } 12401624Smax.romanov@nginx.com 12411624Smax.romanov@nginx.com val = PyIter_Next(h_iter); 12421624Smax.romanov@nginx.com if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) { 12431624Smax.romanov@nginx.com Py_XDECREF(val); 12441624Smax.romanov@nginx.com Py_DECREF(h_iter); 12451624Smax.romanov@nginx.com Py_DECREF(header); 12461624Smax.romanov@nginx.com Py_DECREF(iter); 12471624Smax.romanov@nginx.com 12481624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 12491624Smax.romanov@nginx.com "'headers' item #%d 'value' is not a byte string", i); 12501624Smax.romanov@nginx.com } 12511624Smax.romanov@nginx.com 12521624Smax.romanov@nginx.com res = cb(data, i, name, val); 12531624Smax.romanov@nginx.com 12541624Smax.romanov@nginx.com Py_DECREF(name); 12551624Smax.romanov@nginx.com Py_DECREF(val); 12561624Smax.romanov@nginx.com Py_DECREF(h_iter); 12571624Smax.romanov@nginx.com Py_DECREF(header); 12581624Smax.romanov@nginx.com 12591624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 12601624Smax.romanov@nginx.com Py_DECREF(iter); 12611624Smax.romanov@nginx.com 12621624Smax.romanov@nginx.com return NULL; 12631624Smax.romanov@nginx.com } 12641624Smax.romanov@nginx.com 12651624Smax.romanov@nginx.com Py_DECREF(res); 12661624Smax.romanov@nginx.com } 12671624Smax.romanov@nginx.com 12681624Smax.romanov@nginx.com Py_DECREF(iter); 12691624Smax.romanov@nginx.com 12701624Smax.romanov@nginx.com Py_RETURN_NONE; 12711624Smax.romanov@nginx.com } 12721624Smax.romanov@nginx.com 12731624Smax.romanov@nginx.com 12741624Smax.romanov@nginx.com PyObject * 12751624Smax.romanov@nginx.com nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val) 12761624Smax.romanov@nginx.com { 12771624Smax.romanov@nginx.com nxt_py_asgi_calc_size_ctx_t *ctx; 12781624Smax.romanov@nginx.com 12791624Smax.romanov@nginx.com ctx = data; 12801624Smax.romanov@nginx.com 12811624Smax.romanov@nginx.com ctx->fields_count++; 12821624Smax.romanov@nginx.com ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val); 12831624Smax.romanov@nginx.com 12841624Smax.romanov@nginx.com Py_RETURN_NONE; 12851624Smax.romanov@nginx.com } 12861624Smax.romanov@nginx.com 12871624Smax.romanov@nginx.com 12881624Smax.romanov@nginx.com PyObject * 12891624Smax.romanov@nginx.com nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val) 12901624Smax.romanov@nginx.com { 12911624Smax.romanov@nginx.com int rc; 12921624Smax.romanov@nginx.com char *name_str, *val_str; 12931624Smax.romanov@nginx.com uint32_t name_len, val_len; 12941624Smax.romanov@nginx.com nxt_off_t content_length; 12951624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 12961624Smax.romanov@nginx.com nxt_py_asgi_add_field_ctx_t *ctx; 12971624Smax.romanov@nginx.com 12981624Smax.romanov@nginx.com name_str = PyBytes_AS_STRING(name); 12991624Smax.romanov@nginx.com name_len = PyBytes_GET_SIZE(name); 13001624Smax.romanov@nginx.com 13011624Smax.romanov@nginx.com val_str = PyBytes_AS_STRING(val); 13021624Smax.romanov@nginx.com val_len = PyBytes_GET_SIZE(val); 13031624Smax.romanov@nginx.com 13041624Smax.romanov@nginx.com ctx = data; 13051624Smax.romanov@nginx.com req = ctx->req; 13061624Smax.romanov@nginx.com 13071624Smax.romanov@nginx.com rc = nxt_unit_response_add_field(req, name_str, name_len, 13081624Smax.romanov@nginx.com val_str, val_len); 13091624Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 13101624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13111624Smax.romanov@nginx.com "failed to add header #%d", i); 13121624Smax.romanov@nginx.com } 13131624Smax.romanov@nginx.com 13141624Smax.romanov@nginx.com if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { 13151624Smax.romanov@nginx.com content_length = nxt_off_t_parse((u_char *) val_str, val_len); 13161624Smax.romanov@nginx.com if (nxt_slow_path(content_length < 0)) { 13171624Smax.romanov@nginx.com nxt_unit_req_error(req, "failed to parse Content-Length " 13181624Smax.romanov@nginx.com "value %.*s", (int) val_len, val_str); 13191624Smax.romanov@nginx.com 13201624Smax.romanov@nginx.com return PyErr_Format(PyExc_ValueError, 13211624Smax.romanov@nginx.com "Failed to parse Content-Length: '%.*s'", 13221624Smax.romanov@nginx.com (int) val_len, val_str); 13231624Smax.romanov@nginx.com } 13241624Smax.romanov@nginx.com 13251624Smax.romanov@nginx.com ctx->content_length = content_length; 13261624Smax.romanov@nginx.com } 13271624Smax.romanov@nginx.com 13281624Smax.romanov@nginx.com Py_RETURN_NONE; 13291624Smax.romanov@nginx.com } 13301624Smax.romanov@nginx.com 13311624Smax.romanov@nginx.com 13321624Smax.romanov@nginx.com PyObject * 13331681Smax.romanov@nginx.com nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, 13341681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result) 13351624Smax.romanov@nginx.com { 13361624Smax.romanov@nginx.com PyObject *set_result, *res; 13371624Smax.romanov@nginx.com 13381624Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 13391624Smax.romanov@nginx.com Py_DECREF(future); 13401624Smax.romanov@nginx.com 13411624Smax.romanov@nginx.com return NULL; 13421624Smax.romanov@nginx.com } 13431624Smax.romanov@nginx.com 13441624Smax.romanov@nginx.com set_result = PyObject_GetAttrString(future, "set_result"); 13451624Smax.romanov@nginx.com if (nxt_slow_path(set_result == NULL)) { 13461624Smax.romanov@nginx.com nxt_unit_req_alert(req, "failed to get 'set_result' for future"); 13471624Smax.romanov@nginx.com 13481624Smax.romanov@nginx.com Py_CLEAR(future); 13491624Smax.romanov@nginx.com 13501681Smax.romanov@nginx.com goto cleanup_result; 13511624Smax.romanov@nginx.com } 13521624Smax.romanov@nginx.com 13531624Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(set_result) == 0)) { 13541624Smax.romanov@nginx.com nxt_unit_req_alert(req, "'future.set_result' is not a callable"); 13551624Smax.romanov@nginx.com 13561624Smax.romanov@nginx.com Py_CLEAR(future); 13571624Smax.romanov@nginx.com 13581624Smax.romanov@nginx.com goto cleanup; 13591624Smax.romanov@nginx.com } 13601624Smax.romanov@nginx.com 13611681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result, 13621624Smax.romanov@nginx.com result, NULL); 13631624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 13641624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'"); 13651624Smax.romanov@nginx.com nxt_python_print_exception(); 13661624Smax.romanov@nginx.com 13671624Smax.romanov@nginx.com Py_CLEAR(future); 13681624Smax.romanov@nginx.com } 13691624Smax.romanov@nginx.com 13701624Smax.romanov@nginx.com Py_XDECREF(res); 13711624Smax.romanov@nginx.com 13721624Smax.romanov@nginx.com cleanup: 13731624Smax.romanov@nginx.com 13741624Smax.romanov@nginx.com Py_DECREF(set_result); 13751681Smax.romanov@nginx.com 13761681Smax.romanov@nginx.com cleanup_result: 13771681Smax.romanov@nginx.com 13781624Smax.romanov@nginx.com Py_DECREF(result); 13791624Smax.romanov@nginx.com 13801624Smax.romanov@nginx.com return future; 13811624Smax.romanov@nginx.com } 13821624Smax.romanov@nginx.com 13831624Smax.romanov@nginx.com 13841624Smax.romanov@nginx.com PyObject * 13851624Smax.romanov@nginx.com nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type) 13861624Smax.romanov@nginx.com { 13871624Smax.romanov@nginx.com PyObject *msg; 13881624Smax.romanov@nginx.com 13891624Smax.romanov@nginx.com msg = PyDict_New(); 13901624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 13911624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create message dict"); 13921624Smax.romanov@nginx.com nxt_python_print_exception(); 13931624Smax.romanov@nginx.com 13941624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13951624Smax.romanov@nginx.com "failed to create message dict"); 13961624Smax.romanov@nginx.com } 13971624Smax.romanov@nginx.com 13981624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) { 13991624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'msg.type' item"); 14001624Smax.romanov@nginx.com 14011624Smax.romanov@nginx.com Py_DECREF(msg); 14021624Smax.romanov@nginx.com 14031624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14041624Smax.romanov@nginx.com "failed to set 'msg.type' item"); 14051624Smax.romanov@nginx.com } 14061624Smax.romanov@nginx.com 14071624Smax.romanov@nginx.com return msg; 14081624Smax.romanov@nginx.com } 14091624Smax.romanov@nginx.com 14101624Smax.romanov@nginx.com 14111624Smax.romanov@nginx.com PyObject * 14121624Smax.romanov@nginx.com nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, 14131624Smax.romanov@nginx.com PyObject *spec_version) 14141624Smax.romanov@nginx.com { 14151624Smax.romanov@nginx.com PyObject *scope, *asgi; 14161624Smax.romanov@nginx.com 14171624Smax.romanov@nginx.com scope = PyDict_New(); 14181624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 14191624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'scope' dict"); 14201624Smax.romanov@nginx.com nxt_python_print_exception(); 14211624Smax.romanov@nginx.com 14221624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14231624Smax.romanov@nginx.com "failed to create 'scope' dict"); 14241624Smax.romanov@nginx.com } 14251624Smax.romanov@nginx.com 14261624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) { 14271624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.type' item"); 14281624Smax.romanov@nginx.com 14291624Smax.romanov@nginx.com Py_DECREF(scope); 14301624Smax.romanov@nginx.com 14311624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14321624Smax.romanov@nginx.com "failed to set 'scope.type' item"); 14331624Smax.romanov@nginx.com } 14341624Smax.romanov@nginx.com 14351624Smax.romanov@nginx.com asgi = PyDict_New(); 14361624Smax.romanov@nginx.com if (nxt_slow_path(asgi == NULL)) { 14371624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'asgi' dict"); 14381624Smax.romanov@nginx.com nxt_python_print_exception(); 14391624Smax.romanov@nginx.com 14401624Smax.romanov@nginx.com Py_DECREF(scope); 14411624Smax.romanov@nginx.com 14421624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14431624Smax.romanov@nginx.com "failed to create 'asgi' dict"); 14441624Smax.romanov@nginx.com } 14451624Smax.romanov@nginx.com 14461624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) { 14471624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item"); 14481624Smax.romanov@nginx.com 14491624Smax.romanov@nginx.com Py_DECREF(asgi); 14501624Smax.romanov@nginx.com Py_DECREF(scope); 14511624Smax.romanov@nginx.com 14521624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14531624Smax.romanov@nginx.com "failed to set 'scope.asgi' item"); 14541624Smax.romanov@nginx.com } 14551624Smax.romanov@nginx.com 14561624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str, 14571624Smax.romanov@nginx.com nxt_py_3_0_str) == -1)) 14581624Smax.romanov@nginx.com { 14591624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item"); 14601624Smax.romanov@nginx.com 14611624Smax.romanov@nginx.com Py_DECREF(asgi); 14621624Smax.romanov@nginx.com Py_DECREF(scope); 14631624Smax.romanov@nginx.com 14641624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14651624Smax.romanov@nginx.com "failed to set 'asgi.version' item"); 14661624Smax.romanov@nginx.com } 14671624Smax.romanov@nginx.com 14681624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str, 14691624Smax.romanov@nginx.com spec_version) == -1)) 14701624Smax.romanov@nginx.com { 14711624Smax.romanov@nginx.com nxt_unit_req_alert(req, 14721624Smax.romanov@nginx.com "Python failed to set 'asgi.spec_version' item"); 14731624Smax.romanov@nginx.com 14741624Smax.romanov@nginx.com Py_DECREF(asgi); 14751624Smax.romanov@nginx.com Py_DECREF(scope); 14761624Smax.romanov@nginx.com 14771624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14781624Smax.romanov@nginx.com "failed to set 'asgi.spec_version' item"); 14791624Smax.romanov@nginx.com } 14801624Smax.romanov@nginx.com 14811624Smax.romanov@nginx.com Py_DECREF(asgi); 14821624Smax.romanov@nginx.com 14831624Smax.romanov@nginx.com return scope; 14841624Smax.romanov@nginx.com } 14851624Smax.romanov@nginx.com 14861624Smax.romanov@nginx.com 14871624Smax.romanov@nginx.com void 14881681Smax.romanov@nginx.com nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link) 14891681Smax.romanov@nginx.com { 14901681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 14911681Smax.romanov@nginx.com 14921681Smax.romanov@nginx.com ctx_data = req->ctx->data; 14931681Smax.romanov@nginx.com 14941681Smax.romanov@nginx.com nxt_queue_insert_tail(&ctx_data->drain_queue, link); 14951681Smax.romanov@nginx.com } 14961681Smax.romanov@nginx.com 14971681Smax.romanov@nginx.com 14981681Smax.romanov@nginx.com void 14991624Smax.romanov@nginx.com nxt_py_asgi_dealloc(PyObject *self) 15001624Smax.romanov@nginx.com { 15011624Smax.romanov@nginx.com PyObject_Del(self); 15021624Smax.romanov@nginx.com } 15031624Smax.romanov@nginx.com 15041624Smax.romanov@nginx.com 15051624Smax.romanov@nginx.com PyObject * 15061624Smax.romanov@nginx.com nxt_py_asgi_await(PyObject *self) 15071624Smax.romanov@nginx.com { 15081624Smax.romanov@nginx.com Py_INCREF(self); 15091624Smax.romanov@nginx.com return self; 15101624Smax.romanov@nginx.com } 15111624Smax.romanov@nginx.com 15121624Smax.romanov@nginx.com 15131624Smax.romanov@nginx.com PyObject * 15141624Smax.romanov@nginx.com nxt_py_asgi_iter(PyObject *self) 15151624Smax.romanov@nginx.com { 15161624Smax.romanov@nginx.com Py_INCREF(self); 15171624Smax.romanov@nginx.com return self; 15181624Smax.romanov@nginx.com } 15191624Smax.romanov@nginx.com 15201624Smax.romanov@nginx.com 15211624Smax.romanov@nginx.com PyObject * 15221624Smax.romanov@nginx.com nxt_py_asgi_next(PyObject *self) 15231624Smax.romanov@nginx.com { 15241624Smax.romanov@nginx.com return NULL; 15251624Smax.romanov@nginx.com } 15261624Smax.romanov@nginx.com 15271624Smax.romanov@nginx.com 15281681Smax.romanov@nginx.com static void 15291624Smax.romanov@nginx.com nxt_python_asgi_done(void) 15301624Smax.romanov@nginx.com { 15311624Smax.romanov@nginx.com nxt_py_asgi_str_done(); 15321624Smax.romanov@nginx.com 15331624Smax.romanov@nginx.com Py_XDECREF(nxt_py_port_read); 15341624Smax.romanov@nginx.com } 15351624Smax.romanov@nginx.com 15361624Smax.romanov@nginx.com #else /* !(NXT_HAVE_ASGI) */ 15371624Smax.romanov@nginx.com 15381624Smax.romanov@nginx.com 15391624Smax.romanov@nginx.com int 15401624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj) 15411624Smax.romanov@nginx.com { 15421624Smax.romanov@nginx.com return 0; 15431624Smax.romanov@nginx.com } 15441624Smax.romanov@nginx.com 15451624Smax.romanov@nginx.com 15461681Smax.romanov@nginx.com int 15471681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 15481624Smax.romanov@nginx.com { 15491681Smax.romanov@nginx.com nxt_unit_alert(NULL, "ASGI not implemented"); 15501681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 15511624Smax.romanov@nginx.com } 15521624Smax.romanov@nginx.com 15531624Smax.romanov@nginx.com 15541624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */ 1555