Deleted Added
1
2/*
3 * Copyright (C) NGINX, Inc.
4 */
5
6
7#include <python/nxt_python.h>
8
9#if (NXT_HAVE_ASGI)
10
11#include <nxt_main.h>
12#include <python/nxt_python_asgi.h>
13#include <python/nxt_python_asgi_str.h>
14
15
16typedef struct {
17 PyObject_HEAD
18 nxt_py_asgi_ctx_data_t *ctx_data;
19 int disabled;
20 int startup_received;
21 int startup_sent;
22 int shutdown_received;
23 int shutdown_sent;
24 int shutdown_called;
25 PyObject *startup_future;
26 PyObject *shutdown_future;
27 PyObject *receive_future;
28} nxt_py_asgi_lifespan_t;
29
30
31static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
32static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
33static PyObject *nxt_py_asgi_lifespan_send_startup(
34 nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
35static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan,
36 int v, int *sent, PyObject **future);
37static PyObject *nxt_py_asgi_lifespan_send_shutdown(
38 nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
39static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
40static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
41
42
43static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
44 { "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
45 { "send", nxt_py_asgi_lifespan_send, METH_O, 0 },
46 { "_done", nxt_py_asgi_lifespan_done, METH_O, 0 },
47 { NULL, NULL, 0, 0 }
48};
49
50static PyAsyncMethods nxt_py_asgi_async_methods = {

--- 10 unchanged lines hidden (view full) ---

61 .tp_flags = Py_TPFLAGS_DEFAULT,
62 .tp_doc = "unit ASGI Lifespan object",
63 .tp_iter = nxt_py_asgi_iter,
64 .tp_iternext = nxt_py_asgi_next,
65 .tp_methods = nxt_py_asgi_lifespan_methods,
66};
67
68
69int
70nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
71{
72 int rc;
73 PyObject *scope, *res, *py_task, *receive, *send, *done;
74 nxt_py_asgi_lifespan_t *lifespan;
75
76 if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
77 nxt_unit_alert(NULL,
78 "Python failed to initialize the 'asgi_lifespan' type object");
79 return NXT_UNIT_ERROR;
80 }
81
82 lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
83 if (nxt_slow_path(lifespan == NULL)) {
84 nxt_unit_alert(NULL, "Python failed to create lifespan object");
85 return NXT_UNIT_ERROR;
86 }
87
88 rc = NXT_UNIT_ERROR;
89
90 receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
91 if (nxt_slow_path(receive == NULL)) {
92 nxt_unit_alert(NULL, "Python failed to get 'receive' method");
93 goto release_lifespan;
94 }
95
96 send = PyObject_GetAttrString((PyObject *) lifespan, "send");
97 if (nxt_slow_path(receive == NULL)) {
98 nxt_unit_alert(NULL, "Python failed to get 'send' method");
99 goto release_receive;
100 }
101
102 done = PyObject_GetAttrString((PyObject *) lifespan, "_done");
103 if (nxt_slow_path(receive == NULL)) {
104 nxt_unit_alert(NULL, "Python failed to get '_done' method");
105 goto release_send;
106 }
107
108 lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future,
109 NULL);
110 if (nxt_slow_path(lifespan->startup_future == NULL)) {
111 nxt_unit_alert(NULL, "Python failed to create Future object");
112 nxt_python_print_exception();
113
114 goto release_done;
115 }
116
117 lifespan->ctx_data = ctx_data;
118 lifespan->disabled = 0;
119 lifespan->startup_received = 0;
120 lifespan->startup_sent = 0;
121 lifespan->shutdown_received = 0;
122 lifespan->shutdown_sent = 0;
123 lifespan->shutdown_called = 0;
124 lifespan->shutdown_future = NULL;
125 lifespan->receive_future = NULL;
126
127 scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str);
128 if (nxt_slow_path(scope == NULL)) {
129 goto release_future;
130 }
131
132 res = PyObject_CallFunctionObjArgs(nxt_py_application,
133 scope, receive, send, NULL);
134 if (nxt_slow_path(res == NULL)) {
135 nxt_unit_error(NULL, "Python failed to call the application");
136 nxt_python_print_exception();
137 goto release_scope;
138 }
139
140 if (nxt_slow_path(!PyCoro_CheckExact(res))) {
141 nxt_unit_error(NULL, "Application result type is not a coroutine");
142 Py_DECREF(res);
143 goto release_scope;
144 }
145
146 py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
147 if (nxt_slow_path(py_task == NULL)) {
148 nxt_unit_alert(NULL, "Python failed to call the create_task");
149 nxt_python_print_exception();
150 Py_DECREF(res);
151 goto release_scope;
152 }
153
154 Py_DECREF(res);
155
156 res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str,
157 done, NULL);
158 if (nxt_slow_path(res == NULL)) {
159 nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'");
160 nxt_python_print_exception();
161 goto release_task;
162 }
163
164 Py_DECREF(res);
165
166 res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
167 lifespan->startup_future, NULL);
168 if (nxt_slow_path(res == NULL)) {
169 nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
170 nxt_python_print_exception();
171 goto release_task;
172 }
173
174 Py_DECREF(res);
175
176 if (lifespan->startup_sent == 1 || lifespan->disabled) {
177 ctx_data->lifespan = (PyObject *) lifespan;
178 Py_INCREF(ctx_data->lifespan);
179
180 rc = NXT_UNIT_OK;
181 }
182
183release_task:
184 Py_DECREF(py_task);
185release_scope:
186 Py_DECREF(scope);
187release_future:
188 Py_CLEAR(lifespan->startup_future);

