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