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