nxt_python_asgi.c (1682:2de7799a8749) nxt_python_asgi.c (1697:73a335d2911d)
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
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
19static PyObject *nxt_python_asgi_get_func(PyObject *obj);
19static int nxt_python_asgi_ctx_data_alloc(void **pdata);
20static void nxt_python_asgi_ctx_data_free(void *data);
21static int nxt_python_asgi_startup(void *data);
22static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
23
24static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
25 nxt_unit_port_t *port);
26static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);

--- 10 unchanged lines hidden (view full) ---

37static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port);
38static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
39static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
40
41static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
42static void nxt_python_asgi_done(void);
43
44
20static int nxt_python_asgi_ctx_data_alloc(void **pdata);
21static void nxt_python_asgi_ctx_data_free(void *data);
22static int nxt_python_asgi_startup(void *data);
23static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
24
25static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
26 nxt_unit_port_t *port);
27static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);

--- 10 unchanged lines hidden (view full) ---

38static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port);
39static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
40static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
41
42static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
43static void nxt_python_asgi_done(void);
44
45
46int nxt_py_asgi_legacy;
45static PyObject *nxt_py_port_read;
46static nxt_unit_port_t *nxt_py_shared_port;
47
48static PyMethodDef nxt_py_port_read_method =
49 {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
50
51static nxt_python_proto_t nxt_py_asgi_proto = {
52 .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,

--- 6 unchanged lines hidden (view full) ---

59
60#define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A
61
62
63int
64nxt_python_asgi_check(PyObject *obj)
65{
66 int res;
47static PyObject *nxt_py_port_read;
48static nxt_unit_port_t *nxt_py_shared_port;
49
50static PyMethodDef nxt_py_port_read_method =
51 {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
52
53static nxt_python_proto_t nxt_py_asgi_proto = {
54 .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,

--- 6 unchanged lines hidden (view full) ---

61
62#define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A
63
64
65int
66nxt_python_asgi_check(PyObject *obj)
67{
68 int res;
67 PyObject *call;
69 PyObject *func;
68 PyCodeObject *code;
69
70 PyCodeObject *code;
71
70 if (PyFunction_Check(obj)) {
71 code = (PyCodeObject *) PyFunction_GET_CODE(obj);
72 func = nxt_python_asgi_get_func(obj);
72
73
73 return (code->co_flags & CO_COROUTINE) != 0;
74 if (func == NULL) {
75 return 0;
74 }
75
76 }
77
78 code = (PyCodeObject *) PyFunction_GET_CODE(func);
79
80 nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with "
81 "%d argument(s)",
82 (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ",
83 code->co_argcount);
84
85 res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1;
86
87 Py_DECREF(func);
88
89 return res;
90}
91
92
93static PyObject *
94nxt_python_asgi_get_func(PyObject *obj)
95{
96 PyObject *call;
97
98 if (PyFunction_Check(obj)) {
99 Py_INCREF(obj);
100 return obj;
101 }
102
76 if (PyMethod_Check(obj)) {
77 obj = PyMethod_GET_FUNCTION(obj);
78
103 if (PyMethod_Check(obj)) {
104 obj = PyMethod_GET_FUNCTION(obj);
105
79 code = (PyCodeObject *) PyFunction_GET_CODE(obj);
80
81 return (code->co_flags & CO_COROUTINE) != 0;
106 Py_INCREF(obj);
107 return obj;
82 }
83
84 call = PyObject_GetAttrString(obj, "__call__");
85
86 if (call == NULL) {
108 }
109
110 call = PyObject_GetAttrString(obj, "__call__");
111
112 if (call == NULL) {
87 return 0;
113 return NULL;
88 }
89
90 if (PyFunction_Check(call)) {
114 }
115
116 if (PyFunction_Check(call)) {
91 code = (PyCodeObject *) PyFunction_GET_CODE(call);
117 return call;
118 }
92
119
93 res = (code->co_flags & CO_COROUTINE) != 0;
120 if (PyMethod_Check(call)) {
121 obj = PyMethod_GET_FUNCTION(call);
94
122
95 } else {
96 if (PyMethod_Check(call)) {
97 obj = PyMethod_GET_FUNCTION(call);
123 Py_INCREF(obj);
124 Py_DECREF(call);
98
125
99 code = (PyCodeObject *) PyFunction_GET_CODE(obj);
100
101 res = (code->co_flags & CO_COROUTINE) != 0;
102
103 } else {
104 res = 0;
105 }
126 return obj;
106 }
107
108 Py_DECREF(call);
109
127 }
128
129 Py_DECREF(call);
130
110 return res;
131 return NULL;
111}
112
113
114int
115nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
116{
132}
133
134
135int
136nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
137{
138 PyObject *func;
139 PyCodeObject *code;
140
117 nxt_unit_debug(NULL, "asgi_init");
118
119 if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
120 nxt_unit_alert(NULL, "Python failed to init string objects");
121 return NXT_UNIT_ERROR;
122 }
123
124 nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);

--- 6 unchanged lines hidden (view full) ---

131 if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
132 return NXT_UNIT_ERROR;
133 }
134
135 if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
136 return NXT_UNIT_ERROR;
137 }
138
141 nxt_unit_debug(NULL, "asgi_init");
142
143 if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
144 nxt_unit_alert(NULL, "Python failed to init string objects");
145 return NXT_UNIT_ERROR;
146 }
147
148 nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);

