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