--- 5 unchanged lines hidden (view full) ---

194 Py_DECREF(receive);
195release_lifespan:
196 Py_DECREF(lifespan);
197
198 return rc;
199}
200
201
202int
203nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
204{
205 PyObject *msg, *future, *res;
206 nxt_py_asgi_lifespan_t *lifespan;
207 nxt_py_asgi_ctx_data_t *ctx_data;
208
209 ctx_data = ctx->data;
210
211 lifespan = (nxt_py_asgi_lifespan_t *) ctx_data->lifespan;
212
213 if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
214 return NXT_UNIT_OK;
215 }
216
217 lifespan->shutdown_called = 1;
218
219 if (lifespan->receive_future != NULL) {
220 future = lifespan->receive_future;
221 lifespan->receive_future = NULL;
222
223 msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
224
225 if (nxt_fast_path(msg != NULL)) {
226 res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
227 msg, NULL);
228 Py_XDECREF(res);
229 Py_DECREF(msg);
230 }
231
232 Py_DECREF(future);
233 }
234
235 if (lifespan->shutdown_sent) {
236 return NXT_UNIT_OK;
237 }
238
239 lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future,
240 NULL);
241 if (nxt_slow_path(lifespan->shutdown_future == NULL)) {
242 nxt_unit_alert(NULL, "Python failed to create Future object");
243 nxt_python_print_exception();
244 return NXT_UNIT_ERROR;
245 }
246
247 res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
248 lifespan->shutdown_future, NULL);
249 if (nxt_slow_path(res == NULL)) {
250 nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
251 nxt_python_print_exception();
252 return NXT_UNIT_ERROR;
253 }
254
255 Py_DECREF(res);
256 Py_CLEAR(lifespan->shutdown_future);
257
258 return NXT_UNIT_OK;
259}
260
261
262static PyObject *
263nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
264{
265 PyObject *msg, *future;
266 nxt_py_asgi_lifespan_t *lifespan;
267 nxt_py_asgi_ctx_data_t *ctx_data;
268
269 lifespan = (nxt_py_asgi_lifespan_t *) self;
270 ctx_data = lifespan->ctx_data;
271
272 nxt_unit_debug(NULL, "asgi_lifespan_receive");
273
274 future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
275 if (nxt_slow_path(future == NULL)) {
276 nxt_unit_alert(NULL, "Python failed to create Future object");
277 nxt_python_print_exception();
278
279 return PyErr_Format(PyExc_RuntimeError,
280 "failed to create Future object");
281 }
282
283 if (!lifespan->startup_received) {
284 lifespan->startup_received = 1;
285
286 msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str);
287
288 return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
289 }
290
291 if (lifespan->shutdown_called && !lifespan->shutdown_received) {
292 lifespan->shutdown_received = 1;
293
294 msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
295
296 return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
297 }
298
299 Py_INCREF(future);
300 lifespan->receive_future = future;
301
302 return future;
303}
304

--- 205 unchanged lines hidden ---