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