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
30*1872So.canty@f5.com static PyObject *nxt_py_asgi_lifespan_target_startup(
31*1872So.canty@f5.com nxt_py_asgi_ctx_data_t *ctx_data, nxt_python_target_t *target);
32*1872So.canty@f5.com static int nxt_py_asgi_lifespan_target_shutdown(
33*1872So.canty@f5.com nxt_py_asgi_lifespan_t *lifespan);
341624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
351624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
361624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_startup(
371624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
381624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan,
391624Smax.romanov@nginx.com int v, int *sent, PyObject **future);
401624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_shutdown(
411624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
421624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
431624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
441624Smax.romanov@nginx.com
451624Smax.romanov@nginx.com
461624Smax.romanov@nginx.com static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
471624Smax.romanov@nginx.com { "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
481624Smax.romanov@nginx.com { "send", nxt_py_asgi_lifespan_send, METH_O, 0 },
491624Smax.romanov@nginx.com { "_done", nxt_py_asgi_lifespan_done, METH_O, 0 },
501624Smax.romanov@nginx.com { NULL, NULL, 0, 0 }
511624Smax.romanov@nginx.com };
521624Smax.romanov@nginx.com
531624Smax.romanov@nginx.com static PyAsyncMethods nxt_py_asgi_async_methods = {
541624Smax.romanov@nginx.com .am_await = nxt_py_asgi_await,
551624Smax.romanov@nginx.com };
561624Smax.romanov@nginx.com
571624Smax.romanov@nginx.com static PyTypeObject nxt_py_asgi_lifespan_type = {
581624Smax.romanov@nginx.com PyVarObject_HEAD_INIT(NULL, 0)
591624Smax.romanov@nginx.com
601624Smax.romanov@nginx.com .tp_name = "unit._asgi_lifespan",
611624Smax.romanov@nginx.com .tp_basicsize = sizeof(nxt_py_asgi_lifespan_t),
621624Smax.romanov@nginx.com .tp_dealloc = nxt_py_asgi_dealloc,
631624Smax.romanov@nginx.com .tp_as_async = &nxt_py_asgi_async_methods,
641624Smax.romanov@nginx.com .tp_flags = Py_TPFLAGS_DEFAULT,
651624Smax.romanov@nginx.com .tp_doc = "unit ASGI Lifespan object",
661624Smax.romanov@nginx.com .tp_iter = nxt_py_asgi_iter,
671624Smax.romanov@nginx.com .tp_iternext = nxt_py_asgi_next,
681624Smax.romanov@nginx.com .tp_methods = nxt_py_asgi_lifespan_methods,
691624Smax.romanov@nginx.com };
701624Smax.romanov@nginx.com
711624Smax.romanov@nginx.com
721681Smax.romanov@nginx.com int
nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t * ctx_data)731681Smax.romanov@nginx.com nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
741624Smax.romanov@nginx.com {
75*1872So.canty@f5.com size_t size;
76*1872So.canty@f5.com PyObject *lifespan;
77*1872So.canty@f5.com PyObject **target_lifespans;
78*1872So.canty@f5.com nxt_int_t i;
79*1872So.canty@f5.com nxt_python_target_t *target;
80*1872So.canty@f5.com
81*1872So.canty@f5.com size = nxt_py_targets->count * sizeof(PyObject*);
82*1872So.canty@f5.com
83*1872So.canty@f5.com target_lifespans = nxt_unit_malloc(NULL, size);
84*1872So.canty@f5.com if (nxt_slow_path(target_lifespans == NULL)) {
85*1872So.canty@f5.com nxt_unit_alert(NULL, "Failed to allocate lifespan data");
86*1872So.canty@f5.com return NXT_UNIT_ERROR;
87*1872So.canty@f5.com }
88*1872So.canty@f5.com
89*1872So.canty@f5.com memset(target_lifespans, 0, size);
90*1872So.canty@f5.com
91*1872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) {
92*1872So.canty@f5.com target = &nxt_py_targets->target[i];
93*1872So.canty@f5.com
94*1872So.canty@f5.com lifespan = nxt_py_asgi_lifespan_target_startup(ctx_data, target);
95*1872So.canty@f5.com if (nxt_slow_path(lifespan == NULL)) {
96*1872So.canty@f5.com return NXT_UNIT_ERROR;
97*1872So.canty@f5.com }
98*1872So.canty@f5.com
99*1872So.canty@f5.com target_lifespans[i] = lifespan;
100*1872So.canty@f5.com }
101*1872So.canty@f5.com
102*1872So.canty@f5.com ctx_data->target_lifespans = target_lifespans;
103*1872So.canty@f5.com
104*1872So.canty@f5.com return NXT_UNIT_OK;
105*1872So.canty@f5.com }
106*1872So.canty@f5.com
107*1872So.canty@f5.com
108*1872So.canty@f5.com static PyObject *
nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t * ctx_data,nxt_python_target_t * target)109*1872So.canty@f5.com nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data,
110*1872So.canty@f5.com nxt_python_target_t *target)
111*1872So.canty@f5.com {
1121624Smax.romanov@nginx.com PyObject *scope, *res, *py_task, *receive, *send, *done;
1131697Smax.romanov@nginx.com PyObject *stage2;
114*1872So.canty@f5.com nxt_py_asgi_lifespan_t *lifespan, *ret;
1151624Smax.romanov@nginx.com
1161624Smax.romanov@nginx.com if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
1171681Smax.romanov@nginx.com nxt_unit_alert(NULL,
1181624Smax.romanov@nginx.com "Python failed to initialize the 'asgi_lifespan' type object");
119*1872So.canty@f5.com return NULL;
1201624Smax.romanov@nginx.com }
1211624Smax.romanov@nginx.com
1221624Smax.romanov@nginx.com lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
1231624Smax.romanov@nginx.com if (nxt_slow_path(lifespan == NULL)) {
1241681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create lifespan object");
125*1872So.canty@f5.com return NULL;
1261624Smax.romanov@nginx.com }
1271624Smax.romanov@nginx.com
128*1872So.canty@f5.com ret = NULL;
1291624Smax.romanov@nginx.com
1301624Smax.romanov@nginx.com receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
1311624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) {
1321681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'receive' method");
1331624Smax.romanov@nginx.com goto release_lifespan;
1341624Smax.romanov@nginx.com }
1351624Smax.romanov@nginx.com
1361624Smax.romanov@nginx.com send = PyObject_GetAttrString((PyObject *) lifespan, "send");
1371624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) {
1381681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get 'send' method");
1391624Smax.romanov@nginx.com goto release_receive;
1401624Smax.romanov@nginx.com }
1411624Smax.romanov@nginx.com
1421624Smax.romanov@nginx.com done = PyObject_GetAttrString((PyObject *) lifespan, "_done");
1431624Smax.romanov@nginx.com if (nxt_slow_path(receive == NULL)) {
1441681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to get '_done' method");
1451624Smax.romanov@nginx.com goto release_send;
1461624Smax.romanov@nginx.com }
1471624Smax.romanov@nginx.com
1481681Smax.romanov@nginx.com lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future,
1491624Smax.romanov@nginx.com NULL);
1501624Smax.romanov@nginx.com if (nxt_slow_path(lifespan->startup_future == NULL)) {
1511624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object");
1521624Smax.romanov@nginx.com nxt_python_print_exception();
1531624Smax.romanov@nginx.com
1541624Smax.romanov@nginx.com goto release_done;
1551624Smax.romanov@nginx.com }
1561624Smax.romanov@nginx.com
1571681Smax.romanov@nginx.com lifespan->ctx_data = ctx_data;
1581624Smax.romanov@nginx.com lifespan->disabled = 0;
1591624Smax.romanov@nginx.com lifespan->startup_received = 0;
1601624Smax.romanov@nginx.com lifespan->startup_sent = 0;
1611624Smax.romanov@nginx.com lifespan->shutdown_received = 0;
1621624Smax.romanov@nginx.com lifespan->shutdown_sent = 0;
1631624Smax.romanov@nginx.com lifespan->shutdown_called = 0;
1641624Smax.romanov@nginx.com lifespan->shutdown_future = NULL;
1651624Smax.romanov@nginx.com lifespan->receive_future = NULL;
1661624Smax.romanov@nginx.com
1671624Smax.romanov@nginx.com scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str);
1681624Smax.romanov@nginx.com if (nxt_slow_path(scope == NULL)) {
1691624Smax.romanov@nginx.com goto release_future;
1701624Smax.romanov@nginx.com }
1711624Smax.romanov@nginx.com
172*1872So.canty@f5.com if (!target->asgi_legacy) {
1731697Smax.romanov@nginx.com nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
1741697Smax.romanov@nginx.com
175*1872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application,
1761697Smax.romanov@nginx.com scope, receive, send, NULL);
1771697Smax.romanov@nginx.com
1781697Smax.romanov@nginx.com } else {
1791697Smax.romanov@nginx.com nxt_unit_req_debug(NULL, "Python call legacy application");
1801697Smax.romanov@nginx.com
181*1872So.canty@f5.com res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
1821697Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
1831697Smax.romanov@nginx.com nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
1841697Smax.romanov@nginx.com "ASGI Lifespan processing exception");
1851697Smax.romanov@nginx.com nxt_python_print_exception();
1861697Smax.romanov@nginx.com
1871697Smax.romanov@nginx.com lifespan->disabled = 1;
188*1872So.canty@f5.com
189*1872So.canty@f5.com Py_INCREF(lifespan);
190*1872So.canty@f5.com ret = lifespan;
1911697Smax.romanov@nginx.com
1921697Smax.romanov@nginx.com goto release_scope;
1931697Smax.romanov@nginx.com }
1941697Smax.romanov@nginx.com
1951697Smax.romanov@nginx.com if (nxt_slow_path(PyCallable_Check(res) == 0)) {
1961697Smax.romanov@nginx.com nxt_unit_req_error(NULL,
1971697Smax.romanov@nginx.com "Legacy ASGI application returns not a callable");
1981697Smax.romanov@nginx.com
1991697Smax.romanov@nginx.com Py_DECREF(res);
2001697Smax.romanov@nginx.com
2011697Smax.romanov@nginx.com goto release_scope;
2021697Smax.romanov@nginx.com }
2031697Smax.romanov@nginx.com
2041697Smax.romanov@nginx.com stage2 = res;
2051697Smax.romanov@nginx.com
2061697Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
2071697Smax.romanov@nginx.com
2081697Smax.romanov@nginx.com Py_DECREF(stage2);
2091697Smax.romanov@nginx.com }
2101697Smax.romanov@nginx.com
2111624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
2121681Smax.romanov@nginx.com nxt_unit_error(NULL, "Python failed to call the application");
2131624Smax.romanov@nginx.com nxt_python_print_exception();
2141624Smax.romanov@nginx.com goto release_scope;
2151624Smax.romanov@nginx.com }
2161624Smax.romanov@nginx.com
2171624Smax.romanov@nginx.com if (nxt_slow_path(!PyCoro_CheckExact(res))) {
2181681Smax.romanov@nginx.com nxt_unit_error(NULL, "Application result type is not a coroutine");
2191624Smax.romanov@nginx.com Py_DECREF(res);
2201624Smax.romanov@nginx.com goto release_scope;
2211624Smax.romanov@nginx.com }
2221624Smax.romanov@nginx.com
2231697Smax.romanov@nginx.com py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res,
2241697Smax.romanov@nginx.com NULL);
2251624Smax.romanov@nginx.com if (nxt_slow_path(py_task == NULL)) {
2261681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call the create_task");
2271624Smax.romanov@nginx.com nxt_python_print_exception();
2281624Smax.romanov@nginx.com Py_DECREF(res);
2291624Smax.romanov@nginx.com goto release_scope;
2301624Smax.romanov@nginx.com }
2311624Smax.romanov@nginx.com
2321624Smax.romanov@nginx.com Py_DECREF(res);
2331624Smax.romanov@nginx.com
2341624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str,
2351624Smax.romanov@nginx.com done, NULL);
2361624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
2371681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'");
2381624Smax.romanov@nginx.com nxt_python_print_exception();
2391624Smax.romanov@nginx.com goto release_task;
2401624Smax.romanov@nginx.com }
2411624Smax.romanov@nginx.com
2421624Smax.romanov@nginx.com Py_DECREF(res);
2431624Smax.romanov@nginx.com
2441681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
2451624Smax.romanov@nginx.com lifespan->startup_future, NULL);
2461624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
2471681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
2481624Smax.romanov@nginx.com nxt_python_print_exception();
2491624Smax.romanov@nginx.com goto release_task;
2501624Smax.romanov@nginx.com }
2511624Smax.romanov@nginx.com
2521624Smax.romanov@nginx.com Py_DECREF(res);
2531624Smax.romanov@nginx.com
2541624Smax.romanov@nginx.com if (lifespan->startup_sent == 1 || lifespan->disabled) {
255*1872So.canty@f5.com Py_INCREF(lifespan);
2561624Smax.romanov@nginx.com
257*1872So.canty@f5.com ret = lifespan;
2581624Smax.romanov@nginx.com }
2591624Smax.romanov@nginx.com
2601624Smax.romanov@nginx.com release_task:
2611624Smax.romanov@nginx.com Py_DECREF(py_task);
2621624Smax.romanov@nginx.com release_scope:
2631624Smax.romanov@nginx.com Py_DECREF(scope);
2641624Smax.romanov@nginx.com release_future:
2651624Smax.romanov@nginx.com Py_CLEAR(lifespan->startup_future);
2661624Smax.romanov@nginx.com release_done:
2671624Smax.romanov@nginx.com Py_DECREF(done);
2681624Smax.romanov@nginx.com release_send:
2691624Smax.romanov@nginx.com Py_DECREF(send);
2701624Smax.romanov@nginx.com release_receive:
2711624Smax.romanov@nginx.com Py_DECREF(receive);
2721624Smax.romanov@nginx.com release_lifespan:
2731624Smax.romanov@nginx.com Py_DECREF(lifespan);
2741624Smax.romanov@nginx.com
275*1872So.canty@f5.com return (PyObject *) ret;
2761624Smax.romanov@nginx.com }
2771624Smax.romanov@nginx.com
2781624Smax.romanov@nginx.com
2791681Smax.romanov@nginx.com int
nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t * ctx)2801681Smax.romanov@nginx.com nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
2811624Smax.romanov@nginx.com {
282*1872So.canty@f5.com nxt_int_t i, ret;
2831624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan;
2841681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data;
2851624Smax.romanov@nginx.com
2861681Smax.romanov@nginx.com ctx_data = ctx->data;
2871681Smax.romanov@nginx.com
288*1872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) {
289*1872So.canty@f5.com lifespan = (nxt_py_asgi_lifespan_t *)ctx_data->target_lifespans[i];
290*1872So.canty@f5.com
291*1872So.canty@f5.com ret = nxt_py_asgi_lifespan_target_shutdown(lifespan);
292*1872So.canty@f5.com if (nxt_slow_path(ret != NXT_UNIT_OK)) {
293*1872So.canty@f5.com return NXT_UNIT_ERROR;
294*1872So.canty@f5.com }
295*1872So.canty@f5.com }
296*1872So.canty@f5.com
297*1872So.canty@f5.com nxt_unit_free(NULL, ctx_data->target_lifespans);
298*1872So.canty@f5.com
299*1872So.canty@f5.com return NXT_UNIT_OK;
300*1872So.canty@f5.com }
301*1872So.canty@f5.com
302*1872So.canty@f5.com
303*1872So.canty@f5.com static int
nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t * lifespan)304*1872So.canty@f5.com nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t *lifespan)
305*1872So.canty@f5.com {
306*1872So.canty@f5.com PyObject *msg, *future, *res;
307*1872So.canty@f5.com nxt_py_asgi_ctx_data_t *ctx_data;
308*1872So.canty@f5.com
309*1872So.canty@f5.com ctx_data = lifespan->ctx_data;
3101681Smax.romanov@nginx.com
3111681Smax.romanov@nginx.com if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
3121681Smax.romanov@nginx.com return NXT_UNIT_OK;
3131624Smax.romanov@nginx.com }
3141624Smax.romanov@nginx.com
3151624Smax.romanov@nginx.com lifespan->shutdown_called = 1;
3161624Smax.romanov@nginx.com
3171624Smax.romanov@nginx.com if (lifespan->receive_future != NULL) {
3181624Smax.romanov@nginx.com future = lifespan->receive_future;
3191624Smax.romanov@nginx.com lifespan->receive_future = NULL;
3201624Smax.romanov@nginx.com
3211624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
3221624Smax.romanov@nginx.com
3231624Smax.romanov@nginx.com if (nxt_fast_path(msg != NULL)) {
3241624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
3251624Smax.romanov@nginx.com msg, NULL);
3261624Smax.romanov@nginx.com Py_XDECREF(res);
3271624Smax.romanov@nginx.com Py_DECREF(msg);
3281624Smax.romanov@nginx.com }
3291624Smax.romanov@nginx.com
3301624Smax.romanov@nginx.com Py_DECREF(future);
3311624Smax.romanov@nginx.com }
3321624Smax.romanov@nginx.com
3331624Smax.romanov@nginx.com if (lifespan->shutdown_sent) {
3341681Smax.romanov@nginx.com return NXT_UNIT_OK;
3351624Smax.romanov@nginx.com }
3361624Smax.romanov@nginx.com
3371681Smax.romanov@nginx.com lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future,
3381624Smax.romanov@nginx.com NULL);
3391624Smax.romanov@nginx.com if (nxt_slow_path(lifespan->shutdown_future == NULL)) {
3401624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object");
3411624Smax.romanov@nginx.com nxt_python_print_exception();
3421681Smax.romanov@nginx.com return NXT_UNIT_ERROR;
3431624Smax.romanov@nginx.com }
3441624Smax.romanov@nginx.com
3451681Smax.romanov@nginx.com res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
3461624Smax.romanov@nginx.com lifespan->shutdown_future, NULL);
3471624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
3481624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
3491624Smax.romanov@nginx.com nxt_python_print_exception();
3501681Smax.romanov@nginx.com return NXT_UNIT_ERROR;
3511624Smax.romanov@nginx.com }
3521624Smax.romanov@nginx.com
3531624Smax.romanov@nginx.com Py_DECREF(res);
3541624Smax.romanov@nginx.com Py_CLEAR(lifespan->shutdown_future);
3551624Smax.romanov@nginx.com
3561681Smax.romanov@nginx.com return NXT_UNIT_OK;
3571624Smax.romanov@nginx.com }
3581624Smax.romanov@nginx.com
3591624Smax.romanov@nginx.com
3601624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_receive(PyObject * self,PyObject * none)3611624Smax.romanov@nginx.com nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
3621624Smax.romanov@nginx.com {
3631624Smax.romanov@nginx.com PyObject *msg, *future;
3641624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan;
3651681Smax.romanov@nginx.com nxt_py_asgi_ctx_data_t *ctx_data;
3661624Smax.romanov@nginx.com
3671624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self;
3681681Smax.romanov@nginx.com ctx_data = lifespan->ctx_data;
3691624Smax.romanov@nginx.com
3701624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_receive");
3711624Smax.romanov@nginx.com
3721681Smax.romanov@nginx.com future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
3731624Smax.romanov@nginx.com if (nxt_slow_path(future == NULL)) {
3741624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Python failed to create Future object");
3751624Smax.romanov@nginx.com nxt_python_print_exception();
3761624Smax.romanov@nginx.com
3771624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError,
3781624Smax.romanov@nginx.com "failed to create Future object");
3791624Smax.romanov@nginx.com }
3801624Smax.romanov@nginx.com
3811624Smax.romanov@nginx.com if (!lifespan->startup_received) {
3821624Smax.romanov@nginx.com lifespan->startup_received = 1;
3831624Smax.romanov@nginx.com
3841624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str);
3851624Smax.romanov@nginx.com
3861681Smax.romanov@nginx.com return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
3871624Smax.romanov@nginx.com }
3881624Smax.romanov@nginx.com
3891624Smax.romanov@nginx.com if (lifespan->shutdown_called && !lifespan->shutdown_received) {
3901624Smax.romanov@nginx.com lifespan->shutdown_received = 1;
3911624Smax.romanov@nginx.com
3921624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
3931624Smax.romanov@nginx.com
3941681Smax.romanov@nginx.com return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
3951624Smax.romanov@nginx.com }
3961624Smax.romanov@nginx.com
3971624Smax.romanov@nginx.com Py_INCREF(future);
3981624Smax.romanov@nginx.com lifespan->receive_future = future;
3991624Smax.romanov@nginx.com
4001624Smax.romanov@nginx.com return future;
4011624Smax.romanov@nginx.com }
4021624Smax.romanov@nginx.com
4031624Smax.romanov@nginx.com
4041624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send(PyObject * self,PyObject * dict)4051624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict)
4061624Smax.romanov@nginx.com {
4071624Smax.romanov@nginx.com PyObject *type, *msg;
4081624Smax.romanov@nginx.com const char *type_str;
4091624Smax.romanov@nginx.com Py_ssize_t type_len;
4101624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan;
4111624Smax.romanov@nginx.com
4121624Smax.romanov@nginx.com static const nxt_str_t startup_complete
4131624Smax.romanov@nginx.com = nxt_string("lifespan.startup.complete");
4141624Smax.romanov@nginx.com static const nxt_str_t startup_failed
4151624Smax.romanov@nginx.com = nxt_string("lifespan.startup.failed");
4161624Smax.romanov@nginx.com static const nxt_str_t shutdown_complete
4171624Smax.romanov@nginx.com = nxt_string("lifespan.shutdown.complete");
4181624Smax.romanov@nginx.com static const nxt_str_t shutdown_failed
4191624Smax.romanov@nginx.com = nxt_string("lifespan.shutdown.failed");
4201624Smax.romanov@nginx.com
4211624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self;
4221624Smax.romanov@nginx.com
4231624Smax.romanov@nginx.com type = PyDict_GetItem(dict, nxt_py_type_str);
4241624Smax.romanov@nginx.com if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) {
4251624Smax.romanov@nginx.com nxt_unit_error(NULL,
4261624Smax.romanov@nginx.com "asgi_lifespan_send: 'type' is not a unicode string");
4271624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError,
4281624Smax.romanov@nginx.com "'type' is not a unicode string");
4291624Smax.romanov@nginx.com }
4301624Smax.romanov@nginx.com
4311624Smax.romanov@nginx.com type_str = PyUnicode_AsUTF8AndSize(type, &type_len);
4321624Smax.romanov@nginx.com
4331624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_send type is '%.*s'",
4341624Smax.romanov@nginx.com (int) type_len, type_str);
4351624Smax.romanov@nginx.com
4361624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) startup_complete.length
4371624Smax.romanov@nginx.com && memcmp(type_str, startup_complete.start, type_len) == 0)
4381624Smax.romanov@nginx.com {
4391624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_startup(lifespan, 0, NULL);
4401624Smax.romanov@nginx.com }
4411624Smax.romanov@nginx.com
4421624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) startup_failed.length
4431624Smax.romanov@nginx.com && memcmp(type_str, startup_failed.start, type_len) == 0)
4441624Smax.romanov@nginx.com {
4451624Smax.romanov@nginx.com msg = PyDict_GetItem(dict, nxt_py_message_str);
4461624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_startup(lifespan, 1, msg);
4471624Smax.romanov@nginx.com }
4481624Smax.romanov@nginx.com
4491624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) shutdown_complete.length
4501624Smax.romanov@nginx.com && memcmp(type_str, shutdown_complete.start, type_len) == 0)
4511624Smax.romanov@nginx.com {
4521624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_shutdown(lifespan, 0, NULL);
4531624Smax.romanov@nginx.com }
4541624Smax.romanov@nginx.com
4551624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) shutdown_failed.length
4561624Smax.romanov@nginx.com && memcmp(type_str, shutdown_failed.start, type_len) == 0)
4571624Smax.romanov@nginx.com {
4581624Smax.romanov@nginx.com msg = PyDict_GetItem(dict, nxt_py_message_str);
4591624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_shutdown(lifespan, 1, msg);
4601624Smax.romanov@nginx.com }
4611624Smax.romanov@nginx.com
4621624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan);
4631624Smax.romanov@nginx.com }
4641624Smax.romanov@nginx.com
4651624Smax.romanov@nginx.com
4661624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t * lifespan,int v,PyObject * message)4671624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t *lifespan, int v,
4681624Smax.romanov@nginx.com PyObject *message)
4691624Smax.romanov@nginx.com {
4701624Smax.romanov@nginx.com const char *message_str;
4711624Smax.romanov@nginx.com Py_ssize_t message_len;
4721624Smax.romanov@nginx.com
4731624Smax.romanov@nginx.com if (nxt_slow_path(v != 0)) {
4741624Smax.romanov@nginx.com nxt_unit_error(NULL, "Application startup failed");
4751624Smax.romanov@nginx.com
4761624Smax.romanov@nginx.com if (nxt_fast_path(message != NULL && PyUnicode_Check(message))) {
4771624Smax.romanov@nginx.com message_str = PyUnicode_AsUTF8AndSize(message, &message_len);
4781624Smax.romanov@nginx.com
4791624Smax.romanov@nginx.com nxt_unit_error(NULL, "%.*s", (int) message_len, message_str);
4801624Smax.romanov@nginx.com }
4811624Smax.romanov@nginx.com }
4821624Smax.romanov@nginx.com
4831624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_(lifespan, v,
4841624Smax.romanov@nginx.com &lifespan->startup_sent,
4851624Smax.romanov@nginx.com &lifespan->startup_future);
4861624Smax.romanov@nginx.com }
4871624Smax.romanov@nginx.com
4881624Smax.romanov@nginx.com
4891624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t * lifespan,int v,int * sent,PyObject ** pfuture)4901624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan, int v, int *sent,
4911624Smax.romanov@nginx.com PyObject **pfuture)
4921624Smax.romanov@nginx.com {
4931624Smax.romanov@nginx.com PyObject *future, *res;
4941624Smax.romanov@nginx.com
4951624Smax.romanov@nginx.com if (*sent) {
4961624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan);
4971624Smax.romanov@nginx.com }
4981624Smax.romanov@nginx.com
4991624Smax.romanov@nginx.com *sent = 1 + v;
5001624Smax.romanov@nginx.com
5011624Smax.romanov@nginx.com if (*pfuture != NULL) {
5021624Smax.romanov@nginx.com future = *pfuture;
5031624Smax.romanov@nginx.com *pfuture = NULL;
5041624Smax.romanov@nginx.com
5051624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
5061624Smax.romanov@nginx.com Py_None, NULL);
5071624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
5081624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
5091624Smax.romanov@nginx.com nxt_python_print_exception();
5101624Smax.romanov@nginx.com
5111624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_disable(lifespan);
5121624Smax.romanov@nginx.com }
5131624Smax.romanov@nginx.com
5141624Smax.romanov@nginx.com Py_DECREF(res);
5151624Smax.romanov@nginx.com Py_DECREF(future);
5161624Smax.romanov@nginx.com }
5171624Smax.romanov@nginx.com
5181624Smax.romanov@nginx.com Py_INCREF(lifespan);
5191624Smax.romanov@nginx.com
5201624Smax.romanov@nginx.com return (PyObject *) lifespan;
5211624Smax.romanov@nginx.com }
5221624Smax.romanov@nginx.com
5231624Smax.romanov@nginx.com
5241624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t * lifespan)5251624Smax.romanov@nginx.com nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan)
5261624Smax.romanov@nginx.com {
5271624Smax.romanov@nginx.com nxt_unit_warn(NULL, "Got invalid state transition on lifespan protocol");
5281624Smax.romanov@nginx.com
5291624Smax.romanov@nginx.com lifespan->disabled = 1;
5301624Smax.romanov@nginx.com
5311624Smax.romanov@nginx.com return PyErr_Format(PyExc_AssertionError,
5321624Smax.romanov@nginx.com "Got invalid state transition on lifespan protocol");
5331624Smax.romanov@nginx.com }
5341624Smax.romanov@nginx.com
5351624Smax.romanov@nginx.com
5361624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t * lifespan,int v,PyObject * message)5371624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t *lifespan, int v,
5381624Smax.romanov@nginx.com PyObject *message)
5391624Smax.romanov@nginx.com {
5401624Smax.romanov@nginx.com return nxt_py_asgi_lifespan_send_(lifespan, v,
5411624Smax.romanov@nginx.com &lifespan->shutdown_sent,
5421624Smax.romanov@nginx.com &lifespan->shutdown_future);
5431624Smax.romanov@nginx.com }
5441624Smax.romanov@nginx.com
5451624Smax.romanov@nginx.com
5461624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_done(PyObject * self,PyObject * future)5471624Smax.romanov@nginx.com nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future)
5481624Smax.romanov@nginx.com {
5491624Smax.romanov@nginx.com PyObject *res;
5501624Smax.romanov@nginx.com nxt_py_asgi_lifespan_t *lifespan;
5511624Smax.romanov@nginx.com
5521624Smax.romanov@nginx.com nxt_unit_debug(NULL, "asgi_lifespan_done");
5531624Smax.romanov@nginx.com
5541624Smax.romanov@nginx.com lifespan = (nxt_py_asgi_lifespan_t *) self;
5551624Smax.romanov@nginx.com
5561624Smax.romanov@nginx.com if (lifespan->startup_sent == 0) {
5571624Smax.romanov@nginx.com lifespan->disabled = 1;
5581624Smax.romanov@nginx.com }
5591624Smax.romanov@nginx.com
5601624Smax.romanov@nginx.com /*
5611624Smax.romanov@nginx.com * Get Future.result() and it raises an exception, if coroutine exited
5621624Smax.romanov@nginx.com * with exception.
5631624Smax.romanov@nginx.com */
5641624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL);
5651624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
5661624Smax.romanov@nginx.com nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
5671624Smax.romanov@nginx.com "ASGI Lifespan processing exception");
5681624Smax.romanov@nginx.com nxt_python_print_exception();
5691624Smax.romanov@nginx.com }
5701624Smax.romanov@nginx.com
5711624Smax.romanov@nginx.com Py_XDECREF(res);
5721624Smax.romanov@nginx.com
5731624Smax.romanov@nginx.com if (lifespan->startup_future != NULL) {
5741624Smax.romanov@nginx.com future = lifespan->startup_future;
5751624Smax.romanov@nginx.com lifespan->startup_future = NULL;
5761624Smax.romanov@nginx.com
5771624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
5781624Smax.romanov@nginx.com Py_None, NULL);
5791624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
5801624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
5811624Smax.romanov@nginx.com nxt_python_print_exception();
5821624Smax.romanov@nginx.com }
5831624Smax.romanov@nginx.com
5841624Smax.romanov@nginx.com Py_XDECREF(res);
5851624Smax.romanov@nginx.com Py_DECREF(future);
5861624Smax.romanov@nginx.com }
5871624Smax.romanov@nginx.com
5881624Smax.romanov@nginx.com if (lifespan->shutdown_future != NULL) {
5891624Smax.romanov@nginx.com future = lifespan->shutdown_future;
5901624Smax.romanov@nginx.com lifespan->shutdown_future = NULL;
5911624Smax.romanov@nginx.com
5921624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
5931624Smax.romanov@nginx.com Py_None, NULL);
5941624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) {
5951624Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
5961624Smax.romanov@nginx.com nxt_python_print_exception();
5971624Smax.romanov@nginx.com }
5981624Smax.romanov@nginx.com
5991624Smax.romanov@nginx.com Py_XDECREF(res);
6001624Smax.romanov@nginx.com Py_DECREF(future);
6011624Smax.romanov@nginx.com }
6021624Smax.romanov@nginx.com
6031624Smax.romanov@nginx.com Py_RETURN_NONE;
6041624Smax.romanov@nginx.com }
6051624Smax.romanov@nginx.com
6061624Smax.romanov@nginx.com
6071624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */
608