--- 6 unchanged lines hidden (view full) ---

155 if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
156 return NXT_UNIT_ERROR;
157 }
158
159 if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
160 return NXT_UNIT_ERROR;
161 }
162
163 func = nxt_python_asgi_get_func(nxt_py_application);
164 if (nxt_slow_path(func == NULL)) {
165 nxt_unit_alert(NULL, "Python cannot find function for callable");
166 return NXT_UNIT_ERROR;
167 }
168
169 code = (PyCodeObject *) PyFunction_GET_CODE(func);
170
171 if ((code->co_flags & CO_COROUTINE) == 0) {
172 nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
173 "switching to legacy mode");
174 nxt_py_asgi_legacy = 1;
175 }
176
177 Py_DECREF(func);
178
139 init->callbacks.request_handler = nxt_py_asgi_request_handler;
140 init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
141 init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
142 init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler;
143 init->callbacks.quit = nxt_py_asgi_quit;
144 init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
145 init->callbacks.add_port = nxt_py_asgi_add_port;
146 init->callbacks.remove_port = nxt_py_asgi_remove_port;

--- 214 unchanged lines hidden (view full) ---

361 Py_DECREF(fd);
362}
363
364
365static void
366nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
367{
368 PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
179 init->callbacks.request_handler = nxt_py_asgi_request_handler;
180 init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
181 init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
182 init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler;
183 init->callbacks.quit = nxt_py_asgi_quit;
184 init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
185 init->callbacks.add_port = nxt_py_asgi_add_port;
186 init->callbacks.remove_port = nxt_py_asgi_remove_port;

--- 214 unchanged lines hidden (view full) ---

401 Py_DECREF(fd);
402}
403
404
405static void
406nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
407{
408 PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
409 PyObject *stage2;
369 nxt_py_asgi_ctx_data_t *ctx_data;
370
371 if (req->request->websocket_handshake) {
372 asgi = nxt_py_asgi_websocket_create(req);
373
374 } else {
375 asgi = nxt_py_asgi_http_create(req);
376 }

--- 33 unchanged lines hidden (view full) ---

410 if (nxt_slow_path(scope == NULL)) {
411 nxt_unit_request_done(req, NXT_UNIT_ERROR);
412
413 goto release_done;
414 }
415
416 req->data = asgi;
417
410 nxt_py_asgi_ctx_data_t *ctx_data;
411
412 if (req->request->websocket_handshake) {
413 asgi = nxt_py_asgi_websocket_create(req);
414
415 } else {
416 asgi = nxt_py_asgi_http_create(req);
417 }

--- 33 unchanged lines hidden (view full) ---

451 if (nxt_slow_path(scope == NULL)) {
452 nxt_unit_request_done(req, NXT_UNIT_ERROR);
453
454 goto release_done;
455 }
456
457 req->data = asgi;
458
418 res = PyObject_CallFunctionObjArgs(nxt_py_application,
419 scope, receive, send, NULL);
459 if (!nxt_py_asgi_legacy) {
460 nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
461
462 res = PyObject_CallFunctionObjArgs(nxt_py_application,
463 scope, receive, send, NULL);
464
465 } else {
466 nxt_unit_req_debug(req, "Python call legacy application");
467
468 res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
469
470 if (nxt_slow_path(res == NULL)) {
471 nxt_unit_req_error(req, "Python failed to call legacy app stage1");
472 nxt_python_print_exception();
473 nxt_unit_request_done(req, NXT_UNIT_ERROR);
474
475 goto release_scope;
476 }
477
478 if (nxt_slow_path(PyCallable_Check(res) == 0)) {
479 nxt_unit_req_error(req,
480 "Legacy ASGI application returns not a callable");
481 nxt_unit_request_done(req, NXT_UNIT_ERROR);
482
483 Py_DECREF(res);
484
485 goto release_scope;
486 }
487
488 stage2 = res;
489
490 res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
491
492 Py_DECREF(stage2);
493 }
494
420 if (nxt_slow_path(res == NULL)) {
421 nxt_unit_req_error(req, "Python failed to call the application");
422 nxt_python_print_exception();
423 nxt_unit_request_done(req, NXT_UNIT_ERROR);
424
425 goto release_scope;
426 }
427

--- 1015 unchanged lines hidden ---
495 if (nxt_slow_path(res == NULL)) {
496 nxt_unit_req_error(req, "Python failed to call the application");
497 nxt_python_print_exception();
498 nxt_unit_request_done(req, NXT_UNIT_ERROR);
499
500 goto release_scope;
501 }
502

--- 1015 unchanged lines hidden ---