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); 201918Smax.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); 39*1979Smax.romanov@nginx.com static int nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); 401624Smax.romanov@nginx.com static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port); 411624Smax.romanov@nginx.com static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); 421624Smax.romanov@nginx.com static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); 431624Smax.romanov@nginx.com 441624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); 451681Smax.romanov@nginx.com static void nxt_python_asgi_done(void); 461624Smax.romanov@nginx.com 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; 691697Smax.romanov@nginx.com PyObject *func; 701624Smax.romanov@nginx.com PyCodeObject *code; 711624Smax.romanov@nginx.com 721697Smax.romanov@nginx.com func = nxt_python_asgi_get_func(obj); 731697Smax.romanov@nginx.com 741697Smax.romanov@nginx.com if (func == NULL) { 751697Smax.romanov@nginx.com return 0; 761697Smax.romanov@nginx.com } 771697Smax.romanov@nginx.com 781697Smax.romanov@nginx.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 791697Smax.romanov@nginx.com 801697Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with " 811697Smax.romanov@nginx.com "%d argument(s)", 821697Smax.romanov@nginx.com (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ", 831697Smax.romanov@nginx.com code->co_argcount); 841697Smax.romanov@nginx.com 851697Smax.romanov@nginx.com res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1; 861697Smax.romanov@nginx.com 871697Smax.romanov@nginx.com Py_DECREF(func); 881697Smax.romanov@nginx.com 891697Smax.romanov@nginx.com return res; 901697Smax.romanov@nginx.com } 911697Smax.romanov@nginx.com 921697Smax.romanov@nginx.com 931697Smax.romanov@nginx.com static PyObject * 941697Smax.romanov@nginx.com nxt_python_asgi_get_func(PyObject *obj) 951697Smax.romanov@nginx.com { 961697Smax.romanov@nginx.com PyObject *call; 971697Smax.romanov@nginx.com 981624Smax.romanov@nginx.com if (PyFunction_Check(obj)) { 991697Smax.romanov@nginx.com Py_INCREF(obj); 1001697Smax.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 1061697Smax.romanov@nginx.com Py_INCREF(obj); 1071697Smax.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) { 1131697Smax.romanov@nginx.com return NULL; 1141624Smax.romanov@nginx.com } 1151624Smax.romanov@nginx.com 1161624Smax.romanov@nginx.com if (PyFunction_Check(call)) { 1171697Smax.romanov@nginx.com return call; 1181697Smax.romanov@nginx.com } 1191624Smax.romanov@nginx.com 1201697Smax.romanov@nginx.com if (PyMethod_Check(call)) { 1211697Smax.romanov@nginx.com obj = PyMethod_GET_FUNCTION(call); 1221624Smax.romanov@nginx.com 1231697Smax.romanov@nginx.com Py_INCREF(obj); 1241697Smax.romanov@nginx.com Py_DECREF(call); 1251697Smax.romanov@nginx.com 1261697Smax.romanov@nginx.com return obj; 1271624Smax.romanov@nginx.com } 1281624Smax.romanov@nginx.com 1291624Smax.romanov@nginx.com Py_DECREF(call); 1301624Smax.romanov@nginx.com 1311697Smax.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 { 1381697Smax.romanov@nginx.com PyObject *func; 1391872So.canty@f5.com nxt_int_t i; 1401697Smax.romanov@nginx.com PyCodeObject *code; 1411697Smax.romanov@nginx.com 1421681Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_init"); 1431624Smax.romanov@nginx.com 1441681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) { 1451681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to init string objects"); 1461681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1471624Smax.romanov@nginx.com } 1481624Smax.romanov@nginx.com 1491624Smax.romanov@nginx.com nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL); 1501624Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_port_read == NULL)) { 1511681Smax.romanov@nginx.com nxt_unit_alert(NULL, 1521681Smax.romanov@nginx.com "Python failed to initialize the 'port_read' function"); 1531681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1541624Smax.romanov@nginx.com } 1551624Smax.romanov@nginx.com 1561681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) { 1571681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1581624Smax.romanov@nginx.com } 1591624Smax.romanov@nginx.com 1601681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) { 1611681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1621624Smax.romanov@nginx.com } 1631624Smax.romanov@nginx.com 1641872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) { 1651872So.canty@f5.com func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application); 1661872So.canty@f5.com if (nxt_slow_path(func == NULL)) { 1671872So.canty@f5.com nxt_unit_alert(NULL, "Python cannot find function for callable"); 1681872So.canty@f5.com return NXT_UNIT_ERROR; 1691872So.canty@f5.com } 1701697Smax.romanov@nginx.com 1711872So.canty@f5.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 1721697Smax.romanov@nginx.com 1731872So.canty@f5.com if ((code->co_flags & CO_COROUTINE) == 0) { 1741872So.canty@f5.com nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " 1751872So.canty@f5.com "switching to legacy mode"); 1761872So.canty@f5.com nxt_py_targets->target[i].asgi_legacy = 1; 1771872So.canty@f5.com } 1781872So.canty@f5.com 1791872So.canty@f5.com Py_DECREF(func); 1801697Smax.romanov@nginx.com } 1811697Smax.romanov@nginx.com 1821624Smax.romanov@nginx.com init->callbacks.request_handler = nxt_py_asgi_request_handler; 1831624Smax.romanov@nginx.com init->callbacks.data_handler = nxt_py_asgi_http_data_handler; 1841624Smax.romanov@nginx.com init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler; 1851715Smax.romanov@nginx.com init->callbacks.close_handler = nxt_py_asgi_close_handler; 1861624Smax.romanov@nginx.com init->callbacks.quit = nxt_py_asgi_quit; 1871624Smax.romanov@nginx.com init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; 1881624Smax.romanov@nginx.com init->callbacks.add_port = nxt_py_asgi_add_port; 1891624Smax.romanov@nginx.com init->callbacks.remove_port = nxt_py_asgi_remove_port; 1901624Smax.romanov@nginx.com 1911681Smax.romanov@nginx.com *proto = nxt_py_asgi_proto; 1921681Smax.romanov@nginx.com 1931681Smax.romanov@nginx.com return NXT_UNIT_OK; 1941681Smax.romanov@nginx.com } 1951681Smax.romanov@nginx.com 1961681Smax.romanov@nginx.com 1971681Smax.romanov@nginx.com static int 1981918Smax.romanov@nginx.com nxt_python_asgi_ctx_data_alloc(void **pdata, int main) 1991681Smax.romanov@nginx.com { 2001681Smax.romanov@nginx.com uint32_t i; 2011918Smax.romanov@nginx.com PyObject *asyncio, *loop, *event_loop, *obj; 2021918Smax.romanov@nginx.com const char *event_loop_func; 2031681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 2041681Smax.romanov@nginx.com 2051681Smax.romanov@nginx.com ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); 2061681Smax.romanov@nginx.com if (nxt_slow_path(ctx_data == NULL)) { 2071681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate context data"); 2081681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2091681Smax.romanov@nginx.com } 2101681Smax.romanov@nginx.com 2111681Smax.romanov@nginx.com memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t)); 2121681Smax.romanov@nginx.com 2131681Smax.romanov@nginx.com nxt_queue_init(&ctx_data->drain_queue); 2141681Smax.romanov@nginx.com 2151681Smax.romanov@nginx.com struct { 2161681Smax.romanov@nginx.com const char *key; 2171681Smax.romanov@nginx.com PyObject **handler; 2181681Smax.romanov@nginx.com 2191681Smax.romanov@nginx.com } handlers[] = { 2201681Smax.romanov@nginx.com { "create_task", &ctx_data->loop_create_task }, 2211681Smax.romanov@nginx.com { "add_reader", &ctx_data->loop_add_reader }, 2221681Smax.romanov@nginx.com { "remove_reader", &ctx_data->loop_remove_reader }, 2231681Smax.romanov@nginx.com { "call_soon", &ctx_data->loop_call_soon }, 2241681Smax.romanov@nginx.com { "run_until_complete", &ctx_data->loop_run_until_complete }, 2251681Smax.romanov@nginx.com { "create_future", &ctx_data->loop_create_future }, 2261681Smax.romanov@nginx.com }; 2271681Smax.romanov@nginx.com 2281681Smax.romanov@nginx.com loop = NULL; 2291681Smax.romanov@nginx.com 2301681Smax.romanov@nginx.com asyncio = PyImport_ImportModule("asyncio"); 2311681Smax.romanov@nginx.com if (nxt_slow_path(asyncio == NULL)) { 2321681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to import module 'asyncio'"); 2331681Smax.romanov@nginx.com nxt_python_print_exception(); 2341681Smax.romanov@nginx.com goto fail; 2351681Smax.romanov@nginx.com } 2361681Smax.romanov@nginx.com 2371918Smax.romanov@nginx.com event_loop_func = main ? "get_event_loop" : "new_event_loop"; 2381918Smax.romanov@nginx.com 2391918Smax.romanov@nginx.com event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), 2401918Smax.romanov@nginx.com event_loop_func); 2411918Smax.romanov@nginx.com if (nxt_slow_path(event_loop == NULL)) { 2421681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2431918Smax.romanov@nginx.com "Python failed to get '%s' from module 'asyncio'", 2441918Smax.romanov@nginx.com event_loop_func); 2451681Smax.romanov@nginx.com goto fail; 2461681Smax.romanov@nginx.com } 2471681Smax.romanov@nginx.com 2481918Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { 2491681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2501918Smax.romanov@nginx.com "'asyncio.%s' is not a callable object", 2511918Smax.romanov@nginx.com event_loop_func); 2521681Smax.romanov@nginx.com goto fail; 2531681Smax.romanov@nginx.com } 2541681Smax.romanov@nginx.com 2551918Smax.romanov@nginx.com loop = PyObject_CallObject(event_loop, NULL); 2561681Smax.romanov@nginx.com if (nxt_slow_path(loop == NULL)) { 2571918Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", 2581918Smax.romanov@nginx.com event_loop_func); 2591681Smax.romanov@nginx.com goto fail; 2601681Smax.romanov@nginx.com } 2611681Smax.romanov@nginx.com 2621681Smax.romanov@nginx.com for (i = 0; i < nxt_nitems(handlers); i++) { 2631681Smax.romanov@nginx.com obj = PyObject_GetAttrString(loop, handlers[i].key); 2641681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2651681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'loop.%s'", 2661681Smax.romanov@nginx.com handlers[i].key); 2671681Smax.romanov@nginx.com goto fail; 2681681Smax.romanov@nginx.com } 2691681Smax.romanov@nginx.com 2701681Smax.romanov@nginx.com *handlers[i].handler = obj; 2711681Smax.romanov@nginx.com 2721681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 2731681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'loop.%s' is not a callable object", 2741681Smax.romanov@nginx.com handlers[i].key); 2751681Smax.romanov@nginx.com goto fail; 2761681Smax.romanov@nginx.com } 2771681Smax.romanov@nginx.com } 2781681Smax.romanov@nginx.com 2791681Smax.romanov@nginx.com obj = PyObject_CallObject(ctx_data->loop_create_future, NULL); 2801681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2811681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future "); 2821681Smax.romanov@nginx.com nxt_python_print_exception(); 2831681Smax.romanov@nginx.com goto fail; 2841681Smax.romanov@nginx.com } 2851681Smax.romanov@nginx.com 2861681Smax.romanov@nginx.com ctx_data->quit_future = obj; 2871681Smax.romanov@nginx.com 2881681Smax.romanov@nginx.com obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result"); 2891681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2901681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'future.set_result'"); 2911681Smax.romanov@nginx.com goto fail; 2921681Smax.romanov@nginx.com } 2931681Smax.romanov@nginx.com 2941681Smax.romanov@nginx.com ctx_data->quit_future_set_result = obj; 2951681Smax.romanov@nginx.com 2961681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 2971681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'future.set_result' is not a callable object"); 2981681Smax.romanov@nginx.com goto fail; 2991681Smax.romanov@nginx.com } 3001681Smax.romanov@nginx.com 3011624Smax.romanov@nginx.com Py_DECREF(loop); 3021624Smax.romanov@nginx.com Py_DECREF(asyncio); 3031624Smax.romanov@nginx.com 3041681Smax.romanov@nginx.com *pdata = ctx_data; 3051681Smax.romanov@nginx.com 3061681Smax.romanov@nginx.com return NXT_UNIT_OK; 3071624Smax.romanov@nginx.com 3081624Smax.romanov@nginx.com fail: 3091624Smax.romanov@nginx.com 3101681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(ctx_data); 3111681Smax.romanov@nginx.com 3121624Smax.romanov@nginx.com Py_XDECREF(loop); 3131681Smax.romanov@nginx.com Py_XDECREF(asyncio); 3141624Smax.romanov@nginx.com 3151681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3161624Smax.romanov@nginx.com } 3171624Smax.romanov@nginx.com 3181624Smax.romanov@nginx.com 3191681Smax.romanov@nginx.com static void 3201681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(void *data) 3211681Smax.romanov@nginx.com { 3221681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3231681Smax.romanov@nginx.com 3241681Smax.romanov@nginx.com ctx_data = data; 3251681Smax.romanov@nginx.com 3261681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_run_until_complete); 3271681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_future); 3281681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_task); 3291681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_call_soon); 3301681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_add_reader); 3311681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_remove_reader); 3321681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future); 3331681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future_set_result); 3341681Smax.romanov@nginx.com 3351681Smax.romanov@nginx.com nxt_unit_free(NULL, ctx_data); 3361681Smax.romanov@nginx.com } 3371681Smax.romanov@nginx.com 3381681Smax.romanov@nginx.com 3391681Smax.romanov@nginx.com static int 3401681Smax.romanov@nginx.com nxt_python_asgi_startup(void *data) 3411681Smax.romanov@nginx.com { 3421681Smax.romanov@nginx.com return nxt_py_asgi_lifespan_startup(data); 3431681Smax.romanov@nginx.com } 3441681Smax.romanov@nginx.com 3451681Smax.romanov@nginx.com 3461681Smax.romanov@nginx.com static int 3471624Smax.romanov@nginx.com nxt_python_asgi_run(nxt_unit_ctx_t *ctx) 3481624Smax.romanov@nginx.com { 3491681Smax.romanov@nginx.com PyObject *res; 3501681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3511624Smax.romanov@nginx.com 3521681Smax.romanov@nginx.com ctx_data = ctx->data; 3531681Smax.romanov@nginx.com 3541681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 3551681Smax.romanov@nginx.com ctx_data->quit_future, NULL); 3561624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 3571624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete"); 3581624Smax.romanov@nginx.com nxt_python_print_exception(); 3591624Smax.romanov@nginx.com 3601681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3611624Smax.romanov@nginx.com } 3621624Smax.romanov@nginx.com 3631624Smax.romanov@nginx.com Py_DECREF(res); 3641624Smax.romanov@nginx.com 3651681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port); 3661681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(ctx, ctx_data->port); 3671681Smax.romanov@nginx.com 3681681Smax.romanov@nginx.com if (ctx_data->port != 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 { 897*1979Smax.romanov@nginx.com nxt_unit_port_t *port; 8981681Smax.romanov@nginx.com 8991681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_shared_port == NULL)) { 9001681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9011681Smax.romanov@nginx.com } 9021681Smax.romanov@nginx.com 9031681Smax.romanov@nginx.com port = nxt_py_shared_port; 9041681Smax.romanov@nginx.com 9051681Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port); 9061681Smax.romanov@nginx.com 907*1979Smax.romanov@nginx.com return nxt_py_asgi_add_reader(ctx, port); 9081681Smax.romanov@nginx.com } 9091681Smax.romanov@nginx.com 9101681Smax.romanov@nginx.com 9111681Smax.romanov@nginx.com static int 9121624Smax.romanov@nginx.com nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 9131624Smax.romanov@nginx.com { 914*1979Smax.romanov@nginx.com int nb; 9151681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 9161624Smax.romanov@nginx.com 9171624Smax.romanov@nginx.com if (port->in_fd == -1) { 9181624Smax.romanov@nginx.com return NXT_UNIT_OK; 9191624Smax.romanov@nginx.com } 9201624Smax.romanov@nginx.com 9211624Smax.romanov@nginx.com nb = 1; 9221624Smax.romanov@nginx.com 9231624Smax.romanov@nginx.com if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) { 9241624Smax.romanov@nginx.com nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)", 9251624Smax.romanov@nginx.com port->in_fd, strerror(errno), errno); 9261624Smax.romanov@nginx.com 9271624Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9281624Smax.romanov@nginx.com } 9291624Smax.romanov@nginx.com 9301624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); 9311624Smax.romanov@nginx.com 9321681Smax.romanov@nginx.com if (port->id.id == NXT_UNIT_SHARED_PORT_ID) { 9331681Smax.romanov@nginx.com nxt_py_shared_port = port; 9341681Smax.romanov@nginx.com 9351681Smax.romanov@nginx.com return NXT_UNIT_OK; 9361681Smax.romanov@nginx.com } 9371681Smax.romanov@nginx.com 9381681Smax.romanov@nginx.com ctx_data = ctx->data; 9391681Smax.romanov@nginx.com 9401681Smax.romanov@nginx.com ctx_data->port = port; 941*1979Smax.romanov@nginx.com 942*1979Smax.romanov@nginx.com return nxt_py_asgi_add_reader(ctx, port); 943*1979Smax.romanov@nginx.com } 944*1979Smax.romanov@nginx.com 9451681Smax.romanov@nginx.com 946*1979Smax.romanov@nginx.com static int 947*1979Smax.romanov@nginx.com nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 948*1979Smax.romanov@nginx.com { 949*1979Smax.romanov@nginx.com int rc; 950*1979Smax.romanov@nginx.com PyObject *res, *fd, *py_ctx, *py_port; 951*1979Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 952*1979Smax.romanov@nginx.com 953*1979Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_add_reader %d %p %p", port->in_fd, ctx, port); 954*1979Smax.romanov@nginx.com 955*1979Smax.romanov@nginx.com ctx_data = ctx->data; 9561682Smax.romanov@nginx.com 9571682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 9581682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 9591682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create fd"); 9601682Smax.romanov@nginx.com nxt_python_print_exception(); 9611682Smax.romanov@nginx.com 962*1979Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9631682Smax.romanov@nginx.com } 9641682Smax.romanov@nginx.com 965*1979Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 966*1979Smax.romanov@nginx.com 9671682Smax.romanov@nginx.com py_ctx = PyLong_FromVoidPtr(ctx); 9681682Smax.romanov@nginx.com if (nxt_slow_path(py_ctx == NULL)) { 9691682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_ctx"); 9701682Smax.romanov@nginx.com nxt_python_print_exception(); 9711682Smax.romanov@nginx.com 9721682Smax.romanov@nginx.com goto clean_fd; 9731682Smax.romanov@nginx.com } 9741682Smax.romanov@nginx.com 9751682Smax.romanov@nginx.com py_port = PyLong_FromVoidPtr(port); 9761682Smax.romanov@nginx.com if (nxt_slow_path(py_port == NULL)) { 9771682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_port"); 9781682Smax.romanov@nginx.com nxt_python_print_exception(); 9791682Smax.romanov@nginx.com 9801682Smax.romanov@nginx.com goto clean_py_ctx; 9811682Smax.romanov@nginx.com } 9821682Smax.romanov@nginx.com 9831681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 9841682Smax.romanov@nginx.com fd, nxt_py_port_read, 9851682Smax.romanov@nginx.com py_ctx, py_port, NULL); 9861624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 9871624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to add_reader"); 9881681Smax.romanov@nginx.com nxt_python_print_exception(); 9891624Smax.romanov@nginx.com 9901682Smax.romanov@nginx.com } else { 9911682Smax.romanov@nginx.com Py_DECREF(res); 9921682Smax.romanov@nginx.com 9931682Smax.romanov@nginx.com rc = NXT_UNIT_OK; 9941624Smax.romanov@nginx.com } 9951624Smax.romanov@nginx.com 9961682Smax.romanov@nginx.com Py_DECREF(py_port); 9971682Smax.romanov@nginx.com 9981682Smax.romanov@nginx.com clean_py_ctx: 9991682Smax.romanov@nginx.com 10001682Smax.romanov@nginx.com Py_DECREF(py_ctx); 10011624Smax.romanov@nginx.com 10021682Smax.romanov@nginx.com clean_fd: 10031682Smax.romanov@nginx.com 10041682Smax.romanov@nginx.com Py_DECREF(fd); 10051682Smax.romanov@nginx.com 10061682Smax.romanov@nginx.com return rc; 10071624Smax.romanov@nginx.com } 10081624Smax.romanov@nginx.com 10091624Smax.romanov@nginx.com 10101624Smax.romanov@nginx.com static void 10111624Smax.romanov@nginx.com nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) 10121624Smax.romanov@nginx.com { 10131624Smax.romanov@nginx.com if (port->in_fd == -1) { 10141624Smax.romanov@nginx.com return; 10151624Smax.romanov@nginx.com } 10161624Smax.romanov@nginx.com 10171681Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); 10181681Smax.romanov@nginx.com 10191681Smax.romanov@nginx.com if (nxt_py_shared_port == port) { 10201681Smax.romanov@nginx.com nxt_py_shared_port = NULL; 10211624Smax.romanov@nginx.com } 10221624Smax.romanov@nginx.com } 10231624Smax.romanov@nginx.com 10241624Smax.romanov@nginx.com 10251624Smax.romanov@nginx.com static void 10261624Smax.romanov@nginx.com nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) 10271624Smax.romanov@nginx.com { 10281682Smax.romanov@nginx.com PyObject *res, *p; 10291681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 10301624Smax.romanov@nginx.com 10311624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_quit %p", ctx); 10321624Smax.romanov@nginx.com 10331681Smax.romanov@nginx.com ctx_data = ctx->data; 10341681Smax.romanov@nginx.com 10351681Smax.romanov@nginx.com if (nxt_py_shared_port != NULL) { 10361682Smax.romanov@nginx.com p = PyLong_FromLong(nxt_py_shared_port->in_fd); 10371682Smax.romanov@nginx.com if (nxt_slow_path(p == NULL)) { 10381682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Long"); 10391682Smax.romanov@nginx.com nxt_python_print_exception(); 10401682Smax.romanov@nginx.com 10411682Smax.romanov@nginx.com } else { 10421682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, 10431682Smax.romanov@nginx.com p, NULL); 10441682Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10451682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to remove_reader"); 10461682Smax.romanov@nginx.com nxt_python_print_exception(); 10471682Smax.romanov@nginx.com 10481682Smax.romanov@nginx.com } else { 10491682Smax.romanov@nginx.com Py_DECREF(res); 10501682Smax.romanov@nginx.com } 10511682Smax.romanov@nginx.com 10521682Smax.romanov@nginx.com Py_DECREF(p); 10531682Smax.romanov@nginx.com } 10541682Smax.romanov@nginx.com } 10551682Smax.romanov@nginx.com 10561682Smax.romanov@nginx.com p = PyLong_FromLong(0); 10571682Smax.romanov@nginx.com if (nxt_slow_path(p == NULL)) { 10581682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Long"); 10591682Smax.romanov@nginx.com nxt_python_print_exception(); 10601682Smax.romanov@nginx.com 10611682Smax.romanov@nginx.com } else { 10621682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, 10631682Smax.romanov@nginx.com p, NULL); 10641681Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10651682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to set_result"); 10661681Smax.romanov@nginx.com nxt_python_print_exception(); 10671681Smax.romanov@nginx.com 10681681Smax.romanov@nginx.com } else { 10691681Smax.romanov@nginx.com Py_DECREF(res); 10701681Smax.romanov@nginx.com } 10711681Smax.romanov@nginx.com 10721682Smax.romanov@nginx.com Py_DECREF(p); 10731681Smax.romanov@nginx.com } 10741624Smax.romanov@nginx.com } 10751624Smax.romanov@nginx.com 10761624Smax.romanov@nginx.com 10771624Smax.romanov@nginx.com static void 10781624Smax.romanov@nginx.com nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx) 10791624Smax.romanov@nginx.com { 10801681Smax.romanov@nginx.com int rc; 10811681Smax.romanov@nginx.com nxt_queue_link_t *lnk; 10821681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 10831624Smax.romanov@nginx.com 10841681Smax.romanov@nginx.com ctx_data = ctx->data; 10851681Smax.romanov@nginx.com 10861681Smax.romanov@nginx.com while (!nxt_queue_is_empty(&ctx_data->drain_queue)) { 10871681Smax.romanov@nginx.com lnk = nxt_queue_first(&ctx_data->drain_queue); 10881624Smax.romanov@nginx.com 10891624Smax.romanov@nginx.com rc = nxt_py_asgi_http_drain(lnk); 10901624Smax.romanov@nginx.com if (rc == NXT_UNIT_AGAIN) { 10911681Smax.romanov@nginx.com return; 10921624Smax.romanov@nginx.com } 10931624Smax.romanov@nginx.com 10941624Smax.romanov@nginx.com nxt_queue_remove(lnk); 10951624Smax.romanov@nginx.com } 10961624Smax.romanov@nginx.com } 10971624Smax.romanov@nginx.com 10981624Smax.romanov@nginx.com 10991624Smax.romanov@nginx.com static PyObject * 11001624Smax.romanov@nginx.com nxt_py_asgi_port_read(PyObject *self, PyObject *args) 11011624Smax.romanov@nginx.com { 11021767Smax.romanov@nginx.com int rc; 11031767Smax.romanov@nginx.com PyObject *arg0, *arg1, *res; 11041767Smax.romanov@nginx.com Py_ssize_t n; 11051767Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 11061767Smax.romanov@nginx.com nxt_unit_port_t *port; 11071767Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 11081624Smax.romanov@nginx.com 11091624Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 11101624Smax.romanov@nginx.com 11111624Smax.romanov@nginx.com if (n != 2) { 11121624Smax.romanov@nginx.com nxt_unit_alert(NULL, 11131624Smax.romanov@nginx.com "nxt_py_asgi_port_read: invalid number of arguments %d", 11141624Smax.romanov@nginx.com (int) n); 11151624Smax.romanov@nginx.com 11161624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 11171624Smax.romanov@nginx.com } 11181624Smax.romanov@nginx.com 11191767Smax.romanov@nginx.com arg0 = PyTuple_GET_ITEM(args, 0); 11201767Smax.romanov@nginx.com if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) { 11211624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11221624Smax.romanov@nginx.com "the first argument is not a long"); 11231624Smax.romanov@nginx.com } 11241624Smax.romanov@nginx.com 11251767Smax.romanov@nginx.com ctx = PyLong_AsVoidPtr(arg0); 11261624Smax.romanov@nginx.com 11271767Smax.romanov@nginx.com arg1 = PyTuple_GET_ITEM(args, 1); 11281767Smax.romanov@nginx.com if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) { 11291624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11301624Smax.romanov@nginx.com "the second argument is not a long"); 11311624Smax.romanov@nginx.com } 11321624Smax.romanov@nginx.com 11331767Smax.romanov@nginx.com port = PyLong_AsVoidPtr(arg1); 11341624Smax.romanov@nginx.com 11351624Smax.romanov@nginx.com rc = nxt_unit_process_port_msg(ctx, port); 11361624Smax.romanov@nginx.com 11371767Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc); 11381767Smax.romanov@nginx.com 11391624Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 11401624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 11411681Smax.romanov@nginx.com "error processing port %d message", port->id.id); 11421624Smax.romanov@nginx.com } 11431624Smax.romanov@nginx.com 11441767Smax.romanov@nginx.com if (rc == NXT_UNIT_OK) { 11451767Smax.romanov@nginx.com ctx_data = ctx->data; 11461767Smax.romanov@nginx.com 11471767Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, 11481767Smax.romanov@nginx.com nxt_py_port_read, 11491767Smax.romanov@nginx.com arg0, arg1, NULL); 11501767Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 11511767Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'"); 11521767Smax.romanov@nginx.com nxt_python_print_exception(); 11531767Smax.romanov@nginx.com } 11541767Smax.romanov@nginx.com 11551767Smax.romanov@nginx.com Py_XDECREF(res); 11561767Smax.romanov@nginx.com } 11571767Smax.romanov@nginx.com 11581624Smax.romanov@nginx.com Py_RETURN_NONE; 11591624Smax.romanov@nginx.com } 11601624Smax.romanov@nginx.com 11611624Smax.romanov@nginx.com 11621624Smax.romanov@nginx.com PyObject * 11631624Smax.romanov@nginx.com nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb, 11641624Smax.romanov@nginx.com void *data) 11651624Smax.romanov@nginx.com { 11661624Smax.romanov@nginx.com int i; 11671624Smax.romanov@nginx.com PyObject *iter, *header, *h_iter, *name, *val, *res; 11681624Smax.romanov@nginx.com 11691624Smax.romanov@nginx.com iter = PyObject_GetIter(headers); 11701624Smax.romanov@nginx.com if (nxt_slow_path(iter == NULL)) { 11711624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable"); 11721624Smax.romanov@nginx.com } 11731624Smax.romanov@nginx.com 11741624Smax.romanov@nginx.com for (i = 0; /* void */; i++) { 11751624Smax.romanov@nginx.com header = PyIter_Next(iter); 11761624Smax.romanov@nginx.com if (header == NULL) { 11771624Smax.romanov@nginx.com break; 11781624Smax.romanov@nginx.com } 11791624Smax.romanov@nginx.com 11801624Smax.romanov@nginx.com h_iter = PyObject_GetIter(header); 11811624Smax.romanov@nginx.com if (nxt_slow_path(h_iter == NULL)) { 11821624Smax.romanov@nginx.com Py_DECREF(header); 11831624Smax.romanov@nginx.com Py_DECREF(iter); 11841624Smax.romanov@nginx.com 11851624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11861624Smax.romanov@nginx.com "'headers' item #%d is not an iterable", i); 11871624Smax.romanov@nginx.com } 11881624Smax.romanov@nginx.com 11891624Smax.romanov@nginx.com name = PyIter_Next(h_iter); 11901624Smax.romanov@nginx.com if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) { 11911624Smax.romanov@nginx.com Py_XDECREF(name); 11921624Smax.romanov@nginx.com Py_DECREF(h_iter); 11931624Smax.romanov@nginx.com Py_DECREF(header); 11941624Smax.romanov@nginx.com Py_DECREF(iter); 11951624Smax.romanov@nginx.com 11961624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11971624Smax.romanov@nginx.com "'headers' item #%d 'name' is not a byte string", i); 11981624Smax.romanov@nginx.com } 11991624Smax.romanov@nginx.com 12001624Smax.romanov@nginx.com val = PyIter_Next(h_iter); 12011624Smax.romanov@nginx.com if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) { 12021624Smax.romanov@nginx.com Py_XDECREF(val); 12031624Smax.romanov@nginx.com Py_DECREF(h_iter); 12041624Smax.romanov@nginx.com Py_DECREF(header); 12051624Smax.romanov@nginx.com Py_DECREF(iter); 12061624Smax.romanov@nginx.com 12071624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 12081624Smax.romanov@nginx.com "'headers' item #%d 'value' is not a byte string", i); 12091624Smax.romanov@nginx.com } 12101624Smax.romanov@nginx.com 12111624Smax.romanov@nginx.com res = cb(data, i, name, val); 12121624Smax.romanov@nginx.com 12131624Smax.romanov@nginx.com Py_DECREF(name); 12141624Smax.romanov@nginx.com Py_DECREF(val); 12151624Smax.romanov@nginx.com Py_DECREF(h_iter); 12161624Smax.romanov@nginx.com Py_DECREF(header); 12171624Smax.romanov@nginx.com 12181624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 12191624Smax.romanov@nginx.com Py_DECREF(iter); 12201624Smax.romanov@nginx.com 12211624Smax.romanov@nginx.com return NULL; 12221624Smax.romanov@nginx.com } 12231624Smax.romanov@nginx.com 12241624Smax.romanov@nginx.com Py_DECREF(res); 12251624Smax.romanov@nginx.com } 12261624Smax.romanov@nginx.com 12271624Smax.romanov@nginx.com Py_DECREF(iter); 12281624Smax.romanov@nginx.com 12291624Smax.romanov@nginx.com Py_RETURN_NONE; 12301624Smax.romanov@nginx.com } 12311624Smax.romanov@nginx.com 12321624Smax.romanov@nginx.com 12331624Smax.romanov@nginx.com PyObject * 12341624Smax.romanov@nginx.com nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val) 12351624Smax.romanov@nginx.com { 12361624Smax.romanov@nginx.com nxt_py_asgi_calc_size_ctx_t *ctx; 12371624Smax.romanov@nginx.com 12381624Smax.romanov@nginx.com ctx = data; 12391624Smax.romanov@nginx.com 12401624Smax.romanov@nginx.com ctx->fields_count++; 12411624Smax.romanov@nginx.com ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val); 12421624Smax.romanov@nginx.com 12431624Smax.romanov@nginx.com Py_RETURN_NONE; 12441624Smax.romanov@nginx.com } 12451624Smax.romanov@nginx.com 12461624Smax.romanov@nginx.com 12471624Smax.romanov@nginx.com PyObject * 12481624Smax.romanov@nginx.com nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val) 12491624Smax.romanov@nginx.com { 12501624Smax.romanov@nginx.com int rc; 12511624Smax.romanov@nginx.com char *name_str, *val_str; 12521624Smax.romanov@nginx.com uint32_t name_len, val_len; 12531624Smax.romanov@nginx.com nxt_off_t content_length; 12541624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 12551624Smax.romanov@nginx.com nxt_py_asgi_add_field_ctx_t *ctx; 12561624Smax.romanov@nginx.com 12571624Smax.romanov@nginx.com name_str = PyBytes_AS_STRING(name); 12581624Smax.romanov@nginx.com name_len = PyBytes_GET_SIZE(name); 12591624Smax.romanov@nginx.com 12601624Smax.romanov@nginx.com val_str = PyBytes_AS_STRING(val); 12611624Smax.romanov@nginx.com val_len = PyBytes_GET_SIZE(val); 12621624Smax.romanov@nginx.com 12631624Smax.romanov@nginx.com ctx = data; 12641624Smax.romanov@nginx.com req = ctx->req; 12651624Smax.romanov@nginx.com 12661624Smax.romanov@nginx.com rc = nxt_unit_response_add_field(req, name_str, name_len, 12671624Smax.romanov@nginx.com val_str, val_len); 12681624Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 12691624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 12701624Smax.romanov@nginx.com "failed to add header #%d", i); 12711624Smax.romanov@nginx.com } 12721624Smax.romanov@nginx.com 12731624Smax.romanov@nginx.com if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { 12741624Smax.romanov@nginx.com content_length = nxt_off_t_parse((u_char *) val_str, val_len); 12751624Smax.romanov@nginx.com if (nxt_slow_path(content_length < 0)) { 12761624Smax.romanov@nginx.com nxt_unit_req_error(req, "failed to parse Content-Length " 12771624Smax.romanov@nginx.com "value %.*s", (int) val_len, val_str); 12781624Smax.romanov@nginx.com 12791624Smax.romanov@nginx.com return PyErr_Format(PyExc_ValueError, 12801624Smax.romanov@nginx.com "Failed to parse Content-Length: '%.*s'", 12811624Smax.romanov@nginx.com (int) val_len, val_str); 12821624Smax.romanov@nginx.com } 12831624Smax.romanov@nginx.com 12841624Smax.romanov@nginx.com ctx->content_length = content_length; 12851624Smax.romanov@nginx.com } 12861624Smax.romanov@nginx.com 12871624Smax.romanov@nginx.com Py_RETURN_NONE; 12881624Smax.romanov@nginx.com } 12891624Smax.romanov@nginx.com 12901624Smax.romanov@nginx.com 12911624Smax.romanov@nginx.com PyObject * 12921681Smax.romanov@nginx.com nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, 12931681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result) 12941624Smax.romanov@nginx.com { 12951624Smax.romanov@nginx.com PyObject *set_result, *res; 12961624Smax.romanov@nginx.com 12971624Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 12981624Smax.romanov@nginx.com Py_DECREF(future); 12991624Smax.romanov@nginx.com 13001624Smax.romanov@nginx.com return NULL; 13011624Smax.romanov@nginx.com } 13021624Smax.romanov@nginx.com 13031624Smax.romanov@nginx.com set_result = PyObject_GetAttrString(future, "set_result"); 13041624Smax.romanov@nginx.com if (nxt_slow_path(set_result == NULL)) { 13051624Smax.romanov@nginx.com nxt_unit_req_alert(req, "failed to get 'set_result' for future"); 13061624Smax.romanov@nginx.com 13071624Smax.romanov@nginx.com Py_CLEAR(future); 13081624Smax.romanov@nginx.com 13091681Smax.romanov@nginx.com goto cleanup_result; 13101624Smax.romanov@nginx.com } 13111624Smax.romanov@nginx.com 13121624Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(set_result) == 0)) { 13131624Smax.romanov@nginx.com nxt_unit_req_alert(req, "'future.set_result' is not a callable"); 13141624Smax.romanov@nginx.com 13151624Smax.romanov@nginx.com Py_CLEAR(future); 13161624Smax.romanov@nginx.com 13171624Smax.romanov@nginx.com goto cleanup; 13181624Smax.romanov@nginx.com } 13191624Smax.romanov@nginx.com 13201681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result, 13211624Smax.romanov@nginx.com result, NULL); 13221624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 13231624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'"); 13241624Smax.romanov@nginx.com nxt_python_print_exception(); 13251624Smax.romanov@nginx.com 13261624Smax.romanov@nginx.com Py_CLEAR(future); 13271624Smax.romanov@nginx.com } 13281624Smax.romanov@nginx.com 13291624Smax.romanov@nginx.com Py_XDECREF(res); 13301624Smax.romanov@nginx.com 13311624Smax.romanov@nginx.com cleanup: 13321624Smax.romanov@nginx.com 13331624Smax.romanov@nginx.com Py_DECREF(set_result); 13341681Smax.romanov@nginx.com 13351681Smax.romanov@nginx.com cleanup_result: 13361681Smax.romanov@nginx.com 13371624Smax.romanov@nginx.com Py_DECREF(result); 13381624Smax.romanov@nginx.com 13391624Smax.romanov@nginx.com return future; 13401624Smax.romanov@nginx.com } 13411624Smax.romanov@nginx.com 13421624Smax.romanov@nginx.com 13431624Smax.romanov@nginx.com PyObject * 13441624Smax.romanov@nginx.com nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type) 13451624Smax.romanov@nginx.com { 13461624Smax.romanov@nginx.com PyObject *msg; 13471624Smax.romanov@nginx.com 13481624Smax.romanov@nginx.com msg = PyDict_New(); 13491624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 13501624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create message dict"); 13511624Smax.romanov@nginx.com nxt_python_print_exception(); 13521624Smax.romanov@nginx.com 13531624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13541624Smax.romanov@nginx.com "failed to create message dict"); 13551624Smax.romanov@nginx.com } 13561624Smax.romanov@nginx.com 13571624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) { 13581624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'msg.type' item"); 13591624Smax.romanov@nginx.com 13601624Smax.romanov@nginx.com Py_DECREF(msg); 13611624Smax.romanov@nginx.com 13621624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13631624Smax.romanov@nginx.com "failed to set 'msg.type' item"); 13641624Smax.romanov@nginx.com } 13651624Smax.romanov@nginx.com 13661624Smax.romanov@nginx.com return msg; 13671624Smax.romanov@nginx.com } 13681624Smax.romanov@nginx.com 13691624Smax.romanov@nginx.com 13701624Smax.romanov@nginx.com PyObject * 13711624Smax.romanov@nginx.com nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, 13721624Smax.romanov@nginx.com PyObject *spec_version) 13731624Smax.romanov@nginx.com { 13741624Smax.romanov@nginx.com PyObject *scope, *asgi; 13751624Smax.romanov@nginx.com 13761624Smax.romanov@nginx.com scope = PyDict_New(); 13771624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 13781624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'scope' dict"); 13791624Smax.romanov@nginx.com nxt_python_print_exception(); 13801624Smax.romanov@nginx.com 13811624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13821624Smax.romanov@nginx.com "failed to create 'scope' dict"); 13831624Smax.romanov@nginx.com } 13841624Smax.romanov@nginx.com 13851624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) { 13861624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.type' item"); 13871624Smax.romanov@nginx.com 13881624Smax.romanov@nginx.com Py_DECREF(scope); 13891624Smax.romanov@nginx.com 13901624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13911624Smax.romanov@nginx.com "failed to set 'scope.type' item"); 13921624Smax.romanov@nginx.com } 13931624Smax.romanov@nginx.com 13941624Smax.romanov@nginx.com asgi = PyDict_New(); 13951624Smax.romanov@nginx.com if (nxt_slow_path(asgi == NULL)) { 13961624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'asgi' dict"); 13971624Smax.romanov@nginx.com nxt_python_print_exception(); 13981624Smax.romanov@nginx.com 13991624Smax.romanov@nginx.com Py_DECREF(scope); 14001624Smax.romanov@nginx.com 14011624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14021624Smax.romanov@nginx.com "failed to create 'asgi' dict"); 14031624Smax.romanov@nginx.com } 14041624Smax.romanov@nginx.com 14051624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) { 14061624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item"); 14071624Smax.romanov@nginx.com 14081624Smax.romanov@nginx.com Py_DECREF(asgi); 14091624Smax.romanov@nginx.com Py_DECREF(scope); 14101624Smax.romanov@nginx.com 14111624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14121624Smax.romanov@nginx.com "failed to set 'scope.asgi' item"); 14131624Smax.romanov@nginx.com } 14141624Smax.romanov@nginx.com 14151624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str, 14161624Smax.romanov@nginx.com nxt_py_3_0_str) == -1)) 14171624Smax.romanov@nginx.com { 14181624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item"); 14191624Smax.romanov@nginx.com 14201624Smax.romanov@nginx.com Py_DECREF(asgi); 14211624Smax.romanov@nginx.com Py_DECREF(scope); 14221624Smax.romanov@nginx.com 14231624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14241624Smax.romanov@nginx.com "failed to set 'asgi.version' item"); 14251624Smax.romanov@nginx.com } 14261624Smax.romanov@nginx.com 14271624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str, 14281624Smax.romanov@nginx.com spec_version) == -1)) 14291624Smax.romanov@nginx.com { 14301624Smax.romanov@nginx.com nxt_unit_req_alert(req, 14311624Smax.romanov@nginx.com "Python failed to set 'asgi.spec_version' item"); 14321624Smax.romanov@nginx.com 14331624Smax.romanov@nginx.com Py_DECREF(asgi); 14341624Smax.romanov@nginx.com Py_DECREF(scope); 14351624Smax.romanov@nginx.com 14361624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 14371624Smax.romanov@nginx.com "failed to set 'asgi.spec_version' item"); 14381624Smax.romanov@nginx.com } 14391624Smax.romanov@nginx.com 14401624Smax.romanov@nginx.com Py_DECREF(asgi); 14411624Smax.romanov@nginx.com 14421624Smax.romanov@nginx.com return scope; 14431624Smax.romanov@nginx.com } 14441624Smax.romanov@nginx.com 14451624Smax.romanov@nginx.com 14461624Smax.romanov@nginx.com void 14471681Smax.romanov@nginx.com nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link) 14481681Smax.romanov@nginx.com { 14491681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 14501681Smax.romanov@nginx.com 14511681Smax.romanov@nginx.com ctx_data = req->ctx->data; 14521681Smax.romanov@nginx.com 14531681Smax.romanov@nginx.com nxt_queue_insert_tail(&ctx_data->drain_queue, link); 14541681Smax.romanov@nginx.com } 14551681Smax.romanov@nginx.com 14561681Smax.romanov@nginx.com 14571681Smax.romanov@nginx.com void 14581624Smax.romanov@nginx.com nxt_py_asgi_dealloc(PyObject *self) 14591624Smax.romanov@nginx.com { 14601624Smax.romanov@nginx.com PyObject_Del(self); 14611624Smax.romanov@nginx.com } 14621624Smax.romanov@nginx.com 14631624Smax.romanov@nginx.com 14641624Smax.romanov@nginx.com PyObject * 14651624Smax.romanov@nginx.com nxt_py_asgi_await(PyObject *self) 14661624Smax.romanov@nginx.com { 14671624Smax.romanov@nginx.com Py_INCREF(self); 14681624Smax.romanov@nginx.com return self; 14691624Smax.romanov@nginx.com } 14701624Smax.romanov@nginx.com 14711624Smax.romanov@nginx.com 14721624Smax.romanov@nginx.com PyObject * 14731624Smax.romanov@nginx.com nxt_py_asgi_iter(PyObject *self) 14741624Smax.romanov@nginx.com { 14751624Smax.romanov@nginx.com Py_INCREF(self); 14761624Smax.romanov@nginx.com return self; 14771624Smax.romanov@nginx.com } 14781624Smax.romanov@nginx.com 14791624Smax.romanov@nginx.com 14801624Smax.romanov@nginx.com PyObject * 14811624Smax.romanov@nginx.com nxt_py_asgi_next(PyObject *self) 14821624Smax.romanov@nginx.com { 14831624Smax.romanov@nginx.com return NULL; 14841624Smax.romanov@nginx.com } 14851624Smax.romanov@nginx.com 14861624Smax.romanov@nginx.com 14871681Smax.romanov@nginx.com static void 14881624Smax.romanov@nginx.com nxt_python_asgi_done(void) 14891624Smax.romanov@nginx.com { 14901624Smax.romanov@nginx.com nxt_py_asgi_str_done(); 14911624Smax.romanov@nginx.com 14921624Smax.romanov@nginx.com Py_XDECREF(nxt_py_port_read); 14931624Smax.romanov@nginx.com } 14941624Smax.romanov@nginx.com 14951624Smax.romanov@nginx.com #else /* !(NXT_HAVE_ASGI) */ 14961624Smax.romanov@nginx.com 14971624Smax.romanov@nginx.com 14981624Smax.romanov@nginx.com int 14991624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj) 15001624Smax.romanov@nginx.com { 15011624Smax.romanov@nginx.com return 0; 15021624Smax.romanov@nginx.com } 15031624Smax.romanov@nginx.com 15041624Smax.romanov@nginx.com 15051681Smax.romanov@nginx.com int 15061681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 15071624Smax.romanov@nginx.com { 15081681Smax.romanov@nginx.com nxt_unit_alert(NULL, "ASGI not implemented"); 15091681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 15101624Smax.romanov@nginx.com } 15111624Smax.romanov@nginx.com 15121624Smax.romanov@nginx.com 15131624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */ 1514