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 361624Smax.romanov@nginx.com static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); 371979Smax.romanov@nginx.com static int nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); 381980Smax.romanov@nginx.com static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx, 391980Smax.romanov@nginx.com nxt_unit_port_t *port); 401624Smax.romanov@nginx.com static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); 411624Smax.romanov@nginx.com static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); 421624Smax.romanov@nginx.com 431624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); 441681Smax.romanov@nginx.com static void nxt_python_asgi_done(void); 451624Smax.romanov@nginx.com 461681Smax.romanov@nginx.com static PyObject *nxt_py_port_read; 471624Smax.romanov@nginx.com 481624Smax.romanov@nginx.com static PyMethodDef nxt_py_port_read_method = 491624Smax.romanov@nginx.com {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""}; 501624Smax.romanov@nginx.com 511681Smax.romanov@nginx.com static nxt_python_proto_t nxt_py_asgi_proto = { 521681Smax.romanov@nginx.com .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc, 531681Smax.romanov@nginx.com .ctx_data_free = nxt_python_asgi_ctx_data_free, 541681Smax.romanov@nginx.com .startup = nxt_python_asgi_startup, 551681Smax.romanov@nginx.com .run = nxt_python_asgi_run, 561681Smax.romanov@nginx.com .done = nxt_python_asgi_done, 571681Smax.romanov@nginx.com }; 581681Smax.romanov@nginx.com 591624Smax.romanov@nginx.com #define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A 601624Smax.romanov@nginx.com 611624Smax.romanov@nginx.com 621624Smax.romanov@nginx.com int 631624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj) 641624Smax.romanov@nginx.com { 651624Smax.romanov@nginx.com int res; 661697Smax.romanov@nginx.com PyObject *func; 671624Smax.romanov@nginx.com PyCodeObject *code; 681624Smax.romanov@nginx.com 691697Smax.romanov@nginx.com func = nxt_python_asgi_get_func(obj); 701697Smax.romanov@nginx.com 711697Smax.romanov@nginx.com if (func == NULL) { 721697Smax.romanov@nginx.com return 0; 731697Smax.romanov@nginx.com } 741697Smax.romanov@nginx.com 751697Smax.romanov@nginx.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 761697Smax.romanov@nginx.com 771697Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with " 781697Smax.romanov@nginx.com "%d argument(s)", 791697Smax.romanov@nginx.com (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ", 801697Smax.romanov@nginx.com code->co_argcount); 811697Smax.romanov@nginx.com 821697Smax.romanov@nginx.com res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1; 831697Smax.romanov@nginx.com 841697Smax.romanov@nginx.com Py_DECREF(func); 851697Smax.romanov@nginx.com 861697Smax.romanov@nginx.com return res; 871697Smax.romanov@nginx.com } 881697Smax.romanov@nginx.com 891697Smax.romanov@nginx.com 901697Smax.romanov@nginx.com static PyObject * 911697Smax.romanov@nginx.com nxt_python_asgi_get_func(PyObject *obj) 921697Smax.romanov@nginx.com { 931697Smax.romanov@nginx.com PyObject *call; 941697Smax.romanov@nginx.com 951624Smax.romanov@nginx.com if (PyFunction_Check(obj)) { 961697Smax.romanov@nginx.com Py_INCREF(obj); 971697Smax.romanov@nginx.com return obj; 981624Smax.romanov@nginx.com } 991624Smax.romanov@nginx.com 1001624Smax.romanov@nginx.com if (PyMethod_Check(obj)) { 1011624Smax.romanov@nginx.com obj = PyMethod_GET_FUNCTION(obj); 1021624Smax.romanov@nginx.com 1031697Smax.romanov@nginx.com Py_INCREF(obj); 1041697Smax.romanov@nginx.com return obj; 1051624Smax.romanov@nginx.com } 1061624Smax.romanov@nginx.com 1071624Smax.romanov@nginx.com call = PyObject_GetAttrString(obj, "__call__"); 1081624Smax.romanov@nginx.com 1091624Smax.romanov@nginx.com if (call == NULL) { 1101697Smax.romanov@nginx.com return NULL; 1111624Smax.romanov@nginx.com } 1121624Smax.romanov@nginx.com 1131624Smax.romanov@nginx.com if (PyFunction_Check(call)) { 1141697Smax.romanov@nginx.com return call; 1151697Smax.romanov@nginx.com } 1161624Smax.romanov@nginx.com 1171697Smax.romanov@nginx.com if (PyMethod_Check(call)) { 1181697Smax.romanov@nginx.com obj = PyMethod_GET_FUNCTION(call); 1191624Smax.romanov@nginx.com 1202067Smax.romanov@nginx.com if (PyFunction_Check(obj)) { 1212067Smax.romanov@nginx.com Py_INCREF(obj); 1221697Smax.romanov@nginx.com 1232067Smax.romanov@nginx.com } else { 1242067Smax.romanov@nginx.com obj = NULL; 1252067Smax.romanov@nginx.com } 1262067Smax.romanov@nginx.com 1272067Smax.romanov@nginx.com } else { 1282067Smax.romanov@nginx.com obj = NULL; 1291624Smax.romanov@nginx.com } 1301624Smax.romanov@nginx.com 1311624Smax.romanov@nginx.com Py_DECREF(call); 1321624Smax.romanov@nginx.com 1332067Smax.romanov@nginx.com return obj; 1341624Smax.romanov@nginx.com } 1351624Smax.romanov@nginx.com 1361624Smax.romanov@nginx.com 1371681Smax.romanov@nginx.com int 1381681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 1391624Smax.romanov@nginx.com { 1401697Smax.romanov@nginx.com PyObject *func; 1411872So.canty@f5.com nxt_int_t i; 1421697Smax.romanov@nginx.com PyCodeObject *code; 1431697Smax.romanov@nginx.com 1441681Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_init"); 1451624Smax.romanov@nginx.com 1461681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) { 1471681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to init string objects"); 1481681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1491624Smax.romanov@nginx.com } 1501624Smax.romanov@nginx.com 1511624Smax.romanov@nginx.com nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL); 1521624Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_port_read == NULL)) { 1531681Smax.romanov@nginx.com nxt_unit_alert(NULL, 1541681Smax.romanov@nginx.com "Python failed to initialize the 'port_read' function"); 1551681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1561624Smax.romanov@nginx.com } 1571624Smax.romanov@nginx.com 1581681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) { 1591681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1601624Smax.romanov@nginx.com } 1611624Smax.romanov@nginx.com 1621681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) { 1631681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1641624Smax.romanov@nginx.com } 1651624Smax.romanov@nginx.com 1661872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) { 1671872So.canty@f5.com func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application); 1681872So.canty@f5.com if (nxt_slow_path(func == NULL)) { 1692067Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi: cannot find function for callable, " 170*2068Smax.romanov@nginx.com "unable to check for legacy mode (#%d)", 171*2068Smax.romanov@nginx.com (int) i); 1722067Smax.romanov@nginx.com continue; 1731872So.canty@f5.com } 1741697Smax.romanov@nginx.com 1751872So.canty@f5.com code = (PyCodeObject *) PyFunction_GET_CODE(func); 1761697Smax.romanov@nginx.com 1771872So.canty@f5.com if ((code->co_flags & CO_COROUTINE) == 0) { 1781872So.canty@f5.com nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " 1791872So.canty@f5.com "switching to legacy mode"); 1801872So.canty@f5.com nxt_py_targets->target[i].asgi_legacy = 1; 1811872So.canty@f5.com } 1821872So.canty@f5.com 1831872So.canty@f5.com Py_DECREF(func); 1841697Smax.romanov@nginx.com } 1851697Smax.romanov@nginx.com 1861624Smax.romanov@nginx.com init->callbacks.request_handler = nxt_py_asgi_request_handler; 1871624Smax.romanov@nginx.com init->callbacks.data_handler = nxt_py_asgi_http_data_handler; 1881624Smax.romanov@nginx.com init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler; 1891715Smax.romanov@nginx.com init->callbacks.close_handler = nxt_py_asgi_close_handler; 1901624Smax.romanov@nginx.com init->callbacks.quit = nxt_py_asgi_quit; 1911624Smax.romanov@nginx.com init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; 1921624Smax.romanov@nginx.com init->callbacks.add_port = nxt_py_asgi_add_port; 1931624Smax.romanov@nginx.com init->callbacks.remove_port = nxt_py_asgi_remove_port; 1941624Smax.romanov@nginx.com 1951681Smax.romanov@nginx.com *proto = nxt_py_asgi_proto; 1961681Smax.romanov@nginx.com 1971681Smax.romanov@nginx.com return NXT_UNIT_OK; 1981681Smax.romanov@nginx.com } 1991681Smax.romanov@nginx.com 2001681Smax.romanov@nginx.com 2011681Smax.romanov@nginx.com static int 2021918Smax.romanov@nginx.com nxt_python_asgi_ctx_data_alloc(void **pdata, int main) 2031681Smax.romanov@nginx.com { 2041681Smax.romanov@nginx.com uint32_t i; 2051918Smax.romanov@nginx.com PyObject *asyncio, *loop, *event_loop, *obj; 2061918Smax.romanov@nginx.com const char *event_loop_func; 2071681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 2081681Smax.romanov@nginx.com 2091681Smax.romanov@nginx.com ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); 2101681Smax.romanov@nginx.com if (nxt_slow_path(ctx_data == NULL)) { 2111681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate context data"); 2121681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2131681Smax.romanov@nginx.com } 2141681Smax.romanov@nginx.com 2151681Smax.romanov@nginx.com memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t)); 2161681Smax.romanov@nginx.com 2171681Smax.romanov@nginx.com nxt_queue_init(&ctx_data->drain_queue); 2181681Smax.romanov@nginx.com 2191681Smax.romanov@nginx.com struct { 2201681Smax.romanov@nginx.com const char *key; 2211681Smax.romanov@nginx.com PyObject **handler; 2221681Smax.romanov@nginx.com 2231681Smax.romanov@nginx.com } handlers[] = { 2241681Smax.romanov@nginx.com { "create_task", &ctx_data->loop_create_task }, 2251681Smax.romanov@nginx.com { "add_reader", &ctx_data->loop_add_reader }, 2261681Smax.romanov@nginx.com { "remove_reader", &ctx_data->loop_remove_reader }, 2271681Smax.romanov@nginx.com { "call_soon", &ctx_data->loop_call_soon }, 2281681Smax.romanov@nginx.com { "run_until_complete", &ctx_data->loop_run_until_complete }, 2291681Smax.romanov@nginx.com { "create_future", &ctx_data->loop_create_future }, 2301681Smax.romanov@nginx.com }; 2311681Smax.romanov@nginx.com 2321681Smax.romanov@nginx.com loop = NULL; 2331681Smax.romanov@nginx.com 2341681Smax.romanov@nginx.com asyncio = PyImport_ImportModule("asyncio"); 2351681Smax.romanov@nginx.com if (nxt_slow_path(asyncio == NULL)) { 2361681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to import module 'asyncio'"); 2371681Smax.romanov@nginx.com nxt_python_print_exception(); 2381681Smax.romanov@nginx.com goto fail; 2391681Smax.romanov@nginx.com } 2401681Smax.romanov@nginx.com 2411918Smax.romanov@nginx.com event_loop_func = main ? "get_event_loop" : "new_event_loop"; 2421918Smax.romanov@nginx.com 2431918Smax.romanov@nginx.com event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), 2441918Smax.romanov@nginx.com event_loop_func); 2451918Smax.romanov@nginx.com if (nxt_slow_path(event_loop == NULL)) { 2461681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2471918Smax.romanov@nginx.com "Python failed to get '%s' from module 'asyncio'", 2481918Smax.romanov@nginx.com event_loop_func); 2491681Smax.romanov@nginx.com goto fail; 2501681Smax.romanov@nginx.com } 2511681Smax.romanov@nginx.com 2521918Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { 2531681Smax.romanov@nginx.com nxt_unit_alert(NULL, 2541918Smax.romanov@nginx.com "'asyncio.%s' is not a callable object", 2551918Smax.romanov@nginx.com event_loop_func); 2561681Smax.romanov@nginx.com goto fail; 2571681Smax.romanov@nginx.com } 2581681Smax.romanov@nginx.com 2591918Smax.romanov@nginx.com loop = PyObject_CallObject(event_loop, NULL); 2601681Smax.romanov@nginx.com if (nxt_slow_path(loop == NULL)) { 2611918Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", 2621918Smax.romanov@nginx.com event_loop_func); 2631681Smax.romanov@nginx.com goto fail; 2641681Smax.romanov@nginx.com } 2651681Smax.romanov@nginx.com 2661681Smax.romanov@nginx.com for (i = 0; i < nxt_nitems(handlers); i++) { 2671681Smax.romanov@nginx.com obj = PyObject_GetAttrString(loop, handlers[i].key); 2681681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2691681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'loop.%s'", 2701681Smax.romanov@nginx.com handlers[i].key); 2711681Smax.romanov@nginx.com goto fail; 2721681Smax.romanov@nginx.com } 2731681Smax.romanov@nginx.com 2741681Smax.romanov@nginx.com *handlers[i].handler = obj; 2751681Smax.romanov@nginx.com 2761681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 2771681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'loop.%s' is not a callable object", 2781681Smax.romanov@nginx.com handlers[i].key); 2791681Smax.romanov@nginx.com goto fail; 2801681Smax.romanov@nginx.com } 2811681Smax.romanov@nginx.com } 2821681Smax.romanov@nginx.com 2831681Smax.romanov@nginx.com obj = PyObject_CallObject(ctx_data->loop_create_future, NULL); 2841681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2851681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future "); 2861681Smax.romanov@nginx.com nxt_python_print_exception(); 2871681Smax.romanov@nginx.com goto fail; 2881681Smax.romanov@nginx.com } 2891681Smax.romanov@nginx.com 2901681Smax.romanov@nginx.com ctx_data->quit_future = obj; 2911681Smax.romanov@nginx.com 2921681Smax.romanov@nginx.com obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result"); 2931681Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 2941681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'future.set_result'"); 2951681Smax.romanov@nginx.com goto fail; 2961681Smax.romanov@nginx.com } 2971681Smax.romanov@nginx.com 2981681Smax.romanov@nginx.com ctx_data->quit_future_set_result = obj; 2991681Smax.romanov@nginx.com 3001681Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 3011681Smax.romanov@nginx.com nxt_unit_alert(NULL, "'future.set_result' is not a callable object"); 3021681Smax.romanov@nginx.com goto fail; 3031681Smax.romanov@nginx.com } 3041681Smax.romanov@nginx.com 3051624Smax.romanov@nginx.com Py_DECREF(loop); 3061624Smax.romanov@nginx.com Py_DECREF(asyncio); 3071624Smax.romanov@nginx.com 3081681Smax.romanov@nginx.com *pdata = ctx_data; 3091681Smax.romanov@nginx.com 3101681Smax.romanov@nginx.com return NXT_UNIT_OK; 3111624Smax.romanov@nginx.com 3121624Smax.romanov@nginx.com fail: 3131624Smax.romanov@nginx.com 3141681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(ctx_data); 3151681Smax.romanov@nginx.com 3161624Smax.romanov@nginx.com Py_XDECREF(loop); 3171681Smax.romanov@nginx.com Py_XDECREF(asyncio); 3181624Smax.romanov@nginx.com 3191681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3201624Smax.romanov@nginx.com } 3211624Smax.romanov@nginx.com 3221624Smax.romanov@nginx.com 3231681Smax.romanov@nginx.com static void 3241681Smax.romanov@nginx.com nxt_python_asgi_ctx_data_free(void *data) 3251681Smax.romanov@nginx.com { 3261681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3271681Smax.romanov@nginx.com 3281681Smax.romanov@nginx.com ctx_data = data; 3291681Smax.romanov@nginx.com 3301681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_run_until_complete); 3311681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_future); 3321681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_create_task); 3331681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_call_soon); 3341681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_add_reader); 3351681Smax.romanov@nginx.com Py_XDECREF(ctx_data->loop_remove_reader); 3361681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future); 3371681Smax.romanov@nginx.com Py_XDECREF(ctx_data->quit_future_set_result); 3381681Smax.romanov@nginx.com 3391681Smax.romanov@nginx.com nxt_unit_free(NULL, ctx_data); 3401681Smax.romanov@nginx.com } 3411681Smax.romanov@nginx.com 3421681Smax.romanov@nginx.com 3431681Smax.romanov@nginx.com static int 3441681Smax.romanov@nginx.com nxt_python_asgi_startup(void *data) 3451681Smax.romanov@nginx.com { 3461681Smax.romanov@nginx.com return nxt_py_asgi_lifespan_startup(data); 3471681Smax.romanov@nginx.com } 3481681Smax.romanov@nginx.com 3491681Smax.romanov@nginx.com 3501681Smax.romanov@nginx.com static int 3511624Smax.romanov@nginx.com nxt_python_asgi_run(nxt_unit_ctx_t *ctx) 3521624Smax.romanov@nginx.com { 3531681Smax.romanov@nginx.com PyObject *res; 3541681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3551624Smax.romanov@nginx.com 3561681Smax.romanov@nginx.com ctx_data = ctx->data; 3571681Smax.romanov@nginx.com 3581681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 3591681Smax.romanov@nginx.com ctx_data->quit_future, NULL); 3601624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 3611624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete"); 3621624Smax.romanov@nginx.com nxt_python_print_exception(); 3631624Smax.romanov@nginx.com 3641681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 3651624Smax.romanov@nginx.com } 3661624Smax.romanov@nginx.com 3671624Smax.romanov@nginx.com Py_DECREF(res); 3681624Smax.romanov@nginx.com 3691681Smax.romanov@nginx.com nxt_py_asgi_lifespan_shutdown(ctx); 3701681Smax.romanov@nginx.com 3711681Smax.romanov@nginx.com return NXT_UNIT_OK; 3721681Smax.romanov@nginx.com } 3731681Smax.romanov@nginx.com 3741624Smax.romanov@nginx.com 3751681Smax.romanov@nginx.com static void 3761681Smax.romanov@nginx.com nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 3771681Smax.romanov@nginx.com { 3781682Smax.romanov@nginx.com PyObject *res, *fd; 3791681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3801681Smax.romanov@nginx.com 3811681Smax.romanov@nginx.com if (port == NULL || port->in_fd == -1) { 3821681Smax.romanov@nginx.com return; 3831681Smax.romanov@nginx.com } 3841681Smax.romanov@nginx.com 3851681Smax.romanov@nginx.com ctx_data = ctx->data; 3861681Smax.romanov@nginx.com 3871681Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port); 3881681Smax.romanov@nginx.com 3891682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 3901682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 3911682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create Long object"); 3921681Smax.romanov@nginx.com nxt_python_print_exception(); 3931681Smax.romanov@nginx.com 3941681Smax.romanov@nginx.com return; 3951681Smax.romanov@nginx.com } 3961681Smax.romanov@nginx.com 3971682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL); 3981682Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 3991682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to remove_reader"); 4001682Smax.romanov@nginx.com nxt_python_print_exception(); 4011682Smax.romanov@nginx.com 4021682Smax.romanov@nginx.com } else { 4031682Smax.romanov@nginx.com Py_DECREF(res); 4041682Smax.romanov@nginx.com } 4051682Smax.romanov@nginx.com 4061682Smax.romanov@nginx.com Py_DECREF(fd); 4071624Smax.romanov@nginx.com } 4081624Smax.romanov@nginx.com 4091624Smax.romanov@nginx.com 4101624Smax.romanov@nginx.com static void 4111624Smax.romanov@nginx.com nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) 4121624Smax.romanov@nginx.com { 4131681Smax.romanov@nginx.com PyObject *scope, *res, *task, *receive, *send, *done, *asgi; 4141697Smax.romanov@nginx.com PyObject *stage2; 4151872So.canty@f5.com nxt_python_target_t *target; 4161681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 4171624Smax.romanov@nginx.com 4181624Smax.romanov@nginx.com if (req->request->websocket_handshake) { 4191624Smax.romanov@nginx.com asgi = nxt_py_asgi_websocket_create(req); 4201624Smax.romanov@nginx.com 4211624Smax.romanov@nginx.com } else { 4221624Smax.romanov@nginx.com asgi = nxt_py_asgi_http_create(req); 4231624Smax.romanov@nginx.com } 4241624Smax.romanov@nginx.com 4251624Smax.romanov@nginx.com if (nxt_slow_path(asgi == NULL)) { 4261624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create asgi object"); 4271624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4281624Smax.romanov@nginx.com 4291624Smax.romanov@nginx.com return; 4301624Smax.romanov@nginx.com } 4311624Smax.romanov@nginx.com 4321624Smax.romanov@nginx.com receive = PyObject_GetAttrString(asgi, "receive"); 4331624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4341624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get 'receive' method"); 4351624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4361624Smax.romanov@nginx.com 4371624Smax.romanov@nginx.com goto release_asgi; 4381624Smax.romanov@nginx.com } 4391624Smax.romanov@nginx.com 4401624Smax.romanov@nginx.com send = PyObject_GetAttrString(asgi, "send"); 4411624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4421624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get 'send' method"); 4431624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4441624Smax.romanov@nginx.com 4451624Smax.romanov@nginx.com goto release_receive; 4461624Smax.romanov@nginx.com } 4471624Smax.romanov@nginx.com 4481624Smax.romanov@nginx.com done = PyObject_GetAttrString(asgi, "_done"); 4491624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 4501624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to get '_done' method"); 4511624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4521624Smax.romanov@nginx.com 4531624Smax.romanov@nginx.com goto release_send; 4541624Smax.romanov@nginx.com } 4551624Smax.romanov@nginx.com 4561624Smax.romanov@nginx.com scope = nxt_py_asgi_create_http_scope(req); 4571624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 4581624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4591624Smax.romanov@nginx.com 4601624Smax.romanov@nginx.com goto release_done; 4611624Smax.romanov@nginx.com } 4621624Smax.romanov@nginx.com 4631624Smax.romanov@nginx.com req->data = asgi; 4641872So.canty@f5.com target = &nxt_py_targets->target[req->request->app_target]; 4651624Smax.romanov@nginx.com 4661872So.canty@f5.com if (!target->asgi_legacy) { 4671697Smax.romanov@nginx.com nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); 4681697Smax.romanov@nginx.com 4691872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application, 4701697Smax.romanov@nginx.com scope, receive, send, NULL); 4711697Smax.romanov@nginx.com 4721697Smax.romanov@nginx.com } else { 4731697Smax.romanov@nginx.com nxt_unit_req_debug(req, "Python call legacy application"); 4741697Smax.romanov@nginx.com 4751872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application, scope, NULL); 4761697Smax.romanov@nginx.com 4771697Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 4781697Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call legacy app stage1"); 4791697Smax.romanov@nginx.com nxt_python_print_exception(); 4801697Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4811697Smax.romanov@nginx.com 4821697Smax.romanov@nginx.com goto release_scope; 4831697Smax.romanov@nginx.com } 4841697Smax.romanov@nginx.com 4851697Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(res) == 0)) { 4861697Smax.romanov@nginx.com nxt_unit_req_error(req, 4871697Smax.romanov@nginx.com "Legacy ASGI application returns not a callable"); 4881697Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 4891697Smax.romanov@nginx.com 4901697Smax.romanov@nginx.com Py_DECREF(res); 4911697Smax.romanov@nginx.com 4921697Smax.romanov@nginx.com goto release_scope; 4931697Smax.romanov@nginx.com } 4941697Smax.romanov@nginx.com 4951697Smax.romanov@nginx.com stage2 = res; 4961697Smax.romanov@nginx.com 4971697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); 4981697Smax.romanov@nginx.com 4991697Smax.romanov@nginx.com Py_DECREF(stage2); 5001697Smax.romanov@nginx.com } 5011697Smax.romanov@nginx.com 5021624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5031624Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the application"); 5041624Smax.romanov@nginx.com nxt_python_print_exception(); 5051624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5061624Smax.romanov@nginx.com 5071624Smax.romanov@nginx.com goto release_scope; 5081624Smax.romanov@nginx.com } 5091624Smax.romanov@nginx.com 5101624Smax.romanov@nginx.com if (nxt_slow_path(!PyCoro_CheckExact(res))) { 5111624Smax.romanov@nginx.com nxt_unit_req_error(req, "Application result type is not a coroutine"); 5121624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5131624Smax.romanov@nginx.com 5141624Smax.romanov@nginx.com Py_DECREF(res); 5151624Smax.romanov@nginx.com 5161624Smax.romanov@nginx.com goto release_scope; 5171624Smax.romanov@nginx.com } 5181624Smax.romanov@nginx.com 5191681Smax.romanov@nginx.com ctx_data = req->ctx->data; 5201681Smax.romanov@nginx.com 5211681Smax.romanov@nginx.com task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); 5221624Smax.romanov@nginx.com if (nxt_slow_path(task == NULL)) { 5231624Smax.romanov@nginx.com nxt_unit_req_error(req, "Python failed to call the create_task"); 5241624Smax.romanov@nginx.com nxt_python_print_exception(); 5251624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5261624Smax.romanov@nginx.com 5271624Smax.romanov@nginx.com Py_DECREF(res); 5281624Smax.romanov@nginx.com 5291624Smax.romanov@nginx.com goto release_scope; 5301624Smax.romanov@nginx.com } 5311624Smax.romanov@nginx.com 5321624Smax.romanov@nginx.com Py_DECREF(res); 5331624Smax.romanov@nginx.com 5341624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done, 5351624Smax.romanov@nginx.com NULL); 5361624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5371624Smax.romanov@nginx.com nxt_unit_req_error(req, 5381624Smax.romanov@nginx.com "Python failed to call 'task.add_done_callback'"); 5391624Smax.romanov@nginx.com nxt_python_print_exception(); 5401624Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5411624Smax.romanov@nginx.com 5421624Smax.romanov@nginx.com goto release_task; 5431624Smax.romanov@nginx.com } 5441624Smax.romanov@nginx.com 5451624Smax.romanov@nginx.com Py_DECREF(res); 5461624Smax.romanov@nginx.com release_task: 5471624Smax.romanov@nginx.com Py_DECREF(task); 5481624Smax.romanov@nginx.com release_scope: 5491624Smax.romanov@nginx.com Py_DECREF(scope); 5501624Smax.romanov@nginx.com release_done: 5511624Smax.romanov@nginx.com Py_DECREF(done); 5521624Smax.romanov@nginx.com release_send: 5531624Smax.romanov@nginx.com Py_DECREF(send); 5541624Smax.romanov@nginx.com release_receive: 5551624Smax.romanov@nginx.com Py_DECREF(receive); 5561624Smax.romanov@nginx.com release_asgi: 5571624Smax.romanov@nginx.com Py_DECREF(asgi); 5581624Smax.romanov@nginx.com } 5591624Smax.romanov@nginx.com 5601624Smax.romanov@nginx.com 5611715Smax.romanov@nginx.com static void 5621715Smax.romanov@nginx.com nxt_py_asgi_close_handler(nxt_unit_request_info_t *req) 5631715Smax.romanov@nginx.com { 5641715Smax.romanov@nginx.com if (req->request->websocket_handshake) { 5651715Smax.romanov@nginx.com nxt_py_asgi_websocket_close_handler(req); 5661715Smax.romanov@nginx.com 5671715Smax.romanov@nginx.com } else { 5681715Smax.romanov@nginx.com nxt_py_asgi_http_close_handler(req); 5691715Smax.romanov@nginx.com } 5701715Smax.romanov@nginx.com } 5711715Smax.romanov@nginx.com 5721715Smax.romanov@nginx.com 5731624Smax.romanov@nginx.com static PyObject * 5741624Smax.romanov@nginx.com nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) 5751624Smax.romanov@nginx.com { 5761624Smax.romanov@nginx.com char *p, *target, *query; 5771624Smax.romanov@nginx.com uint32_t target_length, i; 5781624Smax.romanov@nginx.com PyObject *scope, *v, *type, *scheme; 5791624Smax.romanov@nginx.com PyObject *headers, *header; 5801624Smax.romanov@nginx.com nxt_unit_field_t *f; 5811624Smax.romanov@nginx.com nxt_unit_request_t *r; 5821624Smax.romanov@nginx.com 5831624Smax.romanov@nginx.com static const nxt_str_t ws_protocol = nxt_string("sec-websocket-protocol"); 5841624Smax.romanov@nginx.com 5851624Smax.romanov@nginx.com #define SET_ITEM(dict, key, value) \ 5861624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \ 5871624Smax.romanov@nginx.com == -1)) \ 5881624Smax.romanov@nginx.com { \ 5891624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set '" \ 5901624Smax.romanov@nginx.com #dict "." #key "' item"); \ 5911624Smax.romanov@nginx.com goto fail; \ 5921624Smax.romanov@nginx.com } 5931624Smax.romanov@nginx.com 5941624Smax.romanov@nginx.com v = NULL; 5951624Smax.romanov@nginx.com headers = NULL; 5961624Smax.romanov@nginx.com 5971624Smax.romanov@nginx.com r = req->request; 5981624Smax.romanov@nginx.com 5991624Smax.romanov@nginx.com if (r->websocket_handshake) { 6001624Smax.romanov@nginx.com type = nxt_py_websocket_str; 6011624Smax.romanov@nginx.com scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str; 6021624Smax.romanov@nginx.com 6031624Smax.romanov@nginx.com } else { 6041624Smax.romanov@nginx.com type = nxt_py_http_str; 6051624Smax.romanov@nginx.com scheme = r->tls ? nxt_py_https_str : nxt_py_http_str; 6061624Smax.romanov@nginx.com } 6071624Smax.romanov@nginx.com 6081624Smax.romanov@nginx.com scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str); 6091624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 6101624Smax.romanov@nginx.com return NULL; 6111624Smax.romanov@nginx.com } 6121624Smax.romanov@nginx.com 6131624Smax.romanov@nginx.com p = nxt_unit_sptr_get(&r->version); 6141624Smax.romanov@nginx.com SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str 6151624Smax.romanov@nginx.com : nxt_py_1_0_str) 6161624Smax.romanov@nginx.com SET_ITEM(scope, scheme, scheme) 6171624Smax.romanov@nginx.com 6181624Smax.romanov@nginx.com v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method), 6191624Smax.romanov@nginx.com r->method_length); 6201624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6211624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'method' string"); 6221624Smax.romanov@nginx.com goto fail; 6231624Smax.romanov@nginx.com } 6241624Smax.romanov@nginx.com 6251624Smax.romanov@nginx.com SET_ITEM(scope, method, v) 6261624Smax.romanov@nginx.com Py_DECREF(v); 6271624Smax.romanov@nginx.com 6281624Smax.romanov@nginx.com v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length, 6291624Smax.romanov@nginx.com "replace"); 6301624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6311624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'path' string"); 6321624Smax.romanov@nginx.com goto fail; 6331624Smax.romanov@nginx.com } 6341624Smax.romanov@nginx.com 6351624Smax.romanov@nginx.com SET_ITEM(scope, path, v) 6361624Smax.romanov@nginx.com Py_DECREF(v); 6371624Smax.romanov@nginx.com 6381624Smax.romanov@nginx.com target = nxt_unit_sptr_get(&r->target); 6391624Smax.romanov@nginx.com query = nxt_unit_sptr_get(&r->query); 6401624Smax.romanov@nginx.com 6411624Smax.romanov@nginx.com if (r->query.offset != 0) { 6421624Smax.romanov@nginx.com target_length = query - target - 1; 6431624Smax.romanov@nginx.com 6441624Smax.romanov@nginx.com } else { 6451624Smax.romanov@nginx.com target_length = r->target_length; 6461624Smax.romanov@nginx.com } 6471624Smax.romanov@nginx.com 6481624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(target, target_length); 6491624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6501624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'raw_path' string"); 6511624Smax.romanov@nginx.com goto fail; 6521624Smax.romanov@nginx.com } 6531624Smax.romanov@nginx.com 6541624Smax.romanov@nginx.com SET_ITEM(scope, raw_path, v) 6551624Smax.romanov@nginx.com Py_DECREF(v); 6561624Smax.romanov@nginx.com 6571624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(query, r->query_length); 6581624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6591624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'query' string"); 6601624Smax.romanov@nginx.com goto fail; 6611624Smax.romanov@nginx.com } 6621624Smax.romanov@nginx.com 6631624Smax.romanov@nginx.com SET_ITEM(scope, query_string, v) 6641624Smax.romanov@nginx.com Py_DECREF(v); 6651624Smax.romanov@nginx.com 6661624Smax.romanov@nginx.com v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0); 6671624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6681624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'client' pair"); 6691624Smax.romanov@nginx.com goto fail; 6701624Smax.romanov@nginx.com } 6711624Smax.romanov@nginx.com 6721624Smax.romanov@nginx.com SET_ITEM(scope, client, v) 6731624Smax.romanov@nginx.com Py_DECREF(v); 6741624Smax.romanov@nginx.com 6751624Smax.romanov@nginx.com v = nxt_py_asgi_create_address(&r->local, r->local_length, 80); 6761624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 6771624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'server' pair"); 6781624Smax.romanov@nginx.com goto fail; 6791624Smax.romanov@nginx.com } 6801624Smax.romanov@nginx.com 6811624Smax.romanov@nginx.com SET_ITEM(scope, server, v) 6821624Smax.romanov@nginx.com Py_DECREF(v); 6831624Smax.romanov@nginx.com 6841624Smax.romanov@nginx.com v = NULL; 6851624Smax.romanov@nginx.com 6861624Smax.romanov@nginx.com headers = PyTuple_New(r->fields_count); 6871624Smax.romanov@nginx.com if (nxt_slow_path(headers == NULL)) { 6881624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'headers' object"); 6891624Smax.romanov@nginx.com goto fail; 6901624Smax.romanov@nginx.com } 6911624Smax.romanov@nginx.com 6921624Smax.romanov@nginx.com for (i = 0; i < r->fields_count; i++) { 6931624Smax.romanov@nginx.com f = r->fields + i; 6941624Smax.romanov@nginx.com 6951624Smax.romanov@nginx.com header = nxt_py_asgi_create_header(f); 6961624Smax.romanov@nginx.com if (nxt_slow_path(header == NULL)) { 6971624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'header' pair"); 6981624Smax.romanov@nginx.com goto fail; 6991624Smax.romanov@nginx.com } 7001624Smax.romanov@nginx.com 7011624Smax.romanov@nginx.com PyTuple_SET_ITEM(headers, i, header); 7021624Smax.romanov@nginx.com 7031624Smax.romanov@nginx.com if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL 7041624Smax.romanov@nginx.com && f->name_length == ws_protocol.length 7051624Smax.romanov@nginx.com && f->value_length > 0 7061624Smax.romanov@nginx.com && r->websocket_handshake) 7071624Smax.romanov@nginx.com { 7081624Smax.romanov@nginx.com v = nxt_py_asgi_create_subprotocols(f); 7091624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7101624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Failed to create subprotocols"); 7111624Smax.romanov@nginx.com goto fail; 7121624Smax.romanov@nginx.com } 7131624Smax.romanov@nginx.com 7141624Smax.romanov@nginx.com SET_ITEM(scope, subprotocols, v); 7151624Smax.romanov@nginx.com Py_DECREF(v); 7161624Smax.romanov@nginx.com } 7171624Smax.romanov@nginx.com } 7181624Smax.romanov@nginx.com 7191624Smax.romanov@nginx.com SET_ITEM(scope, headers, headers) 7201624Smax.romanov@nginx.com Py_DECREF(headers); 7211624Smax.romanov@nginx.com 7221624Smax.romanov@nginx.com return scope; 7231624Smax.romanov@nginx.com 7241624Smax.romanov@nginx.com fail: 7251624Smax.romanov@nginx.com 7261624Smax.romanov@nginx.com Py_XDECREF(v); 7271624Smax.romanov@nginx.com Py_XDECREF(headers); 7281624Smax.romanov@nginx.com Py_DECREF(scope); 7291624Smax.romanov@nginx.com 7301624Smax.romanov@nginx.com return NULL; 7311624Smax.romanov@nginx.com 7321624Smax.romanov@nginx.com #undef SET_ITEM 7331624Smax.romanov@nginx.com } 7341624Smax.romanov@nginx.com 7351624Smax.romanov@nginx.com 7361624Smax.romanov@nginx.com static PyObject * 7371624Smax.romanov@nginx.com nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port) 7381624Smax.romanov@nginx.com { 7391624Smax.romanov@nginx.com char *p, *s; 7401624Smax.romanov@nginx.com PyObject *pair, *v; 7411624Smax.romanov@nginx.com 7421624Smax.romanov@nginx.com pair = PyTuple_New(2); 7431624Smax.romanov@nginx.com if (nxt_slow_path(pair == NULL)) { 7441624Smax.romanov@nginx.com return NULL; 7451624Smax.romanov@nginx.com } 7461624Smax.romanov@nginx.com 7471624Smax.romanov@nginx.com p = nxt_unit_sptr_get(sptr); 7481624Smax.romanov@nginx.com s = memchr(p, ':', len); 7491624Smax.romanov@nginx.com 7501624Smax.romanov@nginx.com v = PyString_FromStringAndSize(p, s == NULL ? len : s - p); 7511624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7521624Smax.romanov@nginx.com Py_DECREF(pair); 7531624Smax.romanov@nginx.com 7541624Smax.romanov@nginx.com return NULL; 7551624Smax.romanov@nginx.com } 7561624Smax.romanov@nginx.com 7571624Smax.romanov@nginx.com PyTuple_SET_ITEM(pair, 0, v); 7581624Smax.romanov@nginx.com 7591624Smax.romanov@nginx.com if (s != NULL) { 7601624Smax.romanov@nginx.com p += len; 7611624Smax.romanov@nginx.com v = PyLong_FromString(s + 1, &p, 10); 7621624Smax.romanov@nginx.com 7631624Smax.romanov@nginx.com } else { 7641624Smax.romanov@nginx.com v = PyLong_FromLong(port); 7651624Smax.romanov@nginx.com } 7661624Smax.romanov@nginx.com 7671624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 7681624Smax.romanov@nginx.com Py_DECREF(pair); 7691624Smax.romanov@nginx.com 7701624Smax.romanov@nginx.com return NULL; 7711624Smax.romanov@nginx.com } 7721624Smax.romanov@nginx.com 7731624Smax.romanov@nginx.com PyTuple_SET_ITEM(pair, 1, v); 7741624Smax.romanov@nginx.com 7751624Smax.romanov@nginx.com return pair; 7761624Smax.romanov@nginx.com } 7771624Smax.romanov@nginx.com 7781624Smax.romanov@nginx.com 7791624Smax.romanov@nginx.com static PyObject * 7801624Smax.romanov@nginx.com nxt_py_asgi_create_header(nxt_unit_field_t *f) 7811624Smax.romanov@nginx.com { 7821624Smax.romanov@nginx.com char c, *name; 7831624Smax.romanov@nginx.com uint8_t pos; 7841624Smax.romanov@nginx.com PyObject *header, *v; 7851624Smax.romanov@nginx.com 7861624Smax.romanov@nginx.com header = PyTuple_New(2); 7871624Smax.romanov@nginx.com if (nxt_slow_path(header == NULL)) { 7881624Smax.romanov@nginx.com return NULL; 7891624Smax.romanov@nginx.com } 7901624Smax.romanov@nginx.com 7911624Smax.romanov@nginx.com name = nxt_unit_sptr_get(&f->name); 7921624Smax.romanov@nginx.com 7931624Smax.romanov@nginx.com for (pos = 0; pos < f->name_length; pos++) { 7941624Smax.romanov@nginx.com c = name[pos]; 7951624Smax.romanov@nginx.com if (c >= 'A' && c <= 'Z') { 7961624Smax.romanov@nginx.com name[pos] = (c | 0x20); 7971624Smax.romanov@nginx.com } 7981624Smax.romanov@nginx.com } 7991624Smax.romanov@nginx.com 8001624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(name, f->name_length); 8011624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 8021624Smax.romanov@nginx.com Py_DECREF(header); 8031624Smax.romanov@nginx.com 8041624Smax.romanov@nginx.com return NULL; 8051624Smax.romanov@nginx.com } 8061624Smax.romanov@nginx.com 8071624Smax.romanov@nginx.com PyTuple_SET_ITEM(header, 0, v); 8081624Smax.romanov@nginx.com 8091624Smax.romanov@nginx.com v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value), 8101624Smax.romanov@nginx.com f->value_length); 8111624Smax.romanov@nginx.com if (nxt_slow_path(v == NULL)) { 8121624Smax.romanov@nginx.com Py_DECREF(header); 8131624Smax.romanov@nginx.com 8141624Smax.romanov@nginx.com return NULL; 8151624Smax.romanov@nginx.com } 8161624Smax.romanov@nginx.com 8171624Smax.romanov@nginx.com PyTuple_SET_ITEM(header, 1, v); 8181624Smax.romanov@nginx.com 8191624Smax.romanov@nginx.com return header; 8201624Smax.romanov@nginx.com } 8211624Smax.romanov@nginx.com 8221624Smax.romanov@nginx.com 8231624Smax.romanov@nginx.com static PyObject * 8241624Smax.romanov@nginx.com nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f) 8251624Smax.romanov@nginx.com { 8261624Smax.romanov@nginx.com char *v; 8271624Smax.romanov@nginx.com uint32_t i, n, start; 8281624Smax.romanov@nginx.com PyObject *res, *proto; 8291624Smax.romanov@nginx.com 8301624Smax.romanov@nginx.com v = nxt_unit_sptr_get(&f->value); 8311624Smax.romanov@nginx.com n = 1; 8321624Smax.romanov@nginx.com 8331624Smax.romanov@nginx.com for (i = 0; i < f->value_length; i++) { 8341624Smax.romanov@nginx.com if (v[i] == ',') { 8351624Smax.romanov@nginx.com n++; 8361624Smax.romanov@nginx.com } 8371624Smax.romanov@nginx.com } 8381624Smax.romanov@nginx.com 8391624Smax.romanov@nginx.com res = PyTuple_New(n); 8401624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 8411624Smax.romanov@nginx.com return NULL; 8421624Smax.romanov@nginx.com } 8431624Smax.romanov@nginx.com 8441624Smax.romanov@nginx.com n = 0; 8451624Smax.romanov@nginx.com start = 0; 8461624Smax.romanov@nginx.com 8471624Smax.romanov@nginx.com for (i = 0; i < f->value_length; ) { 8481624Smax.romanov@nginx.com if (v[i] != ',') { 8491624Smax.romanov@nginx.com i++; 8501624Smax.romanov@nginx.com 8511624Smax.romanov@nginx.com continue; 8521624Smax.romanov@nginx.com } 8531624Smax.romanov@nginx.com 8541624Smax.romanov@nginx.com if (i - start > 0) { 8551624Smax.romanov@nginx.com proto = PyString_FromStringAndSize(v + start, i - start); 8561624Smax.romanov@nginx.com if (nxt_slow_path(proto == NULL)) { 8571624Smax.romanov@nginx.com goto fail; 8581624Smax.romanov@nginx.com } 8591624Smax.romanov@nginx.com 8601624Smax.romanov@nginx.com PyTuple_SET_ITEM(res, n, proto); 8611624Smax.romanov@nginx.com 8621624Smax.romanov@nginx.com n++; 8631624Smax.romanov@nginx.com } 8641624Smax.romanov@nginx.com 8651624Smax.romanov@nginx.com do { 8661624Smax.romanov@nginx.com i++; 8671624Smax.romanov@nginx.com } while (i < f->value_length && v[i] == ' '); 8681624Smax.romanov@nginx.com 8691624Smax.romanov@nginx.com start = i; 8701624Smax.romanov@nginx.com } 8711624Smax.romanov@nginx.com 8721624Smax.romanov@nginx.com if (i - start > 0) { 8731624Smax.romanov@nginx.com proto = PyString_FromStringAndSize(v + start, i - start); 8741624Smax.romanov@nginx.com if (nxt_slow_path(proto == NULL)) { 8751624Smax.romanov@nginx.com goto fail; 8761624Smax.romanov@nginx.com } 8771624Smax.romanov@nginx.com 8781624Smax.romanov@nginx.com PyTuple_SET_ITEM(res, n, proto); 8791624Smax.romanov@nginx.com } 8801624Smax.romanov@nginx.com 8811624Smax.romanov@nginx.com return res; 8821624Smax.romanov@nginx.com 8831624Smax.romanov@nginx.com fail: 8841624Smax.romanov@nginx.com 8851624Smax.romanov@nginx.com Py_DECREF(res); 8861624Smax.romanov@nginx.com 8871624Smax.romanov@nginx.com return NULL; 8881624Smax.romanov@nginx.com } 8891624Smax.romanov@nginx.com 8901624Smax.romanov@nginx.com 8911624Smax.romanov@nginx.com static int 8921624Smax.romanov@nginx.com nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 8931624Smax.romanov@nginx.com { 8941980Smax.romanov@nginx.com int nb; 8951624Smax.romanov@nginx.com 8961624Smax.romanov@nginx.com if (port->in_fd == -1) { 8971624Smax.romanov@nginx.com return NXT_UNIT_OK; 8981624Smax.romanov@nginx.com } 8991624Smax.romanov@nginx.com 9001624Smax.romanov@nginx.com nb = 1; 9011624Smax.romanov@nginx.com 9021624Smax.romanov@nginx.com if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) { 9031624Smax.romanov@nginx.com nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)", 9041624Smax.romanov@nginx.com port->in_fd, strerror(errno), errno); 9051624Smax.romanov@nginx.com 9061624Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9071624Smax.romanov@nginx.com } 9081624Smax.romanov@nginx.com 9091624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); 9101624Smax.romanov@nginx.com 9111979Smax.romanov@nginx.com return nxt_py_asgi_add_reader(ctx, port); 9121979Smax.romanov@nginx.com } 9131979Smax.romanov@nginx.com 9141681Smax.romanov@nginx.com 9151979Smax.romanov@nginx.com static int 9161979Smax.romanov@nginx.com nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 9171979Smax.romanov@nginx.com { 9181979Smax.romanov@nginx.com int rc; 9191979Smax.romanov@nginx.com PyObject *res, *fd, *py_ctx, *py_port; 9201979Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 9211979Smax.romanov@nginx.com 9221979Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_add_reader %d %p %p", port->in_fd, ctx, port); 9231979Smax.romanov@nginx.com 9241979Smax.romanov@nginx.com ctx_data = ctx->data; 9251682Smax.romanov@nginx.com 9261682Smax.romanov@nginx.com fd = PyLong_FromLong(port->in_fd); 9271682Smax.romanov@nginx.com if (nxt_slow_path(fd == NULL)) { 9281682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create fd"); 9291682Smax.romanov@nginx.com nxt_python_print_exception(); 9301682Smax.romanov@nginx.com 9311979Smax.romanov@nginx.com return NXT_UNIT_ERROR; 9321682Smax.romanov@nginx.com } 9331682Smax.romanov@nginx.com 9341979Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 9351979Smax.romanov@nginx.com 9361682Smax.romanov@nginx.com py_ctx = PyLong_FromVoidPtr(ctx); 9371682Smax.romanov@nginx.com if (nxt_slow_path(py_ctx == NULL)) { 9381682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_ctx"); 9391682Smax.romanov@nginx.com nxt_python_print_exception(); 9401682Smax.romanov@nginx.com 9411682Smax.romanov@nginx.com goto clean_fd; 9421682Smax.romanov@nginx.com } 9431682Smax.romanov@nginx.com 9441682Smax.romanov@nginx.com py_port = PyLong_FromVoidPtr(port); 9451682Smax.romanov@nginx.com if (nxt_slow_path(py_port == NULL)) { 9461682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to create py_port"); 9471682Smax.romanov@nginx.com nxt_python_print_exception(); 9481682Smax.romanov@nginx.com 9491682Smax.romanov@nginx.com goto clean_py_ctx; 9501682Smax.romanov@nginx.com } 9511682Smax.romanov@nginx.com 9521681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 9531682Smax.romanov@nginx.com fd, nxt_py_port_read, 9541682Smax.romanov@nginx.com py_ctx, py_port, NULL); 9551624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 9561624Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to add_reader"); 9571681Smax.romanov@nginx.com nxt_python_print_exception(); 9581624Smax.romanov@nginx.com 9591682Smax.romanov@nginx.com } else { 9601682Smax.romanov@nginx.com Py_DECREF(res); 9611682Smax.romanov@nginx.com 9621682Smax.romanov@nginx.com rc = NXT_UNIT_OK; 9631624Smax.romanov@nginx.com } 9641624Smax.romanov@nginx.com 9651682Smax.romanov@nginx.com Py_DECREF(py_port); 9661682Smax.romanov@nginx.com 9671682Smax.romanov@nginx.com clean_py_ctx: 9681682Smax.romanov@nginx.com 9691682Smax.romanov@nginx.com Py_DECREF(py_ctx); 9701624Smax.romanov@nginx.com 9711682Smax.romanov@nginx.com clean_fd: 9721682Smax.romanov@nginx.com 9731682Smax.romanov@nginx.com Py_DECREF(fd); 9741682Smax.romanov@nginx.com 9751682Smax.romanov@nginx.com return rc; 9761624Smax.romanov@nginx.com } 9771624Smax.romanov@nginx.com 9781624Smax.romanov@nginx.com 9791624Smax.romanov@nginx.com static void 9801980Smax.romanov@nginx.com nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx, 9811980Smax.romanov@nginx.com nxt_unit_port_t *port) 9821624Smax.romanov@nginx.com { 9831980Smax.romanov@nginx.com if (port->in_fd == -1 || ctx == NULL) { 9841624Smax.romanov@nginx.com return; 9851624Smax.romanov@nginx.com } 9861624Smax.romanov@nginx.com 9871681Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); 9881681Smax.romanov@nginx.com 9891980Smax.romanov@nginx.com nxt_py_asgi_remove_reader(ctx, port); 9901624Smax.romanov@nginx.com } 9911624Smax.romanov@nginx.com 9921624Smax.romanov@nginx.com 9931624Smax.romanov@nginx.com static void 9941624Smax.romanov@nginx.com nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) 9951624Smax.romanov@nginx.com { 9961682Smax.romanov@nginx.com PyObject *res, *p; 9971681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 9981624Smax.romanov@nginx.com 9991624Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_quit %p", ctx); 10001624Smax.romanov@nginx.com 10011681Smax.romanov@nginx.com ctx_data = ctx->data; 10021681Smax.romanov@nginx.com 10031682Smax.romanov@nginx.com p = PyLong_FromLong(0); 10041682Smax.romanov@nginx.com if (nxt_slow_path(p == NULL)) { 10051682Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Long"); 10061682Smax.romanov@nginx.com nxt_python_print_exception(); 10071682Smax.romanov@nginx.com 10081682Smax.romanov@nginx.com } else { 10091682Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, 10101682Smax.romanov@nginx.com p, NULL); 10111681Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10121682Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to set_result"); 10131681Smax.romanov@nginx.com nxt_python_print_exception(); 10141681Smax.romanov@nginx.com 10151681Smax.romanov@nginx.com } else { 10161681Smax.romanov@nginx.com Py_DECREF(res); 10171681Smax.romanov@nginx.com } 10181681Smax.romanov@nginx.com 10191682Smax.romanov@nginx.com Py_DECREF(p); 10201681Smax.romanov@nginx.com } 10211624Smax.romanov@nginx.com } 10221624Smax.romanov@nginx.com 10231624Smax.romanov@nginx.com 10241624Smax.romanov@nginx.com static void 10251624Smax.romanov@nginx.com nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx) 10261624Smax.romanov@nginx.com { 10271681Smax.romanov@nginx.com int rc; 10281681Smax.romanov@nginx.com nxt_queue_link_t *lnk; 10291681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 10301624Smax.romanov@nginx.com 10311681Smax.romanov@nginx.com ctx_data = ctx->data; 10321681Smax.romanov@nginx.com 10331681Smax.romanov@nginx.com while (!nxt_queue_is_empty(&ctx_data->drain_queue)) { 10341681Smax.romanov@nginx.com lnk = nxt_queue_first(&ctx_data->drain_queue); 10351624Smax.romanov@nginx.com 10361624Smax.romanov@nginx.com rc = nxt_py_asgi_http_drain(lnk); 10371624Smax.romanov@nginx.com if (rc == NXT_UNIT_AGAIN) { 10381681Smax.romanov@nginx.com return; 10391624Smax.romanov@nginx.com } 10401624Smax.romanov@nginx.com 10411624Smax.romanov@nginx.com nxt_queue_remove(lnk); 10421624Smax.romanov@nginx.com } 10431624Smax.romanov@nginx.com } 10441624Smax.romanov@nginx.com 10451624Smax.romanov@nginx.com 10461624Smax.romanov@nginx.com static PyObject * 10471624Smax.romanov@nginx.com nxt_py_asgi_port_read(PyObject *self, PyObject *args) 10481624Smax.romanov@nginx.com { 10491767Smax.romanov@nginx.com int rc; 10501767Smax.romanov@nginx.com PyObject *arg0, *arg1, *res; 10511767Smax.romanov@nginx.com Py_ssize_t n; 10521767Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 10531767Smax.romanov@nginx.com nxt_unit_port_t *port; 10541767Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 10551624Smax.romanov@nginx.com 10561624Smax.romanov@nginx.com n = PyTuple_GET_SIZE(args); 10571624Smax.romanov@nginx.com 10581624Smax.romanov@nginx.com if (n != 2) { 10591624Smax.romanov@nginx.com nxt_unit_alert(NULL, 10601624Smax.romanov@nginx.com "nxt_py_asgi_port_read: invalid number of arguments %d", 10611624Smax.romanov@nginx.com (int) n); 10621624Smax.romanov@nginx.com 10631624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 10641624Smax.romanov@nginx.com } 10651624Smax.romanov@nginx.com 10661767Smax.romanov@nginx.com arg0 = PyTuple_GET_ITEM(args, 0); 10671767Smax.romanov@nginx.com if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) { 10681624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 10691624Smax.romanov@nginx.com "the first argument is not a long"); 10701624Smax.romanov@nginx.com } 10711624Smax.romanov@nginx.com 10721767Smax.romanov@nginx.com ctx = PyLong_AsVoidPtr(arg0); 10731624Smax.romanov@nginx.com 10741767Smax.romanov@nginx.com arg1 = PyTuple_GET_ITEM(args, 1); 10751767Smax.romanov@nginx.com if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) { 10761624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 10771624Smax.romanov@nginx.com "the second argument is not a long"); 10781624Smax.romanov@nginx.com } 10791624Smax.romanov@nginx.com 10801767Smax.romanov@nginx.com port = PyLong_AsVoidPtr(arg1); 10811624Smax.romanov@nginx.com 10821624Smax.romanov@nginx.com rc = nxt_unit_process_port_msg(ctx, port); 10831624Smax.romanov@nginx.com 10841767Smax.romanov@nginx.com nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc); 10851767Smax.romanov@nginx.com 10861624Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 10871624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 10881681Smax.romanov@nginx.com "error processing port %d message", port->id.id); 10891624Smax.romanov@nginx.com } 10901624Smax.romanov@nginx.com 10911767Smax.romanov@nginx.com if (rc == NXT_UNIT_OK) { 10921767Smax.romanov@nginx.com ctx_data = ctx->data; 10931767Smax.romanov@nginx.com 10941767Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, 10951767Smax.romanov@nginx.com nxt_py_port_read, 10961767Smax.romanov@nginx.com arg0, arg1, NULL); 10971767Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 10981767Smax.romanov@nginx.com nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'"); 10991767Smax.romanov@nginx.com nxt_python_print_exception(); 11001767Smax.romanov@nginx.com } 11011767Smax.romanov@nginx.com 11021767Smax.romanov@nginx.com Py_XDECREF(res); 11031767Smax.romanov@nginx.com } 11041767Smax.romanov@nginx.com 11051624Smax.romanov@nginx.com Py_RETURN_NONE; 11061624Smax.romanov@nginx.com } 11071624Smax.romanov@nginx.com 11081624Smax.romanov@nginx.com 11091624Smax.romanov@nginx.com PyObject * 11101624Smax.romanov@nginx.com nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb, 11111624Smax.romanov@nginx.com void *data) 11121624Smax.romanov@nginx.com { 11131624Smax.romanov@nginx.com int i; 11141624Smax.romanov@nginx.com PyObject *iter, *header, *h_iter, *name, *val, *res; 11151624Smax.romanov@nginx.com 11161624Smax.romanov@nginx.com iter = PyObject_GetIter(headers); 11171624Smax.romanov@nginx.com if (nxt_slow_path(iter == NULL)) { 11181624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable"); 11191624Smax.romanov@nginx.com } 11201624Smax.romanov@nginx.com 11211624Smax.romanov@nginx.com for (i = 0; /* void */; i++) { 11221624Smax.romanov@nginx.com header = PyIter_Next(iter); 11231624Smax.romanov@nginx.com if (header == NULL) { 11241624Smax.romanov@nginx.com break; 11251624Smax.romanov@nginx.com } 11261624Smax.romanov@nginx.com 11271624Smax.romanov@nginx.com h_iter = PyObject_GetIter(header); 11281624Smax.romanov@nginx.com if (nxt_slow_path(h_iter == NULL)) { 11291624Smax.romanov@nginx.com Py_DECREF(header); 11301624Smax.romanov@nginx.com Py_DECREF(iter); 11311624Smax.romanov@nginx.com 11321624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11331624Smax.romanov@nginx.com "'headers' item #%d is not an iterable", i); 11341624Smax.romanov@nginx.com } 11351624Smax.romanov@nginx.com 11361624Smax.romanov@nginx.com name = PyIter_Next(h_iter); 11371624Smax.romanov@nginx.com if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) { 11381624Smax.romanov@nginx.com Py_XDECREF(name); 11391624Smax.romanov@nginx.com Py_DECREF(h_iter); 11401624Smax.romanov@nginx.com Py_DECREF(header); 11411624Smax.romanov@nginx.com Py_DECREF(iter); 11421624Smax.romanov@nginx.com 11431624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11441624Smax.romanov@nginx.com "'headers' item #%d 'name' is not a byte string", i); 11451624Smax.romanov@nginx.com } 11461624Smax.romanov@nginx.com 11471624Smax.romanov@nginx.com val = PyIter_Next(h_iter); 11481624Smax.romanov@nginx.com if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) { 11491624Smax.romanov@nginx.com Py_XDECREF(val); 11501624Smax.romanov@nginx.com Py_DECREF(h_iter); 11511624Smax.romanov@nginx.com Py_DECREF(header); 11521624Smax.romanov@nginx.com Py_DECREF(iter); 11531624Smax.romanov@nginx.com 11541624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 11551624Smax.romanov@nginx.com "'headers' item #%d 'value' is not a byte string", i); 11561624Smax.romanov@nginx.com } 11571624Smax.romanov@nginx.com 11581624Smax.romanov@nginx.com res = cb(data, i, name, val); 11591624Smax.romanov@nginx.com 11601624Smax.romanov@nginx.com Py_DECREF(name); 11611624Smax.romanov@nginx.com Py_DECREF(val); 11621624Smax.romanov@nginx.com Py_DECREF(h_iter); 11631624Smax.romanov@nginx.com Py_DECREF(header); 11641624Smax.romanov@nginx.com 11651624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 11661624Smax.romanov@nginx.com Py_DECREF(iter); 11671624Smax.romanov@nginx.com 11681624Smax.romanov@nginx.com return NULL; 11691624Smax.romanov@nginx.com } 11701624Smax.romanov@nginx.com 11711624Smax.romanov@nginx.com Py_DECREF(res); 11721624Smax.romanov@nginx.com } 11731624Smax.romanov@nginx.com 11741624Smax.romanov@nginx.com Py_DECREF(iter); 11751624Smax.romanov@nginx.com 11761624Smax.romanov@nginx.com Py_RETURN_NONE; 11771624Smax.romanov@nginx.com } 11781624Smax.romanov@nginx.com 11791624Smax.romanov@nginx.com 11801624Smax.romanov@nginx.com PyObject * 11811624Smax.romanov@nginx.com nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val) 11821624Smax.romanov@nginx.com { 11831624Smax.romanov@nginx.com nxt_py_asgi_calc_size_ctx_t *ctx; 11841624Smax.romanov@nginx.com 11851624Smax.romanov@nginx.com ctx = data; 11861624Smax.romanov@nginx.com 11871624Smax.romanov@nginx.com ctx->fields_count++; 11881624Smax.romanov@nginx.com ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val); 11891624Smax.romanov@nginx.com 11901624Smax.romanov@nginx.com Py_RETURN_NONE; 11911624Smax.romanov@nginx.com } 11921624Smax.romanov@nginx.com 11931624Smax.romanov@nginx.com 11941624Smax.romanov@nginx.com PyObject * 11951624Smax.romanov@nginx.com nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val) 11961624Smax.romanov@nginx.com { 11971624Smax.romanov@nginx.com int rc; 11981624Smax.romanov@nginx.com char *name_str, *val_str; 11991624Smax.romanov@nginx.com uint32_t name_len, val_len; 12001624Smax.romanov@nginx.com nxt_off_t content_length; 12011624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 12021624Smax.romanov@nginx.com nxt_py_asgi_add_field_ctx_t *ctx; 12031624Smax.romanov@nginx.com 12041624Smax.romanov@nginx.com name_str = PyBytes_AS_STRING(name); 12051624Smax.romanov@nginx.com name_len = PyBytes_GET_SIZE(name); 12061624Smax.romanov@nginx.com 12071624Smax.romanov@nginx.com val_str = PyBytes_AS_STRING(val); 12081624Smax.romanov@nginx.com val_len = PyBytes_GET_SIZE(val); 12091624Smax.romanov@nginx.com 12101624Smax.romanov@nginx.com ctx = data; 12111624Smax.romanov@nginx.com req = ctx->req; 12121624Smax.romanov@nginx.com 12131624Smax.romanov@nginx.com rc = nxt_unit_response_add_field(req, name_str, name_len, 12141624Smax.romanov@nginx.com val_str, val_len); 12151624Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 12161624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 12171624Smax.romanov@nginx.com "failed to add header #%d", i); 12181624Smax.romanov@nginx.com } 12191624Smax.romanov@nginx.com 12201624Smax.romanov@nginx.com if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { 12211624Smax.romanov@nginx.com content_length = nxt_off_t_parse((u_char *) val_str, val_len); 12221624Smax.romanov@nginx.com if (nxt_slow_path(content_length < 0)) { 12231624Smax.romanov@nginx.com nxt_unit_req_error(req, "failed to parse Content-Length " 12241624Smax.romanov@nginx.com "value %.*s", (int) val_len, val_str); 12251624Smax.romanov@nginx.com 12261624Smax.romanov@nginx.com return PyErr_Format(PyExc_ValueError, 12271624Smax.romanov@nginx.com "Failed to parse Content-Length: '%.*s'", 12281624Smax.romanov@nginx.com (int) val_len, val_str); 12291624Smax.romanov@nginx.com } 12301624Smax.romanov@nginx.com 12311624Smax.romanov@nginx.com ctx->content_length = content_length; 12321624Smax.romanov@nginx.com } 12331624Smax.romanov@nginx.com 12341624Smax.romanov@nginx.com Py_RETURN_NONE; 12351624Smax.romanov@nginx.com } 12361624Smax.romanov@nginx.com 12371624Smax.romanov@nginx.com 12381624Smax.romanov@nginx.com PyObject * 12391681Smax.romanov@nginx.com nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, 12401681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result) 12411624Smax.romanov@nginx.com { 12421624Smax.romanov@nginx.com PyObject *set_result, *res; 12431624Smax.romanov@nginx.com 12441624Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 12451624Smax.romanov@nginx.com Py_DECREF(future); 12461624Smax.romanov@nginx.com 12471624Smax.romanov@nginx.com return NULL; 12481624Smax.romanov@nginx.com } 12491624Smax.romanov@nginx.com 12501624Smax.romanov@nginx.com set_result = PyObject_GetAttrString(future, "set_result"); 12511624Smax.romanov@nginx.com if (nxt_slow_path(set_result == NULL)) { 12521624Smax.romanov@nginx.com nxt_unit_req_alert(req, "failed to get 'set_result' for future"); 12531624Smax.romanov@nginx.com 12541624Smax.romanov@nginx.com Py_CLEAR(future); 12551624Smax.romanov@nginx.com 12561681Smax.romanov@nginx.com goto cleanup_result; 12571624Smax.romanov@nginx.com } 12581624Smax.romanov@nginx.com 12591624Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(set_result) == 0)) { 12601624Smax.romanov@nginx.com nxt_unit_req_alert(req, "'future.set_result' is not a callable"); 12611624Smax.romanov@nginx.com 12621624Smax.romanov@nginx.com Py_CLEAR(future); 12631624Smax.romanov@nginx.com 12641624Smax.romanov@nginx.com goto cleanup; 12651624Smax.romanov@nginx.com } 12661624Smax.romanov@nginx.com 12671681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result, 12681624Smax.romanov@nginx.com result, NULL); 12691624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 12701624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'"); 12711624Smax.romanov@nginx.com nxt_python_print_exception(); 12721624Smax.romanov@nginx.com 12731624Smax.romanov@nginx.com Py_CLEAR(future); 12741624Smax.romanov@nginx.com } 12751624Smax.romanov@nginx.com 12761624Smax.romanov@nginx.com Py_XDECREF(res); 12771624Smax.romanov@nginx.com 12781624Smax.romanov@nginx.com cleanup: 12791624Smax.romanov@nginx.com 12801624Smax.romanov@nginx.com Py_DECREF(set_result); 12811681Smax.romanov@nginx.com 12821681Smax.romanov@nginx.com cleanup_result: 12831681Smax.romanov@nginx.com 12841624Smax.romanov@nginx.com Py_DECREF(result); 12851624Smax.romanov@nginx.com 12861624Smax.romanov@nginx.com return future; 12871624Smax.romanov@nginx.com } 12881624Smax.romanov@nginx.com 12891624Smax.romanov@nginx.com 12901624Smax.romanov@nginx.com PyObject * 12911624Smax.romanov@nginx.com nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type) 12921624Smax.romanov@nginx.com { 12931624Smax.romanov@nginx.com PyObject *msg; 12941624Smax.romanov@nginx.com 12951624Smax.romanov@nginx.com msg = PyDict_New(); 12961624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 12971624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create message dict"); 12981624Smax.romanov@nginx.com nxt_python_print_exception(); 12991624Smax.romanov@nginx.com 13001624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13011624Smax.romanov@nginx.com "failed to create message dict"); 13021624Smax.romanov@nginx.com } 13031624Smax.romanov@nginx.com 13041624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) { 13051624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'msg.type' item"); 13061624Smax.romanov@nginx.com 13071624Smax.romanov@nginx.com Py_DECREF(msg); 13081624Smax.romanov@nginx.com 13091624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13101624Smax.romanov@nginx.com "failed to set 'msg.type' item"); 13111624Smax.romanov@nginx.com } 13121624Smax.romanov@nginx.com 13131624Smax.romanov@nginx.com return msg; 13141624Smax.romanov@nginx.com } 13151624Smax.romanov@nginx.com 13161624Smax.romanov@nginx.com 13171624Smax.romanov@nginx.com PyObject * 13181624Smax.romanov@nginx.com nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, 13191624Smax.romanov@nginx.com PyObject *spec_version) 13201624Smax.romanov@nginx.com { 13211624Smax.romanov@nginx.com PyObject *scope, *asgi; 13221624Smax.romanov@nginx.com 13231624Smax.romanov@nginx.com scope = PyDict_New(); 13241624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 13251624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'scope' dict"); 13261624Smax.romanov@nginx.com nxt_python_print_exception(); 13271624Smax.romanov@nginx.com 13281624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13291624Smax.romanov@nginx.com "failed to create 'scope' dict"); 13301624Smax.romanov@nginx.com } 13311624Smax.romanov@nginx.com 13321624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) { 13331624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.type' item"); 13341624Smax.romanov@nginx.com 13351624Smax.romanov@nginx.com Py_DECREF(scope); 13361624Smax.romanov@nginx.com 13371624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13381624Smax.romanov@nginx.com "failed to set 'scope.type' item"); 13391624Smax.romanov@nginx.com } 13401624Smax.romanov@nginx.com 13411624Smax.romanov@nginx.com asgi = PyDict_New(); 13421624Smax.romanov@nginx.com if (nxt_slow_path(asgi == NULL)) { 13431624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create 'asgi' dict"); 13441624Smax.romanov@nginx.com nxt_python_print_exception(); 13451624Smax.romanov@nginx.com 13461624Smax.romanov@nginx.com Py_DECREF(scope); 13471624Smax.romanov@nginx.com 13481624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13491624Smax.romanov@nginx.com "failed to create 'asgi' dict"); 13501624Smax.romanov@nginx.com } 13511624Smax.romanov@nginx.com 13521624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) { 13531624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item"); 13541624Smax.romanov@nginx.com 13551624Smax.romanov@nginx.com Py_DECREF(asgi); 13561624Smax.romanov@nginx.com Py_DECREF(scope); 13571624Smax.romanov@nginx.com 13581624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13591624Smax.romanov@nginx.com "failed to set 'scope.asgi' item"); 13601624Smax.romanov@nginx.com } 13611624Smax.romanov@nginx.com 13621624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str, 13631624Smax.romanov@nginx.com nxt_py_3_0_str) == -1)) 13641624Smax.romanov@nginx.com { 13651624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item"); 13661624Smax.romanov@nginx.com 13671624Smax.romanov@nginx.com Py_DECREF(asgi); 13681624Smax.romanov@nginx.com Py_DECREF(scope); 13691624Smax.romanov@nginx.com 13701624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13711624Smax.romanov@nginx.com "failed to set 'asgi.version' item"); 13721624Smax.romanov@nginx.com } 13731624Smax.romanov@nginx.com 13741624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str, 13751624Smax.romanov@nginx.com spec_version) == -1)) 13761624Smax.romanov@nginx.com { 13771624Smax.romanov@nginx.com nxt_unit_req_alert(req, 13781624Smax.romanov@nginx.com "Python failed to set 'asgi.spec_version' item"); 13791624Smax.romanov@nginx.com 13801624Smax.romanov@nginx.com Py_DECREF(asgi); 13811624Smax.romanov@nginx.com Py_DECREF(scope); 13821624Smax.romanov@nginx.com 13831624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 13841624Smax.romanov@nginx.com "failed to set 'asgi.spec_version' item"); 13851624Smax.romanov@nginx.com } 13861624Smax.romanov@nginx.com 13871624Smax.romanov@nginx.com Py_DECREF(asgi); 13881624Smax.romanov@nginx.com 13891624Smax.romanov@nginx.com return scope; 13901624Smax.romanov@nginx.com } 13911624Smax.romanov@nginx.com 13921624Smax.romanov@nginx.com 13931624Smax.romanov@nginx.com void 13941681Smax.romanov@nginx.com nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link) 13951681Smax.romanov@nginx.com { 13961681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 13971681Smax.romanov@nginx.com 13981681Smax.romanov@nginx.com ctx_data = req->ctx->data; 13991681Smax.romanov@nginx.com 14001681Smax.romanov@nginx.com nxt_queue_insert_tail(&ctx_data->drain_queue, link); 14011681Smax.romanov@nginx.com } 14021681Smax.romanov@nginx.com 14031681Smax.romanov@nginx.com 14041681Smax.romanov@nginx.com void 14051624Smax.romanov@nginx.com nxt_py_asgi_dealloc(PyObject *self) 14061624Smax.romanov@nginx.com { 14071624Smax.romanov@nginx.com PyObject_Del(self); 14081624Smax.romanov@nginx.com } 14091624Smax.romanov@nginx.com 14101624Smax.romanov@nginx.com 14111624Smax.romanov@nginx.com PyObject * 14121624Smax.romanov@nginx.com nxt_py_asgi_await(PyObject *self) 14131624Smax.romanov@nginx.com { 14141624Smax.romanov@nginx.com Py_INCREF(self); 14151624Smax.romanov@nginx.com return self; 14161624Smax.romanov@nginx.com } 14171624Smax.romanov@nginx.com 14181624Smax.romanov@nginx.com 14191624Smax.romanov@nginx.com PyObject * 14201624Smax.romanov@nginx.com nxt_py_asgi_iter(PyObject *self) 14211624Smax.romanov@nginx.com { 14221624Smax.romanov@nginx.com Py_INCREF(self); 14231624Smax.romanov@nginx.com return self; 14241624Smax.romanov@nginx.com } 14251624Smax.romanov@nginx.com 14261624Smax.romanov@nginx.com 14271624Smax.romanov@nginx.com PyObject * 14281624Smax.romanov@nginx.com nxt_py_asgi_next(PyObject *self) 14291624Smax.romanov@nginx.com { 14301624Smax.romanov@nginx.com return NULL; 14311624Smax.romanov@nginx.com } 14321624Smax.romanov@nginx.com 14331624Smax.romanov@nginx.com 14341681Smax.romanov@nginx.com static void 14351624Smax.romanov@nginx.com nxt_python_asgi_done(void) 14361624Smax.romanov@nginx.com { 14371624Smax.romanov@nginx.com nxt_py_asgi_str_done(); 14381624Smax.romanov@nginx.com 14391624Smax.romanov@nginx.com Py_XDECREF(nxt_py_port_read); 14401624Smax.romanov@nginx.com } 14411624Smax.romanov@nginx.com 14421624Smax.romanov@nginx.com #else /* !(NXT_HAVE_ASGI) */ 14431624Smax.romanov@nginx.com 14441624Smax.romanov@nginx.com 14451624Smax.romanov@nginx.com int 14461624Smax.romanov@nginx.com nxt_python_asgi_check(PyObject *obj) 14471624Smax.romanov@nginx.com { 14481624Smax.romanov@nginx.com return 0; 14491624Smax.romanov@nginx.com } 14501624Smax.romanov@nginx.com 14511624Smax.romanov@nginx.com 14521681Smax.romanov@nginx.com int 14531681Smax.romanov@nginx.com nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 14541624Smax.romanov@nginx.com { 14551681Smax.romanov@nginx.com nxt_unit_alert(NULL, "ASGI not implemented"); 14561681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 14571624Smax.romanov@nginx.com } 14581624Smax.romanov@nginx.com 14591624Smax.romanov@nginx.com 14601624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */ 1461