Python: support for multiple targets.
This commit is contained in:
@@ -31,6 +31,12 @@ NGINX Unit updated to 1.24.0.
|
|||||||
date="" time=""
|
date="" time=""
|
||||||
packager="Andrei Belov <defan@nginx.com>">
|
packager="Andrei Belov <defan@nginx.com>">
|
||||||
|
|
||||||
|
<change type="feature">
|
||||||
|
<para>
|
||||||
|
multiple "targets" in Python applications.
|
||||||
|
</para>
|
||||||
|
</change>
|
||||||
|
|
||||||
<change type="feature">
|
<change type="feature">
|
||||||
<para>
|
<para>
|
||||||
a shim for automatic overriding "http" and "websocket" modules in Node.js.
|
a shim for automatic overriding "http" and "websocket" modules in Node.js.
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ typedef struct {
|
|||||||
nxt_str_t protocol;
|
nxt_str_t protocol;
|
||||||
uint32_t threads;
|
uint32_t threads;
|
||||||
uint32_t thread_stack_size;
|
uint32_t thread_stack_size;
|
||||||
|
nxt_conf_value_t *targets;
|
||||||
} nxt_python_app_conf_t;
|
} nxt_python_app_conf_t;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,14 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
|
|||||||
nxt_conf_value_t *value, void *data);
|
nxt_conf_value_t *value, void *data);
|
||||||
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
|
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
|
||||||
nxt_conf_value_t *value, void *data);
|
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,
|
static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
|
||||||
nxt_conf_value_t *value, void *data);
|
nxt_conf_value_t *value, void *data);
|
||||||
static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
|
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"),
|
.name = nxt_string("home"),
|
||||||
.type = NXT_CONF_VLDT_STRING,
|
.type = NXT_CONF_VLDT_STRING,
|
||||||
@@ -526,13 +534,6 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
|
|||||||
.name = nxt_string("path"),
|
.name = nxt_string("path"),
|
||||||
.type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
|
.type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
|
||||||
.validator = nxt_conf_vldt_python_path,
|
.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"),
|
.name = nxt_string("protocol"),
|
||||||
.type = NXT_CONF_VLDT_STRING,
|
.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)
|
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[] = {
|
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
|
static nxt_int_t
|
||||||
nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
|
nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
|
||||||
nxt_conf_value_t *value, void *data)
|
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[] = {
|
} types[] = {
|
||||||
{ nxt_conf_vldt_object, nxt_conf_vldt_external_members },
|
{ 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_php, NULL },
|
||||||
{ nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
|
{ nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
|
||||||
{ nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
|
{ nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
|
||||||
|
|||||||
@@ -210,6 +210,12 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
|
|||||||
offsetof(nxt_common_app_conf_t, u.python.threads),
|
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_string("thread_stack_size"),
|
||||||
NXT_CONF_MAP_INT32,
|
NXT_CONF_MAP_INT32,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ typedef struct {
|
|||||||
|
|
||||||
static nxt_int_t nxt_python_start(nxt_task_t *task,
|
static nxt_int_t nxt_python_start(nxt_task_t *task,
|
||||||
nxt_process_data_t *data);
|
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 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_init_threads(nxt_python_app_conf_t *c);
|
||||||
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
|
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;
|
static PyObject *nxt_py_stderr_flush;
|
||||||
PyObject *nxt_py_application;
|
nxt_python_targets_t *nxt_py_targets;
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION == 3
|
#if PY_MAJOR_VERSION == 3
|
||||||
static wchar_t *nxt_py_home;
|
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)
|
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
char *nxt_py_module;
|
size_t len, size;
|
||||||
size_t len;
|
uint32_t next;
|
||||||
PyObject *obj, *module;
|
PyObject *obj, *module;
|
||||||
nxt_str_t proto;
|
nxt_str_t proto, probe_proto, name;
|
||||||
const char *callable;
|
nxt_int_t ret, n, i;
|
||||||
nxt_unit_ctx_t *unit_ctx;
|
nxt_unit_ctx_t *unit_ctx;
|
||||||
nxt_unit_init_t python_init;
|
nxt_unit_init_t python_init;
|
||||||
|
nxt_conf_value_t *cv;
|
||||||
|
nxt_python_targets_t *targets;
|
||||||
nxt_common_app_conf_t *app_conf;
|
nxt_common_app_conf_t *app_conf;
|
||||||
nxt_python_app_conf_t *c;
|
nxt_python_app_conf_t *c;
|
||||||
#if PY_MAJOR_VERSION == 3
|
#if PY_MAJOR_VERSION == 3
|
||||||
char *path;
|
char *path;
|
||||||
size_t size;
|
|
||||||
nxt_int_t pep405;
|
nxt_int_t pep405;
|
||||||
|
|
||||||
static const char pyvenv[] = "/pyvenv.cfg";
|
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);
|
Py_CLEAR(obj);
|
||||||
|
|
||||||
nxt_py_module = nxt_alloca(c->module.length + 1);
|
n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
|
||||||
nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
|
|
||||||
nxt_py_module[c->module.length] = '\0';
|
|
||||||
|
|
||||||
module = PyImport_ImportModule(nxt_py_module);
|
size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
|
||||||
if (nxt_slow_path(module == NULL)) {
|
|
||||||
nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
|
targets = nxt_unit_malloc(NULL, size);
|
||||||
nxt_python_print_exception();
|
if (nxt_slow_path(targets == NULL)) {
|
||||||
|
nxt_alert(task, "Could not allocate targets");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
callable = (c->callable != NULL) ? c->callable : "application";
|
memset(targets, 0, size);
|
||||||
|
|
||||||
obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
|
targets->count = n;
|
||||||
if (nxt_slow_path(obj == NULL)) {
|
nxt_py_targets = targets;
|
||||||
nxt_alert(task, "Python failed to get \"%s\" "
|
|
||||||
"from module \"%s\"", callable, nxt_py_module);
|
if (c->targets != NULL) {
|
||||||
goto fail;
|
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);
|
nxt_unit_default_init(task, &python_init);
|
||||||
|
|
||||||
python_init.data = c;
|
python_init.data = c;
|
||||||
@@ -232,7 +239,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
|||||||
proto = c->protocol;
|
proto = c->protocol;
|
||||||
|
|
||||||
if (proto.length == 0) {
|
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)) {
|
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
|
static nxt_int_t
|
||||||
nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
|
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
|
static void
|
||||||
nxt_python_atexit(void)
|
nxt_python_atexit(void)
|
||||||
{
|
{
|
||||||
|
nxt_int_t i;
|
||||||
|
|
||||||
if (nxt_py_proto.done != NULL) {
|
if (nxt_py_proto.done != NULL) {
|
||||||
nxt_py_proto.done();
|
nxt_py_proto.done();
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_XDECREF(nxt_py_stderr_flush);
|
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();
|
Py_Finalize();
|
||||||
|
|
||||||
|
|||||||
@@ -37,13 +37,28 @@
|
|||||||
#define NXT_HAVE_ASGI 1
|
#define NXT_HAVE_ASGI 1
|
||||||
#endif
|
#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 {
|
typedef struct {
|
||||||
nxt_str_t string;
|
nxt_str_t string;
|
||||||
PyObject **object_p;
|
PyObject **object_p;
|
||||||
} nxt_python_string_t;
|
} nxt_python_string_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int (*ctx_data_alloc)(void **pdata);
|
int (*ctx_data_alloc)(void **pdata);
|
||||||
void (*ctx_data_free)(void *data);
|
void (*ctx_data_free)(void *data);
|
||||||
|
|||||||
@@ -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 PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
|
||||||
static void nxt_python_asgi_done(void);
|
static void nxt_python_asgi_done(void);
|
||||||
|
|
||||||
|
|
||||||
int nxt_py_asgi_legacy;
|
|
||||||
static PyObject *nxt_py_port_read;
|
static PyObject *nxt_py_port_read;
|
||||||
static nxt_unit_port_t *nxt_py_shared_port;
|
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)
|
nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
|
||||||
{
|
{
|
||||||
PyObject *func;
|
PyObject *func;
|
||||||
|
nxt_int_t i;
|
||||||
PyCodeObject *code;
|
PyCodeObject *code;
|
||||||
|
|
||||||
nxt_unit_debug(NULL, "asgi_init");
|
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;
|
return NXT_UNIT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
func = nxt_python_asgi_get_func(nxt_py_application);
|
for (i = 0; i < nxt_py_targets->count; i++) {
|
||||||
if (nxt_slow_path(func == NULL)) {
|
func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application);
|
||||||
nxt_unit_alert(NULL, "Python cannot find function for callable");
|
if (nxt_slow_path(func == NULL)) {
|
||||||
return NXT_UNIT_ERROR;
|
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.request_handler = nxt_py_asgi_request_handler;
|
||||||
init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
|
init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
|
||||||
init->callbacks.websocket_handler = nxt_py_asgi_websocket_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 *scope, *res, *task, *receive, *send, *done, *asgi;
|
||||||
PyObject *stage2;
|
PyObject *stage2;
|
||||||
|
nxt_python_target_t *target;
|
||||||
nxt_py_asgi_ctx_data_t *ctx_data;
|
nxt_py_asgi_ctx_data_t *ctx_data;
|
||||||
|
|
||||||
if (req->request->websocket_handshake) {
|
if (req->request->websocket_handshake) {
|
||||||
@@ -456,17 +458,18 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
req->data = asgi;
|
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");
|
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);
|
scope, receive, send, NULL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
nxt_unit_req_debug(req, "Python call legacy application");
|
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)) {
|
if (nxt_slow_path(res == NULL)) {
|
||||||
nxt_unit_req_error(req, "Python failed to call legacy app stage1");
|
nxt_unit_req_error(req, "Python failed to call legacy app stage1");
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ typedef struct {
|
|||||||
PyObject *loop_remove_reader;
|
PyObject *loop_remove_reader;
|
||||||
PyObject *quit_future;
|
PyObject *quit_future;
|
||||||
PyObject *quit_future_set_result;
|
PyObject *quit_future_set_result;
|
||||||
PyObject *lifespan;
|
PyObject **target_lifespans;
|
||||||
nxt_unit_port_t *port;
|
nxt_unit_port_t *port;
|
||||||
} nxt_py_asgi_ctx_data_t;
|
} 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);
|
int nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx);
|
||||||
|
|
||||||
|
|
||||||
extern int nxt_py_asgi_legacy;
|
|
||||||
|
|
||||||
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */
|
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ typedef struct {
|
|||||||
PyObject *receive_future;
|
PyObject *receive_future;
|
||||||
} nxt_py_asgi_lifespan_t;
|
} 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_receive(PyObject *self, PyObject *none);
|
||||||
static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
|
static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
|
||||||
static PyObject *nxt_py_asgi_lifespan_send_startup(
|
static PyObject *nxt_py_asgi_lifespan_send_startup(
|
||||||
@@ -69,24 +72,60 @@ static PyTypeObject nxt_py_asgi_lifespan_type = {
|
|||||||
int
|
int
|
||||||
nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
|
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 *scope, *res, *py_task, *receive, *send, *done;
|
||||||
PyObject *stage2;
|
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)) {
|
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
|
||||||
nxt_unit_alert(NULL,
|
nxt_unit_alert(NULL,
|
||||||
"Python failed to initialize the 'asgi_lifespan' type object");
|
"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);
|
lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
|
||||||
if (nxt_slow_path(lifespan == NULL)) {
|
if (nxt_slow_path(lifespan == NULL)) {
|
||||||
nxt_unit_alert(NULL, "Python failed to create lifespan object");
|
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");
|
receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
|
||||||
if (nxt_slow_path(receive == NULL)) {
|
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;
|
goto release_future;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nxt_py_asgi_legacy) {
|
if (!target->asgi_legacy) {
|
||||||
nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
|
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);
|
scope, receive, send, NULL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
nxt_unit_req_debug(NULL, "Python call legacy application");
|
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)) {
|
if (nxt_slow_path(res == NULL)) {
|
||||||
nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
|
nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
|
||||||
"ASGI Lifespan processing exception");
|
"ASGI Lifespan processing exception");
|
||||||
nxt_python_print_exception();
|
nxt_python_print_exception();
|
||||||
|
|
||||||
lifespan->disabled = 1;
|
lifespan->disabled = 1;
|
||||||
rc = NXT_UNIT_OK;
|
|
||||||
|
Py_INCREF(lifespan);
|
||||||
|
ret = lifespan;
|
||||||
|
|
||||||
goto release_scope;
|
goto release_scope;
|
||||||
}
|
}
|
||||||
@@ -211,10 +252,9 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
|
|||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
|
|
||||||
if (lifespan->startup_sent == 1 || lifespan->disabled) {
|
if (lifespan->startup_sent == 1 || lifespan->disabled) {
|
||||||
ctx_data->lifespan = (PyObject *) lifespan;
|
Py_INCREF(lifespan);
|
||||||
Py_INCREF(ctx_data->lifespan);
|
|
||||||
|
|
||||||
rc = NXT_UNIT_OK;
|
ret = lifespan;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_task:
|
release_task:
|
||||||
@@ -232,20 +272,41 @@ release_receive:
|
|||||||
release_lifespan:
|
release_lifespan:
|
||||||
Py_DECREF(lifespan);
|
Py_DECREF(lifespan);
|
||||||
|
|
||||||
return rc;
|
return (PyObject *) ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
|
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_lifespan_t *lifespan;
|
||||||
nxt_py_asgi_ctx_data_t *ctx_data;
|
nxt_py_asgi_ctx_data_t *ctx_data;
|
||||||
|
|
||||||
ctx_data = 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)) {
|
if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
|
||||||
return NXT_UNIT_OK;
|
return NXT_UNIT_OK;
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
PyObject *environ, *args, *response, *iterator, *item;
|
PyObject *environ, *args, *response, *iterator, *item;
|
||||||
PyObject *close, *result;
|
PyObject *close, *result, *application;
|
||||||
nxt_bool_t prepare_environ;
|
nxt_bool_t prepare_environ;
|
||||||
nxt_python_ctx_t *pctx;
|
nxt_python_ctx_t *pctx;
|
||||||
|
|
||||||
@@ -348,7 +348,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
|
|||||||
Py_INCREF(pctx->start_resp);
|
Py_INCREF(pctx->start_resp);
|
||||||
PyTuple_SET_ITEM(args, 1, 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);
|
Py_DECREF(args);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user