Python: support for multiple targets.

This commit is contained in:
Oisin Canty
2021-05-20 13:02:45 +00:00
parent 2f0cca2e2b
commit f60389a782
10 changed files with 391 additions and 84 deletions

View File

@@ -31,6 +31,12 @@ NGINX Unit updated to 1.24.0.
date="" time=""
packager="Andrei Belov <defan@nginx.com>">
<change type="feature">
<para>
multiple "targets" in Python applications.
</para>
</change>
<change type="feature">
<para>
a shim for automatic overriding "http" and "websocket" modules in Node.js.

View File

@@ -54,6 +54,7 @@ typedef struct {
nxt_str_t protocol;
uint32_t threads;
uint32_t thread_stack_size;
nxt_conf_value_t *targets;
} nxt_python_app_conf_t;

View File

@@ -100,6 +100,14 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_targets_exclusive(
nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_targets(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_target(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
@@ -518,7 +526,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = {
};
static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
static nxt_conf_vldt_object_t nxt_conf_vldt_python_common_members[] = {
{
.name = nxt_string("home"),
.type = NXT_CONF_VLDT_STRING,
@@ -526,13 +534,6 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.name = nxt_string("path"),
.type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
.validator = nxt_conf_vldt_python_path,
}, {
.name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING,
.flags = NXT_CONF_VLDT_REQUIRED,
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
}, {
.name = nxt_string("protocol"),
.type = NXT_CONF_VLDT_STRING,
@@ -550,6 +551,54 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
};
static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
{
.name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_python_targets_exclusive,
.u.string = "module",
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_python_targets_exclusive,
.u.string = "callable",
}, {
.name = nxt_string("targets"),
.type = NXT_CONF_VLDT_OBJECT,
.validator = nxt_conf_vldt_python_targets,
},
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
};
static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = {
{
.name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING,
.flags = NXT_CONF_VLDT_REQUIRED,
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
},
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = {
{
.name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING,
.flags = NXT_CONF_VLDT_REQUIRED,
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
},
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
};
static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
{
@@ -1419,6 +1468,71 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
}
static nxt_int_t
nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
nxt_conf_value_t *targets;
static nxt_str_t targets_str = nxt_string("targets");
targets = nxt_conf_get_object_member(value, &targets_str, NULL);
if (targets != NULL) {
return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_python_members);
}
return nxt_conf_vldt_object(vldt, value,
nxt_conf_vldt_python_notargets_members);
}
static nxt_int_t
nxt_conf_vldt_python_targets_exclusive(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive "
"with the \"targets\" object.", data);
}
static nxt_int_t
nxt_conf_vldt_python_targets(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
nxt_uint_t n;
n = nxt_conf_object_members_count(value);
if (n > 254) {
return nxt_conf_vldt_error(vldt, "The \"targets\" object must not "
"contain more than 254 members.");
}
return nxt_conf_vldt_object_iterator(vldt, value,
&nxt_conf_vldt_python_target);
}
static nxt_int_t
nxt_conf_vldt_python_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_value_t *value)
{
if (name->length == 0) {
return nxt_conf_vldt_error(vldt,
"The Python target name must not be empty.");
}
if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
return nxt_conf_vldt_error(vldt, "The \"%V\" Python target must be "
"an object.", name);
}
return nxt_conf_vldt_object(vldt, value,
&nxt_conf_vldt_python_target_members);
}
static nxt_int_t
nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
@@ -1959,7 +2073,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
} types[] = {
{ nxt_conf_vldt_object, nxt_conf_vldt_external_members },
{ nxt_conf_vldt_object, nxt_conf_vldt_python_members },
{ nxt_conf_vldt_python, NULL },
{ nxt_conf_vldt_php, NULL },
{ nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
{ nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },

View File

@@ -210,6 +210,12 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
offsetof(nxt_common_app_conf_t, u.python.threads),
},
{
nxt_string("targets"),
NXT_CONF_MAP_PTR,
offsetof(nxt_common_app_conf_t, u.python.targets),
},
{
nxt_string("thread_stack_size"),
NXT_CONF_MAP_INT32,

View File

@@ -24,6 +24,8 @@ typedef struct {
static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data);
static nxt_int_t nxt_python_set_target(nxt_task_t *task,
nxt_python_target_t *target, nxt_conf_value_t *conf);
static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
static int nxt_python_init_threads(nxt_python_app_conf_t *c);
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
@@ -49,7 +51,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
};
static PyObject *nxt_py_stderr_flush;
PyObject *nxt_py_application;
nxt_python_targets_t *nxt_py_targets;
#if PY_MAJOR_VERSION == 3
static wchar_t *nxt_py_home;
@@ -66,18 +68,19 @@ static nxt_int_t
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
{
int rc;
char *nxt_py_module;
size_t len;
size_t len, size;
uint32_t next;
PyObject *obj, *module;
nxt_str_t proto;
const char *callable;
nxt_str_t proto, probe_proto, name;
nxt_int_t ret, n, i;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t python_init;
nxt_conf_value_t *cv;
nxt_python_targets_t *targets;
nxt_common_app_conf_t *app_conf;
nxt_python_app_conf_t *c;
#if PY_MAJOR_VERSION == 3
char *path;
size_t size;
nxt_int_t pep405;
static const char pyvenv[] = "/pyvenv.cfg";
@@ -190,39 +193,43 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
Py_CLEAR(obj);
nxt_py_module = nxt_alloca(c->module.length + 1);
nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
nxt_py_module[c->module.length] = '\0';
n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
module = PyImport_ImportModule(nxt_py_module);
if (nxt_slow_path(module == NULL)) {
nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
nxt_python_print_exception();
size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
targets = nxt_unit_malloc(NULL, size);
if (nxt_slow_path(targets == NULL)) {
nxt_alert(task, "Could not allocate targets");
goto fail;
}
callable = (c->callable != NULL) ? c->callable : "application";
memset(targets, 0, size);
obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to get \"%s\" "
"from module \"%s\"", callable, nxt_py_module);
goto fail;
targets->count = n;
nxt_py_targets = targets;
if (c->targets != NULL) {
next = 0;
for (i = 0; /* void */; i++) {
cv = nxt_conf_next_object_member(c->targets, &name, &next);
if (cv == NULL) {
break;
}
ret = nxt_python_set_target(task, &targets->target[i], cv);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
}
} else {
ret = nxt_python_set_target(task, &targets->target[0], app_conf->self);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
}
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"%s\" in module \"%s\" "
"is not a callable object", callable, nxt_py_module);
goto fail;
}
nxt_py_application = obj;
obj = NULL;
Py_INCREF(nxt_py_application);
Py_CLEAR(module);
nxt_unit_default_init(task, &python_init);
python_init.data = c;
@@ -232,7 +239,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
proto = c->protocol;
if (proto.length == 0) {
proto = nxt_python_asgi_check(nxt_py_application) ? asgi : wsgi;
proto = nxt_python_asgi_check(targets->target[0].application)
? asgi : wsgi;
for (i = 1; i < targets->count; i++) {
probe_proto = nxt_python_asgi_check(targets->target[i].application)
? asgi : wsgi;
if (probe_proto.start != proto.start) {
nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
"specify protocol in config if incorrect");
goto fail;
}
}
}
if (nxt_strstr_eq(&proto, &asgi)) {
@@ -298,6 +316,81 @@ fail:
}
static nxt_int_t
nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
nxt_conf_value_t *conf)
{
char *callable, *module_name;
PyObject *module, *obj;
nxt_str_t str;
nxt_conf_value_t *value;
static nxt_str_t module_str = nxt_string("module");
static nxt_str_t callable_str = nxt_string("callable");
module = obj = NULL;
value = nxt_conf_get_object_member(conf, &module_str, NULL);
if (nxt_slow_path(value == NULL)) {
goto fail;
}
nxt_conf_get_string(value, &str);
module_name = nxt_alloca(str.length + 1);
nxt_memcpy(module_name, str.start, str.length);
module_name[str.length] = '\0';
module = PyImport_ImportModule(module_name);
if (nxt_slow_path(module == NULL)) {
nxt_alert(task, "Python failed to import module \"%s\"", module_name);
nxt_python_print_exception();
goto fail;
}
value = nxt_conf_get_object_member(conf, &callable_str, NULL);
if (value == NULL) {
callable = nxt_alloca(12);
nxt_memcpy(callable, "application", 12);
} else {
nxt_conf_get_string(value, &str);
callable = nxt_alloca(str.length + 1);
nxt_memcpy(callable, str.start, str.length);
callable[str.length] = '\0';
}
obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
callable, module);
goto fail;
}
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
callable, module);
goto fail;
}
target->application = obj;
obj = NULL;
Py_INCREF(target->application);
Py_CLEAR(module);
return NXT_OK;
fail:
Py_XDECREF(obj);
Py_XDECREF(module);
return NXT_ERROR;
}
static nxt_int_t
nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
{
@@ -596,12 +689,21 @@ nxt_python_done_strings(nxt_python_string_t *pstr)
static void
nxt_python_atexit(void)
{
nxt_int_t i;
if (nxt_py_proto.done != NULL) {
nxt_py_proto.done();
}
Py_XDECREF(nxt_py_stderr_flush);
Py_XDECREF(nxt_py_application);
if (nxt_py_targets != NULL) {
for (i = 0; i < nxt_py_targets->count; i++) {
Py_XDECREF(nxt_py_targets->target[i].application);
}
nxt_unit_free(NULL, nxt_py_targets);
}
Py_Finalize();

View File

@@ -37,13 +37,28 @@
#define NXT_HAVE_ASGI 1
#endif
extern PyObject *nxt_py_application;
typedef struct {
PyObject *application;
nxt_bool_t asgi_legacy;
} nxt_python_target_t;
typedef struct {
nxt_int_t count;
nxt_python_target_t target[0];
} nxt_python_targets_t;
extern nxt_python_targets_t *nxt_py_targets;
typedef struct {
nxt_str_t string;
PyObject **object_p;
} nxt_python_string_t;
typedef struct {
int (*ctx_data_alloc)(void **pdata);
void (*ctx_data_free)(void *data);

View File

@@ -43,8 +43,6 @@ static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
static void nxt_python_asgi_done(void);
int nxt_py_asgi_legacy;
static PyObject *nxt_py_port_read;
static nxt_unit_port_t *nxt_py_shared_port;
@@ -137,6 +135,7 @@ int
nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
{
PyObject *func;
nxt_int_t i;
PyCodeObject *code;
nxt_unit_debug(NULL, "asgi_init");
@@ -161,22 +160,24 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
return NXT_UNIT_ERROR;
}
func = nxt_python_asgi_get_func(nxt_py_application);
if (nxt_slow_path(func == NULL)) {
nxt_unit_alert(NULL, "Python cannot find function for callable");
return NXT_UNIT_ERROR;
for (i = 0; i < nxt_py_targets->count; i++) {
func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application);
if (nxt_slow_path(func == NULL)) {
nxt_unit_alert(NULL, "Python cannot find function for callable");
return NXT_UNIT_ERROR;
}
code = (PyCodeObject *) PyFunction_GET_CODE(func);
if ((code->co_flags & CO_COROUTINE) == 0) {
nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
"switching to legacy mode");
nxt_py_targets->target[i].asgi_legacy = 1;
}
Py_DECREF(func);
}
code = (PyCodeObject *) PyFunction_GET_CODE(func);
if ((code->co_flags & CO_COROUTINE) == 0) {
nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
"switching to legacy mode");
nxt_py_asgi_legacy = 1;
}
Py_DECREF(func);
init->callbacks.request_handler = nxt_py_asgi_request_handler;
init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
@@ -408,6 +409,7 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
{
PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
PyObject *stage2;
nxt_python_target_t *target;
nxt_py_asgi_ctx_data_t *ctx_data;
if (req->request->websocket_handshake) {
@@ -456,17 +458,18 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
}
req->data = asgi;
target = &nxt_py_targets->target[req->request->app_target];
if (!nxt_py_asgi_legacy) {
if (!target->asgi_legacy) {
nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
res = PyObject_CallFunctionObjArgs(nxt_py_application,
res = PyObject_CallFunctionObjArgs(target->application,
scope, receive, send, NULL);
} else {
nxt_unit_req_debug(req, "Python call legacy application");
res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_req_error(req, "Python failed to call legacy app stage1");

View File

@@ -33,7 +33,7 @@ typedef struct {
PyObject *loop_remove_reader;
PyObject *quit_future;
PyObject *quit_future_set_result;
PyObject *lifespan;
PyObject **target_lifespans;
nxt_unit_port_t *port;
} nxt_py_asgi_ctx_data_t;
@@ -69,6 +69,4 @@ int nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data);
int nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx);
extern int nxt_py_asgi_legacy;
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */

View File

@@ -27,7 +27,10 @@ typedef struct {
PyObject *receive_future;
} nxt_py_asgi_lifespan_t;
static PyObject *nxt_py_asgi_lifespan_target_startup(
nxt_py_asgi_ctx_data_t *ctx_data, nxt_python_target_t *target);
static int nxt_py_asgi_lifespan_target_shutdown(
nxt_py_asgi_lifespan_t *lifespan);
static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
static PyObject *nxt_py_asgi_lifespan_send_startup(
@@ -69,24 +72,60 @@ static PyTypeObject nxt_py_asgi_lifespan_type = {
int
nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
{
int rc;
size_t size;
PyObject *lifespan;
PyObject **target_lifespans;
nxt_int_t i;
nxt_python_target_t *target;
size = nxt_py_targets->count * sizeof(PyObject*);
target_lifespans = nxt_unit_malloc(NULL, size);
if (nxt_slow_path(target_lifespans == NULL)) {
nxt_unit_alert(NULL, "Failed to allocate lifespan data");
return NXT_UNIT_ERROR;
}
memset(target_lifespans, 0, size);
for (i = 0; i < nxt_py_targets->count; i++) {
target = &nxt_py_targets->target[i];
lifespan = nxt_py_asgi_lifespan_target_startup(ctx_data, target);
if (nxt_slow_path(lifespan == NULL)) {
return NXT_UNIT_ERROR;
}
target_lifespans[i] = lifespan;
}
ctx_data->target_lifespans = target_lifespans;
return NXT_UNIT_OK;
}
static PyObject *
nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data,
nxt_python_target_t *target)
{
PyObject *scope, *res, *py_task, *receive, *send, *done;
PyObject *stage2;
nxt_py_asgi_lifespan_t *lifespan;
nxt_py_asgi_lifespan_t *lifespan, *ret;
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
nxt_unit_alert(NULL,
"Python failed to initialize the 'asgi_lifespan' type object");
return NXT_UNIT_ERROR;
return NULL;
}
lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
if (nxt_slow_path(lifespan == NULL)) {
nxt_unit_alert(NULL, "Python failed to create lifespan object");
return NXT_UNIT_ERROR;
return NULL;
}
rc = NXT_UNIT_ERROR;
ret = NULL;
receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
if (nxt_slow_path(receive == NULL)) {
@@ -130,23 +169,25 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
goto release_future;
}
if (!nxt_py_asgi_legacy) {
if (!target->asgi_legacy) {
nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
res = PyObject_CallFunctionObjArgs(nxt_py_application,
res = PyObject_CallFunctionObjArgs(target->application,
scope, receive, send, NULL);
} else {
nxt_unit_req_debug(NULL, "Python call legacy application");
res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
"ASGI Lifespan processing exception");
nxt_python_print_exception();
lifespan->disabled = 1;
rc = NXT_UNIT_OK;
Py_INCREF(lifespan);
ret = lifespan;
goto release_scope;
}
@@ -211,10 +252,9 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
Py_DECREF(res);
if (lifespan->startup_sent == 1 || lifespan->disabled) {
ctx_data->lifespan = (PyObject *) lifespan;
Py_INCREF(ctx_data->lifespan);
Py_INCREF(lifespan);
rc = NXT_UNIT_OK;
ret = lifespan;
}
release_task:
@@ -232,20 +272,41 @@ release_receive:
release_lifespan:
Py_DECREF(lifespan);
return rc;
return (PyObject *) ret;
}
int
nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
{
PyObject *msg, *future, *res;
nxt_int_t i, ret;
nxt_py_asgi_lifespan_t *lifespan;
nxt_py_asgi_ctx_data_t *ctx_data;
ctx_data = ctx->data;
lifespan = (nxt_py_asgi_lifespan_t *) ctx_data->lifespan;
for (i = 0; i < nxt_py_targets->count; i++) {
lifespan = (nxt_py_asgi_lifespan_t *)ctx_data->target_lifespans[i];
ret = nxt_py_asgi_lifespan_target_shutdown(lifespan);
if (nxt_slow_path(ret != NXT_UNIT_OK)) {
return NXT_UNIT_ERROR;
}
}
nxt_unit_free(NULL, ctx_data->target_lifespans);
return NXT_UNIT_OK;
}
static int
nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t *lifespan)
{
PyObject *msg, *future, *res;
nxt_py_asgi_ctx_data_t *ctx_data;
ctx_data = lifespan->ctx_data;
if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
return NXT_UNIT_OK;

View File

@@ -302,7 +302,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
{
int rc;
PyObject *environ, *args, *response, *iterator, *item;
PyObject *close, *result;
PyObject *close, *result, *application;
nxt_bool_t prepare_environ;
nxt_python_ctx_t *pctx;
@@ -348,7 +348,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
Py_INCREF(pctx->start_resp);
PyTuple_SET_ITEM(args, 1, pctx->start_resp);
response = PyObject_CallObject(nxt_py_application, args);
application = nxt_py_targets->target[req->request->app_target].application;
response = PyObject_CallObject(application, args);
Py_DECREF(args);