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 <nxt_unit.h>
13 #include <nxt_unit_request.h>
14 #include <nxt_unit_response.h>
15 #include <python/nxt_python_asgi.h>
16 #include <python/nxt_python_asgi_str.h>
17
18
19 static PyObject *nxt_python_asgi_get_func(PyObject *obj);
20 static PyObject *nxt_python_asgi_get_event_loop(PyObject *asyncio,
21 const char *event_loop_func);
22 static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main);
23 static void nxt_python_asgi_ctx_data_free(void *data);
24 static int nxt_python_asgi_startup(void *data);
25 static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
26
27 static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
28 nxt_unit_port_t *port);
29 static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);
30 static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req);
31
32 static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req,
33 nxt_python_target_t *app_target);
34 static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
35 uint16_t port);
36 static PyObject *nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr,
37 uint8_t len, uint16_t port);
38 static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f);
39 static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f);
40
41 static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
42 static int nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
43 static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx,
44 nxt_unit_port_t *port);
45 static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
46 static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
47
48 static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
49 static void nxt_python_asgi_done(void);
50
51 static PyObject *nxt_py_port_read;
52
53 static PyMethodDef nxt_py_port_read_method =
54 {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
55
56 static nxt_python_proto_t nxt_py_asgi_proto = {
57 .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,
58 .ctx_data_free = nxt_python_asgi_ctx_data_free,
59 .startup = nxt_python_asgi_startup,
60 .run = nxt_python_asgi_run,
61 .done = nxt_python_asgi_done,
62 };
63
64 #define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A
65
66
67 int
nxt_python_asgi_check(PyObject * obj)68 nxt_python_asgi_check(PyObject *obj)
69 {
70 int res;
71 PyObject *func;
72 PyCodeObject *code;
73
74 func = nxt_python_asgi_get_func(obj);
75
76 if (func == NULL) {
77 return 0;
78 }
79
80 code = (PyCodeObject *) PyFunction_GET_CODE(func);
81
82 nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with "
83 "%d argument(s)",
84 (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ",
85 code->co_argcount);
86
87 res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1;
88
89 Py_DECREF(func);
90
91 return res;
92 }
93
94
95 static PyObject *
nxt_python_asgi_get_func(PyObject * obj)96 nxt_python_asgi_get_func(PyObject *obj)
97 {
98 PyObject *call;
99
100 if (PyFunction_Check(obj)) {
101 Py_INCREF(obj);
102 return obj;
103 }
104
105 if (PyMethod_Check(obj)) {
106 obj = PyMethod_GET_FUNCTION(obj);
107
108 Py_INCREF(obj);
109 return obj;
110 }
111
112 call = PyObject_GetAttrString(obj, "__call__");
113
114 if (call == NULL) {
115 return NULL;
116 }
117
118 if (PyFunction_Check(call)) {
119 return call;
120 }
121
122 if (PyMethod_Check(call)) {
123 obj = PyMethod_GET_FUNCTION(call);
124
125 if (PyFunction_Check(obj)) {
126 Py_INCREF(obj);
127
128 } else {
129 obj = NULL;
130 }
131
132 } else {
133 obj = NULL;
134 }
135
136 Py_DECREF(call);
137
138 return obj;
139 }
140
141
142 int
nxt_python_asgi_init(nxt_unit_init_t * init,nxt_python_proto_t * proto)143 nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
144 {
145 PyObject *func;
146 nxt_int_t i;
147 PyCodeObject *code;
148
149 nxt_unit_debug(NULL, "asgi_init");
150
151 if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
152 nxt_unit_alert(NULL, "Python failed to init string objects");
153 return NXT_UNIT_ERROR;
154 }
155
156 nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);
157 if (nxt_slow_path(nxt_py_port_read == NULL)) {
158 nxt_unit_alert(NULL,
159 "Python failed to initialize the 'port_read' function");
160 return NXT_UNIT_ERROR;
161 }
162
163 if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
164 return NXT_UNIT_ERROR;
165 }
166
167 if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
168 return NXT_UNIT_ERROR;
169 }
170
171 for (i = 0; i < nxt_py_targets->count; i++) {
172 func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application);
173 if (nxt_slow_path(func == NULL)) {
174 nxt_unit_debug(NULL, "asgi: cannot find function for callable, "
175 "unable to check for legacy mode (#%d)",
176 (int) i);
177 continue;
178 }
179
180 code = (PyCodeObject *) PyFunction_GET_CODE(func);
181
182 if ((code->co_flags & CO_COROUTINE) == 0) {
183 nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
184 "switching to legacy mode");
185 nxt_py_targets->target[i].asgi_legacy = 1;
186 }
187
188 Py_DECREF(func);
189 }
190
191 init->callbacks.request_handler = nxt_py_asgi_request_handler;
192 init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
193 init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
194 init->callbacks.close_handler = nxt_py_asgi_close_handler;
195 init->callbacks.quit = nxt_py_asgi_quit;
196 init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
197 init->callbacks.add_port = nxt_py_asgi_add_port;
198 init->callbacks.remove_port = nxt_py_asgi_remove_port;
199
200 *proto = nxt_py_asgi_proto;
201
202 return NXT_UNIT_OK;
203 }
204
205
206 static PyObject *
nxt_python_asgi_get_event_loop(PyObject * asyncio,const char * event_loop_func)207 nxt_python_asgi_get_event_loop(PyObject *asyncio, const char *event_loop_func)
208 {
209 PyObject *event_loop, *loop;
210
211 event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
212 event_loop_func);
213 if (nxt_slow_path(event_loop == NULL)) {
214 nxt_unit_alert(NULL, "Python failed to get '%s' from module 'asyncio'",
215 event_loop_func);
216 return NULL;
217 }
218
219 if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) {
220 nxt_unit_alert(NULL, "'asyncio.%s' is not a callable object",
221 event_loop_func);
222 return NULL;
223 }
224
225 loop = PyObject_CallObject(event_loop, NULL);
226 if (nxt_slow_path(loop == NULL)) {
227 if (strcmp(event_loop_func, "get_running_loop") != 0) {
228 nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'",
229 event_loop_func);
230 }
231
232 return NULL;
233 }
234
235 return loop;
236 }
237
238
239 static int
nxt_python_asgi_ctx_data_alloc(void ** pdata,int main)240 nxt_python_asgi_ctx_data_alloc(void **pdata, int main)
241 {
242 uint32_t i;
243 PyObject *asyncio, *loop, *obj;
244 const char *event_loop_func;
245 nxt_py_asgi_ctx_data_t *ctx_data;
246
247 #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
248 static const char *main_event_loop_func = "get_event_loop";
249 #else
250 static const char *main_event_loop_func = "get_running_loop";
251 #endif
252
253 ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
254 if (nxt_slow_path(ctx_data == NULL)) {
255 nxt_unit_alert(NULL, "Failed to allocate context data");
256 return NXT_UNIT_ERROR;
257 }
258
259 memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t));
260
261 nxt_queue_init(&ctx_data->drain_queue);
262
263 struct {
264 const char *key;
265 PyObject **handler;
266
267 } handlers[] = {
268 { "create_task", &ctx_data->loop_create_task },
269 { "add_reader", &ctx_data->loop_add_reader },
270 { "remove_reader", &ctx_data->loop_remove_reader },
271 { "call_soon", &ctx_data->loop_call_soon },
272 { "run_until_complete", &ctx_data->loop_run_until_complete },
273 { "create_future", &ctx_data->loop_create_future },
274 };
275
276 loop = NULL;
277
278 asyncio = PyImport_ImportModule("asyncio");
279 if (nxt_slow_path(asyncio == NULL)) {
280 nxt_unit_alert(NULL, "Python failed to import module 'asyncio'");
281 nxt_python_print_exception();
282 goto fail;
283 }
284
285 event_loop_func = main ? main_event_loop_func : "new_event_loop";
286
287 loop = nxt_python_asgi_get_event_loop(asyncio, event_loop_func);
288 if (loop == NULL) {
289 #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
290 goto fail;
291 #else
292 if (!main) {
293 goto fail;
294 }
295
296 PyErr_Clear();
297
298 loop = nxt_python_asgi_get_event_loop(asyncio, "new_event_loop");
299 if (nxt_slow_path(loop == NULL)) {
300 goto fail;
301 }
302 #endif
303 }
304
305 for (i = 0; i < nxt_nitems(handlers); i++) {
306 obj = PyObject_GetAttrString(loop, handlers[i].key);
307 if (nxt_slow_path(obj == NULL)) {
308 nxt_unit_alert(NULL, "Python failed to get 'loop.%s'",
309 handlers[i].key);
310 goto fail;
311 }
312
313 *handlers[i].handler = obj;
314
315 if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
316 nxt_unit_alert(NULL, "'loop.%s' is not a callable object",
317 handlers[i].key);
318 goto fail;
319 }
320 }
321
322 obj = PyObject_CallObject(ctx_data->loop_create_future, NULL);
323 if (nxt_slow_path(obj == NULL)) {
324 nxt_unit_alert(NULL, "Python failed to create Future ");
325 nxt_python_print_exception();
326 goto fail;
327 }
328
329 ctx_data->quit_future = obj;
330
331 obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result");
332 if (nxt_slow_path(obj == NULL)) {
333 nxt_unit_alert(NULL, "Python failed to get 'future.set_result'");
334 goto fail;
335 }
336
337 ctx_data->quit_future_set_result = obj;
338
339 if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
340 nxt_unit_alert(NULL, "'future.set_result' is not a callable object");
341 goto fail;
342 }
343
344 Py_DECREF(loop);
345 Py_DECREF(asyncio);
346
347 *pdata = ctx_data;
348
349 return NXT_UNIT_OK;
350
351 fail:
352
353 nxt_python_asgi_ctx_data_free(ctx_data);
354
355 Py_XDECREF(loop);
356 Py_XDECREF(asyncio);
357
358 return NXT_UNIT_ERROR;
359 }
360
361
362 static void
nxt_python_asgi_ctx_data_free(void * data)363 nxt_python_asgi_ctx_data_free(void *data)
364 {
365 nxt_py_asgi_ctx_data_t *ctx_data;
366
367 ctx_data = data;
368
369 Py_XDECREF(ctx_data->loop_run_until_complete);
370 Py_XDECREF(ctx_data->loop_create_future);
371 Py_XDECREF(ctx_data->loop_create_task);
372 Py_XDECREF(ctx_data->loop_call_soon);
373 Py_XDECREF(ctx_data->loop_add_reader);
374 Py_XDECREF(ctx_data->loop_remove_reader);
375 Py_XDECREF(ctx_data->quit_future);
376 Py_XDECREF(ctx_data->quit_future_set_result);
377
378 nxt_unit_free(NULL, ctx_data);
379 }
380
381
382 static int
nxt_python_asgi_startup(void * data)383 nxt_python_asgi_startup(void *data)
384 {
385 return nxt_py_asgi_lifespan_startup(data);
386 }
387
388
389 static int
nxt_python_asgi_run(nxt_unit_ctx_t * ctx)390 nxt_python_asgi_run(nxt_unit_ctx_t *ctx)
391 {
392 PyObject *res;
393 nxt_py_asgi_ctx_data_t *ctx_data;
394
395 ctx_data = ctx->data;
396
397 res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
398 ctx_data->quit_future, NULL);
399 if (nxt_slow_path(res == NULL)) {
400 nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete");
401 nxt_python_print_exception();
402
403 return NXT_UNIT_ERROR;
404 }
405
406 Py_DECREF(res);
407
408 nxt_py_asgi_lifespan_shutdown(ctx);
409
410 return NXT_UNIT_OK;
411 }
412
413
414 static void
nxt_py_asgi_remove_reader(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)415 nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
416 {
417 PyObject *res, *fd;
418 nxt_py_asgi_ctx_data_t *ctx_data;
419
420 if (port == NULL || port->in_fd == -1) {
421 return;
422 }
423
424 ctx_data = ctx->data;
425
426 nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port);
427
428 fd = PyLong_FromLong(port->in_fd);
429 if (nxt_slow_path(fd == NULL)) {
430 nxt_unit_alert(ctx, "Python failed to create Long object");
431 nxt_python_print_exception();
432
433 return;
434 }
435
436 res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL);
437 if (nxt_slow_path(res == NULL)) {
438 nxt_unit_alert(ctx, "Python failed to remove_reader");
439 nxt_python_print_exception();
440
441 } else {
442 Py_DECREF(res);
443 }
444
445 Py_DECREF(fd);
446 }
447
448
449 static void
nxt_py_asgi_request_handler(nxt_unit_request_info_t * req)450 nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
451 {
452 PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
453 PyObject *state, *newstate, *lifespan;
454 PyObject *stage2;
455 nxt_python_target_t *target;
456 nxt_py_asgi_ctx_data_t *ctx_data;
457
458 if (req->request->websocket_handshake) {
459 asgi = nxt_py_asgi_websocket_create(req);
460
461 } else {
462 asgi = nxt_py_asgi_http_create(req);
463 }
464
465 if (nxt_slow_path(asgi == NULL)) {
466 nxt_unit_req_alert(req, "Python failed to create asgi object");
467 nxt_unit_request_done(req, NXT_UNIT_ERROR);
468
469 return;
470 }
471
472 receive = PyObject_GetAttrString(asgi, "receive");
473 if (nxt_slow_path(receive == NULL)) {
474 nxt_unit_req_alert(req, "Python failed to get 'receive' method");
475 nxt_unit_request_done(req, NXT_UNIT_ERROR);
476
477 goto release_asgi;
478 }
479
480 send = PyObject_GetAttrString(asgi, "send");
481 if (nxt_slow_path(send == NULL)) {
482 nxt_unit_req_alert(req, "Python failed to get 'send' method");
483 nxt_unit_request_done(req, NXT_UNIT_ERROR);
484
485 goto release_receive;
486 }
487
488 done = PyObject_GetAttrString(asgi, "_done");
489 if (nxt_slow_path(done == NULL)) {
490 nxt_unit_req_alert(req, "Python failed to get '_done' method");
491 nxt_unit_request_done(req, NXT_UNIT_ERROR);
492
493 goto release_send;
494 }
495
496 req->data = asgi;
497 ctx_data = req->ctx->data;
498 target = &nxt_py_targets->target[req->request->app_target];
499 lifespan = ctx_data->target_lifespans[req->request->app_target];
500 state = PyObject_GetAttr(lifespan, nxt_py_state_str);
501 if (nxt_slow_path(state == NULL)) {
502 nxt_unit_req_alert(req, "Python failed to get 'state' attribute");
503 nxt_unit_request_done(req, NXT_UNIT_ERROR);
504
505 goto release_done;
506 }
507
508 newstate = PyDict_Copy(state);
509 if (nxt_slow_path(newstate == NULL)) {
510 nxt_unit_req_alert(req, "Python failed to call state.copy()");
511 nxt_unit_request_done(req, NXT_UNIT_ERROR);
512 Py_DECREF(state);
513 goto release_done;
514 }
515 Py_DECREF(state);
516
517 scope = nxt_py_asgi_create_http_scope(req, target);
518 if (nxt_slow_path(scope == NULL)) {
519 nxt_unit_request_done(req, NXT_UNIT_ERROR);
520 Py_DECREF(newstate);
521 goto release_done;
522 }
523
524 if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str, newstate)
525 == -1))
526 {
527 Py_DECREF(newstate);
528 goto release_scope;
529 }
530 Py_DECREF(newstate);
531
532 if (!target->asgi_legacy) {
533 nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
534
535 res = PyObject_CallFunctionObjArgs(target->application,
536 scope, receive, send, NULL);
537
538 } else {
539 nxt_unit_req_debug(req, "Python call legacy application");
540
541 res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
542
543 if (nxt_slow_path(res == NULL)) {
544 nxt_unit_req_error(req, "Python failed to call legacy app stage1");
545 nxt_python_print_exception();
546 nxt_unit_request_done(req, NXT_UNIT_ERROR);
547
548 goto release_scope;
549 }
550
551 if (nxt_slow_path(PyCallable_Check(res) == 0)) {
552 nxt_unit_req_error(req,
553 "Legacy ASGI application returns not a callable");
554 nxt_unit_request_done(req, NXT_UNIT_ERROR);
555
556 Py_DECREF(res);
557
558 goto release_scope;
559 }
560
561 stage2 = res;
562
563 res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
564
565 Py_DECREF(stage2);
566 }
567
568 if (nxt_slow_path(res == NULL)) {
569 nxt_unit_req_error(req, "Python failed to call the application");
570 nxt_python_print_exception();
571 nxt_unit_request_done(req, NXT_UNIT_ERROR);
572
573 goto release_scope;
574 }
575
576 if (nxt_slow_path(!PyCoro_CheckExact(res))) {
577 nxt_unit_req_error(req, "Application result type is not a coroutine");
578 nxt_unit_request_done(req, NXT_UNIT_ERROR);
579
580 Py_DECREF(res);
581
582 goto release_scope;
583 }
584
585
586 task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
587 if (nxt_slow_path(task == NULL)) {
588 nxt_unit_req_error(req, "Python failed to call the create_task");
589 nxt_python_print_exception();
590 nxt_unit_request_done(req, NXT_UNIT_ERROR);
591
592 Py_DECREF(res);
593
594 goto release_scope;
595 }
596
597 Py_DECREF(res);
598
599 res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done,
600 NULL);
601 if (nxt_slow_path(res == NULL)) {
602 nxt_unit_req_error(req,
603 "Python failed to call 'task.add_done_callback'");
604 nxt_python_print_exception();
605 nxt_unit_request_done(req, NXT_UNIT_ERROR);
606
607 goto release_task;
608 }
609
610 Py_DECREF(res);
611 release_task:
612 Py_DECREF(task);
613 release_scope:
614 Py_DECREF(scope);
615 release_done:
616 Py_DECREF(done);
617 release_send:
618 Py_DECREF(send);
619 release_receive:
620 Py_DECREF(receive);
621 release_asgi:
622 Py_DECREF(asgi);
623 }
624
625
626 static void
nxt_py_asgi_close_handler(nxt_unit_request_info_t * req)627 nxt_py_asgi_close_handler(nxt_unit_request_info_t *req)
628 {
629 if (req->request->websocket_handshake) {
630 nxt_py_asgi_websocket_close_handler(req);
631
632 } else {
633 nxt_py_asgi_http_close_handler(req);
634 }
635 }
636
637
638 static PyObject *
nxt_py_asgi_create_http_scope(nxt_unit_request_info_t * req,nxt_python_target_t * app_target)639 nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req,
640 nxt_python_target_t *app_target)
641 {
642 char *p, *target, *query;
643 uint32_t target_length, i, path_length;
644 PyObject *scope, *v, *type, *scheme;
645 PyObject *headers, *header;
646 nxt_str_t prefix;
647 nxt_unit_field_t *f;
648 nxt_unit_request_t *r;
649
650 static const nxt_str_t ws_protocol = nxt_string("sec-websocket-protocol");
651
652 #define SET_ITEM(dict, key, value) \
653 if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \
654 == -1)) \
655 { \
656 nxt_unit_req_alert(req, "Python failed to set '" \
657 #dict "." #key "' item"); \
658 goto fail; \
659 }
660
661 v = NULL;
662 headers = NULL;
663
664 r = req->request;
665
666 if (r->websocket_handshake) {
667 type = nxt_py_websocket_str;
668 scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str;
669
670 } else {
671 type = nxt_py_http_str;
672 scheme = r->tls ? nxt_py_https_str : nxt_py_http_str;
673 }
674
675 scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str);
676 if (nxt_slow_path(scope == NULL)) {
677 return NULL;
678 }
679
680 prefix = app_target->prefix;
681 path_length = r->path_length;
682 p = nxt_unit_sptr_get(&r->path);
683 if (prefix.length > 0
684 && ((path_length > prefix.length && p[prefix.length] == '/')
685 || path_length == prefix.length)
686 && memcmp(prefix.start, p, prefix.length) == 0)
687 {
688 SET_ITEM(scope, root_path, app_target->py_prefix);
689 }
690
691 p = nxt_unit_sptr_get(&r->version);
692 SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str
693 : nxt_py_1_0_str)
694 SET_ITEM(scope, scheme, scheme)
695
696 v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method),
697 r->method_length);
698 if (nxt_slow_path(v == NULL)) {
699 nxt_unit_req_alert(req, "Python failed to create 'method' string");
700 goto fail;
701 }
702
703 SET_ITEM(scope, method, v)
704 Py_DECREF(v);
705
706 v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length,
707 "replace");
708 if (nxt_slow_path(v == NULL)) {
709 nxt_unit_req_alert(req, "Python failed to create 'path' string");
710 goto fail;
711 }
712
713 SET_ITEM(scope, path, v)
714 Py_DECREF(v);
715
716 target = nxt_unit_sptr_get(&r->target);
717 query = nxt_unit_sptr_get(&r->query);
718
719 if (r->query.offset != 0) {
720 target_length = query - target - 1;
721
722 } else {
723 target_length = r->target_length;
724 }
725
726 v = PyBytes_FromStringAndSize(target, target_length);
727 if (nxt_slow_path(v == NULL)) {
728 nxt_unit_req_alert(req, "Python failed to create 'raw_path' string");
729 goto fail;
730 }
731
732 SET_ITEM(scope, raw_path, v)
733 Py_DECREF(v);
734
735 v = PyBytes_FromStringAndSize(query, r->query_length);
736 if (nxt_slow_path(v == NULL)) {
737 nxt_unit_req_alert(req, "Python failed to create 'query' string");
738 goto fail;
739 }
740
741 SET_ITEM(scope, query_string, v)
742 Py_DECREF(v);
743
744 v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0);
745 if (nxt_slow_path(v == NULL)) {
746 nxt_unit_req_alert(req, "Python failed to create 'client' pair");
747 goto fail;
748 }
749
750 SET_ITEM(scope, client, v)
751 Py_DECREF(v);
752
753 v = nxt_py_asgi_create_address(&r->local_addr, r->local_addr_length, 80);
754 if (nxt_slow_path(v == NULL)) {
755 nxt_unit_req_alert(req, "Python failed to create 'server' pair");
756 goto fail;
757 }
758
759 SET_ITEM(scope, server, v)
760 Py_DECREF(v);
761
762 v = NULL;
763
764 headers = PyTuple_New(r->fields_count);
765 if (nxt_slow_path(headers == NULL)) {
766 nxt_unit_req_alert(req, "Python failed to create 'headers' object");
767 goto fail;
768 }
769
770 for (i = 0; i < r->fields_count; i++) {
771 f = r->fields + i;
772
773 header = nxt_py_asgi_create_header(f);
774 if (nxt_slow_path(header == NULL)) {
775 nxt_unit_req_alert(req, "Python failed to create 'header' pair");
776 goto fail;
777 }
778
779 PyTuple_SET_ITEM(headers, i, header);
780
781 if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL
782 && f->name_length == ws_protocol.length
783 && f->value_length > 0
784 && r->websocket_handshake)
785 {
786 v = nxt_py_asgi_create_subprotocols(f);
787 if (nxt_slow_path(v == NULL)) {
788 nxt_unit_req_alert(req, "Failed to create subprotocols");
789 goto fail;
790 }
791
792 SET_ITEM(scope, subprotocols, v);
793 Py_DECREF(v);
794 }
795 }
796
797 SET_ITEM(scope, headers, headers)
798 Py_DECREF(headers);
799
800 return scope;
801
802 fail:
803
804 Py_XDECREF(v);
805 Py_XDECREF(headers);
806 Py_DECREF(scope);
807
808 return NULL;
809
810 #undef SET_ITEM
811 }
812
813
814 static PyObject *
nxt_py_asgi_create_address(nxt_unit_sptr_t * sptr,uint8_t len,uint16_t port)815 nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
816 {
817 #if (NXT_HAVE_UNIX_DOMAIN)
818 size_t prefix_len;
819 PyObject *pair, *v;
820 nxt_str_t addr;
821
822 addr.length = len;
823 addr.start = nxt_unit_sptr_get(sptr);
824
825 prefix_len = nxt_length("unix:");
826 if (nxt_str_start(&addr, "unix:", prefix_len)) {
827
828 pair = PyTuple_New(2);
829 if (nxt_slow_path(pair == NULL)) {
830 return NULL;
831 }
832
833 addr.start += prefix_len;
834 addr.length -= prefix_len;
835
836 v = PyString_FromStringAndSize((const char *) addr.start, addr.length);
837 if (nxt_slow_path(v == NULL)) {
838 Py_DECREF(pair);
839
840 return NULL;
841 }
842
843 PyTuple_SET_ITEM(pair, 0, v);
844 PyTuple_SET_ITEM(pair, 1, Py_None);
845
846 return pair;
847 }
848
849 #endif
850 return nxt_py_asgi_create_ip_address(sptr, len, port);
851 }
852
853
854 static PyObject *
nxt_py_asgi_create_ip_address(nxt_unit_sptr_t * sptr,uint8_t len,uint16_t port)855 nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
856 {
857 char *p;
858 PyObject *pair, *v;
859
860 pair = PyTuple_New(2);
861 if (nxt_slow_path(pair == NULL)) {
862 return NULL;
863 }
864
865 p = nxt_unit_sptr_get(sptr);
866
867 v = PyString_FromStringAndSize(p, len);
868 if (nxt_slow_path(v == NULL)) {
869 Py_DECREF(pair);
870
871 return NULL;
872 }
873
874 PyTuple_SET_ITEM(pair, 0, v);
875
876 v = PyLong_FromLong(port);
877 if (nxt_slow_path(v == NULL)) {
878 Py_DECREF(pair);
879
880 return NULL;
881 }
882
883 PyTuple_SET_ITEM(pair, 1, v);
884
885 return pair;
886 }
887
888
889 static PyObject *
nxt_py_asgi_create_header(nxt_unit_field_t * f)890 nxt_py_asgi_create_header(nxt_unit_field_t *f)
891 {
892 char c, *name;
893 uint8_t pos;
894 PyObject *header, *v;
895
896 header = PyTuple_New(2);
897 if (nxt_slow_path(header == NULL)) {
898 return NULL;
899 }
900
901 name = nxt_unit_sptr_get(&f->name);
902
903 for (pos = 0; pos < f->name_length; pos++) {
904 c = name[pos];
905 if (c >= 'A' && c <= 'Z') {
906 name[pos] = (c | 0x20);
907 }
908 }
909
910 v = PyBytes_FromStringAndSize(name, f->name_length);
911 if (nxt_slow_path(v == NULL)) {
912 Py_DECREF(header);
913
914 return NULL;
915 }
916
917 PyTuple_SET_ITEM(header, 0, v);
918
919 v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value),
920 f->value_length);
921 if (nxt_slow_path(v == NULL)) {
922 Py_DECREF(header);
923
924 return NULL;
925 }
926
927 PyTuple_SET_ITEM(header, 1, v);
928
929 return header;
930 }
931
932
933 static PyObject *
nxt_py_asgi_create_subprotocols(nxt_unit_field_t * f)934 nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f)
935 {
936 char *v;
937 uint32_t i, n, start;
938 PyObject *res, *proto;
939
940 v = nxt_unit_sptr_get(&f->value);
941 n = 1;
942
943 for (i = 0; i < f->value_length; i++) {
944 if (v[i] == ',') {
945 n++;
946 }
947 }
948
949 res = PyTuple_New(n);
950 if (nxt_slow_path(res == NULL)) {
951 return NULL;
952 }
953
954 n = 0;
955 start = 0;
956
957 for (i = 0; i < f->value_length; ) {
958 if (v[i] != ',') {
959 i++;
960
961 continue;
962 }
963
964 if (i - start > 0) {
965 proto = PyString_FromStringAndSize(v + start, i - start);
966 if (nxt_slow_path(proto == NULL)) {
967 goto fail;
968 }
969
970 PyTuple_SET_ITEM(res, n, proto);
971
972 n++;
973 }
974
975 do {
976 i++;
977 } while (i < f->value_length && v[i] == ' ');
978
979 start = i;
980 }
981
982 if (i - start > 0) {
983 proto = PyString_FromStringAndSize(v + start, i - start);
984 if (nxt_slow_path(proto == NULL)) {
985 goto fail;
986 }
987
988 PyTuple_SET_ITEM(res, n, proto);
989 }
990
991 return res;
992
993 fail:
994
995 Py_DECREF(res);
996
997 return NULL;
998 }
999
1000
1001 static int
nxt_py_asgi_add_port(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)1002 nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
1003 {
1004 int nb;
1005
1006 if (port->in_fd == -1) {
1007 return NXT_UNIT_OK;
1008 }
1009
1010 nb = 1;
1011
1012 if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) {
1013 nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)",
1014 port->in_fd, strerror(errno), errno);
1015
1016 return NXT_UNIT_ERROR;
1017 }
1018
1019 nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port);
1020
1021 return nxt_py_asgi_add_reader(ctx, port);
1022 }
1023
1024
1025 static int
nxt_py_asgi_add_reader(nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)1026 nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
1027 {
1028 int rc;
1029 PyObject *res, *fd, *py_ctx, *py_port;
1030 nxt_py_asgi_ctx_data_t *ctx_data;
1031
1032 nxt_unit_debug(ctx, "asgi_add_reader %d %p %p", port->in_fd, ctx, port);
1033
1034 ctx_data = ctx->data;
1035
1036 fd = PyLong_FromLong(port->in_fd);
1037 if (nxt_slow_path(fd == NULL)) {
1038 nxt_unit_alert(ctx, "Python failed to create fd");
1039 nxt_python_print_exception();
1040
1041 return NXT_UNIT_ERROR;
1042 }
1043
1044 rc = NXT_UNIT_ERROR;
1045
1046 py_ctx = PyLong_FromVoidPtr(ctx);
1047 if (nxt_slow_path(py_ctx == NULL)) {
1048 nxt_unit_alert(ctx, "Python failed to create py_ctx");
1049 nxt_python_print_exception();
1050
1051 goto clean_fd;
1052 }
1053
1054 py_port = PyLong_FromVoidPtr(port);
1055 if (nxt_slow_path(py_port == NULL)) {
1056 nxt_unit_alert(ctx, "Python failed to create py_port");
1057 nxt_python_print_exception();
1058
1059 goto clean_py_ctx;
1060 }
1061
1062 res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
1063 fd, nxt_py_port_read,
1064 py_ctx, py_port, NULL);
1065 if (nxt_slow_path(res == NULL)) {
1066 nxt_unit_alert(ctx, "Python failed to add_reader");
1067 nxt_python_print_exception();
1068
1069 } else {
1070 Py_DECREF(res);
1071
1072 rc = NXT_UNIT_OK;
1073 }
1074
1075 Py_DECREF(py_port);
1076
1077 clean_py_ctx:
1078
1079 Py_DECREF(py_ctx);
1080
1081 clean_fd:
1082
1083 Py_DECREF(fd);
1084
1085 return rc;
1086 }
1087
1088
1089 static void
nxt_py_asgi_remove_port(nxt_unit_t * lib,nxt_unit_ctx_t * ctx,nxt_unit_port_t * port)1090 nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx,
1091 nxt_unit_port_t *port)
1092 {
1093 if (port->in_fd == -1 || ctx == NULL) {
1094 return;
1095 }
1096
1097 nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port);
1098
1099 nxt_py_asgi_remove_reader(ctx, port);
1100 }
1101
1102
1103 static void
nxt_py_asgi_quit(nxt_unit_ctx_t * ctx)1104 nxt_py_asgi_quit(nxt_unit_ctx_t *ctx)
1105 {
1106 PyObject *res, *p;
1107 nxt_py_asgi_ctx_data_t *ctx_data;
1108
1109 nxt_unit_debug(ctx, "asgi_quit %p", ctx);
1110
1111 ctx_data = ctx->data;
1112
1113 p = PyLong_FromLong(0);
1114 if (nxt_slow_path(p == NULL)) {
1115 nxt_unit_alert(NULL, "Python failed to create Long");
1116 nxt_python_print_exception();
1117
1118 } else {
1119 res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result,
1120 p, NULL);
1121 if (nxt_slow_path(res == NULL)) {
1122 nxt_unit_alert(ctx, "Python failed to set_result");
1123 nxt_python_print_exception();
1124
1125 } else {
1126 Py_DECREF(res);
1127 }
1128
1129 Py_DECREF(p);
1130 }
1131 }
1132
1133
1134 static void
nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t * ctx)1135 nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx)
1136 {
1137 int rc;
1138 nxt_queue_link_t *lnk;
1139 nxt_py_asgi_ctx_data_t *ctx_data;
1140
1141 ctx_data = ctx->data;
1142
1143 while (!nxt_queue_is_empty(&ctx_data->drain_queue)) {
1144 lnk = nxt_queue_first(&ctx_data->drain_queue);
1145
1146 rc = nxt_py_asgi_http_drain(lnk);
1147 if (rc == NXT_UNIT_AGAIN) {
1148 return;
1149 }
1150
1151 nxt_queue_remove(lnk);
1152 }
1153 }
1154
1155
1156 static PyObject *
nxt_py_asgi_port_read(PyObject * self,PyObject * args)1157 nxt_py_asgi_port_read(PyObject *self, PyObject *args)
1158 {
1159 int rc;
1160 PyObject *arg0, *arg1, *res;
1161 Py_ssize_t n;
1162 nxt_unit_ctx_t *ctx;
1163 nxt_unit_port_t *port;
1164 nxt_py_asgi_ctx_data_t *ctx_data;
1165
1166 n = PyTuple_GET_SIZE(args);
1167
1168 if (n != 2) {
1169 nxt_unit_alert(NULL,
1170 "nxt_py_asgi_port_read: invalid number of arguments %d",
1171 (int) n);
1172
1173 return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
1174 }
1175
1176 arg0 = PyTuple_GET_ITEM(args, 0);
1177 if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) {
1178 return PyErr_Format(PyExc_TypeError,
1179 "the first argument is not a long");
1180 }
1181
1182 ctx = PyLong_AsVoidPtr(arg0);
1183
1184 arg1 = PyTuple_GET_ITEM(args, 1);
1185 if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) {
1186 return PyErr_Format(PyExc_TypeError,
1187 "the second argument is not a long");
1188 }
1189
1190 port = PyLong_AsVoidPtr(arg1);
1191
1192 rc = nxt_unit_process_port_msg(ctx, port);
1193
1194 nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc);
1195
1196 if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
1197 return PyErr_Format(PyExc_RuntimeError,
1198 "error processing port %d message", port->id.id);
1199 }
1200
1201 if (rc == NXT_UNIT_OK) {
1202 ctx_data = ctx->data;
1203
1204 res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon,
1205 nxt_py_port_read,
1206 arg0, arg1, NULL);
1207 if (nxt_slow_path(res == NULL)) {
1208 nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'");
1209 nxt_python_print_exception();
1210 }
1211
1212 Py_XDECREF(res);
1213 }
1214
1215 Py_RETURN_NONE;
1216 }
1217
1218
1219 PyObject *
nxt_py_asgi_enum_headers(PyObject * headers,nxt_py_asgi_enum_header_cb cb,void * data)1220 nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb,
1221 void *data)
1222 {
1223 int i;
1224 PyObject *iter, *header, *h_iter, *name, *val, *res;
1225
1226 iter = PyObject_GetIter(headers);
1227 if (nxt_slow_path(iter == NULL)) {
1228 return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable");
1229 }
1230
1231 for (i = 0; /* void */; i++) {
1232 header = PyIter_Next(iter);
1233 if (header == NULL) {
1234 break;
1235 }
1236
1237 h_iter = PyObject_GetIter(header);
1238 if (nxt_slow_path(h_iter == NULL)) {
1239 Py_DECREF(header);
1240 Py_DECREF(iter);
1241
1242 return PyErr_Format(PyExc_TypeError,
1243 "'headers' item #%d is not an iterable", i);
1244 }
1245
1246 name = PyIter_Next(h_iter);
1247 if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) {
1248 Py_XDECREF(name);
1249 Py_DECREF(h_iter);
1250 Py_DECREF(header);
1251 Py_DECREF(iter);
1252
1253 return PyErr_Format(PyExc_TypeError,
1254 "'headers' item #%d 'name' is not a byte string", i);
1255 }
1256
1257 val = PyIter_Next(h_iter);
1258 if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) {
1259 Py_XDECREF(val);
1260 Py_DECREF(h_iter);
1261 Py_DECREF(header);
1262 Py_DECREF(iter);
1263
1264 return PyErr_Format(PyExc_TypeError,
1265 "'headers' item #%d 'value' is not a byte string", i);
1266 }
1267
1268 res = cb(data, i, name, val);
1269
1270 Py_DECREF(name);
1271 Py_DECREF(val);
1272 Py_DECREF(h_iter);
1273 Py_DECREF(header);
1274
1275 if (nxt_slow_path(res == NULL)) {
1276 Py_DECREF(iter);
1277
1278 return NULL;
1279 }
1280
1281 Py_DECREF(res);
1282 }
1283
1284 Py_DECREF(iter);
1285
1286 Py_RETURN_NONE;
1287 }
1288
1289
1290 PyObject *
nxt_py_asgi_calc_size(void * data,int i,PyObject * name,PyObject * val)1291 nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val)
1292 {
1293 nxt_py_asgi_calc_size_ctx_t *ctx;
1294
1295 ctx = data;
1296
1297 ctx->fields_count++;
1298 ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val);
1299
1300 Py_RETURN_NONE;
1301 }
1302
1303
1304 PyObject *
nxt_py_asgi_add_field(void * data,int i,PyObject * name,PyObject * val)1305 nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val)
1306 {
1307 int rc;
1308 char *name_str, *val_str;
1309 uint32_t name_len, val_len;
1310 nxt_off_t content_length;
1311 nxt_unit_request_info_t *req;
1312 nxt_py_asgi_add_field_ctx_t *ctx;
1313
1314 name_str = PyBytes_AS_STRING(name);
1315 name_len = PyBytes_GET_SIZE(name);
1316
1317 val_str = PyBytes_AS_STRING(val);
1318 val_len = PyBytes_GET_SIZE(val);
1319
1320 ctx = data;
1321 req = ctx->req;
1322
1323 rc = nxt_unit_response_add_field(req, name_str, name_len,
1324 val_str, val_len);
1325 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1326 return PyErr_Format(PyExc_RuntimeError,
1327 "failed to add header #%d", i);
1328 }
1329
1330 if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
1331 content_length = nxt_off_t_parse((u_char *) val_str, val_len);
1332 if (nxt_slow_path(content_length < 0)) {
1333 nxt_unit_req_error(req, "failed to parse Content-Length "
1334 "value %.*s", (int) val_len, val_str);
1335
1336 return PyErr_Format(PyExc_ValueError,
1337 "Failed to parse Content-Length: '%.*s'",
1338 (int) val_len, val_str);
1339 }
1340
1341 ctx->content_length = content_length;
1342 }
1343
1344 Py_RETURN_NONE;
1345 }
1346
1347
1348 PyObject *
nxt_py_asgi_set_result_soon(nxt_unit_request_info_t * req,nxt_py_asgi_ctx_data_t * ctx_data,PyObject * future,PyObject * result)1349 nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
1350 nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result)
1351 {
1352 PyObject *set_result, *res;
1353
1354 if (nxt_slow_path(result == NULL)) {
1355 Py_DECREF(future);
1356
1357 return NULL;
1358 }
1359
1360 set_result = PyObject_GetAttrString(future, "set_result");
1361 if (nxt_slow_path(set_result == NULL)) {
1362 nxt_unit_req_alert(req, "failed to get 'set_result' for future");
1363
1364 Py_CLEAR(future);
1365
1366 goto cleanup_result;
1367 }
1368
1369 if (nxt_slow_path(PyCallable_Check(set_result) == 0)) {
1370 nxt_unit_req_alert(req, "'future.set_result' is not a callable");
1371
1372 Py_CLEAR(future);
1373
1374 goto cleanup;
1375 }
1376
1377 res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result,
1378 result, NULL);
1379 if (nxt_slow_path(res == NULL)) {
1380 nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'");
1381 nxt_python_print_exception();
1382
1383 Py_CLEAR(future);
1384 }
1385
1386 Py_XDECREF(res);
1387
1388 cleanup:
1389
1390 Py_DECREF(set_result);
1391
1392 cleanup_result:
1393
1394 Py_DECREF(result);
1395
1396 return future;
1397 }
1398
1399
1400 PyObject *
nxt_py_asgi_new_msg(nxt_unit_request_info_t * req,PyObject * type)1401 nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type)
1402 {
1403 PyObject *msg;
1404
1405 msg = PyDict_New();
1406 if (nxt_slow_path(msg == NULL)) {
1407 nxt_unit_req_alert(req, "Python failed to create message dict");
1408 nxt_python_print_exception();
1409
1410 return PyErr_Format(PyExc_RuntimeError,
1411 "failed to create message dict");
1412 }
1413
1414 if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) {
1415 nxt_unit_req_alert(req, "Python failed to set 'msg.type' item");
1416
1417 Py_DECREF(msg);
1418
1419 return PyErr_Format(PyExc_RuntimeError,
1420 "failed to set 'msg.type' item");
1421 }
1422
1423 return msg;
1424 }
1425
1426
1427 PyObject *
nxt_py_asgi_new_scope(nxt_unit_request_info_t * req,PyObject * type,PyObject * spec_version)1428 nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
1429 PyObject *spec_version)
1430 {
1431 PyObject *scope, *asgi;
1432
1433 scope = PyDict_New();
1434 if (nxt_slow_path(scope == NULL)) {
1435 nxt_unit_req_alert(req, "Python failed to create 'scope' dict");
1436 nxt_python_print_exception();
1437
1438 return PyErr_Format(PyExc_RuntimeError,
1439 "failed to create 'scope' dict");
1440 }
1441
1442 if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) {
1443 nxt_unit_req_alert(req, "Python failed to set 'scope.type' item");
1444
1445 Py_DECREF(scope);
1446
1447 return PyErr_Format(PyExc_RuntimeError,
1448 "failed to set 'scope.type' item");
1449 }
1450
1451 asgi = PyDict_New();
1452 if (nxt_slow_path(asgi == NULL)) {
1453 nxt_unit_req_alert(req, "Python failed to create 'asgi' dict");
1454 nxt_python_print_exception();
1455
1456 Py_DECREF(scope);
1457
1458 return PyErr_Format(PyExc_RuntimeError,
1459 "failed to create 'asgi' dict");
1460 }
1461
1462 if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) {
1463 nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item");
1464
1465 Py_DECREF(asgi);
1466 Py_DECREF(scope);
1467
1468 return PyErr_Format(PyExc_RuntimeError,
1469 "failed to set 'scope.asgi' item");
1470 }
1471
1472 if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str,
1473 nxt_py_3_0_str) == -1))
1474 {
1475 nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item");
1476
1477 Py_DECREF(asgi);
1478 Py_DECREF(scope);
1479
1480 return PyErr_Format(PyExc_RuntimeError,
1481 "failed to set 'asgi.version' item");
1482 }
1483
1484 if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str,
1485 spec_version) == -1))
1486 {
1487 nxt_unit_req_alert(req,
1488 "Python failed to set 'asgi.spec_version' item");
1489
1490 Py_DECREF(asgi);
1491 Py_DECREF(scope);
1492
1493 return PyErr_Format(PyExc_RuntimeError,
1494 "failed to set 'asgi.spec_version' item");
1495 }
1496
1497 Py_DECREF(asgi);
1498
1499 return scope;
1500 }
1501
1502
1503 void
nxt_py_asgi_drain_wait(nxt_unit_request_info_t * req,nxt_queue_link_t * link)1504 nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link)
1505 {
1506 nxt_py_asgi_ctx_data_t *ctx_data;
1507
1508 ctx_data = req->ctx->data;
1509
1510 nxt_queue_insert_tail(&ctx_data->drain_queue, link);
1511 }
1512
1513
1514 void
nxt_py_asgi_dealloc(PyObject * self)1515 nxt_py_asgi_dealloc(PyObject *self)
1516 {
1517 PyObject_Del(self);
1518 }
1519
1520
1521 PyObject *
nxt_py_asgi_await(PyObject * self)1522 nxt_py_asgi_await(PyObject *self)
1523 {
1524 Py_INCREF(self);
1525 return self;
1526 }
1527
1528
1529 PyObject *
nxt_py_asgi_iter(PyObject * self)1530 nxt_py_asgi_iter(PyObject *self)
1531 {
1532 Py_INCREF(self);
1533 return self;
1534 }
1535
1536
1537 PyObject *
nxt_py_asgi_next(PyObject * self)1538 nxt_py_asgi_next(PyObject *self)
1539 {
1540 return NULL;
1541 }
1542
1543
1544 static void
nxt_python_asgi_done(void)1545 nxt_python_asgi_done(void)
1546 {
1547 nxt_py_asgi_str_done();
1548
1549 Py_XDECREF(nxt_py_port_read);
1550 }
1551
1552 #else /* !(NXT_HAVE_ASGI) */
1553
1554
1555 int
nxt_python_asgi_check(PyObject * obj)1556 nxt_python_asgi_check(PyObject *obj)
1557 {
1558 return 0;
1559 }
1560
1561
1562 int
nxt_python_asgi_init(nxt_unit_init_t * init,nxt_python_proto_t * proto)1563 nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
1564 {
1565 nxt_unit_alert(NULL, "ASGI not implemented");
1566 return NXT_UNIT_ERROR;
1567 }
1568
1569
1570 #endif /* NXT_HAVE_ASGI */
1571