nxt_python_asgi_lifespan.c (1624:e46b1b422545) nxt_python_asgi_lifespan.c (1681:542b5b8c0647)
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
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 int disabled;
19 int startup_received;
20 int startup_sent;
21 int shutdown_received;
22 int shutdown_sent;
23 int shutdown_called;
24 PyObject *startup_future;
25 PyObject *shutdown_future;
26 PyObject *receive_future;
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;
27} nxt_py_asgi_lifespan_t;
28
29
30static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
31static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
32static PyObject *nxt_py_asgi_lifespan_send_startup(
33 nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
34static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan,
35 int v, int *sent, PyObject **future);
36static PyObject *nxt_py_asgi_lifespan_send_shutdown(
37 nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
38static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
39static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
40
41
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
42static nxt_py_asgi_lifespan_t *nxt_py_lifespan;
43
44static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
45 { "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
46 { "send", nxt_py_asgi_lifespan_send, METH_O, 0 },
47 { "_done", nxt_py_asgi_lifespan_done, METH_O, 0 },
48 { NULL, NULL, 0, 0 }
49};
50
51static PyAsyncMethods nxt_py_asgi_async_methods = {

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

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

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

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

--- 205 unchanged lines hidden ---
297 }
298
299 Py_INCREF(future);
300 lifespan->receive_future = future;
301
302 return future;
303}
304

--- 205 unchanged lines hidden ---