xref: /unit/src/python/nxt_python_asgi_lifespan.c (revision 2483:9d1e9b09cc6f)
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 
15*2483Sdiguohuangjiajinweijun@gmail.com #include <structmember.h>
16*2483Sdiguohuangjiajinweijun@gmail.com 
171624Smax.romanov@nginx.com 
181624Smax.romanov@nginx.com typedef struct  {
191624Smax.romanov@nginx.com     PyObject_HEAD
201681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
211681Smax.romanov@nginx.com     int                     disabled;
221681Smax.romanov@nginx.com     int                     startup_received;
231681Smax.romanov@nginx.com     int                     startup_sent;
241681Smax.romanov@nginx.com     int                     shutdown_received;
251681Smax.romanov@nginx.com     int                     shutdown_sent;
261681Smax.romanov@nginx.com     int                     shutdown_called;
271681Smax.romanov@nginx.com     PyObject                *startup_future;
281681Smax.romanov@nginx.com     PyObject                *shutdown_future;
291681Smax.romanov@nginx.com     PyObject                *receive_future;
30*2483Sdiguohuangjiajinweijun@gmail.com     PyObject                *state;
311624Smax.romanov@nginx.com } nxt_py_asgi_lifespan_t;
321624Smax.romanov@nginx.com 
331872So.canty@f5.com static PyObject *nxt_py_asgi_lifespan_target_startup(
341872So.canty@f5.com     nxt_py_asgi_ctx_data_t *ctx_data, nxt_python_target_t *target);
351872So.canty@f5.com static int nxt_py_asgi_lifespan_target_shutdown(
361872So.canty@f5.com     nxt_py_asgi_lifespan_t *lifespan);
371624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
381624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
391624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_startup(
401624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
411624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan,
421624Smax.romanov@nginx.com     int v, int *sent, PyObject **future);
431624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_send_shutdown(
441624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
451624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
461624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
47*2483Sdiguohuangjiajinweijun@gmail.com static void nxt_py_asgi_lifespan_dealloc(PyObject *self);
481624Smax.romanov@nginx.com 
491624Smax.romanov@nginx.com 
501624Smax.romanov@nginx.com static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
511624Smax.romanov@nginx.com     { "receive",   nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
521624Smax.romanov@nginx.com     { "send",      nxt_py_asgi_lifespan_send,    METH_O,      0 },
531624Smax.romanov@nginx.com     { "_done",     nxt_py_asgi_lifespan_done,    METH_O,      0 },
541624Smax.romanov@nginx.com     { NULL, NULL, 0, 0 }
551624Smax.romanov@nginx.com };
561624Smax.romanov@nginx.com 
57*2483Sdiguohuangjiajinweijun@gmail.com static PyMemberDef nxt_py_asgi_lifespan_members[] = {
58*2483Sdiguohuangjiajinweijun@gmail.com     {
59*2483Sdiguohuangjiajinweijun@gmail.com #if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7)
60*2483Sdiguohuangjiajinweijun@gmail.com         .name   = "state",
61*2483Sdiguohuangjiajinweijun@gmail.com #else
62*2483Sdiguohuangjiajinweijun@gmail.com         .name   = (char *)"state",
63*2483Sdiguohuangjiajinweijun@gmail.com #endif
64*2483Sdiguohuangjiajinweijun@gmail.com         .type   = T_OBJECT_EX,
65*2483Sdiguohuangjiajinweijun@gmail.com         .offset = offsetof(nxt_py_asgi_lifespan_t, state),
66*2483Sdiguohuangjiajinweijun@gmail.com         .flags  = READONLY,
67*2483Sdiguohuangjiajinweijun@gmail.com #if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7)
68*2483Sdiguohuangjiajinweijun@gmail.com         .doc    = PyDoc_STR("lifespan.state")
69*2483Sdiguohuangjiajinweijun@gmail.com #else
70*2483Sdiguohuangjiajinweijun@gmail.com         .doc    = (char *)PyDoc_STR("lifespan.state")
71*2483Sdiguohuangjiajinweijun@gmail.com #endif
72*2483Sdiguohuangjiajinweijun@gmail.com     },
73*2483Sdiguohuangjiajinweijun@gmail.com 
74*2483Sdiguohuangjiajinweijun@gmail.com     { NULL, 0, 0, 0, NULL }
75*2483Sdiguohuangjiajinweijun@gmail.com };
76*2483Sdiguohuangjiajinweijun@gmail.com 
771624Smax.romanov@nginx.com static PyAsyncMethods nxt_py_asgi_async_methods = {
781624Smax.romanov@nginx.com     .am_await = nxt_py_asgi_await,
791624Smax.romanov@nginx.com };
801624Smax.romanov@nginx.com 
811624Smax.romanov@nginx.com static PyTypeObject nxt_py_asgi_lifespan_type = {
821624Smax.romanov@nginx.com     PyVarObject_HEAD_INIT(NULL, 0)
831624Smax.romanov@nginx.com 
841624Smax.romanov@nginx.com     .tp_name      = "unit._asgi_lifespan",
851624Smax.romanov@nginx.com     .tp_basicsize = sizeof(nxt_py_asgi_lifespan_t),
86*2483Sdiguohuangjiajinweijun@gmail.com     .tp_dealloc   = nxt_py_asgi_lifespan_dealloc,
871624Smax.romanov@nginx.com     .tp_as_async  = &nxt_py_asgi_async_methods,
881624Smax.romanov@nginx.com     .tp_flags     = Py_TPFLAGS_DEFAULT,
891624Smax.romanov@nginx.com     .tp_doc       = "unit ASGI Lifespan object",
901624Smax.romanov@nginx.com     .tp_iter      = nxt_py_asgi_iter,
911624Smax.romanov@nginx.com     .tp_iternext  = nxt_py_asgi_next,
921624Smax.romanov@nginx.com     .tp_methods   = nxt_py_asgi_lifespan_methods,
93*2483Sdiguohuangjiajinweijun@gmail.com     .tp_members   = nxt_py_asgi_lifespan_members,
941624Smax.romanov@nginx.com };
951624Smax.romanov@nginx.com 
961624Smax.romanov@nginx.com 
971681Smax.romanov@nginx.com int
nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t * ctx_data)981681Smax.romanov@nginx.com nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
991624Smax.romanov@nginx.com {
1001872So.canty@f5.com     size_t               size;
1011872So.canty@f5.com     PyObject             *lifespan;
1021872So.canty@f5.com     PyObject             **target_lifespans;
1031872So.canty@f5.com     nxt_int_t            i;
1041872So.canty@f5.com     nxt_python_target_t  *target;
1051872So.canty@f5.com 
1061872So.canty@f5.com     size = nxt_py_targets->count * sizeof(PyObject*);
1071872So.canty@f5.com 
1081872So.canty@f5.com     target_lifespans = nxt_unit_malloc(NULL, size);
1091872So.canty@f5.com     if (nxt_slow_path(target_lifespans == NULL)) {
1101872So.canty@f5.com         nxt_unit_alert(NULL, "Failed to allocate lifespan data");
1111872So.canty@f5.com         return NXT_UNIT_ERROR;
1121872So.canty@f5.com     }
1131872So.canty@f5.com 
1141872So.canty@f5.com     memset(target_lifespans, 0, size);
1151872So.canty@f5.com 
1161872So.canty@f5.com     for (i = 0; i < nxt_py_targets->count; i++) {
1171872So.canty@f5.com         target = &nxt_py_targets->target[i];
1181872So.canty@f5.com 
1191872So.canty@f5.com         lifespan = nxt_py_asgi_lifespan_target_startup(ctx_data, target);
1201872So.canty@f5.com         if (nxt_slow_path(lifespan == NULL)) {
1211872So.canty@f5.com             return NXT_UNIT_ERROR;
1221872So.canty@f5.com         }
1231872So.canty@f5.com 
1241872So.canty@f5.com         target_lifespans[i] = lifespan;
1251872So.canty@f5.com     }
1261872So.canty@f5.com 
1271872So.canty@f5.com     ctx_data->target_lifespans = target_lifespans;
1281872So.canty@f5.com 
1291872So.canty@f5.com     return NXT_UNIT_OK;
1301872So.canty@f5.com }
1311872So.canty@f5.com 
1321872So.canty@f5.com 
1331872So.canty@f5.com static PyObject *
nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t * ctx_data,nxt_python_target_t * target)1341872So.canty@f5.com nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data,
1351872So.canty@f5.com     nxt_python_target_t *target)
1361872So.canty@f5.com {
1371624Smax.romanov@nginx.com     PyObject                *scope, *res, *py_task, *receive, *send, *done;
1381697Smax.romanov@nginx.com     PyObject                *stage2;
1391872So.canty@f5.com     nxt_py_asgi_lifespan_t  *lifespan, *ret;
1401624Smax.romanov@nginx.com 
1411624Smax.romanov@nginx.com     if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
1421681Smax.romanov@nginx.com         nxt_unit_alert(NULL,
1431624Smax.romanov@nginx.com                  "Python failed to initialize the 'asgi_lifespan' type object");
1441872So.canty@f5.com         return NULL;
1451624Smax.romanov@nginx.com     }
1461624Smax.romanov@nginx.com 
1471624Smax.romanov@nginx.com     lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
1481624Smax.romanov@nginx.com     if (nxt_slow_path(lifespan == NULL)) {
1491681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create lifespan object");
1501872So.canty@f5.com         return NULL;
1511624Smax.romanov@nginx.com     }
1521624Smax.romanov@nginx.com 
1531872So.canty@f5.com     ret = NULL;
1541624Smax.romanov@nginx.com 
1551624Smax.romanov@nginx.com     receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
1561624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
1571681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get 'receive' method");
1581624Smax.romanov@nginx.com         goto release_lifespan;
1591624Smax.romanov@nginx.com     }
1601624Smax.romanov@nginx.com 
1611624Smax.romanov@nginx.com     send = PyObject_GetAttrString((PyObject *) lifespan, "send");
1621624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
1631681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get 'send' method");
1641624Smax.romanov@nginx.com         goto release_receive;
1651624Smax.romanov@nginx.com     }
1661624Smax.romanov@nginx.com 
1671624Smax.romanov@nginx.com     done = PyObject_GetAttrString((PyObject *) lifespan, "_done");
1681624Smax.romanov@nginx.com     if (nxt_slow_path(receive == NULL)) {
1691681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to get '_done' method");
1701624Smax.romanov@nginx.com         goto release_send;
1711624Smax.romanov@nginx.com     }
1721624Smax.romanov@nginx.com 
1731681Smax.romanov@nginx.com     lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future,
1741624Smax.romanov@nginx.com                                                    NULL);
1751624Smax.romanov@nginx.com     if (nxt_slow_path(lifespan->startup_future == NULL)) {
1761624Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Future object");
1771624Smax.romanov@nginx.com         nxt_python_print_exception();
1781624Smax.romanov@nginx.com 
1791624Smax.romanov@nginx.com         goto release_done;
1801624Smax.romanov@nginx.com     }
1811624Smax.romanov@nginx.com 
1821681Smax.romanov@nginx.com     lifespan->ctx_data = ctx_data;
1831624Smax.romanov@nginx.com     lifespan->disabled = 0;
1841624Smax.romanov@nginx.com     lifespan->startup_received = 0;
1851624Smax.romanov@nginx.com     lifespan->startup_sent = 0;
1861624Smax.romanov@nginx.com     lifespan->shutdown_received = 0;
1871624Smax.romanov@nginx.com     lifespan->shutdown_sent = 0;
1881624Smax.romanov@nginx.com     lifespan->shutdown_called = 0;
1891624Smax.romanov@nginx.com     lifespan->shutdown_future = NULL;
1901624Smax.romanov@nginx.com     lifespan->receive_future = NULL;
191*2483Sdiguohuangjiajinweijun@gmail.com     lifespan->state = NULL;
1921624Smax.romanov@nginx.com 
1931624Smax.romanov@nginx.com     scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str);
1941624Smax.romanov@nginx.com     if (nxt_slow_path(scope == NULL)) {
1951624Smax.romanov@nginx.com         goto release_future;
1961624Smax.romanov@nginx.com     }
1971624Smax.romanov@nginx.com 
198*2483Sdiguohuangjiajinweijun@gmail.com     lifespan->state = PyDict_New();
199*2483Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(lifespan->state == NULL)) {
200*2483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_req_error(NULL,
201*2483Sdiguohuangjiajinweijun@gmail.com                            "Python failed to create 'state' dict");
202*2483Sdiguohuangjiajinweijun@gmail.com         goto release_future;
203*2483Sdiguohuangjiajinweijun@gmail.com     }
204*2483Sdiguohuangjiajinweijun@gmail.com 
205*2483Sdiguohuangjiajinweijun@gmail.com     if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str,
206*2483Sdiguohuangjiajinweijun@gmail.com                                      lifespan->state) == -1))
207*2483Sdiguohuangjiajinweijun@gmail.com     {
208*2483Sdiguohuangjiajinweijun@gmail.com         nxt_unit_req_error(NULL,
209*2483Sdiguohuangjiajinweijun@gmail.com                            "Python failed to set 'scope.state' item");
210*2483Sdiguohuangjiajinweijun@gmail.com         Py_CLEAR(lifespan->state);
211*2483Sdiguohuangjiajinweijun@gmail.com         goto release_future;
212*2483Sdiguohuangjiajinweijun@gmail.com     }
213*2483Sdiguohuangjiajinweijun@gmail.com 
2141872So.canty@f5.com     if (!target->asgi_legacy) {
2151697Smax.romanov@nginx.com         nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
2161697Smax.romanov@nginx.com 
2171872So.canty@f5.com         res = PyObject_CallFunctionObjArgs(target->application,
2181697Smax.romanov@nginx.com                                            scope, receive, send, NULL);
2191697Smax.romanov@nginx.com 
2201697Smax.romanov@nginx.com     } else {
2211697Smax.romanov@nginx.com         nxt_unit_req_debug(NULL, "Python call legacy application");
2221697Smax.romanov@nginx.com 
2231872So.canty@f5.com         res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
2241697Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
2251697Smax.romanov@nginx.com             nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
2261697Smax.romanov@nginx.com                          "ASGI Lifespan processing exception");
2271697Smax.romanov@nginx.com             nxt_python_print_exception();
2281697Smax.romanov@nginx.com 
2291697Smax.romanov@nginx.com             lifespan->disabled = 1;
2301872So.canty@f5.com 
2311872So.canty@f5.com             Py_INCREF(lifespan);
2321872So.canty@f5.com             ret = lifespan;
2331697Smax.romanov@nginx.com 
2341697Smax.romanov@nginx.com             goto release_scope;
2351697Smax.romanov@nginx.com         }
2361697Smax.romanov@nginx.com 
2371697Smax.romanov@nginx.com         if (nxt_slow_path(PyCallable_Check(res) == 0)) {
2381697Smax.romanov@nginx.com             nxt_unit_req_error(NULL,
2391697Smax.romanov@nginx.com                               "Legacy ASGI application returns not a callable");
2401697Smax.romanov@nginx.com 
2411697Smax.romanov@nginx.com             Py_DECREF(res);
2421697Smax.romanov@nginx.com 
2431697Smax.romanov@nginx.com             goto release_scope;
2441697Smax.romanov@nginx.com         }
2451697Smax.romanov@nginx.com 
2461697Smax.romanov@nginx.com         stage2 = res;
2471697Smax.romanov@nginx.com 
2481697Smax.romanov@nginx.com         res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
2491697Smax.romanov@nginx.com 
2501697Smax.romanov@nginx.com         Py_DECREF(stage2);
2511697Smax.romanov@nginx.com     }
2521697Smax.romanov@nginx.com 
2531624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
2541681Smax.romanov@nginx.com         nxt_unit_error(NULL, "Python failed to call the application");
2551624Smax.romanov@nginx.com         nxt_python_print_exception();
2561624Smax.romanov@nginx.com         goto release_scope;
2571624Smax.romanov@nginx.com     }
2581624Smax.romanov@nginx.com 
2591624Smax.romanov@nginx.com     if (nxt_slow_path(!PyCoro_CheckExact(res))) {
2601681Smax.romanov@nginx.com         nxt_unit_error(NULL, "Application result type is not a coroutine");
2611624Smax.romanov@nginx.com         Py_DECREF(res);
2621624Smax.romanov@nginx.com         goto release_scope;
2631624Smax.romanov@nginx.com     }
2641624Smax.romanov@nginx.com 
2651697Smax.romanov@nginx.com     py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res,
2661697Smax.romanov@nginx.com                                            NULL);
2671624Smax.romanov@nginx.com     if (nxt_slow_path(py_task == NULL)) {
2681681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to call the create_task");
2691624Smax.romanov@nginx.com         nxt_python_print_exception();
2701624Smax.romanov@nginx.com         Py_DECREF(res);
2711624Smax.romanov@nginx.com         goto release_scope;
2721624Smax.romanov@nginx.com     }
2731624Smax.romanov@nginx.com 
2741624Smax.romanov@nginx.com     Py_DECREF(res);
2751624Smax.romanov@nginx.com 
2761624Smax.romanov@nginx.com     res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str,
2771624Smax.romanov@nginx.com                                      done, NULL);
2781624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
2791681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'");
2801624Smax.romanov@nginx.com         nxt_python_print_exception();
2811624Smax.romanov@nginx.com         goto release_task;
2821624Smax.romanov@nginx.com     }
2831624Smax.romanov@nginx.com 
2841624Smax.romanov@nginx.com     Py_DECREF(res);
2851624Smax.romanov@nginx.com 
2861681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
2871624Smax.romanov@nginx.com                                        lifespan->startup_future, NULL);
2881624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
2891681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
2901624Smax.romanov@nginx.com         nxt_python_print_exception();
2911624Smax.romanov@nginx.com         goto release_task;
2921624Smax.romanov@nginx.com     }
2931624Smax.romanov@nginx.com 
2941624Smax.romanov@nginx.com     Py_DECREF(res);
2951624Smax.romanov@nginx.com 
2961624Smax.romanov@nginx.com     if (lifespan->startup_sent == 1 || lifespan->disabled) {
2971872So.canty@f5.com         Py_INCREF(lifespan);
2981624Smax.romanov@nginx.com 
2991872So.canty@f5.com         ret = lifespan;
3001624Smax.romanov@nginx.com     }
3011624Smax.romanov@nginx.com 
3021624Smax.romanov@nginx.com release_task:
3031624Smax.romanov@nginx.com     Py_DECREF(py_task);
3041624Smax.romanov@nginx.com release_scope:
3051624Smax.romanov@nginx.com     Py_DECREF(scope);
3061624Smax.romanov@nginx.com release_future:
3071624Smax.romanov@nginx.com     Py_CLEAR(lifespan->startup_future);
3081624Smax.romanov@nginx.com release_done:
3091624Smax.romanov@nginx.com     Py_DECREF(done);
3101624Smax.romanov@nginx.com release_send:
3111624Smax.romanov@nginx.com     Py_DECREF(send);
3121624Smax.romanov@nginx.com release_receive:
3131624Smax.romanov@nginx.com     Py_DECREF(receive);
3141624Smax.romanov@nginx.com release_lifespan:
3151624Smax.romanov@nginx.com     Py_DECREF(lifespan);
3161624Smax.romanov@nginx.com 
3171872So.canty@f5.com     return (PyObject *) ret;
3181624Smax.romanov@nginx.com }
3191624Smax.romanov@nginx.com 
3201624Smax.romanov@nginx.com 
3211681Smax.romanov@nginx.com int
nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t * ctx)3221681Smax.romanov@nginx.com nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
3231624Smax.romanov@nginx.com {
3241872So.canty@f5.com     nxt_int_t               i, ret;
3251624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t  *lifespan;
3261681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3271624Smax.romanov@nginx.com 
3281681Smax.romanov@nginx.com     ctx_data = ctx->data;
3291681Smax.romanov@nginx.com 
3301872So.canty@f5.com     for (i = 0; i < nxt_py_targets->count; i++) {
3311872So.canty@f5.com         lifespan = (nxt_py_asgi_lifespan_t *)ctx_data->target_lifespans[i];
3321872So.canty@f5.com 
3331872So.canty@f5.com         ret = nxt_py_asgi_lifespan_target_shutdown(lifespan);
3341872So.canty@f5.com         if (nxt_slow_path(ret != NXT_UNIT_OK)) {
3351872So.canty@f5.com             return NXT_UNIT_ERROR;
3361872So.canty@f5.com         }
3371872So.canty@f5.com     }
3381872So.canty@f5.com 
3391872So.canty@f5.com     nxt_unit_free(NULL, ctx_data->target_lifespans);
3401872So.canty@f5.com 
3411872So.canty@f5.com     return NXT_UNIT_OK;
3421872So.canty@f5.com }
3431872So.canty@f5.com 
3441872So.canty@f5.com 
3451872So.canty@f5.com static int
nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t * lifespan)3461872So.canty@f5.com nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t *lifespan)
3471872So.canty@f5.com {
3481872So.canty@f5.com     PyObject                *msg, *future, *res;
3491872So.canty@f5.com     nxt_py_asgi_ctx_data_t  *ctx_data;
3501872So.canty@f5.com 
3511872So.canty@f5.com     ctx_data = lifespan->ctx_data;
3521681Smax.romanov@nginx.com 
3531681Smax.romanov@nginx.com     if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
3541681Smax.romanov@nginx.com         return NXT_UNIT_OK;
3551624Smax.romanov@nginx.com     }
3561624Smax.romanov@nginx.com 
3571624Smax.romanov@nginx.com     lifespan->shutdown_called = 1;
3581624Smax.romanov@nginx.com 
3591624Smax.romanov@nginx.com     if (lifespan->receive_future != NULL) {
3601624Smax.romanov@nginx.com         future = lifespan->receive_future;
3611624Smax.romanov@nginx.com         lifespan->receive_future = NULL;
3621624Smax.romanov@nginx.com 
3631624Smax.romanov@nginx.com         msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
3641624Smax.romanov@nginx.com 
3651624Smax.romanov@nginx.com         if (nxt_fast_path(msg != NULL)) {
3661624Smax.romanov@nginx.com             res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
3671624Smax.romanov@nginx.com                                              msg, NULL);
3681624Smax.romanov@nginx.com             Py_XDECREF(res);
3691624Smax.romanov@nginx.com             Py_DECREF(msg);
3701624Smax.romanov@nginx.com         }
3711624Smax.romanov@nginx.com 
3721624Smax.romanov@nginx.com         Py_DECREF(future);
3731624Smax.romanov@nginx.com     }
3741624Smax.romanov@nginx.com 
3751624Smax.romanov@nginx.com     if (lifespan->shutdown_sent) {
3761681Smax.romanov@nginx.com         return NXT_UNIT_OK;
3771624Smax.romanov@nginx.com     }
3781624Smax.romanov@nginx.com 
3791681Smax.romanov@nginx.com     lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future,
3801624Smax.romanov@nginx.com                                                     NULL);
3811624Smax.romanov@nginx.com     if (nxt_slow_path(lifespan->shutdown_future == NULL)) {
3821624Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Future object");
3831624Smax.romanov@nginx.com         nxt_python_print_exception();
3841681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
3851624Smax.romanov@nginx.com     }
3861624Smax.romanov@nginx.com 
3871681Smax.romanov@nginx.com     res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
3881624Smax.romanov@nginx.com                                        lifespan->shutdown_future, NULL);
3891624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
3901624Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
3911624Smax.romanov@nginx.com         nxt_python_print_exception();
3921681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
3931624Smax.romanov@nginx.com     }
3941624Smax.romanov@nginx.com 
3951624Smax.romanov@nginx.com     Py_DECREF(res);
3961624Smax.romanov@nginx.com     Py_CLEAR(lifespan->shutdown_future);
3971624Smax.romanov@nginx.com 
3981681Smax.romanov@nginx.com     return NXT_UNIT_OK;
3991624Smax.romanov@nginx.com }
4001624Smax.romanov@nginx.com 
4011624Smax.romanov@nginx.com 
4021624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_receive(PyObject * self,PyObject * none)4031624Smax.romanov@nginx.com nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
4041624Smax.romanov@nginx.com {
4051624Smax.romanov@nginx.com     PyObject                *msg, *future;
4061624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t  *lifespan;
4071681Smax.romanov@nginx.com     nxt_py_asgi_ctx_data_t  *ctx_data;
4081624Smax.romanov@nginx.com 
4091624Smax.romanov@nginx.com     lifespan = (nxt_py_asgi_lifespan_t *) self;
4101681Smax.romanov@nginx.com     ctx_data = lifespan->ctx_data;
4111624Smax.romanov@nginx.com 
4121624Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_lifespan_receive");
4131624Smax.romanov@nginx.com 
4141681Smax.romanov@nginx.com     future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
4151624Smax.romanov@nginx.com     if (nxt_slow_path(future == NULL)) {
4161624Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Python failed to create Future object");
4171624Smax.romanov@nginx.com         nxt_python_print_exception();
4181624Smax.romanov@nginx.com 
4191624Smax.romanov@nginx.com         return PyErr_Format(PyExc_RuntimeError,
4201624Smax.romanov@nginx.com                             "failed to create Future object");
4211624Smax.romanov@nginx.com     }
4221624Smax.romanov@nginx.com 
4231624Smax.romanov@nginx.com     if (!lifespan->startup_received) {
4241624Smax.romanov@nginx.com         lifespan->startup_received = 1;
4251624Smax.romanov@nginx.com 
4261624Smax.romanov@nginx.com         msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str);
4271624Smax.romanov@nginx.com 
4281681Smax.romanov@nginx.com         return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
4291624Smax.romanov@nginx.com     }
4301624Smax.romanov@nginx.com 
4311624Smax.romanov@nginx.com     if (lifespan->shutdown_called && !lifespan->shutdown_received) {
4321624Smax.romanov@nginx.com         lifespan->shutdown_received = 1;
4331624Smax.romanov@nginx.com 
4341624Smax.romanov@nginx.com         msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
4351624Smax.romanov@nginx.com 
4361681Smax.romanov@nginx.com         return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
4371624Smax.romanov@nginx.com     }
4381624Smax.romanov@nginx.com 
4391624Smax.romanov@nginx.com     Py_INCREF(future);
4401624Smax.romanov@nginx.com     lifespan->receive_future = future;
4411624Smax.romanov@nginx.com 
4421624Smax.romanov@nginx.com     return future;
4431624Smax.romanov@nginx.com }
4441624Smax.romanov@nginx.com 
4451624Smax.romanov@nginx.com 
4461624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send(PyObject * self,PyObject * dict)4471624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict)
4481624Smax.romanov@nginx.com {
4491624Smax.romanov@nginx.com     PyObject                *type, *msg;
4501624Smax.romanov@nginx.com     const char              *type_str;
4511624Smax.romanov@nginx.com     Py_ssize_t              type_len;
4521624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t  *lifespan;
4531624Smax.romanov@nginx.com 
4541624Smax.romanov@nginx.com     static const nxt_str_t  startup_complete
4551624Smax.romanov@nginx.com                                 = nxt_string("lifespan.startup.complete");
4561624Smax.romanov@nginx.com     static const nxt_str_t  startup_failed
4571624Smax.romanov@nginx.com                                 = nxt_string("lifespan.startup.failed");
4581624Smax.romanov@nginx.com     static const nxt_str_t  shutdown_complete
4591624Smax.romanov@nginx.com                                 = nxt_string("lifespan.shutdown.complete");
4601624Smax.romanov@nginx.com     static const nxt_str_t  shutdown_failed
4611624Smax.romanov@nginx.com                                 = nxt_string("lifespan.shutdown.failed");
4621624Smax.romanov@nginx.com 
4631624Smax.romanov@nginx.com     lifespan = (nxt_py_asgi_lifespan_t *) self;
4641624Smax.romanov@nginx.com 
4651624Smax.romanov@nginx.com     type = PyDict_GetItem(dict, nxt_py_type_str);
4661624Smax.romanov@nginx.com     if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) {
4671624Smax.romanov@nginx.com         nxt_unit_error(NULL,
4681624Smax.romanov@nginx.com                        "asgi_lifespan_send: 'type' is not a unicode string");
4691624Smax.romanov@nginx.com         return PyErr_Format(PyExc_TypeError,
4701624Smax.romanov@nginx.com                             "'type' is not a unicode string");
4711624Smax.romanov@nginx.com     }
4721624Smax.romanov@nginx.com 
4731624Smax.romanov@nginx.com     type_str = PyUnicode_AsUTF8AndSize(type, &type_len);
4741624Smax.romanov@nginx.com 
4751624Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_lifespan_send type is '%.*s'",
4761624Smax.romanov@nginx.com                    (int) type_len, type_str);
4771624Smax.romanov@nginx.com 
4781624Smax.romanov@nginx.com     if (type_len == (Py_ssize_t) startup_complete.length
4791624Smax.romanov@nginx.com         && memcmp(type_str, startup_complete.start, type_len) == 0)
4801624Smax.romanov@nginx.com     {
4811624Smax.romanov@nginx.com         return nxt_py_asgi_lifespan_send_startup(lifespan, 0, NULL);
4821624Smax.romanov@nginx.com     }
4831624Smax.romanov@nginx.com 
4841624Smax.romanov@nginx.com     if (type_len == (Py_ssize_t) startup_failed.length
4851624Smax.romanov@nginx.com         && memcmp(type_str, startup_failed.start, type_len) == 0)
4861624Smax.romanov@nginx.com     {
4871624Smax.romanov@nginx.com         msg = PyDict_GetItem(dict, nxt_py_message_str);
4881624Smax.romanov@nginx.com         return nxt_py_asgi_lifespan_send_startup(lifespan, 1, msg);
4891624Smax.romanov@nginx.com     }
4901624Smax.romanov@nginx.com 
4911624Smax.romanov@nginx.com     if (type_len == (Py_ssize_t) shutdown_complete.length
4921624Smax.romanov@nginx.com         && memcmp(type_str, shutdown_complete.start, type_len) == 0)
4931624Smax.romanov@nginx.com     {
4941624Smax.romanov@nginx.com         return nxt_py_asgi_lifespan_send_shutdown(lifespan, 0, NULL);
4951624Smax.romanov@nginx.com     }
4961624Smax.romanov@nginx.com 
4971624Smax.romanov@nginx.com     if (type_len == (Py_ssize_t) shutdown_failed.length
4981624Smax.romanov@nginx.com         && memcmp(type_str, shutdown_failed.start, type_len) == 0)
4991624Smax.romanov@nginx.com     {
5001624Smax.romanov@nginx.com         msg = PyDict_GetItem(dict, nxt_py_message_str);
5011624Smax.romanov@nginx.com         return nxt_py_asgi_lifespan_send_shutdown(lifespan, 1, msg);
5021624Smax.romanov@nginx.com     }
5031624Smax.romanov@nginx.com 
5041624Smax.romanov@nginx.com     return nxt_py_asgi_lifespan_disable(lifespan);
5051624Smax.romanov@nginx.com }
5061624Smax.romanov@nginx.com 
5071624Smax.romanov@nginx.com 
5081624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t * lifespan,int v,PyObject * message)5091624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t *lifespan, int v,
5101624Smax.romanov@nginx.com     PyObject *message)
5111624Smax.romanov@nginx.com {
5121624Smax.romanov@nginx.com     const char  *message_str;
5131624Smax.romanov@nginx.com     Py_ssize_t  message_len;
5141624Smax.romanov@nginx.com 
5151624Smax.romanov@nginx.com     if (nxt_slow_path(v != 0)) {
5161624Smax.romanov@nginx.com         nxt_unit_error(NULL, "Application startup failed");
5171624Smax.romanov@nginx.com 
5181624Smax.romanov@nginx.com         if (nxt_fast_path(message != NULL && PyUnicode_Check(message))) {
5191624Smax.romanov@nginx.com             message_str = PyUnicode_AsUTF8AndSize(message, &message_len);
5201624Smax.romanov@nginx.com 
5211624Smax.romanov@nginx.com             nxt_unit_error(NULL, "%.*s", (int) message_len, message_str);
5221624Smax.romanov@nginx.com         }
5231624Smax.romanov@nginx.com     }
5241624Smax.romanov@nginx.com 
5251624Smax.romanov@nginx.com     return nxt_py_asgi_lifespan_send_(lifespan, v,
5261624Smax.romanov@nginx.com                                       &lifespan->startup_sent,
5271624Smax.romanov@nginx.com                                       &lifespan->startup_future);
5281624Smax.romanov@nginx.com }
5291624Smax.romanov@nginx.com 
5301624Smax.romanov@nginx.com 
5311624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t * lifespan,int v,int * sent,PyObject ** pfuture)5321624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan, int v, int *sent,
5331624Smax.romanov@nginx.com     PyObject **pfuture)
5341624Smax.romanov@nginx.com {
5351624Smax.romanov@nginx.com     PyObject  *future, *res;
5361624Smax.romanov@nginx.com 
5371624Smax.romanov@nginx.com     if (*sent) {
5381624Smax.romanov@nginx.com         return nxt_py_asgi_lifespan_disable(lifespan);
5391624Smax.romanov@nginx.com     }
5401624Smax.romanov@nginx.com 
5411624Smax.romanov@nginx.com     *sent = 1 + v;
5421624Smax.romanov@nginx.com 
5431624Smax.romanov@nginx.com     if (*pfuture != NULL) {
5441624Smax.romanov@nginx.com         future = *pfuture;
5451624Smax.romanov@nginx.com         *pfuture = NULL;
5461624Smax.romanov@nginx.com 
5471624Smax.romanov@nginx.com         res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
5481624Smax.romanov@nginx.com                                          Py_None, NULL);
5491624Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
5501624Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
5511624Smax.romanov@nginx.com             nxt_python_print_exception();
5521624Smax.romanov@nginx.com 
5531624Smax.romanov@nginx.com             return nxt_py_asgi_lifespan_disable(lifespan);
5541624Smax.romanov@nginx.com         }
5551624Smax.romanov@nginx.com 
5561624Smax.romanov@nginx.com         Py_DECREF(res);
5571624Smax.romanov@nginx.com         Py_DECREF(future);
5581624Smax.romanov@nginx.com     }
5591624Smax.romanov@nginx.com 
5601624Smax.romanov@nginx.com     Py_INCREF(lifespan);
5611624Smax.romanov@nginx.com 
5621624Smax.romanov@nginx.com     return (PyObject *) lifespan;
5631624Smax.romanov@nginx.com }
5641624Smax.romanov@nginx.com 
5651624Smax.romanov@nginx.com 
5661624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t * lifespan)5671624Smax.romanov@nginx.com nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan)
5681624Smax.romanov@nginx.com {
5691624Smax.romanov@nginx.com     nxt_unit_warn(NULL, "Got invalid state transition on lifespan protocol");
5701624Smax.romanov@nginx.com 
5711624Smax.romanov@nginx.com     lifespan->disabled = 1;
5721624Smax.romanov@nginx.com 
5731624Smax.romanov@nginx.com     return PyErr_Format(PyExc_AssertionError,
5741624Smax.romanov@nginx.com                         "Got invalid state transition on lifespan protocol");
5751624Smax.romanov@nginx.com }
5761624Smax.romanov@nginx.com 
5771624Smax.romanov@nginx.com 
5781624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t * lifespan,int v,PyObject * message)5791624Smax.romanov@nginx.com nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t *lifespan, int v,
5801624Smax.romanov@nginx.com     PyObject *message)
5811624Smax.romanov@nginx.com {
5821624Smax.romanov@nginx.com     return nxt_py_asgi_lifespan_send_(lifespan, v,
5831624Smax.romanov@nginx.com                                       &lifespan->shutdown_sent,
5841624Smax.romanov@nginx.com                                       &lifespan->shutdown_future);
5851624Smax.romanov@nginx.com }
5861624Smax.romanov@nginx.com 
5871624Smax.romanov@nginx.com 
5881624Smax.romanov@nginx.com static PyObject *
nxt_py_asgi_lifespan_done(PyObject * self,PyObject * future)5891624Smax.romanov@nginx.com nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future)
5901624Smax.romanov@nginx.com {
5911624Smax.romanov@nginx.com     PyObject                *res;
5921624Smax.romanov@nginx.com     nxt_py_asgi_lifespan_t  *lifespan;
5931624Smax.romanov@nginx.com 
5941624Smax.romanov@nginx.com     nxt_unit_debug(NULL, "asgi_lifespan_done");
5951624Smax.romanov@nginx.com 
5961624Smax.romanov@nginx.com     lifespan = (nxt_py_asgi_lifespan_t *) self;
5971624Smax.romanov@nginx.com 
5981624Smax.romanov@nginx.com     if (lifespan->startup_sent == 0) {
5991624Smax.romanov@nginx.com         lifespan->disabled = 1;
6001624Smax.romanov@nginx.com     }
6011624Smax.romanov@nginx.com 
6021624Smax.romanov@nginx.com     /*
6031624Smax.romanov@nginx.com      * Get Future.result() and it raises an exception, if coroutine exited
6041624Smax.romanov@nginx.com      * with exception.
6051624Smax.romanov@nginx.com      */
6061624Smax.romanov@nginx.com     res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL);
6071624Smax.romanov@nginx.com     if (nxt_slow_path(res == NULL)) {
6081624Smax.romanov@nginx.com         nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
6091624Smax.romanov@nginx.com                      "ASGI Lifespan processing exception");
6101624Smax.romanov@nginx.com         nxt_python_print_exception();
6111624Smax.romanov@nginx.com     }
6121624Smax.romanov@nginx.com 
6131624Smax.romanov@nginx.com     Py_XDECREF(res);
6141624Smax.romanov@nginx.com 
6151624Smax.romanov@nginx.com     if (lifespan->startup_future != NULL) {
6161624Smax.romanov@nginx.com         future = lifespan->startup_future;
6171624Smax.romanov@nginx.com         lifespan->startup_future = NULL;
6181624Smax.romanov@nginx.com 
6191624Smax.romanov@nginx.com         res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
6201624Smax.romanov@nginx.com                                          Py_None, NULL);
6211624Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
6221624Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
6231624Smax.romanov@nginx.com             nxt_python_print_exception();
6241624Smax.romanov@nginx.com         }
6251624Smax.romanov@nginx.com 
6261624Smax.romanov@nginx.com         Py_XDECREF(res);
6271624Smax.romanov@nginx.com         Py_DECREF(future);
6281624Smax.romanov@nginx.com     }
6291624Smax.romanov@nginx.com 
6301624Smax.romanov@nginx.com     if (lifespan->shutdown_future != NULL) {
6311624Smax.romanov@nginx.com         future = lifespan->shutdown_future;
6321624Smax.romanov@nginx.com         lifespan->shutdown_future = NULL;
6331624Smax.romanov@nginx.com 
6341624Smax.romanov@nginx.com         res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
6351624Smax.romanov@nginx.com                                          Py_None, NULL);
6361624Smax.romanov@nginx.com         if (nxt_slow_path(res == NULL)) {
6371624Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
6381624Smax.romanov@nginx.com             nxt_python_print_exception();
6391624Smax.romanov@nginx.com         }
6401624Smax.romanov@nginx.com 
6411624Smax.romanov@nginx.com         Py_XDECREF(res);
6421624Smax.romanov@nginx.com         Py_DECREF(future);
6431624Smax.romanov@nginx.com     }
6441624Smax.romanov@nginx.com 
6451624Smax.romanov@nginx.com     Py_RETURN_NONE;
6461624Smax.romanov@nginx.com }
6471624Smax.romanov@nginx.com 
6481624Smax.romanov@nginx.com 
649*2483Sdiguohuangjiajinweijun@gmail.com static void
nxt_py_asgi_lifespan_dealloc(PyObject * self)650*2483Sdiguohuangjiajinweijun@gmail.com nxt_py_asgi_lifespan_dealloc(PyObject *self)
651*2483Sdiguohuangjiajinweijun@gmail.com {
652*2483Sdiguohuangjiajinweijun@gmail.com     nxt_py_asgi_lifespan_t *lifespan = (nxt_py_asgi_lifespan_t *)self;
653*2483Sdiguohuangjiajinweijun@gmail.com 
654*2483Sdiguohuangjiajinweijun@gmail.com     Py_CLEAR(lifespan->state);
655*2483Sdiguohuangjiajinweijun@gmail.com     PyObject_Del(self);
656*2483Sdiguohuangjiajinweijun@gmail.com }
657*2483Sdiguohuangjiajinweijun@gmail.com 
658*2483Sdiguohuangjiajinweijun@gmail.com 
6591624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */
660