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 <python/nxt_python_asgi.h> 131624Smax.romanov@nginx.com #include <python/nxt_python_asgi_str.h> 141624Smax.romanov@nginx.com 151624Smax.romanov@nginx.com 161624Smax.romanov@nginx.com typedef struct { 171624Smax.romanov@nginx.com PyObject_HEAD 181681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 191681Smax.romanov@nginx.com int disabled; 201681Smax.romanov@nginx.com int startup_received; 211681Smax.romanov@nginx.com int startup_sent; 221681Smax.romanov@nginx.com int shutdown_received; 231681Smax.romanov@nginx.com int shutdown_sent; 241681Smax.romanov@nginx.com int shutdown_called; 251681Smax.romanov@nginx.com PyObject *startup_future; 261681Smax.romanov@nginx.com PyObject *shutdown_future; 271681Smax.romanov@nginx.com PyObject *receive_future; 281624Smax.romanov@nginx.com } nxt_py_asgi_lifespan_t; 291624Smax.romanov@nginx.com 301624Smax.romanov@nginx.com 311624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none); 321624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict); 331624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_startup( 341624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict); 351624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan, 361624Smax.romanov@nginx.com int v, int *sent, PyObject **future); 371624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_shutdown( 381624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict); 391624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan); 401624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future); 411624Smax.romanov@nginx.com 421624Smax.romanov@nginx.com 431624Smax.romanov@nginx.com static PyMethodDef nxt_py_asgi_lifespan_methods[] = { 441624Smax.romanov@nginx.com { "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 }, 451624Smax.romanov@nginx.com { "send", nxt_py_asgi_lifespan_send, METH_O, 0 }, 461624Smax.romanov@nginx.com { "_done", nxt_py_asgi_lifespan_done, METH_O, 0 }, 471624Smax.romanov@nginx.com { NULL, NULL, 0, 0 } 481624Smax.romanov@nginx.com }; 491624Smax.romanov@nginx.com 501624Smax.romanov@nginx.com static PyAsyncMethods nxt_py_asgi_async_methods = { 511624Smax.romanov@nginx.com .am_await = nxt_py_asgi_await, 521624Smax.romanov@nginx.com }; 531624Smax.romanov@nginx.com 541624Smax.romanov@nginx.com static PyTypeObject nxt_py_asgi_lifespan_type = { 551624Smax.romanov@nginx.com PyVarObject_HEAD_INIT(NULL, 0) 561624Smax.romanov@nginx.com 571624Smax.romanov@nginx.com .tp_name = "unit._asgi_lifespan", 581624Smax.romanov@nginx.com .tp_basicsize = sizeof(nxt_py_asgi_lifespan_t), 591624Smax.romanov@nginx.com .tp_dealloc = nxt_py_asgi_dealloc, 601624Smax.romanov@nginx.com .tp_as_async = &nxt_py_asgi_async_methods, 611624Smax.romanov@nginx.com .tp_flags = Py_TPFLAGS_DEFAULT, 621624Smax.romanov@nginx.com .tp_doc = "unit ASGI Lifespan object", 631624Smax.romanov@nginx.com .tp_iter = nxt_py_asgi_iter, 641624Smax.romanov@nginx.com .tp_iternext = nxt_py_asgi_next, 651624Smax.romanov@nginx.com .tp_methods = nxt_py_asgi_lifespan_methods, 661624Smax.romanov@nginx.com }; 671624Smax.romanov@nginx.com 681624Smax.romanov@nginx.com 691681Smax.romanov@nginx.com int 701681Smax.romanov@nginx.com nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) 711624Smax.romanov@nginx.com { 721681Smax.romanov@nginx.com int rc; 731624Smax.romanov@nginx.com PyObject *scope, *res, *py_task, *receive, *send, *done; 74*1697Smax.romanov@nginx.com PyObject *stage2; 751624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan; 761624Smax.romanov@nginx.com 771624Smax.romanov@nginx.com if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) { 781681Smax.romanov@nginx.com nxt_unit_alert(NULL, 791624Smax.romanov@nginx.com "Python failed to initialize the 'asgi_lifespan' type object"); 801681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 811624Smax.romanov@nginx.com } 821624Smax.romanov@nginx.com 831624Smax.romanov@nginx.com lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type); 841624Smax.romanov@nginx.com if (nxt_slow_path(lifespan == NULL)) { 851681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create lifespan object"); 861681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 871624Smax.romanov@nginx.com } 881624Smax.romanov@nginx.com 891681Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 901624Smax.romanov@nginx.com 911624Smax.romanov@nginx.com receive = PyObject_GetAttrString((PyObject *) lifespan, "receive"); 921624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 931681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'receive' method"); 941624Smax.romanov@nginx.com goto release_lifespan; 951624Smax.romanov@nginx.com } 961624Smax.romanov@nginx.com 971624Smax.romanov@nginx.com send = PyObject_GetAttrString((PyObject *) lifespan, "send"); 981624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 991681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'send' method"); 1001624Smax.romanov@nginx.com goto release_receive; 1011624Smax.romanov@nginx.com } 1021624Smax.romanov@nginx.com 1031624Smax.romanov@nginx.com done = PyObject_GetAttrString((PyObject *) lifespan, "_done"); 1041624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) { 1051681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get '_done' method"); 1061624Smax.romanov@nginx.com goto release_send; 1071624Smax.romanov@nginx.com } 1081624Smax.romanov@nginx.com 1091681Smax.romanov@nginx.com lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future, 1101624Smax.romanov@nginx.com NULL); 1111624Smax.romanov@nginx.com if (nxt_slow_path(lifespan->startup_future == NULL)) { 1121624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object"); 1131624Smax.romanov@nginx.com nxt_python_print_exception(); 1141624Smax.romanov@nginx.com 1151624Smax.romanov@nginx.com goto release_done; 1161624Smax.romanov@nginx.com } 1171624Smax.romanov@nginx.com 1181681Smax.romanov@nginx.com lifespan->ctx_data = ctx_data; 1191624Smax.romanov@nginx.com lifespan->disabled = 0; 1201624Smax.romanov@nginx.com lifespan->startup_received = 0; 1211624Smax.romanov@nginx.com lifespan->startup_sent = 0; 1221624Smax.romanov@nginx.com lifespan->shutdown_received = 0; 1231624Smax.romanov@nginx.com lifespan->shutdown_sent = 0; 1241624Smax.romanov@nginx.com lifespan->shutdown_called = 0; 1251624Smax.romanov@nginx.com lifespan->shutdown_future = NULL; 1261624Smax.romanov@nginx.com lifespan->receive_future = NULL; 1271624Smax.romanov@nginx.com 1281624Smax.romanov@nginx.com scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str); 1291624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) { 1301624Smax.romanov@nginx.com goto release_future; 1311624Smax.romanov@nginx.com } 1321624Smax.romanov@nginx.com 133*1697Smax.romanov@nginx.com if (!nxt_py_asgi_legacy) { 134*1697Smax.romanov@nginx.com nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application"); 135*1697Smax.romanov@nginx.com 136*1697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(nxt_py_application, 137*1697Smax.romanov@nginx.com scope, receive, send, NULL); 138*1697Smax.romanov@nginx.com 139*1697Smax.romanov@nginx.com } else { 140*1697Smax.romanov@nginx.com nxt_unit_req_debug(NULL, "Python call legacy application"); 141*1697Smax.romanov@nginx.com 142*1697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL); 143*1697Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 144*1697Smax.romanov@nginx.com nxt_unit_log(NULL, NXT_UNIT_LOG_INFO, 145*1697Smax.romanov@nginx.com "ASGI Lifespan processing exception"); 146*1697Smax.romanov@nginx.com nxt_python_print_exception(); 147*1697Smax.romanov@nginx.com 148*1697Smax.romanov@nginx.com lifespan->disabled = 1; 149*1697Smax.romanov@nginx.com rc = NXT_UNIT_OK; 150*1697Smax.romanov@nginx.com 151*1697Smax.romanov@nginx.com goto release_scope; 152*1697Smax.romanov@nginx.com } 153*1697Smax.romanov@nginx.com 154*1697Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(res) == 0)) { 155*1697Smax.romanov@nginx.com nxt_unit_req_error(NULL, 156*1697Smax.romanov@nginx.com "Legacy ASGI application returns not a callable"); 157*1697Smax.romanov@nginx.com 158*1697Smax.romanov@nginx.com Py_DECREF(res); 159*1697Smax.romanov@nginx.com 160*1697Smax.romanov@nginx.com goto release_scope; 161*1697Smax.romanov@nginx.com } 162*1697Smax.romanov@nginx.com 163*1697Smax.romanov@nginx.com stage2 = res; 164*1697Smax.romanov@nginx.com 165*1697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); 166*1697Smax.romanov@nginx.com 167*1697Smax.romanov@nginx.com Py_DECREF(stage2); 168*1697Smax.romanov@nginx.com } 169*1697Smax.romanov@nginx.com 1701624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 1711681Smax.romanov@nginx.com nxt_unit_error(NULL, "Python failed to call the application"); 1721624Smax.romanov@nginx.com nxt_python_print_exception(); 1731624Smax.romanov@nginx.com goto release_scope; 1741624Smax.romanov@nginx.com } 1751624Smax.romanov@nginx.com 1761624Smax.romanov@nginx.com if (nxt_slow_path(!PyCoro_CheckExact(res))) { 1771681Smax.romanov@nginx.com nxt_unit_error(NULL, "Application result type is not a coroutine"); 1781624Smax.romanov@nginx.com Py_DECREF(res); 1791624Smax.romanov@nginx.com goto release_scope; 1801624Smax.romanov@nginx.com } 1811624Smax.romanov@nginx.com 182*1697Smax.romanov@nginx.com py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, 183*1697Smax.romanov@nginx.com NULL); 1841624Smax.romanov@nginx.com if (nxt_slow_path(py_task == NULL)) { 1851681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call the create_task"); 1861624Smax.romanov@nginx.com nxt_python_print_exception(); 1871624Smax.romanov@nginx.com Py_DECREF(res); 1881624Smax.romanov@nginx.com goto release_scope; 1891624Smax.romanov@nginx.com } 1901624Smax.romanov@nginx.com 1911624Smax.romanov@nginx.com Py_DECREF(res); 1921624Smax.romanov@nginx.com 1931624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str, 1941624Smax.romanov@nginx.com done, NULL); 1951624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 1961681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'"); 1971624Smax.romanov@nginx.com nxt_python_print_exception(); 1981624Smax.romanov@nginx.com goto release_task; 1991624Smax.romanov@nginx.com } 2001624Smax.romanov@nginx.com 2011624Smax.romanov@nginx.com Py_DECREF(res); 2021624Smax.romanov@nginx.com 2031681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 2041624Smax.romanov@nginx.com lifespan->startup_future, NULL); 2051624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 2061681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete"); 2071624Smax.romanov@nginx.com nxt_python_print_exception(); 2081624Smax.romanov@nginx.com goto release_task; 2091624Smax.romanov@nginx.com } 2101624Smax.romanov@nginx.com 2111624Smax.romanov@nginx.com Py_DECREF(res); 2121624Smax.romanov@nginx.com 2131624Smax.romanov@nginx.com if (lifespan->startup_sent == 1 || lifespan->disabled) { 2141681Smax.romanov@nginx.com ctx_data->lifespan = (PyObject *) lifespan; 2151681Smax.romanov@nginx.com Py_INCREF(ctx_data->lifespan); 2161624Smax.romanov@nginx.com 2171681Smax.romanov@nginx.com rc = NXT_UNIT_OK; 2181624Smax.romanov@nginx.com } 2191624Smax.romanov@nginx.com 2201624Smax.romanov@nginx.com release_task: 2211624Smax.romanov@nginx.com Py_DECREF(py_task); 2221624Smax.romanov@nginx.com release_scope: 2231624Smax.romanov@nginx.com Py_DECREF(scope); 2241624Smax.romanov@nginx.com release_future: 2251624Smax.romanov@nginx.com Py_CLEAR(lifespan->startup_future); 2261624Smax.romanov@nginx.com release_done: 2271624Smax.romanov@nginx.com Py_DECREF(done); 2281624Smax.romanov@nginx.com release_send: 2291624Smax.romanov@nginx.com Py_DECREF(send); 2301624Smax.romanov@nginx.com release_receive: 2311624Smax.romanov@nginx.com Py_DECREF(receive); 2321624Smax.romanov@nginx.com release_lifespan: 2331624Smax.romanov@nginx.com Py_DECREF(lifespan); 2341624Smax.romanov@nginx.com 2351624Smax.romanov@nginx.com return rc; 2361624Smax.romanov@nginx.com } 2371624Smax.romanov@nginx.com 2381624Smax.romanov@nginx.com 2391681Smax.romanov@nginx.com int 2401681Smax.romanov@nginx.com nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx) 2411624Smax.romanov@nginx.com { 2421624Smax.romanov@nginx.com PyObject *msg, *future, *res; 2431624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan; 2441681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 2451624Smax.romanov@nginx.com 2461681Smax.romanov@nginx.com ctx_data = ctx->data; 2471681Smax.romanov@nginx.com 2481681Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) ctx_data->lifespan; 2491681Smax.romanov@nginx.com 2501681Smax.romanov@nginx.com if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) { 2511681Smax.romanov@nginx.com return NXT_UNIT_OK; 2521624Smax.romanov@nginx.com } 2531624Smax.romanov@nginx.com 2541624Smax.romanov@nginx.com lifespan->shutdown_called = 1; 2551624Smax.romanov@nginx.com 2561624Smax.romanov@nginx.com if (lifespan->receive_future != NULL) { 2571624Smax.romanov@nginx.com future = lifespan->receive_future; 2581624Smax.romanov@nginx.com lifespan->receive_future = NULL; 2591624Smax.romanov@nginx.com 2601624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str); 2611624Smax.romanov@nginx.com 2621624Smax.romanov@nginx.com if (nxt_fast_path(msg != NULL)) { 2631624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, 2641624Smax.romanov@nginx.com msg, NULL); 2651624Smax.romanov@nginx.com Py_XDECREF(res); 2661624Smax.romanov@nginx.com Py_DECREF(msg); 2671624Smax.romanov@nginx.com } 2681624Smax.romanov@nginx.com 2691624Smax.romanov@nginx.com Py_DECREF(future); 2701624Smax.romanov@nginx.com } 2711624Smax.romanov@nginx.com 2721624Smax.romanov@nginx.com if (lifespan->shutdown_sent) { 2731681Smax.romanov@nginx.com return NXT_UNIT_OK; 2741624Smax.romanov@nginx.com } 2751624Smax.romanov@nginx.com 2761681Smax.romanov@nginx.com lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future, 2771624Smax.romanov@nginx.com NULL); 2781624Smax.romanov@nginx.com if (nxt_slow_path(lifespan->shutdown_future == NULL)) { 2791624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object"); 2801624Smax.romanov@nginx.com nxt_python_print_exception(); 2811681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2821624Smax.romanov@nginx.com } 2831624Smax.romanov@nginx.com 2841681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 2851624Smax.romanov@nginx.com lifespan->shutdown_future, NULL); 2861624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 2871624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete"); 2881624Smax.romanov@nginx.com nxt_python_print_exception(); 2891681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 2901624Smax.romanov@nginx.com } 2911624Smax.romanov@nginx.com 2921624Smax.romanov@nginx.com Py_DECREF(res); 2931624Smax.romanov@nginx.com Py_CLEAR(lifespan->shutdown_future); 2941624Smax.romanov@nginx.com 2951681Smax.romanov@nginx.com return NXT_UNIT_OK; 2961624Smax.romanov@nginx.com } 2971624Smax.romanov@nginx.com 2981624Smax.romanov@nginx.com 2991624Smax.romanov@nginx.com static PyObject * 3001624Smax.romanov@nginx.com nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none) 3011624Smax.romanov@nginx.com { 3021624Smax.romanov@nginx.com PyObject *msg, *future; 3031624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan; 3041681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data; 3051624Smax.romanov@nginx.com 3061624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self; 3071681Smax.romanov@nginx.com ctx_data = lifespan->ctx_data; 3081624Smax.romanov@nginx.com 3091624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_receive"); 3101624Smax.romanov@nginx.com 3111681Smax.romanov@nginx.com future = PyObject_CallObject(ctx_data->loop_create_future, NULL); 3121624Smax.romanov@nginx.com if (nxt_slow_path(future == NULL)) { 3131624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object"); 3141624Smax.romanov@nginx.com nxt_python_print_exception(); 3151624Smax.romanov@nginx.com 3161624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 3171624Smax.romanov@nginx.com "failed to create Future object"); 3181624Smax.romanov@nginx.com } 3191624Smax.romanov@nginx.com 3201624Smax.romanov@nginx.com if (!lifespan->startup_received) { 3211624Smax.romanov@nginx.com lifespan->startup_received = 1; 3221624Smax.romanov@nginx.com 3231624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str); 3241624Smax.romanov@nginx.com 3251681Smax.romanov@nginx.com return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg); 3261624Smax.romanov@nginx.com } 3271624Smax.romanov@nginx.com 3281624Smax.romanov@nginx.com if (lifespan->shutdown_called && !lifespan->shutdown_received) { 3291624Smax.romanov@nginx.com lifespan->shutdown_received = 1; 3301624Smax.romanov@nginx.com 3311624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str); 3321624Smax.romanov@nginx.com 3331681Smax.romanov@nginx.com return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg); 3341624Smax.romanov@nginx.com } 3351624Smax.romanov@nginx.com 3361624Smax.romanov@nginx.com Py_INCREF(future); 3371624Smax.romanov@nginx.com lifespan->receive_future = future; 3381624Smax.romanov@nginx.com 3391624Smax.romanov@nginx.com return future; 3401624Smax.romanov@nginx.com } 3411624Smax.romanov@nginx.com 3421624Smax.romanov@nginx.com 3431624Smax.romanov@nginx.com static PyObject * 3441624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict) 3451624Smax.romanov@nginx.com { 3461624Smax.romanov@nginx.com PyObject *type, *msg; 3471624Smax.romanov@nginx.com const char *type_str; 3481624Smax.romanov@nginx.com Py_ssize_t type_len; 3491624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan; 3501624Smax.romanov@nginx.com 3511624Smax.romanov@nginx.com static const nxt_str_t startup_complete 3521624Smax.romanov@nginx.com = nxt_string("lifespan.startup.complete"); 3531624Smax.romanov@nginx.com static const nxt_str_t startup_failed 3541624Smax.romanov@nginx.com = nxt_string("lifespan.startup.failed"); 3551624Smax.romanov@nginx.com static const nxt_str_t shutdown_complete 3561624Smax.romanov@nginx.com = nxt_string("lifespan.shutdown.complete"); 3571624Smax.romanov@nginx.com static const nxt_str_t shutdown_failed 3581624Smax.romanov@nginx.com = nxt_string("lifespan.shutdown.failed"); 3591624Smax.romanov@nginx.com 3601624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self; 3611624Smax.romanov@nginx.com 3621624Smax.romanov@nginx.com type = PyDict_GetItem(dict, nxt_py_type_str); 3631624Smax.romanov@nginx.com if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) { 3641624Smax.romanov@nginx.com nxt_unit_error(NULL, 3651624Smax.romanov@nginx.com "asgi_lifespan_send: 'type' is not a unicode string"); 3661624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, 3671624Smax.romanov@nginx.com "'type' is not a unicode string"); 3681624Smax.romanov@nginx.com } 3691624Smax.romanov@nginx.com 3701624Smax.romanov@nginx.com type_str = PyUnicode_AsUTF8AndSize(type, &type_len); 3711624Smax.romanov@nginx.com 3721624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_send type is '%.*s'", 3731624Smax.romanov@nginx.com (int) type_len, type_str); 3741624Smax.romanov@nginx.com 3751624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) startup_complete.length 3761624Smax.romanov@nginx.com && memcmp(type_str, startup_complete.start, type_len) == 0) 3771624Smax.romanov@nginx.com { 3781624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_startup(lifespan, 0, NULL); 3791624Smax.romanov@nginx.com } 3801624Smax.romanov@nginx.com 3811624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) startup_failed.length 3821624Smax.romanov@nginx.com && memcmp(type_str, startup_failed.start, type_len) == 0) 3831624Smax.romanov@nginx.com { 3841624Smax.romanov@nginx.com msg = PyDict_GetItem(dict, nxt_py_message_str); 3851624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_startup(lifespan, 1, msg); 3861624Smax.romanov@nginx.com } 3871624Smax.romanov@nginx.com 3881624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) shutdown_complete.length 3891624Smax.romanov@nginx.com && memcmp(type_str, shutdown_complete.start, type_len) == 0) 3901624Smax.romanov@nginx.com { 3911624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_shutdown(lifespan, 0, NULL); 3921624Smax.romanov@nginx.com } 3931624Smax.romanov@nginx.com 3941624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) shutdown_failed.length 3951624Smax.romanov@nginx.com && memcmp(type_str, shutdown_failed.start, type_len) == 0) 3961624Smax.romanov@nginx.com { 3971624Smax.romanov@nginx.com msg = PyDict_GetItem(dict, nxt_py_message_str); 3981624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_shutdown(lifespan, 1, msg); 3991624Smax.romanov@nginx.com } 4001624Smax.romanov@nginx.com 4011624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan); 4021624Smax.romanov@nginx.com } 4031624Smax.romanov@nginx.com 4041624Smax.romanov@nginx.com 4051624Smax.romanov@nginx.com static PyObject * 4061624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t *lifespan, int v, 4071624Smax.romanov@nginx.com PyObject *message) 4081624Smax.romanov@nginx.com { 4091624Smax.romanov@nginx.com const char *message_str; 4101624Smax.romanov@nginx.com Py_ssize_t message_len; 4111624Smax.romanov@nginx.com 4121624Smax.romanov@nginx.com if (nxt_slow_path(v != 0)) { 4131624Smax.romanov@nginx.com nxt_unit_error(NULL, "Application startup failed"); 4141624Smax.romanov@nginx.com 4151624Smax.romanov@nginx.com if (nxt_fast_path(message != NULL && PyUnicode_Check(message))) { 4161624Smax.romanov@nginx.com message_str = PyUnicode_AsUTF8AndSize(message, &message_len); 4171624Smax.romanov@nginx.com 4181624Smax.romanov@nginx.com nxt_unit_error(NULL, "%.*s", (int) message_len, message_str); 4191624Smax.romanov@nginx.com } 4201624Smax.romanov@nginx.com } 4211624Smax.romanov@nginx.com 4221624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_(lifespan, v, 4231624Smax.romanov@nginx.com &lifespan->startup_sent, 4241624Smax.romanov@nginx.com &lifespan->startup_future); 4251624Smax.romanov@nginx.com } 4261624Smax.romanov@nginx.com 4271624Smax.romanov@nginx.com 4281624Smax.romanov@nginx.com static PyObject * 4291624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan, int v, int *sent, 4301624Smax.romanov@nginx.com PyObject **pfuture) 4311624Smax.romanov@nginx.com { 4321624Smax.romanov@nginx.com PyObject *future, *res; 4331624Smax.romanov@nginx.com 4341624Smax.romanov@nginx.com if (*sent) { 4351624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan); 4361624Smax.romanov@nginx.com } 4371624Smax.romanov@nginx.com 4381624Smax.romanov@nginx.com *sent = 1 + v; 4391624Smax.romanov@nginx.com 4401624Smax.romanov@nginx.com if (*pfuture != NULL) { 4411624Smax.romanov@nginx.com future = *pfuture; 4421624Smax.romanov@nginx.com *pfuture = NULL; 4431624Smax.romanov@nginx.com 4441624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, 4451624Smax.romanov@nginx.com Py_None, NULL); 4461624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 4471624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'"); 4481624Smax.romanov@nginx.com nxt_python_print_exception(); 4491624Smax.romanov@nginx.com 4501624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan); 4511624Smax.romanov@nginx.com } 4521624Smax.romanov@nginx.com 4531624Smax.romanov@nginx.com Py_DECREF(res); 4541624Smax.romanov@nginx.com Py_DECREF(future); 4551624Smax.romanov@nginx.com } 4561624Smax.romanov@nginx.com 4571624Smax.romanov@nginx.com Py_INCREF(lifespan); 4581624Smax.romanov@nginx.com 4591624Smax.romanov@nginx.com return (PyObject *) lifespan; 4601624Smax.romanov@nginx.com } 4611624Smax.romanov@nginx.com 4621624Smax.romanov@nginx.com 4631624Smax.romanov@nginx.com static PyObject * 4641624Smax.romanov@nginx.com nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan) 4651624Smax.romanov@nginx.com { 4661624Smax.romanov@nginx.com nxt_unit_warn(NULL, "Got invalid state transition on lifespan protocol"); 4671624Smax.romanov@nginx.com 4681624Smax.romanov@nginx.com lifespan->disabled = 1; 4691624Smax.romanov@nginx.com 4701624Smax.romanov@nginx.com return PyErr_Format(PyExc_AssertionError, 4711624Smax.romanov@nginx.com "Got invalid state transition on lifespan protocol"); 4721624Smax.romanov@nginx.com } 4731624Smax.romanov@nginx.com 4741624Smax.romanov@nginx.com 4751624Smax.romanov@nginx.com static PyObject * 4761624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t *lifespan, int v, 4771624Smax.romanov@nginx.com PyObject *message) 4781624Smax.romanov@nginx.com { 4791624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_(lifespan, v, 4801624Smax.romanov@nginx.com &lifespan->shutdown_sent, 4811624Smax.romanov@nginx.com &lifespan->shutdown_future); 4821624Smax.romanov@nginx.com } 4831624Smax.romanov@nginx.com 4841624Smax.romanov@nginx.com 4851624Smax.romanov@nginx.com static PyObject * 4861624Smax.romanov@nginx.com nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future) 4871624Smax.romanov@nginx.com { 4881624Smax.romanov@nginx.com PyObject *res; 4891624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan; 4901624Smax.romanov@nginx.com 4911624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_done"); 4921624Smax.romanov@nginx.com 4931624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self; 4941624Smax.romanov@nginx.com 4951624Smax.romanov@nginx.com if (lifespan->startup_sent == 0) { 4961624Smax.romanov@nginx.com lifespan->disabled = 1; 4971624Smax.romanov@nginx.com } 4981624Smax.romanov@nginx.com 4991624Smax.romanov@nginx.com /* 5001624Smax.romanov@nginx.com * Get Future.result() and it raises an exception, if coroutine exited 5011624Smax.romanov@nginx.com * with exception. 5021624Smax.romanov@nginx.com */ 5031624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL); 5041624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5051624Smax.romanov@nginx.com nxt_unit_log(NULL, NXT_UNIT_LOG_INFO, 5061624Smax.romanov@nginx.com "ASGI Lifespan processing exception"); 5071624Smax.romanov@nginx.com nxt_python_print_exception(); 5081624Smax.romanov@nginx.com } 5091624Smax.romanov@nginx.com 5101624Smax.romanov@nginx.com Py_XDECREF(res); 5111624Smax.romanov@nginx.com 5121624Smax.romanov@nginx.com if (lifespan->startup_future != NULL) { 5131624Smax.romanov@nginx.com future = lifespan->startup_future; 5141624Smax.romanov@nginx.com lifespan->startup_future = NULL; 5151624Smax.romanov@nginx.com 5161624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, 5171624Smax.romanov@nginx.com Py_None, NULL); 5181624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5191624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'"); 5201624Smax.romanov@nginx.com nxt_python_print_exception(); 5211624Smax.romanov@nginx.com } 5221624Smax.romanov@nginx.com 5231624Smax.romanov@nginx.com Py_XDECREF(res); 5241624Smax.romanov@nginx.com Py_DECREF(future); 5251624Smax.romanov@nginx.com } 5261624Smax.romanov@nginx.com 5271624Smax.romanov@nginx.com if (lifespan->shutdown_future != NULL) { 5281624Smax.romanov@nginx.com future = lifespan->shutdown_future; 5291624Smax.romanov@nginx.com lifespan->shutdown_future = NULL; 5301624Smax.romanov@nginx.com 5311624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, 5321624Smax.romanov@nginx.com Py_None, NULL); 5331624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 5341624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'"); 5351624Smax.romanov@nginx.com nxt_python_print_exception(); 5361624Smax.romanov@nginx.com } 5371624Smax.romanov@nginx.com 5381624Smax.romanov@nginx.com Py_XDECREF(res); 5391624Smax.romanov@nginx.com Py_DECREF(future); 5401624Smax.romanov@nginx.com } 5411624Smax.romanov@nginx.com 5421624Smax.romanov@nginx.com Py_RETURN_NONE; 5431624Smax.romanov@nginx.com } 5441624Smax.romanov@nginx.com 5451624Smax.romanov@nginx.com 5461624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */ 547