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