From 091916614539c01e571486f2a55c68f87bd435bf Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 13 Oct 2020 01:37:39 +0300 Subject: Fixed building with Python 3.9. PyUnicode_GET_SIZE() in deprecated since 3.3 and will be removed in 3.12. In version 3.9 it was explicitly marked by deprecation warning causing compilation error with Unit. PyUnicode_GET_LENGTH() must be used instead. --- src/python/nxt_python.h | 1 + src/python/nxt_python_wsgi.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index 3211026b..3dd04e31 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -28,6 +28,7 @@ #define PyBytes_AS_STRING PyString_AS_STRING #define PyUnicode_InternInPlace PyString_InternInPlace #define PyUnicode_AsUTF8 PyString_AS_STRING +#define PyUnicode_GET_LENGTH PyUnicode_GET_SIZE #endif #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5 diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 97030cd3..8f692941 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -831,7 +831,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args) fields_size += PyBytes_GET_SIZE(string); } else if (PyUnicode_Check(string)) { - fields_size += PyUnicode_GET_SIZE(string); + fields_size += PyUnicode_GET_LENGTH(string); } else { return PyErr_Format(PyExc_TypeError, @@ -843,7 +843,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args) fields_size += PyBytes_GET_SIZE(string); } else if (PyUnicode_Check(string)) { - fields_size += PyUnicode_GET_SIZE(string); + fields_size += PyUnicode_GET_LENGTH(string); } else { return PyErr_Format(PyExc_TypeError, -- cgit From edafa954d42a83d2f6c81a4da45ddfc0678601b2 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 13 Oct 2020 12:56:56 +0300 Subject: Reordering declarations. --- src/nxt_conf_validation.c | 613 ++++++++++++++++++++++++---------------------- 1 file changed, 317 insertions(+), 296 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 4364057b..c01cee62 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -170,21 +170,60 @@ static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); #endif -static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = { - { nxt_string("read_timeout"), - NXT_CONF_VLDT_INTEGER, + +static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[]; +#if (NXT_TLS) +static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[]; +#endif +static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[]; +#if (NXT_HAVE_ISOLATION_ROOTFS) +static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[]; +#endif + + +static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { + { nxt_string("settings"), + NXT_CONF_VLDT_OBJECT, 0, - NULL, - NULL }, + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_setting_members }, - { nxt_string("keepalive_interval"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("listeners"), + NXT_CONF_VLDT_OBJECT, 0, - NULL, + &nxt_conf_vldt_object_iterator, + (void *) &nxt_conf_vldt_listener }, + + { nxt_string("routes"), + NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_routes, NULL }, - { nxt_string("max_frame_size"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("applications"), + NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_object_iterator, + (void *) &nxt_conf_vldt_app }, + + { nxt_string("upstreams"), + NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_object_iterator, + (void *) &nxt_conf_vldt_upstream }, + + { nxt_string("access_log"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, @@ -193,12 +232,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = { - { nxt_string("mime_types"), +static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = { + { nxt_string("http"), NXT_CONF_VLDT_OBJECT, 0, - &nxt_conf_vldt_mtypes, - NULL }, + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_http_members }, NXT_CONF_VLDT_END }; @@ -263,50 +302,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = { - { nxt_string("http"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_http_members }, - - NXT_CONF_VLDT_END -}; - - -static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { - { nxt_string("settings"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_setting_members }, - - { nxt_string("listeners"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_listener }, - - { nxt_string("routes"), - NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT, +static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = { + { nxt_string("read_timeout"), + NXT_CONF_VLDT_INTEGER, 0, - &nxt_conf_vldt_routes, + NULL, NULL }, - { nxt_string("applications"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_app }, - - { nxt_string("upstreams"), - NXT_CONF_VLDT_OBJECT, + { nxt_string("keepalive_interval"), + NXT_CONF_VLDT_INTEGER, 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_upstream }, + NULL, + NULL }, - { nxt_string("access_log"), - NXT_CONF_VLDT_STRING, + { nxt_string("max_frame_size"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, @@ -315,20 +325,16 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { }; -#if (NXT_TLS) - -static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { - { nxt_string("certificate"), - NXT_CONF_VLDT_STRING, +static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = { + { nxt_string("mime_types"), + NXT_CONF_VLDT_OBJECT, 0, - &nxt_conf_vldt_certificate, + &nxt_conf_vldt_mtypes, NULL }, NXT_CONF_VLDT_END }; -#endif - static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { { nxt_string("pass"), @@ -357,6 +363,38 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { }; +#if (NXT_TLS) + +static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { + { nxt_string("certificate"), + NXT_CONF_VLDT_STRING, + 0, + &nxt_conf_vldt_certificate, + NULL }, + + NXT_CONF_VLDT_END +}; + +#endif + + +static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = { + { nxt_string("match"), + NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_match_members }, + + { nxt_string("action"), + NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_action, + NULL }, + + NXT_CONF_VLDT_END +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { { nxt_string("method"), NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, @@ -472,215 +510,209 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_proxy_action_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = { - { nxt_string("match"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_match_members }, +static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { + { nxt_string("executable"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, + NULL, + NULL }, - { nxt_string("action"), - NXT_CONF_VLDT_OBJECT, + { nxt_string("arguments"), + NXT_CONF_VLDT_ARRAY, 0, - &nxt_conf_vldt_action, - NULL }, + &nxt_conf_vldt_array_iterator, + (void *) &nxt_conf_vldt_argument }, - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; -static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = { - { nxt_string("timeout"), - NXT_CONF_VLDT_INTEGER, +static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { + { nxt_string("home"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, - { nxt_string("requests"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("path"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, - { nxt_string("shm"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("module"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, + NULL, + NULL }, + + { nxt_string("callable"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; -static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = { - { nxt_string("spare"), - NXT_CONF_VLDT_INTEGER, +static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_ANY_TYPE, 0, - NULL, - NULL }, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "root" }, - { nxt_string("max"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("script"), + NXT_CONF_VLDT_ANY_TYPE, 0, - NULL, - NULL }, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "script" }, - { nxt_string("idle_timeout"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("index"), + NXT_CONF_VLDT_ANY_TYPE, 0, - NULL, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "index" }, + + { nxt_string("targets"), + NXT_CONF_VLDT_OBJECT, + 0, + &nxt_conf_vldt_php_targets, NULL }, - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; -static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = { - -#if (NXT_HAVE_CLONE_NEWUSER) - { nxt_string("credential"), - NXT_CONF_VLDT_BOOLEAN, +static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { + { nxt_string("options"), + NXT_CONF_VLDT_OBJECT, 0, - NULL, - NULL }, -#endif + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_php_options_members }, -#if (NXT_HAVE_CLONE_NEWPID) - { nxt_string("pid"), - NXT_CONF_VLDT_BOOLEAN, + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { + { nxt_string("file"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, -#endif -#if (NXT_HAVE_CLONE_NEWNET) - { nxt_string("network"), - NXT_CONF_VLDT_BOOLEAN, + { nxt_string("admin"), + NXT_CONF_VLDT_OBJECT, 0, - NULL, - NULL }, -#endif + &nxt_conf_vldt_object_iterator, + (void *) &nxt_conf_vldt_php_option }, -#if (NXT_HAVE_CLONE_NEWNS) - { nxt_string("mount"), - NXT_CONF_VLDT_BOOLEAN, + { nxt_string("user"), + NXT_CONF_VLDT_OBJECT, 0, + &nxt_conf_vldt_object_iterator, + (void *) &nxt_conf_vldt_php_option }, + + NXT_CONF_VLDT_END +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, NULL, NULL }, -#endif -#if (NXT_HAVE_CLONE_NEWUTS) - { nxt_string("uname"), - NXT_CONF_VLDT_BOOLEAN, + { nxt_string("script"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, -#endif -#if (NXT_HAVE_CLONE_NEWCGROUP) - { nxt_string("cgroup"), - NXT_CONF_VLDT_BOOLEAN, + { nxt_string("index"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, -#endif NXT_CONF_VLDT_END }; -#if (NXT_HAVE_CLONE_NEWUSER) - -static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { - { nxt_string("container"), - NXT_CONF_VLDT_INTEGER, - 0, +static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, NULL, NULL }, - { nxt_string("host"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("script"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, - { nxt_string("size"), - NXT_CONF_VLDT_INTEGER, + { nxt_string("index"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; -#endif - -#if (NXT_HAVE_ISOLATION_ROOTFS) - -static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { - { nxt_string("language_deps"), - NXT_CONF_VLDT_BOOLEAN, - 0, +static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { + { nxt_string("script"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, NULL, NULL }, - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; -#endif - -static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { - { nxt_string("namespaces"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_clone_namespaces, - (void *) &nxt_conf_vldt_app_namespaces_members }, +static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { + { nxt_string("script"), + NXT_CONF_VLDT_STRING, + NXT_CONF_VLDT_REQUIRED, + NULL, + NULL }, -#if (NXT_HAVE_CLONE_NEWUSER) + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) +}; - { nxt_string("uidmap"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_clone_uidmap }, - { nxt_string("gidmap"), +static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { + { nxt_string("classpath"), NXT_CONF_VLDT_ARRAY, 0, &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_clone_gidmap }, - -#endif - -#if (NXT_HAVE_ISOLATION_ROOTFS) + (void *) &nxt_conf_vldt_java_classpath }, - { nxt_string("rootfs"), + { nxt_string("webapp"), NXT_CONF_VLDT_STRING, - 0, + NXT_CONF_VLDT_REQUIRED, NULL, NULL }, - { nxt_string("automount"), - NXT_CONF_VLDT_OBJECT, + { nxt_string("options"), + NXT_CONF_VLDT_ARRAY, 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_app_automount_members }, - -#endif - -#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + &nxt_conf_vldt_array_iterator, + (void *) &nxt_conf_vldt_java_option }, - { nxt_string("new_privs"), - NXT_CONF_VLDT_BOOLEAN, + { nxt_string("unit_jars"), + NXT_CONF_VLDT_STRING, 0, NULL, NULL }, -#endif - - NXT_CONF_VLDT_END + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; @@ -737,67 +769,44 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { - { nxt_string("executable"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("arguments"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_argument }, - - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) -}; - - -static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { - { nxt_string("home"), - NXT_CONF_VLDT_STRING, +static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = { + { nxt_string("timeout"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, - { nxt_string("path"), - NXT_CONF_VLDT_STRING, + { nxt_string("requests"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, - { nxt_string("module"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("callable"), - NXT_CONF_VLDT_STRING, + { nxt_string("shm"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_END }; -static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, +static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = { + { nxt_string("spare"), + NXT_CONF_VLDT_INTEGER, + 0, NULL, NULL }, - { nxt_string("script"), - NXT_CONF_VLDT_STRING, + { nxt_string("max"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, - { nxt_string("index"), - NXT_CONF_VLDT_STRING, + { nxt_string("idle_timeout"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, @@ -806,142 +815,154 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { - { nxt_string("file"), - NXT_CONF_VLDT_STRING, +static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { + { nxt_string("namespaces"), + NXT_CONF_VLDT_OBJECT, 0, - NULL, - NULL }, + &nxt_conf_vldt_clone_namespaces, + (void *) &nxt_conf_vldt_app_namespaces_members }, - { nxt_string("admin"), - NXT_CONF_VLDT_OBJECT, +#if (NXT_HAVE_CLONE_NEWUSER) + + { nxt_string("uidmap"), + NXT_CONF_VLDT_ARRAY, 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_php_option }, + &nxt_conf_vldt_array_iterator, + (void *) &nxt_conf_vldt_clone_uidmap }, - { nxt_string("user"), - NXT_CONF_VLDT_OBJECT, + { nxt_string("gidmap"), + NXT_CONF_VLDT_ARRAY, 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_php_option }, + &nxt_conf_vldt_array_iterator, + (void *) &nxt_conf_vldt_clone_gidmap }, - NXT_CONF_VLDT_END -}; +#endif +#if (NXT_HAVE_ISOLATION_ROOTFS) -static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { - { nxt_string("options"), + { nxt_string("rootfs"), + NXT_CONF_VLDT_STRING, + 0, + NULL, + NULL }, + + { nxt_string("automount"), NXT_CONF_VLDT_OBJECT, 0, &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_php_options_members }, + (void *) &nxt_conf_vldt_app_automount_members }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) -}; +#endif +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) -static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, + { nxt_string("new_privs"), + NXT_CONF_VLDT_BOOLEAN, + 0, NULL, NULL }, - { nxt_string("script"), - NXT_CONF_VLDT_STRING, +#endif + + NXT_CONF_VLDT_END +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = { + +#if (NXT_HAVE_CLONE_NEWUSER) + { nxt_string("credential"), + NXT_CONF_VLDT_BOOLEAN, 0, NULL, NULL }, +#endif - { nxt_string("index"), - NXT_CONF_VLDT_STRING, +#if (NXT_HAVE_CLONE_NEWPID) + { nxt_string("pid"), + NXT_CONF_VLDT_BOOLEAN, 0, NULL, NULL }, +#endif - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) -}; - - -static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_ANY_TYPE, +#if (NXT_HAVE_CLONE_NEWNET) + { nxt_string("network"), + NXT_CONF_VLDT_BOOLEAN, 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "root" }, + NULL, + NULL }, +#endif - { nxt_string("script"), - NXT_CONF_VLDT_ANY_TYPE, +#if (NXT_HAVE_CLONE_NEWNS) + { nxt_string("mount"), + NXT_CONF_VLDT_BOOLEAN, 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "script" }, + NULL, + NULL }, +#endif - { nxt_string("index"), - NXT_CONF_VLDT_ANY_TYPE, +#if (NXT_HAVE_CLONE_NEWUTS) + { nxt_string("uname"), + NXT_CONF_VLDT_BOOLEAN, 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "index" }, + NULL, + NULL }, +#endif - { nxt_string("targets"), - NXT_CONF_VLDT_OBJECT, +#if (NXT_HAVE_CLONE_NEWCGROUP) + { nxt_string("cgroup"), + NXT_CONF_VLDT_BOOLEAN, 0, - &nxt_conf_vldt_php_targets, + NULL, NULL }, +#endif - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) + NXT_CONF_VLDT_END }; -static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { + { nxt_string("language_deps"), + NXT_CONF_VLDT_BOOLEAN, + 0, NULL, NULL }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_END }; +#endif -static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) -}; +#if (NXT_HAVE_CLONE_NEWUSER) -static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { - { nxt_string("classpath"), - NXT_CONF_VLDT_ARRAY, +static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { + { nxt_string("container"), + NXT_CONF_VLDT_INTEGER, 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_java_classpath }, - - { nxt_string("webapp"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, NULL, NULL }, - { nxt_string("options"), - NXT_CONF_VLDT_ARRAY, + { nxt_string("host"), + NXT_CONF_VLDT_INTEGER, 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_java_option }, + NULL, + NULL }, - { nxt_string("unit_jars"), - NXT_CONF_VLDT_STRING, + { nxt_string("size"), + NXT_CONF_VLDT_INTEGER, 0, NULL, NULL }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_END }; +#endif + static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_members[] = { { nxt_string("servers"), -- cgit From f541cbcce4f3790551c325755c74ef2cf18b8a2c Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 13 Oct 2020 12:56:56 +0300 Subject: Using C99 style declaration. --- src/nxt_conf_validation.c | 986 ++++++++++++++++++++-------------------------- 1 file changed, 423 insertions(+), 563 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index c01cee62..fec471b1 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -50,8 +50,8 @@ typedef struct { } nxt_conf_vldt_object_t; -#define NXT_CONF_VLDT_NEXT(f) { nxt_null_string, 0, 0, NULL, (f) } -#define NXT_CONF_VLDT_END { nxt_null_string, 0, 0, NULL, NULL } +#define NXT_CONF_VLDT_NEXT(f) { .data = f } +#define NXT_CONF_VLDT_END { .name = nxt_null_string } typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt, @@ -192,171 +192,135 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { - { nxt_string("settings"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_setting_members }, - - { nxt_string("listeners"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_listener }, - - { nxt_string("routes"), - NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_routes, - NULL }, - - { nxt_string("applications"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_app }, - - { nxt_string("upstreams"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_upstream }, - - { nxt_string("access_log"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("settings"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_setting_members, + }, { + .name = nxt_string("listeners"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_listener, + }, { + .name = nxt_string("routes"), + .type = NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_routes, + }, { + .name = nxt_string("applications"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_app, + }, { + .name = nxt_string("upstreams"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_upstream, + }, { + .name = nxt_string("access_log"), + .type = NXT_CONF_VLDT_STRING, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = { - { nxt_string("http"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_http_members }, + { + .name = nxt_string("http"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_http_members, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { - { nxt_string("header_read_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("body_read_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("send_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("idle_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("body_buffer_size"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("max_body_size"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("body_temp_path"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("websocket"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_websocket_members }, - - { nxt_string("static"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_static_members }, + { + .name = nxt_string("header_read_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("body_read_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("send_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("idle_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("body_buffer_size"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("max_body_size"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("body_temp_path"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("websocket"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_websocket_members, + }, { + .name = nxt_string("static"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_static_members, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = { - { nxt_string("read_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("keepalive_interval"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("max_frame_size"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, + { + .name = nxt_string("read_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + + .name = nxt_string("keepalive_interval"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("max_frame_size"), + .type = NXT_CONF_VLDT_INTEGER, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = { - { nxt_string("mime_types"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_mtypes, - NULL }, + { + .name = nxt_string("mime_types"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_mtypes, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { - { nxt_string("pass"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_pass, - NULL }, - - { nxt_string("application"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_app_name, - NULL }, + { + .name = nxt_string("pass"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_pass, + }, { + .name = nxt_string("application"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_app_name, + }, #if (NXT_TLS) - - { nxt_string("tls"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_tls_members }, - + { + .name = nxt_string("tls"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_tls_members, + }, #endif NXT_CONF_VLDT_END @@ -366,11 +330,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { - { nxt_string("certificate"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_certificate, - NULL }, + { + .name = nxt_string("certificate"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_certificate, + }, NXT_CONF_VLDT_END }; @@ -379,489 +343,398 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = { - { nxt_string("match"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_match_members }, - - { nxt_string("action"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_action, - NULL }, + { + .name = nxt_string("match"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_match_members, + }, { + .name = nxt_string("action"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_action, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { - { nxt_string("method"), - NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_patterns, - NULL }, - - { nxt_string("scheme"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_match_scheme_pattern, - NULL }, - - { nxt_string("host"), - NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_patterns, - NULL }, - - { nxt_string("source"), - NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_addrs, - NULL }, - - { nxt_string("destination"), - NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_addrs, - NULL }, - - { nxt_string("uri"), - NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_encoded_patterns, - NULL }, - - { nxt_string("arguments"), - NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_encoded_patterns_sets, - NULL }, - - { nxt_string("headers"), - NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_patterns_sets, - NULL }, - - { nxt_string("cookies"), - NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_match_patterns_sets, - NULL }, + { + .name = nxt_string("method"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_patterns, + }, { + .name = nxt_string("scheme"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_match_scheme_pattern, + }, { + .name = nxt_string("host"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_patterns, + }, { + .name = nxt_string("source"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_addrs, + }, { + .name = nxt_string("destination"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_addrs, + }, { + .name = nxt_string("uri"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_encoded_patterns, + }, { + .name = nxt_string("arguments"), + .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_encoded_patterns_sets, + }, { + .name = nxt_string("headers"), + .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_patterns_sets, + }, { + .name = nxt_string("cookies"), + .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_match_patterns_sets, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = { - { nxt_string("pass"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_pass, - NULL }, + { + .name = nxt_string("pass"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_pass, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = { - { nxt_string("return"), - NXT_CONF_VLDT_INTEGER, - 0, - &nxt_conf_vldt_return, - NULL }, - - { nxt_string("location"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("return"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = &nxt_conf_vldt_return, + }, { + .name = nxt_string("location"), + .type = NXT_CONF_VLDT_STRING, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { - { nxt_string("share"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("fallback"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_action, - NULL }, + { + .name = nxt_string("share"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("fallback"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_action, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_proxy_action_members[] = { - { nxt_string("proxy"), - NXT_CONF_VLDT_STRING, - 0, - &nxt_conf_vldt_proxy, - NULL }, + { + .name = nxt_string("proxy"), + .type = NXT_CONF_VLDT_STRING, + .validator = &nxt_conf_vldt_proxy, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { - { nxt_string("executable"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("arguments"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_argument }, + { + .name = nxt_string("executable"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("arguments"), + .type = NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_array_iterator, + .data = (void *) &nxt_conf_vldt_argument, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { - { nxt_string("home"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("path"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("module"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("callable"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("home"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("path"), + .type = NXT_CONF_VLDT_STRING, + }, { + .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_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_ANY_TYPE, - 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "root" }, - - { nxt_string("script"), - NXT_CONF_VLDT_ANY_TYPE, - 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "script" }, - - { nxt_string("index"), - NXT_CONF_VLDT_ANY_TYPE, - 0, - &nxt_conf_vldt_php_targets_exclusive, - (void *) "index" }, - - { nxt_string("targets"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_php_targets, - NULL }, + { + .name = nxt_string("root"), + .type = NXT_CONF_VLDT_ANY_TYPE, + .validator = &nxt_conf_vldt_php_targets_exclusive, + .data = (void *) "root", + }, { + .name = nxt_string("script"), + .type = NXT_CONF_VLDT_ANY_TYPE, + .validator = &nxt_conf_vldt_php_targets_exclusive, + .data = (void *) "script", + }, { + .name = nxt_string("index"), + .type = NXT_CONF_VLDT_ANY_TYPE, + .validator = &nxt_conf_vldt_php_targets_exclusive, + .data = (void *) "index", + }, { + .name = nxt_string("targets"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_php_targets, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { - { nxt_string("options"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_php_options_members }, + { + .name = nxt_string("options"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_php_options_members, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { - { nxt_string("file"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("admin"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_php_option }, - - { nxt_string("user"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_php_option }, + { + .name = nxt_string("file"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("admin"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_php_option, + }, { + .name = nxt_string("user"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_php_option, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("index"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("root"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("script"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("index"), + .type = NXT_CONF_VLDT_STRING, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { - { nxt_string("root"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("index"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("root"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("script"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("index"), + .type = NXT_CONF_VLDT_STRING, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, + { + .name = nxt_string("script"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { - { nxt_string("script"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, + { + .name = nxt_string("script"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { - { nxt_string("classpath"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_java_classpath }, - - { nxt_string("webapp"), - NXT_CONF_VLDT_STRING, - NXT_CONF_VLDT_REQUIRED, - NULL, - NULL }, - - { nxt_string("options"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_java_option }, - - { nxt_string("unit_jars"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, + { + .name = nxt_string("classpath"), + .type = NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_array_iterator, + .data = (void *) &nxt_conf_vldt_java_classpath, + }, { + .name = nxt_string("webapp"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("options"), + .type = NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_array_iterator, + .data = (void *) &nxt_conf_vldt_java_option, + }, { + .name = nxt_string("unit_jars"), + .type = NXT_CONF_VLDT_STRING, + }, NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) }; static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { - { nxt_string("type"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("limits"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_app_limits_members }, - - { nxt_string("processes"), - NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_processes, - (void *) &nxt_conf_vldt_app_processes_members }, - - { nxt_string("user"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("group"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("working_directory"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("environment"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_environment }, - - { nxt_string("isolation"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_isolation, - (void *) &nxt_conf_vldt_app_isolation_members }, + { + .name = nxt_string("type"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("limits"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_app_limits_members, + }, { + .name = nxt_string("processes"), + .type = NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_processes, + .data = (void *) &nxt_conf_vldt_app_processes_members, + }, { + .name = nxt_string("user"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("group"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("working_directory"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("environment"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_environment, + }, { + .name = nxt_string("isolation"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_isolation, + .data = (void *) &nxt_conf_vldt_app_isolation_members, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = { - { nxt_string("timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("requests"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("shm"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, + { + .name = nxt_string("timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("requests"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("shm"), + .type = NXT_CONF_VLDT_INTEGER, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = { - { nxt_string("spare"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("max"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("idle_timeout"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, + { + .name = nxt_string("spare"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("max"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("idle_timeout"), + .type = NXT_CONF_VLDT_INTEGER, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { - { nxt_string("namespaces"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_clone_namespaces, - (void *) &nxt_conf_vldt_app_namespaces_members }, + { + .name = nxt_string("namespaces"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_clone_namespaces, + .data = (void *) &nxt_conf_vldt_app_namespaces_members, + }, #if (NXT_HAVE_CLONE_NEWUSER) - - { nxt_string("uidmap"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_clone_uidmap }, - - { nxt_string("gidmap"), - NXT_CONF_VLDT_ARRAY, - 0, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_clone_gidmap }, - + { + .name = nxt_string("uidmap"), + .type = NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_array_iterator, + .data = (void *) &nxt_conf_vldt_clone_uidmap, + }, { + .name = nxt_string("gidmap"), + .type = NXT_CONF_VLDT_ARRAY, + .validator = &nxt_conf_vldt_array_iterator, + .data = (void *) &nxt_conf_vldt_clone_gidmap, + }, #endif #if (NXT_HAVE_ISOLATION_ROOTFS) - - { nxt_string("rootfs"), - NXT_CONF_VLDT_STRING, - 0, - NULL, - NULL }, - - { nxt_string("automount"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_app_automount_members }, - + { + .name = nxt_string("rootfs"), + .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("automount"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object, + .data = (void *) &nxt_conf_vldt_app_automount_members, + }, #endif #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) - - { nxt_string("new_privs"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, - + { + .name = nxt_string("new_privs"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif NXT_CONF_VLDT_END @@ -871,51 +744,45 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = { #if (NXT_HAVE_CLONE_NEWUSER) - { nxt_string("credential"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("credential"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif #if (NXT_HAVE_CLONE_NEWPID) - { nxt_string("pid"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("pid"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif #if (NXT_HAVE_CLONE_NEWNET) - { nxt_string("network"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("network"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif #if (NXT_HAVE_CLONE_NEWNS) - { nxt_string("mount"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("mount"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif #if (NXT_HAVE_CLONE_NEWUTS) - { nxt_string("uname"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("uname"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif #if (NXT_HAVE_CLONE_NEWCGROUP) - { nxt_string("cgroup"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("cgroup"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, #endif NXT_CONF_VLDT_END @@ -925,11 +792,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = { #if (NXT_HAVE_ISOLATION_ROOTFS) static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { - { nxt_string("language_deps"), - NXT_CONF_VLDT_BOOLEAN, - 0, - NULL, - NULL }, + { + .name = nxt_string("language_deps"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, NXT_CONF_VLDT_END }; @@ -940,23 +806,16 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { #if (NXT_HAVE_CLONE_NEWUSER) static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { - { nxt_string("container"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("host"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, - - { nxt_string("size"), - NXT_CONF_VLDT_INTEGER, - 0, - NULL, - NULL }, + { + .name = nxt_string("container"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("host"), + .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("size"), + .type = NXT_CONF_VLDT_INTEGER, + }, NXT_CONF_VLDT_END }; @@ -965,22 +824,23 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_members[] = { - { nxt_string("servers"), - NXT_CONF_VLDT_OBJECT, - 0, - &nxt_conf_vldt_object_iterator, - (void *) &nxt_conf_vldt_server }, + { + .name = nxt_string("servers"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = &nxt_conf_vldt_object_iterator, + .data = (void *) &nxt_conf_vldt_server, + }, NXT_CONF_VLDT_END }; static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_server_members[] = { - { nxt_string("weight"), - NXT_CONF_VLDT_NUMBER, - 0, - &nxt_conf_vldt_server_weight, - NULL }, + { + .name = nxt_string("weight"), + .type = NXT_CONF_VLDT_NUMBER, + .validator = &nxt_conf_vldt_server_weight, + }, NXT_CONF_VLDT_END }; -- cgit From 90b2c9f7d62a1f1f9cee11fba0ca47821de9faa7 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 13 Oct 2020 12:56:56 +0300 Subject: Using union instead of "void *". --- src/nxt_conf_validation.c | 212 ++++++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index fec471b1..b44509e3 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -39,26 +39,34 @@ typedef enum { typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt, + nxt_str_t *name, + nxt_conf_value_t *value); +typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); -typedef struct { - nxt_str_t name; - nxt_conf_vldt_type_t type:32; - nxt_conf_vldt_flags_t flags:32; - nxt_conf_vldt_handler_t validator; - void *data; -} nxt_conf_vldt_object_t; +typedef struct nxt_conf_vldt_object_s nxt_conf_vldt_object_t; +struct nxt_conf_vldt_object_s { + nxt_str_t name; + nxt_conf_vldt_type_t type:32; + nxt_conf_vldt_flags_t flags:32; + nxt_conf_vldt_handler_t validator; -#define NXT_CONF_VLDT_NEXT(f) { .data = f } -#define NXT_CONF_VLDT_END { .name = nxt_null_string } + union { + nxt_conf_vldt_object_t *members; + nxt_conf_vldt_object_t *next; + nxt_conf_vldt_member_t object; + nxt_conf_vldt_element_t array; + const char *string; + } u; +}; -typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt, - nxt_str_t *name, - nxt_conf_value_t *value); -typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value); +#define NXT_CONF_VLDT_NEXT(next) { .u.members = next } +#define NXT_CONF_VLDT_END { .name = nxt_null_string } + static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type); @@ -195,27 +203,27 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { { .name = nxt_string("settings"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_setting_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_setting_members, }, { .name = nxt_string("listeners"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_listener, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_listener, }, { .name = nxt_string("routes"), .type = NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_routes, + .validator = nxt_conf_vldt_routes, }, { .name = nxt_string("applications"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_app, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_app, }, { .name = nxt_string("upstreams"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_upstream, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_upstream, }, { .name = nxt_string("access_log"), .type = NXT_CONF_VLDT_STRING, @@ -229,8 +237,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = { { .name = nxt_string("http"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_http_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_http_members, }, NXT_CONF_VLDT_END @@ -262,13 +270,13 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { }, { .name = nxt_string("websocket"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_websocket_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_websocket_members, }, { .name = nxt_string("static"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_static_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_static_members, }, NXT_CONF_VLDT_END @@ -296,7 +304,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = { { .name = nxt_string("mime_types"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_mtypes, + .validator = nxt_conf_vldt_mtypes, }, NXT_CONF_VLDT_END @@ -307,19 +315,19 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { { .name = nxt_string("pass"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_pass, + .validator = nxt_conf_vldt_pass, }, { .name = nxt_string("application"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_app_name, + .validator = nxt_conf_vldt_app_name, }, #if (NXT_TLS) { .name = nxt_string("tls"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_tls_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_tls_members, }, #endif @@ -333,7 +341,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { { .name = nxt_string("certificate"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_certificate, + .validator = nxt_conf_vldt_certificate, }, NXT_CONF_VLDT_END @@ -346,12 +354,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = { { .name = nxt_string("match"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_match_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_match_members, }, { .name = nxt_string("action"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_action, + .validator = nxt_conf_vldt_action, }, NXT_CONF_VLDT_END @@ -362,39 +370,39 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { { .name = nxt_string("method"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_patterns, + .validator = nxt_conf_vldt_match_patterns, }, { .name = nxt_string("scheme"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_match_scheme_pattern, + .validator = nxt_conf_vldt_match_scheme_pattern, }, { .name = nxt_string("host"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_patterns, + .validator = nxt_conf_vldt_match_patterns, }, { .name = nxt_string("source"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_addrs, + .validator = nxt_conf_vldt_match_addrs, }, { .name = nxt_string("destination"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_addrs, + .validator = nxt_conf_vldt_match_addrs, }, { .name = nxt_string("uri"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_encoded_patterns, + .validator = nxt_conf_vldt_match_encoded_patterns, }, { .name = nxt_string("arguments"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_encoded_patterns_sets, + .validator = nxt_conf_vldt_match_encoded_patterns_sets, }, { .name = nxt_string("headers"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_patterns_sets, + .validator = nxt_conf_vldt_match_patterns_sets, }, { .name = nxt_string("cookies"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_match_patterns_sets, + .validator = nxt_conf_vldt_match_patterns_sets, }, NXT_CONF_VLDT_END @@ -405,7 +413,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = { { .name = nxt_string("pass"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_pass, + .validator = nxt_conf_vldt_pass, }, NXT_CONF_VLDT_END @@ -416,7 +424,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = { { .name = nxt_string("return"), .type = NXT_CONF_VLDT_INTEGER, - .validator = &nxt_conf_vldt_return, + .validator = nxt_conf_vldt_return, }, { .name = nxt_string("location"), .type = NXT_CONF_VLDT_STRING, @@ -433,7 +441,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { }, { .name = nxt_string("fallback"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_action, + .validator = nxt_conf_vldt_action, }, NXT_CONF_VLDT_END @@ -444,7 +452,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_proxy_action_members[] = { { .name = nxt_string("proxy"), .type = NXT_CONF_VLDT_STRING, - .validator = &nxt_conf_vldt_proxy, + .validator = nxt_conf_vldt_proxy, }, NXT_CONF_VLDT_END @@ -459,11 +467,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { }, { .name = nxt_string("arguments"), .type = NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_array_iterator, - .data = (void *) &nxt_conf_vldt_argument, + .validator = nxt_conf_vldt_array_iterator, + .u.array = nxt_conf_vldt_argument, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -483,7 +491,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { .type = NXT_CONF_VLDT_STRING, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -491,25 +499,25 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { { .name = nxt_string("root"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = &nxt_conf_vldt_php_targets_exclusive, - .data = (void *) "root", + .validator = nxt_conf_vldt_php_targets_exclusive, + .u.string = "root", }, { .name = nxt_string("script"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = &nxt_conf_vldt_php_targets_exclusive, - .data = (void *) "script", + .validator = nxt_conf_vldt_php_targets_exclusive, + .u.string = "script", }, { .name = nxt_string("index"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = &nxt_conf_vldt_php_targets_exclusive, - .data = (void *) "index", + .validator = nxt_conf_vldt_php_targets_exclusive, + .u.string = "index", }, { .name = nxt_string("targets"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_php_targets, + .validator = nxt_conf_vldt_php_targets, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members) }; @@ -517,11 +525,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { { .name = nxt_string("options"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_php_options_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_php_options_members, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -532,13 +540,13 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { }, { .name = nxt_string("admin"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_php_option, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_php_option, }, { .name = nxt_string("user"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_php_option, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_php_option, }, NXT_CONF_VLDT_END @@ -575,7 +583,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { .type = NXT_CONF_VLDT_STRING, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members) }; @@ -586,7 +594,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { .flags = NXT_CONF_VLDT_REQUIRED, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -597,7 +605,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { .flags = NXT_CONF_VLDT_REQUIRED, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -605,8 +613,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { { .name = nxt_string("classpath"), .type = NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_array_iterator, - .data = (void *) &nxt_conf_vldt_java_classpath, + .validator = nxt_conf_vldt_array_iterator, + .u.array = nxt_conf_vldt_java_classpath, }, { .name = nxt_string("webapp"), .type = NXT_CONF_VLDT_STRING, @@ -614,14 +622,14 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { }, { .name = nxt_string("options"), .type = NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_array_iterator, - .data = (void *) &nxt_conf_vldt_java_option, + .validator = nxt_conf_vldt_array_iterator, + .u.array = nxt_conf_vldt_java_option, }, { .name = nxt_string("unit_jars"), .type = NXT_CONF_VLDT_STRING, }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; @@ -632,13 +640,13 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { }, { .name = nxt_string("limits"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_app_limits_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_app_limits_members, }, { .name = nxt_string("processes"), .type = NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_processes, - .data = (void *) &nxt_conf_vldt_app_processes_members, + .validator = nxt_conf_vldt_processes, + .u.members = nxt_conf_vldt_app_processes_members, }, { .name = nxt_string("user"), .type = NXT_CONF_VLDT_STRING, @@ -651,13 +659,13 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { }, { .name = nxt_string("environment"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_environment, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_environment, }, { .name = nxt_string("isolation"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_isolation, - .data = (void *) &nxt_conf_vldt_app_isolation_members, + .validator = nxt_conf_vldt_isolation, + .u.members = nxt_conf_vldt_app_isolation_members, }, NXT_CONF_VLDT_END @@ -700,21 +708,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { { .name = nxt_string("namespaces"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_clone_namespaces, - .data = (void *) &nxt_conf_vldt_app_namespaces_members, + .validator = nxt_conf_vldt_clone_namespaces, + .u.members = nxt_conf_vldt_app_namespaces_members, }, #if (NXT_HAVE_CLONE_NEWUSER) { .name = nxt_string("uidmap"), .type = NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_array_iterator, - .data = (void *) &nxt_conf_vldt_clone_uidmap, + .validator = nxt_conf_vldt_array_iterator, + .u.array = nxt_conf_vldt_clone_uidmap, }, { .name = nxt_string("gidmap"), .type = NXT_CONF_VLDT_ARRAY, - .validator = &nxt_conf_vldt_array_iterator, - .data = (void *) &nxt_conf_vldt_clone_gidmap, + .validator = nxt_conf_vldt_array_iterator, + .u.array = nxt_conf_vldt_clone_gidmap, }, #endif @@ -725,8 +733,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { }, { .name = nxt_string("automount"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object, - .data = (void *) &nxt_conf_vldt_app_automount_members, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_app_automount_members, }, #endif @@ -827,8 +835,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_members[] = { { .name = nxt_string("servers"), .type = NXT_CONF_VLDT_OBJECT, - .validator = &nxt_conf_vldt_object_iterator, - .data = (void *) &nxt_conf_vldt_server, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_server, }, NXT_CONF_VLDT_END @@ -839,7 +847,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_server_members[] = { { .name = nxt_string("weight"), .type = NXT_CONF_VLDT_NUMBER, - .validator = &nxt_conf_vldt_server_weight, + .validator = nxt_conf_vldt_server_weight, }, NXT_CONF_VLDT_END @@ -1768,8 +1776,8 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, for ( ;; ) { if (vals->name.length == 0) { - if (vals->data != NULL) { - vals = vals->data; + if (vals->u.members != NULL) { + vals = vals->u.members; continue; } @@ -1802,8 +1810,8 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, for ( ;; ) { if (vals->name.length == 0) { - if (vals->data != NULL) { - vals = vals->data; + if (vals->u.members != NULL) { + vals = vals->u.members; continue; } @@ -1823,7 +1831,7 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } if (vals->validator != NULL) { - ret = vals->validator(vldt, member, vals->data); + ret = vals->validator(vldt, member, vals->u.members); if (ret != NXT_OK) { return ret; -- cgit From 9dcb7ec4b791f542a81e552dbb7a1cfa43430dd4 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 14 Oct 2020 16:18:34 +0300 Subject: Java: response locale methods implemented. This closes #479 issue on GitHub. --- src/java/nginx/unit/Response.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/java/nginx/unit/Response.java b/src/java/nginx/unit/Response.java index 099d7f15..268b359d 100644 --- a/src/java/nginx/unit/Response.java +++ b/src/java/nginx/unit/Response.java @@ -40,11 +40,13 @@ public class Response implements HttpServletResponse { private String characterEncoding = defaultCharacterEncoding; private String contentType = null; private String contentTypeHeader = null; + private Locale locale = null; private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; private static final Charset UTF_8 = StandardCharsets.UTF_8; private static final String CONTENT_TYPE = "Content-Type"; + private static final byte[] CONTENT_LANGUAGE_BYTES = "Content-Language".getBytes(ISO_8859_1); private static final byte[] SET_COOKIE_BYTES = "Set-Cookie".getBytes(ISO_8859_1); private static final byte[] EXPIRES_BYTES = "Expires".getBytes(ISO_8859_1); @@ -590,9 +592,13 @@ public class Response implements HttpServletResponse { @Override public Locale getLocale() { - log("getLocale"); + trace("getLocale"); - return null; + if (locale == null) { + return Locale.getDefault(); + } + + return locale; } @Override @@ -795,7 +801,16 @@ public class Response implements HttpServletResponse { @Override public void setLocale(Locale loc) { - log("setLocale: " + loc); + trace("setLocale: " + loc); + + if (loc == null || isCommitted()) { + return; + } + + locale = loc; + String lang = locale.toString().replace('_', '-'); + + setHeader(req_info_ptr, CONTENT_LANGUAGE_BYTES, lang.getBytes(ISO_8859_1)); } private void log(String msg) -- cgit From d8628a43d0705deeb3473faf0252288038defc2b Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 14 Oct 2020 18:41:31 +0300 Subject: Fixing uninitialized ncpu value on unsupported platforms. Thanks to @geyslan. This closes #455 issue on GitHub. --- src/nxt_lib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_lib.c b/src/nxt_lib.c index 1634a2b8..aba07dda 100644 --- a/src/nxt_lib.c +++ b/src/nxt_lib.c @@ -91,9 +91,12 @@ nxt_lib_start(const char *app, char **argv, char ***envp) #elif (NXT_HPUX) n = mpctl(MPC_GETNUMSPUS, NULL, NULL); +#else + n = 0; + #endif - nxt_debug(&nxt_main_task, "ncpu: %ui", n); + nxt_debug(&nxt_main_task, "ncpu: %d", n); if (n > 1) { nxt_ncpu = n; -- cgit From 434c3228d9bbdc31ff15be470a814344e5f13a4e Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 26 Oct 2020 22:26:02 +0300 Subject: Increased request memory pool size. Previous value was too small, which reduced efficiency of the pool causing a lot of additional allocations even for simple request and response. --- src/nxt_http_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 76fb3427..650c1a89 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -220,7 +220,7 @@ nxt_http_request_create(nxt_task_t *task) nxt_buf_t *last; nxt_http_request_t *r; - mp = nxt_mp_create(1024, 128, 256, 32); + mp = nxt_mp_create(4096, 128, 512, 32); if (nxt_slow_path(mp == NULL)) { return NULL; } -- cgit From 779b1131c56539ed74ab9db9a03f7bff71e203ba Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Router: closing app worker's ports. --- src/nxt_router.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index a3218047..bbd9a54e 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -4667,6 +4667,25 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) nxt_port_use(task, port, -1); } + nxt_thread_mutex_lock(&app->mutex); + + for ( ;; ) { + port = nxt_port_hash_retrieve(&app->port_hash); + if (port == NULL) { + break; + } + + app->port_hash_count--; + + port->app = NULL; + + nxt_port_close(task, port); + + nxt_port_use(task, port, -1); + } + + nxt_thread_mutex_unlock(&app->mutex); + nxt_assert(app->processes == 0); nxt_assert(app->active_requests == 0); nxt_assert(app->port_hash_count == 0); -- cgit From 00561a961f8c7727556271e424e1e425ac9a88ce Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Libunit: protecting the new mmap from being used in another thread. Until the mmap is received by the router, only the creator thread may use this mmap, so the "mmap not found" state in the router is avoided. --- src/nxt_unit.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index f75d61bc..65a76bee 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -314,6 +314,7 @@ struct nxt_unit_ctx_impl_s { struct nxt_unit_mmap_s { nxt_port_mmap_header_t *hdr; + pthread_t src_thread; /* of nxt_unit_read_buf_t */ nxt_queue_t awaiting_rbuf; @@ -3389,7 +3390,10 @@ retry: for (mm = lib->outgoing.elts; mm < mm_end; mm++) { hdr = mm->hdr; - if (hdr->sent_over != 0xFFFFu && hdr->sent_over != port->id.id) { + if (hdr->sent_over != 0xFFFFu + && (hdr->sent_over != port->id.id + || mm->src_thread != pthread_self())) + { continue; } @@ -3657,6 +3661,7 @@ nxt_unit_new_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, int n) hdr->src_pid = lib->pid; hdr->dst_pid = port->id.pid; hdr->sent_over = port->id.id; + mm->src_thread = pthread_self(); /* Mark first n chunk(s) as busy */ for (i = 0; i < n; i++) { -- cgit From 38a9027fe54edcfc8157174a8ce575ed7acefa79 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Router: checking a buffer before accessing its memory fields. This fixes the router's crash on buildbot; the reason was an unexpected 'last' response from the application to the router arriving before the response headers. The last buffer is not a memory buffer, so the result of accessing memory fields is unpredictable. The unexpected 'last' message was caused by an error in libunit; fixed in fee8fd855a00. --- src/nxt_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index bbd9a54e..6277bd47 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3787,7 +3787,7 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_http_request_send_body(task, r, NULL); } else { - size_t b_size = nxt_buf_mem_used_size(&b->mem); + size_t b_size = nxt_buf_is_mem(b) ? nxt_buf_mem_used_size(&b->mem) : 0; if (nxt_slow_path(b_size < sizeof(*resp))) { goto fail; -- cgit From 735bb2f1276a7d768cffd1e780114a10018980cb Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Added error response logging. Every internal server error response should have a clear description in log. --- src/nxt_router.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 6277bd47..15706428 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3722,6 +3722,7 @@ static void nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { + size_t b_size, count; nxt_int_t ret; nxt_app_t *app; nxt_buf_t *b, *next; @@ -3787,15 +3788,20 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_http_request_send_body(task, r, NULL); } else { - size_t b_size = nxt_buf_is_mem(b) ? nxt_buf_mem_used_size(&b->mem) : 0; + b_size = nxt_buf_is_mem(b) ? nxt_buf_mem_used_size(&b->mem) : 0; - if (nxt_slow_path(b_size < sizeof(*resp))) { + if (nxt_slow_path(b_size < sizeof(nxt_unit_response_t))) { + nxt_alert(task, "response buffer too small: %z", b_size); goto fail; } resp = (void *) b->mem.pos; - if (nxt_slow_path(b_size < sizeof(*resp) - + resp->fields_count * sizeof(nxt_unit_field_t))) { + count = (b_size - sizeof(nxt_unit_response_t)) + / sizeof(nxt_unit_field_t); + + if (nxt_slow_path(count < resp->fields_count)) { + nxt_alert(task, "response buffer too small for fields count: %D", + resp->fields_count); goto fail; } -- cgit From ccee391ab28d2742a98c612c42a37d6dcdbcd5f7 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Router: broadcasting the SHM_ACK message to all process ports. --- src/nxt_port_memory.c | 46 +++++++++++++++++++++++++++++++++++++--------- src/nxt_port_memory.h | 2 ++ src/nxt_router.c | 3 +-- 3 files changed, 40 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index 1e01629e..ae9f079c 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -17,6 +17,10 @@ #include +static void nxt_port_broadcast_shm_ack(nxt_task_t *task, nxt_port_t *port, + void *data); + + nxt_inline void nxt_port_mmap_handler_use(nxt_port_mmap_handler_t *mmap_handler, int i) { @@ -112,7 +116,6 @@ nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data) u_char *p; nxt_mp_t *mp; nxt_buf_t *b, *next; - nxt_port_t *port; nxt_process_t *process; nxt_chunk_id_t c; nxt_port_mmap_header_t *hdr; @@ -171,14 +174,7 @@ complete_buf: { process = nxt_runtime_process_find(task->thread->runtime, hdr->src_pid); - if (process != NULL && !nxt_queue_is_empty(&process->ports)) { - port = nxt_process_port_first(process); - - if (port->type == NXT_PROCESS_APP) { - (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK, - -1, 0, 0, NULL); - } - } + nxt_process_broadcast_shm_ack(task, process); } release_buf: @@ -976,3 +972,35 @@ nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b) return m; } + + +void +nxt_process_broadcast_shm_ack(nxt_task_t *task, nxt_process_t *process) +{ + nxt_port_t *port; + + if (nxt_slow_path(process == NULL || nxt_queue_is_empty(&process->ports))) + { + return; + } + + port = nxt_process_port_first(process); + + if (port->type == NXT_PROCESS_APP) { + nxt_port_post(task, port, nxt_port_broadcast_shm_ack, process); + } +} + + +static void +nxt_port_broadcast_shm_ack(nxt_task_t *task, nxt_port_t *port, void *data) +{ + nxt_process_t *process; + + process = data; + + nxt_queue_each(port, &process->ports, nxt_port_t, link) { + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK, + -1, 0, 0, NULL); + } nxt_queue_loop; +} diff --git a/src/nxt_port_memory.h b/src/nxt_port_memory.h index 8e71af3d..a2cdf5dd 100644 --- a/src/nxt_port_memory.h +++ b/src/nxt_port_memory.h @@ -71,4 +71,6 @@ nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b); nxt_int_t nxt_shm_open(nxt_task_t *task, size_t size); +void nxt_process_broadcast_shm_ack(nxt_task_t *task, nxt_process_t *process); + #endif /* _NXT_PORT_MEMORY_H_INCLUDED_ */ diff --git a/src/nxt_router.c b/src/nxt_router.c index 15706428..cf627746 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -5389,8 +5389,7 @@ nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_thread_mutex_unlock(&process->incoming.mutex); if (ack) { - (void) nxt_port_socket_write(task, msg->port, NXT_PORT_MSG_SHM_ACK, - -1, 0, 0, NULL); + nxt_process_broadcast_shm_ack(task, process); } } -- cgit From 28ab1de364d048a4cb3f92179adebdd1eb851d65 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Libunit: gracefully quitting a multicontext application. --- src/nxt_unit.c | 96 +++++++++++++++++++++++++++++++++++++++++++--------------- src/nxt_unit.h | 2 ++ 2 files changed, 74 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 65a76bee..4b0f5230 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -305,6 +305,8 @@ struct nxt_unit_ctx_impl_s { /* of nxt_unit_read_buf_t */ nxt_queue_t free_rbuf; + int online; + nxt_unit_mmap_buf_t ctx_buf[2]; nxt_unit_read_buf_t ctx_read_buf; @@ -354,7 +356,6 @@ struct nxt_unit_impl_s { pid_t pid; int log_fd; - int online; nxt_unit_ctx_impl_t main_ctx; }; @@ -561,7 +562,6 @@ nxt_unit_create(nxt_unit_init_t *init) lib->ports.slot = NULL; lib->log_fd = STDERR_FILENO; - lib->online = 1; nxt_queue_init(&lib->contexts); @@ -615,10 +615,15 @@ nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl, nxt_unit_lib_use(lib); + pthread_mutex_lock(&lib->mutex); + nxt_queue_insert_tail(&lib->contexts, &ctx_impl->link); + pthread_mutex_unlock(&lib->mutex); + ctx_impl->use_count = 1; ctx_impl->wait_items = 0; + ctx_impl->online = 1; nxt_queue_init(&ctx_impl->free_req); nxt_queue_init(&ctx_impl->free_ws); @@ -1119,7 +1124,7 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); - if (new_port_msg->id == (nxt_port_id_t) -1) { + if (new_port_msg->id == NXT_UNIT_SHARED_PORT_ID) { nxt_unit_port_id_init(&new_port.id, lib->pid, new_port_msg->id); new_port.in_fd = recv_msg->fd[0]; @@ -1161,7 +1166,7 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) return NXT_UNIT_ERROR; } - if (new_port_msg->id == (nxt_port_id_t) -1) { + if (new_port_msg->id == NXT_UNIT_SHARED_PORT_ID) { lib->shared_port = port; } else { @@ -4408,18 +4413,20 @@ nxt_unit_process_pop_first(nxt_unit_impl_t *lib) int nxt_unit_run(nxt_unit_ctx_t *ctx) { - int rc; - nxt_unit_impl_t *lib; + int rc; + nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_ctx_use(ctx); - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + rc = NXT_UNIT_OK; - while (nxt_fast_path(lib->online)) { + while (nxt_fast_path(ctx_impl->online)) { rc = nxt_unit_run_once_impl(ctx); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { + nxt_unit_quit(ctx); break; } } @@ -4696,18 +4703,16 @@ int nxt_unit_run_ctx(nxt_unit_ctx_t *ctx) { int rc; - nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_ctx_use(ctx); - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); rc = NXT_UNIT_OK; - while (nxt_fast_path(lib->online)) { + while (nxt_fast_path(ctx_impl->online)) { rbuf = nxt_unit_read_buf_get(ctx); if (nxt_slow_path(rbuf == NULL)) { rc = NXT_UNIT_ERROR; @@ -4802,13 +4807,16 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) int rc; nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; + nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_ctx_use(ctx); lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + rc = NXT_UNIT_OK; - while (nxt_fast_path(lib->online)) { + while (nxt_fast_path(ctx_impl->online)) { rbuf = nxt_unit_read_buf_get(ctx); if (nxt_slow_path(rbuf == NULL)) { rc = NXT_UNIT_ERROR; @@ -4867,6 +4875,7 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) int rc; nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; + nxt_unit_ctx_impl_t *ctx_impl; rbuf = nxt_unit_read_buf_get(ctx); if (nxt_slow_path(rbuf == NULL)) { @@ -4874,6 +4883,7 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) } lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); retry: @@ -4906,7 +4916,7 @@ retry: return NXT_UNIT_ERROR; } - if (lib->online) { + if (ctx_impl->online) { goto retry; } @@ -4950,14 +4960,14 @@ nxt_unit_ctx_alloc(nxt_unit_ctx_t *ctx, void *data) queue_fd = -1; - port = nxt_unit_create_port(ctx); + port = nxt_unit_create_port(&new_ctx->ctx); if (nxt_slow_path(port == NULL)) { goto fail; } new_ctx->read_port = port; - queue_fd = nxt_unit_shm_open(ctx, sizeof(nxt_port_queue_t)); + queue_fd = nxt_unit_shm_open(&new_ctx->ctx, sizeof(nxt_port_queue_t)); if (nxt_slow_path(queue_fd == -1)) { goto fail; } @@ -4976,7 +4986,7 @@ nxt_unit_ctx_alloc(nxt_unit_ctx_t *ctx, void *data) port_impl = nxt_container_of(port, nxt_unit_port_impl_t, port); port_impl->queue = mem; - rc = nxt_unit_send_port(ctx, lib->router_port, port, queue_fd); + rc = nxt_unit_send_port(&new_ctx->ctx, lib->router_port, port, queue_fd); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } @@ -5041,8 +5051,12 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl) pthread_mutex_destroy(&ctx_impl->mutex); + pthread_mutex_lock(&lib->mutex); + nxt_queue_remove(&ctx_impl->link); + pthread_mutex_unlock(&lib->mutex); + if (nxt_fast_path(ctx_impl->read_port != NULL)) { nxt_unit_remove_port(lib, &ctx_impl->read_port->id); nxt_unit_port_release(ctx_impl->read_port); @@ -5229,7 +5243,7 @@ nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port) } if (port_impl->queue != NULL) { - munmap(port_impl->queue, (port->id.id == (nxt_port_id_t) -1) + munmap(port_impl->queue, (port->id.id == NXT_UNIT_SHARED_PORT_ID) ? sizeof(nxt_app_queue_t) : sizeof(nxt_port_queue_t)); } @@ -5346,7 +5360,9 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue) goto unlock; } - if (port->id.id >= process->next_port_id) { + if (port->id.id != NXT_UNIT_SHARED_PORT_ID + && port->id.id >= process->next_port_id) + { process->next_port_id = port->id.id + 1; } @@ -5514,17 +5530,49 @@ nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process) static void nxt_unit_quit(nxt_unit_ctx_t *ctx) { - nxt_unit_impl_t *lib; + nxt_port_msg_t msg; + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); - if (lib->online) { - lib->online = 0; + if (!ctx_impl->online) { + return; + } - if (lib->callbacks.quit != NULL) { - lib->callbacks.quit(ctx); - } + ctx_impl->online = 0; + + if (lib->callbacks.quit != NULL) { + lib->callbacks.quit(ctx); } + + if (ctx != &lib->main_ctx.ctx) { + return; + } + + memset(&msg, 0, sizeof(nxt_port_msg_t)); + + msg.pid = lib->pid; + msg.type = _NXT_PORT_MSG_QUIT; + + pthread_mutex_lock(&lib->mutex); + + nxt_queue_each(ctx_impl, &lib->contexts, nxt_unit_ctx_impl_t, link) { + + if (ctx == &ctx_impl->ctx + || ctx_impl->read_port == NULL + || ctx_impl->read_port->out_fd == -1) + { + continue; + } + + (void) nxt_unit_port_send(ctx, ctx_impl->read_port, + &msg, sizeof(msg), NULL, 0); + + } nxt_queue_loop; + + pthread_mutex_unlock(&lib->mutex); } diff --git a/src/nxt_unit.h b/src/nxt_unit.h index e90f0781..52f9bc27 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -34,6 +34,8 @@ enum { #define NXT_UNIT_INIT_ENV "NXT_UNIT_INIT" +#define NXT_UNIT_SHARED_PORT_ID ((uint16_t) 0xFFFFu) + /* * Mostly opaque structure with library state. * -- cgit From a5508cec7a55fe04ab66451c7510fab0e0d4577c Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Libunit: added a function to discern main and worker contexts. --- src/nxt_unit.c | 11 +++++++++++ src/nxt_unit.h | 2 ++ 2 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 4b0f5230..6a0657c6 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -4854,6 +4854,17 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) } +int +nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + return (ctx == &lib->main_ctx.ctx); +} + + int nxt_unit_process_port_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 52f9bc27..303d5aa1 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -210,6 +210,8 @@ int nxt_unit_run_ctx(nxt_unit_ctx_t *ctx); int nxt_unit_run_shared(nxt_unit_ctx_t *ctx); +int nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx); + /* * Receive and process one message, invoke configured callbacks. * -- cgit From 131b6a7ffab7b3303a00a50f5cf764dc99e23cc0 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Libunit: releasing cached read buffers when destroying context. --- src/nxt_unit.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 6a0657c6..7d2bf2c7 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -5023,6 +5023,7 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl) { nxt_unit_impl_t *lib; nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_read_buf_t *rbuf; nxt_unit_request_info_impl_t *req_impl; nxt_unit_websocket_frame_impl_t *ws_impl; @@ -5060,6 +5061,13 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl) } nxt_queue_loop; + nxt_queue_each(rbuf, &ctx_impl->free_rbuf, nxt_unit_read_buf_t, link) + { + if (rbuf != &ctx_impl->ctx_read_buf) { + nxt_unit_free(&ctx_impl->ctx, rbuf); + } + } nxt_queue_loop; + pthread_mutex_destroy(&ctx_impl->mutex); pthread_mutex_lock(&lib->mutex); -- cgit From 4cb8aeb31a8cf47f6c61aaccb95bbbf47cbc2393 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Router: introducing the PORT_ACK message. The PORT_ACK message is the router's response to the application's NEW_PORT message. After receiving PORT_ACK, the application is safe to process requests using this port. This message avoids a racing condition when the application starts processing a request from the shared queue and sends REQ_HEADERS_ACK. The REQ_HEADERS_ACK message contains the application port ID as reply_port, which the router uses to send request data. When the application creates a new port, it immediately sends it to the main router thread. Because the request is processed outside the main thread, a racing condition can occur between the receipt of the new port in the main thread and the receipt of REQ_HEADERS_ACK in the worker router thread where the same port is specified as reply_port. --- src/nxt_port.h | 3 +++ src/nxt_router.c | 2 ++ src/nxt_unit.c | 37 ++++++++++++++++++++++++++++++++----- src/nxt_unit.h | 2 ++ 4 files changed, 39 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_port.h b/src/nxt_port.h index 3ac8c735..5ece3bfa 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -26,6 +26,7 @@ struct nxt_port_handlers_s { nxt_port_handler_t change_file; nxt_port_handler_t new_port; nxt_port_handler_t get_port; + nxt_port_handler_t port_ack; nxt_port_handler_t mmap; nxt_port_handler_t get_mmap; @@ -84,6 +85,7 @@ typedef enum { _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file), _NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port), _NXT_PORT_MSG_GET_PORT = nxt_port_handler_idx(get_port), + _NXT_PORT_MSG_PORT_ACK = nxt_port_handler_idx(port_ack), _NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap), _NXT_PORT_MSG_GET_MMAP = nxt_port_handler_idx(get_mmap), @@ -120,6 +122,7 @@ typedef enum { NXT_PORT_MSG_CHANGE_FILE = nxt_msg_last(_NXT_PORT_MSG_CHANGE_FILE), NXT_PORT_MSG_NEW_PORT = nxt_msg_last(_NXT_PORT_MSG_NEW_PORT), NXT_PORT_MSG_GET_PORT = nxt_msg_last(_NXT_PORT_MSG_GET_PORT), + NXT_PORT_MSG_PORT_ACK = nxt_msg_last(_NXT_PORT_MSG_PORT_ACK), NXT_PORT_MSG_MMAP = nxt_msg_last(_NXT_PORT_MSG_MMAP) | NXT_PORT_MSG_SYNC, NXT_PORT_MSG_GET_MMAP = nxt_msg_last(_NXT_PORT_MSG_GET_MMAP), diff --git a/src/nxt_router.c b/src/nxt_router.c index cf627746..fbc9a4c8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -688,6 +688,8 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) port->app = app; port->main_app_port = main_app_port; + + nxt_port_socket_write(task, port, NXT_PORT_MSG_PORT_ACK, -1, 0, 0, NULL); } diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 7d2bf2c7..d35a3307 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -57,6 +57,7 @@ static int nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf); static int nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg); +static int nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx); static int nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg); static int nxt_unit_process_req_body(nxt_unit_ctx_t *ctx, @@ -306,6 +307,7 @@ struct nxt_unit_ctx_impl_s { nxt_queue_t free_rbuf; int online; + int ready; nxt_unit_mmap_buf_t ctx_buf[2]; nxt_unit_read_buf_t ctx_read_buf; @@ -624,6 +626,7 @@ nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl, ctx_impl->use_count = 1; ctx_impl->wait_items = 0; ctx_impl->online = 1; + ctx_impl->ready = 0; nxt_queue_init(&ctx_impl->free_req); nxt_queue_init(&ctx_impl->free_ws); @@ -996,6 +999,10 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) rc = nxt_unit_process_new_port(ctx, &recv_msg); break; + case _NXT_PORT_MSG_PORT_ACK: + rc = nxt_unit_ctx_ready(ctx); + break; + case _NXT_PORT_MSG_CHANGE_FILE: nxt_unit_debug(ctx, "#%"PRIu32": change_file: fd %d", port_msg->stream, recv_msg.fd[0]); @@ -1169,8 +1176,28 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) if (new_port_msg->id == NXT_UNIT_SHARED_PORT_ID) { lib->shared_port = port; - } else { - nxt_unit_port_release(port); + return nxt_unit_ctx_ready(ctx); + } + + nxt_unit_port_release(port); + + return NXT_UNIT_OK; +} + + +static int +nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + ctx_impl->ready = 1; + + if (lib->callbacks.ready_handler) { + return lib->callbacks.ready_handler(ctx); } return NXT_UNIT_OK; @@ -4495,17 +4522,17 @@ nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) nxt_unit_port_impl_t *port_impl; struct pollfd fds[2]; - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); - if (ctx_impl->wait_items > 0 || lib->shared_port == NULL) { - + if (ctx_impl->wait_items > 0 || ctx_impl->ready == 0) { return nxt_unit_ctx_port_recv(ctx, ctx_impl->read_port, rbuf); } port_impl = nxt_container_of(ctx_impl->read_port, nxt_unit_port_impl_t, port); + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + retry: if (port_impl->from_socket == 0) { diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 303d5aa1..cb78e862 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -154,6 +154,8 @@ struct nxt_unit_callbacks_s { /* Receive data on port id. Optional. */ ssize_t (*port_recv)(nxt_unit_ctx_t *, nxt_unit_port_t *port, void *buf, size_t buf_size, void *oob, size_t oob_size); + + int (*ready_handler)(nxt_unit_ctx_t *); }; -- cgit From d8cc830ea0363009e40d6bf380db1147ad6fb41e Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Libunit: waking another context with the RPC_READY message. --- src/nxt_unit.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index d35a3307..7e97c050 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -107,6 +107,8 @@ static int nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx, uint32_t min_size, nxt_unit_mmap_buf_t *mmap_buf, char *local_buf); static int nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd); +static void nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, + nxt_unit_ctx_impl_t *ctx_impl); static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps); nxt_inline void nxt_unit_process_use(nxt_unit_process_t *process); nxt_inline void nxt_unit_process_release(nxt_unit_process_t *process); @@ -988,6 +990,10 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) switch (port_msg->type) { + case _NXT_PORT_MSG_RPC_READY: + rc = NXT_UNIT_OK; + break; + case _NXT_PORT_MSG_QUIT: nxt_unit_debug(ctx, "#%"PRIu32": quit", port_msg->stream); @@ -1068,7 +1074,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) break; default: - nxt_unit_debug(ctx, "#%"PRIu32": ignore message type: %d", + nxt_unit_alert(ctx, "#%"PRIu32": ignore message type: %d", port_msg->stream, (int) port_msg->type); rc = NXT_UNIT_ERROR; @@ -4012,12 +4018,40 @@ nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd) nxt_atomic_fetch_add(&ctx_impl->wait_items, -1); + nxt_unit_awake_ctx(ctx, ctx_impl); + } nxt_queue_loop; return rc; } +static void +nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl) +{ + nxt_port_msg_t msg; + + if (nxt_fast_path(ctx == &ctx_impl->ctx)) { + return; + } + + if (nxt_slow_path(ctx_impl->read_port == NULL + || ctx_impl->read_port->out_fd == -1)) + { + nxt_unit_alert(ctx, "target context read_port is NULL or not writable"); + + return; + } + + memset(&msg, 0, sizeof(nxt_port_msg_t)); + + msg.type = _NXT_PORT_MSG_RPC_READY; + + (void) nxt_unit_port_send(ctx, ctx_impl->read_port, + &msg, sizeof(msg), NULL, 0); +} + + static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps) { @@ -5390,6 +5424,8 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue) nxt_atomic_fetch_add(&ctx_impl->wait_items, -1); + nxt_unit_awake_ctx(ctx, ctx_impl); + } nxt_queue_loop; return old_port; -- cgit From 80a8cb835bb780cdb3047b232809c5dfd6e0e794 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Preserving the app port write socket. The socket is required for intercontextual communication in multithreaded apps. --- src/nxt_application.c | 2 +- src/nxt_external.c | 8 +++++++- src/nxt_process.c | 5 +++-- src/nxt_unit.c | 12 ++++++------ 4 files changed, 17 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index 6935346c..3d08643c 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -723,7 +723,7 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) init->read_port.id.pid = my_port->pid; init->read_port.id.id = my_port->id; init->read_port.in_fd = my_port->pair[0]; - init->read_port.out_fd = -1; + init->read_port.out_fd = my_port->pair[1]; init->log_fd = 2; diff --git a/src/nxt_external.c b/src/nxt_external.c index 1adb839c..5703e294 100644 --- a/src/nxt_external.c +++ b/src/nxt_external.c @@ -101,18 +101,24 @@ nxt_external_start(nxt_task_t *task, nxt_process_data_t *data) return NXT_ERROR; } + rc = nxt_external_fd_no_cloexec(task, my_port->pair[1]); + if (nxt_slow_path(rc != NXT_OK)) { + return NXT_ERROR; + } + end = buf + sizeof(buf); p = nxt_sprintf(buf, end, "%s;%uD;" "%PI,%ud,%d;" "%PI,%ud,%d;" - "%PI,%ud,%d;" + "%PI,%ud,%d,%d;" "%d,%z,%Z", NXT_VERSION, my_port->process->stream, main_port->pid, main_port->id, main_port->pair[1], router_port->pid, router_port->id, router_port->pair[1], my_port->pid, my_port->id, my_port->pair[0], + my_port->pair[1], 2, conf->shm_limit); if (nxt_slow_path(p == end)) { diff --git a/src/nxt_process.c b/src/nxt_process.c index 9be7974f..87419313 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -248,8 +248,6 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process) port = nxt_process_port_first(process); - nxt_port_write_close(port); - nxt_port_enable(task, port, init->port_handlers); ret = init->setup(task, process); @@ -272,6 +270,9 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process) } ret = init->start(task, &process->data); + + nxt_port_write_close(port); + break; default: diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 7e97c050..23848f5b 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -780,7 +780,7 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, uint32_t *shm_limit) { int rc; - int ready_fd, router_fd, read_fd; + int ready_fd, router_fd, read_in_fd, read_out_fd; char *unit_init, *version_end; long version_length; int64_t ready_pid, router_pid, read_pid; @@ -812,15 +812,15 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, "%"PRIu32";" "%"PRId64",%"PRIu32",%d;" "%"PRId64",%"PRIu32",%d;" - "%"PRId64",%"PRIu32",%d;" + "%"PRId64",%"PRIu32",%d,%d;" "%d,%"PRIu32, &ready_stream, &ready_pid, &ready_id, &ready_fd, &router_pid, &router_id, &router_fd, - &read_pid, &read_id, &read_fd, + &read_pid, &read_id, &read_in_fd, &read_out_fd, log_fd, shm_limit); - if (nxt_slow_path(rc != 12)) { + if (nxt_slow_path(rc != 13)) { nxt_unit_alert(NULL, "failed to scan variables: %d", rc); return NXT_UNIT_ERROR; @@ -840,8 +840,8 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, nxt_unit_port_id_init(&read_port->id, (pid_t) read_pid, read_id); - read_port->in_fd = read_fd; - read_port->out_fd = -1; + read_port->in_fd = read_in_fd; + read_port->out_fd = read_out_fd; read_port->data = NULL; *stream = ready_stream; -- cgit From f007ad4dcfee0037cd86bf31804795e5f60cb2d9 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 28 Oct 2020 00:01:46 +0300 Subject: Added threading to the libunit test app. --- src/test/nxt_unit_app_test.c | 130 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c index 4ecb8d6b..5b2521e8 100644 --- a/src/test/nxt_unit_app_test.c +++ b/src/test/nxt_unit_app_test.c @@ -3,9 +3,14 @@ * Copyright (C) NGINX, Inc. */ +#include +#include #include #include #include +#include +#include +#include #define CONTENT_TYPE "Content-Type" @@ -28,12 +33,106 @@ #define BODY " Body:\n" -static inline char * -copy(char *p, const void *src, uint32_t len) +static int ready_handler(nxt_unit_ctx_t *ctx); +static void *worker(void *main_ctx); +static void greeting_app_request_handler(nxt_unit_request_info_t *req); +static inline char *copy(char *p, const void *src, uint32_t len); + + +static int thread_count; +static pthread_t *threads; + + +int +main(int argc, char **argv) { - memcpy(p, src, len); + int i, err; + nxt_unit_ctx_t *ctx; + nxt_unit_init_t init; - return p + len; + if (argc == 3 && strcmp(argv[1], "-t") == 0) { + thread_count = atoi(argv[2]); + } + + memset(&init, 0, sizeof(nxt_unit_init_t)); + + init.callbacks.request_handler = greeting_app_request_handler; + init.callbacks.ready_handler = ready_handler; + + ctx = nxt_unit_init(&init); + if (ctx == NULL) { + return 1; + } + + err = nxt_unit_run(ctx); + + nxt_unit_debug(ctx, "main worker finished with %d code", err); + + if (thread_count > 1) { + for (i = 0; i < thread_count - 1; i++) { + err = pthread_join(threads[i], NULL); + + nxt_unit_debug(ctx, "join thread #%d: %d", i, err); + } + + nxt_unit_free(ctx, threads); + } + + nxt_unit_done(ctx); + + nxt_unit_debug(NULL, "main worker done"); + + return 0; +} + + +static int +ready_handler(nxt_unit_ctx_t *ctx) +{ + int i, err; + + nxt_unit_debug(ctx, "ready"); + + if (!nxt_unit_is_main_ctx(ctx) || thread_count <= 1) { + return NXT_UNIT_OK; + } + + threads = nxt_unit_malloc(ctx, sizeof(pthread_t) * (thread_count - 1)); + if (threads == NULL) { + return NXT_UNIT_ERROR; + } + + for (i = 0; i < thread_count - 1; i++) { + err = pthread_create(&threads[i], NULL, worker, ctx); + if (err != 0) { + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + +static void * +worker(void *main_ctx) +{ + int rc; + nxt_unit_ctx_t *ctx; + + ctx = nxt_unit_ctx_alloc(main_ctx, NULL); + if (ctx == NULL) { + return NULL; + } + + nxt_unit_debug(ctx, "start worker"); + + rc = nxt_unit_run(ctx); + + nxt_unit_debug(ctx, "worker finished with %d code", rc); + + nxt_unit_done(ctx); + + return NULL; } @@ -168,24 +267,11 @@ fail: nxt_unit_request_done(req, rc); } -int -main() -{ - nxt_unit_ctx_t *ctx; - nxt_unit_init_t init; - - memset(&init, 0, sizeof(nxt_unit_init_t)); - - init.callbacks.request_handler = greeting_app_request_handler; - ctx = nxt_unit_init(&init); - if (ctx == NULL) { - return 1; - } - - nxt_unit_run(ctx); - - nxt_unit_done(ctx); +static inline char * +copy(char *p, const void *src, uint32_t len) +{ + memcpy(p, src, len); - return 0; + return p + len; } -- cgit From 5ffd88ad7c682f4bc60702d8829d1534aafb09e8 Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Thu, 29 Oct 2020 14:24:38 +0000 Subject: Isolation: correctly unmount non-dependent paths first. When mount points reside within other mount points, this patch sorts them by path length and then unmounts then in an order reverse to their mounting. This results in independent paths being unmounted first. This fixes an issue in buildbots where dependent paths failed to unmount, leading to the build script removing system-wide language libraries. --- src/nxt_isolation.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index ac7a37e8..03160de3 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -41,6 +41,8 @@ static nxt_int_t nxt_isolation_set_mounts(nxt_task_t *task, nxt_process_t *process, nxt_str_t *app_type); static nxt_int_t nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, nxt_array_t *syspaths); +static int nxt_cdecl nxt_isolation_mount_compare(const void *v1, + const void *v2); static void nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process); #if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS) @@ -607,20 +609,48 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, } #endif + qsort(mounts->elts, mounts->nelts, sizeof(nxt_fs_mount_t), + nxt_isolation_mount_compare); + process->isolation.mounts = mounts; return NXT_OK; } +static int nxt_cdecl +nxt_isolation_mount_compare(const void *v1, const void *v2) +{ + const nxt_fs_mount_t *mnt1, *mnt2; + + mnt1 = v1; + mnt2 = v2; + + return nxt_strlen(mnt1->src) > nxt_strlen(mnt2->src); +} + + void nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process) { - size_t i, n; + size_t n; nxt_array_t *mounts; + nxt_runtime_t *rt; nxt_fs_mount_t *mnt; nxt_process_automount_t *automount; + rt = task->thread->runtime; + + if (!rt->capabilities.setid) { + return; + } + +#if (NXT_HAVE_CLONE_NEWNS) + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) { + return; + } +#endif + nxt_debug(task, "unmount all (%s)", process->name); automount = &process->isolation.automount; @@ -628,12 +658,14 @@ nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process) n = mounts->nelts; mnt = mounts->elts; - for (i = 0; i < n; i++) { - if (mnt[i].builtin && !automount->language_deps) { + while (n > 0) { + n--; + + if (mnt[n].builtin && !automount->language_deps) { continue; } - nxt_fs_unmount(mnt[i].dst); + nxt_fs_unmount(mnt[n].dst); } } -- cgit From 0390cb3a61051dd93e206d50591aff5759cf42fc Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Thu, 29 Oct 2020 20:30:53 +0000 Subject: Isolation: mounting of procfs by default when using "rootfs". --- src/nxt_application.c | 17 +++++--- src/nxt_fs.c | 113 ++++++++++++++++++++++++++++++++++++++++--------- src/nxt_fs.h | 65 +++++++++++----------------- src/nxt_isolation.c | 86 +++++++++++++++++++++---------------- src/nxt_main_process.c | 10 ++++- src/nxt_process.h | 2 +- 6 files changed, 185 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index 3d08643c..5d58e60c 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -208,14 +208,14 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) mounts = module[i].mounts; size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", " - "\"fstype\": \"\", \"flags\": , " - "\"data\": \"\"},"); + "\"type\": , \"name\": \"\", " + "\"flags\": , \"data\": \"\"},"); mnt = mounts->elts; for (j = 0; j < mounts->nelts; j++) { size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst) - + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN + + nxt_strlen(mnt[j].name) + (2 * NXT_INT_T_LEN) + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data)); } } @@ -242,9 +242,10 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) for (j = 0; j < mounts->nelts; j++) { p = nxt_sprintf(p, end, "{\"src\": \"%s\", \"dst\": \"%s\", " - "\"fstype\": \"%s\", \"flags\": %d, " + "\"name\": \"%s\", \"type\": %d, \"flags\": %d, " "\"data\": \"%s\"},", - mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags, + mnt[j].src, mnt[j].dst, mnt[j].name, mnt[j].type, + mnt[j].flags, mnt[j].data == NULL ? (u_char *) "" : mnt[j].data); } @@ -386,11 +387,13 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, goto fail; } - to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype); - if (nxt_slow_path(to->fstype == NULL)) { + to->name = nxt_cstr_dup(mp, to->name, from->name); + if (nxt_slow_path(to->name == NULL)) { goto fail; } + to->type = from->type; + if (from->data != NULL) { to->data = nxt_cstr_dup(mp, to->data, from->data); if (nxt_slow_path(to->data == NULL)) { diff --git a/src/nxt_fs.c b/src/nxt_fs.c index 0228c25a..87d3b4a5 100644 --- a/src/nxt_fs.c +++ b/src/nxt_fs.c @@ -18,15 +18,59 @@ static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode); nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) { - int rc; + int rc; + const char *fsname; + unsigned long flags; - rc = mount((const char *) mnt->src, (const char *) mnt->dst, - (const char *) mnt->fstype, mnt->flags, mnt->data); + flags = 0; + + switch (mnt->type) { + case NXT_FS_BIND: + if (nxt_slow_path(mnt->flags != 0)) { + nxt_log(task, NXT_LOG_WARN, + "bind mount ignores additional flags"); + } + + fsname = "bind"; + flags = MS_BIND | MS_REC; + break; + + case NXT_FS_PROC: + fsname = "proc"; + goto getflags; + + case NXT_FS_TMP: + fsname = "tmpfs"; + goto getflags; + + default: + fsname = (const char *) mnt->name; + + getflags: + + if (mnt->flags & NXT_FS_FLAGS_NODEV) { + flags |= MS_NODEV; + } + + if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { + flags |= MS_NOEXEC; + } + + if (mnt->flags & NXT_FS_FLAGS_NOSUID) { + flags |= MS_NOSUID; + } + + if (!(mnt->flags & NXT_FS_FLAGS_NOTIME)) { + flags |= MS_RELATIME; + } + } + + rc = mount((const char *) mnt->src, (const char *) mnt->dst, fsname, flags, + mnt->data); if (nxt_slow_path(rc < 0)) { - nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %d, \"%s\") %E", - mnt->src, mnt->dst, mnt->fstype, mnt->flags, mnt->data, - nxt_errno); + nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %ul, \"%s\") %E", + mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno); return NXT_ERROR; } @@ -34,37 +78,66 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) return NXT_OK; } - #elif (NXT_HAVE_FREEBSD_NMOUNT) nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) { + int flags; u_char *data, *p, *end; size_t iovlen; nxt_int_t ret; - const char *fstype; + const char *fsname; struct iovec iov[128]; char errmsg[256]; - if (nxt_strncmp(mnt->fstype, "bind", 4) == 0) { - fstype = "nullfs"; + if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) { + nxt_alert(task, "nmount(2) doesn't support \"nodev\" option"); - } else if (nxt_strncmp(mnt->fstype, "proc", 4) == 0) { - fstype = "procfs"; + return NXT_ERROR; + } - } else if (nxt_strncmp(mnt->fstype, "tmpfs", 5) == 0) { - fstype = "tmpfs"; + flags = 0; - } else { - nxt_alert(task, "mount type \"%s\" not implemented.", mnt->fstype); - return NXT_ERROR; + switch (mnt->type) { + case NXT_FS_BIND: + fsname = "nullfs"; + break; + + case NXT_FS_PROC: + fsname = "procfs"; + goto getflags; + + case NXT_FS_TMP: + fsname = "tmpfs"; + goto getflags; + + default: + fsname = (const char *) mnt->name; + + getflags: + + if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { + flags |= MNT_NOEXEC; + } + + if (mnt->flags & NXT_FS_FLAGS_NOSUID) { + flags |= MNT_NOSUID; + } + + if (mnt->flags & NXT_FS_FLAGS_NOTIME) { + flags |= MNT_NOATIME; + } + + if (mnt->flags & NXT_FS_FLAGS_RDONLY) { + flags |= MNT_RDONLY; + } } iov[0].iov_base = (void *) "fstype"; iov[0].iov_len = 7; - iov[1].iov_base = (void *) fstype; - iov[1].iov_len = nxt_strlen(fstype) + 1; + iov[1].iov_base = (void *) fsname; + iov[1].iov_len = nxt_strlen(fsname) + 1; iov[2].iov_base = (void *) "fspath"; iov[2].iov_len = 7; iov[3].iov_base = (void *) mnt->dst; @@ -117,7 +190,7 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) ret = NXT_OK; - if (nxt_slow_path(nmount(iov, iovlen, 0) < 0)) { + if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) { nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg); ret = NXT_ERROR; } diff --git a/src/nxt_fs.h b/src/nxt_fs.h index bbd7ab9f..ff589979 100644 --- a/src/nxt_fs.h +++ b/src/nxt_fs.h @@ -6,50 +6,33 @@ #define _NXT_FS_H_INCLUDED_ -#ifdef MS_BIND -#define NXT_MS_BIND MS_BIND -#else -#define NXT_MS_BIND 0 -#endif - -#ifdef MS_REC -#define NXT_MS_REC MS_BIND -#else -#define NXT_MS_REC 0 -#endif - -#ifdef MS_NOSUID -#define NXT_MS_NOSUID MS_NOSUID -#else -#define NXT_MS_NOSUID 0 -#endif - -#ifdef MS_NOEXEC -#define NXT_MS_NOEXEC MS_NOEXEC -#else -#define NXT_MS_NOEXEC 0 -#endif - -#ifdef MS_RELATIME -#define NXT_MS_RELATIME MS_RELATIME -#else -#define NXT_MS_RELATIME 0 -#endif - -#ifdef MS_NODEV -#define NXT_MS_NODEV MS_NODEV -#else -#define NXT_MS_NODEV 0 -#endif +typedef enum { + NXT_FS_UNKNOWN = 0, + NXT_FS_BIND, + NXT_FS_TMP, + NXT_FS_PROC, + NXT_FS_LAST, +} nxt_fs_type_t; + + +typedef enum { + NXT_FS_FLAGS_NOSUID = 1 << 0, + NXT_FS_FLAGS_NOEXEC = 1 << 1, + NXT_FS_FLAGS_NOTIME = 1 << 2, + NXT_FS_FLAGS_NODEV = 1 << 3, + NXT_FS_FLAGS_RDONLY = 1 << 4, +} nxt_fs_flags_t; typedef struct { - u_char *src; - u_char *dst; - u_char *fstype; - nxt_int_t flags; - u_char *data; - nxt_uint_t builtin; /* 1-bit */ + u_char *src; + u_char *dst; + nxt_fs_type_t type; + u_char *name; + nxt_fs_flags_t flags; + u_char *data; + nxt_uint_t builtin; /* 1-bit */ + nxt_uint_t deps; /* 1-bit */ } nxt_fs_mount_t; diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index 03160de3..e0f169aa 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -87,15 +87,6 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process, } #endif -#if (NXT_HAVE_ISOLATION_ROOTFS) - if (process->isolation.rootfs != NULL) { - ret = nxt_isolation_set_mounts(task, process, &app_conf->type); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } - } -#endif - if (cap_setid) { ret = nxt_process_creds_set(task, process, &app_conf->user, &app_conf->group); @@ -126,6 +117,29 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process, } } +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL) { + nxt_int_t has_mnt; + + ret = nxt_isolation_set_mounts(task, process, &app_conf->type); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + + has_mnt = 0; + +#if (NXT_HAVE_CLONE_NEWNS) + has_mnt = nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS); +#endif + + if (process->user_cred->uid == 0 && !has_mnt) { + nxt_log(task, NXT_LOG_WARN, + "setting user \"root\" with \"rootfs\" is unsafe without " + "\"mount\" namespace isolation"); + } + } +#endif + #if (NXT_HAVE_CLONE_NEWUSER) ret = nxt_isolation_vldt_creds(task, process); if (nxt_slow_path(ret != NXT_OK)) { @@ -568,10 +582,13 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, } mnt->src = (u_char *) "tmpfs"; - mnt->fstype = (u_char *) "tmpfs"; - mnt->flags = NXT_MS_NOSUID | NXT_MS_NODEV | NXT_MS_NOEXEC | NXT_MS_RELATIME; + mnt->name = (u_char *) "tmpfs"; + mnt->type = NXT_FS_TMP; + mnt->flags = (NXT_FS_FLAGS_NOSUID | NXT_FS_FLAGS_NODEV + | NXT_FS_FLAGS_NOEXEC); mnt->data = (u_char *) "size=1m,mode=777"; mnt->builtin = 1; + mnt->deps = 0; mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/tmp") + 1); if (nxt_slow_path(mnt->dst == NULL)) { @@ -582,32 +599,27 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, p = nxt_cpymem(p, "/tmp", 4); *p = '\0'; -#if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS) - - if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID) - && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) - { - mnt = nxt_array_add(mounts); - if (nxt_slow_path(mnt == NULL)) { - return NXT_ERROR; - } - - mnt->fstype = (u_char *) "proc"; - mnt->src = (u_char *) "proc"; + mnt = nxt_array_add(mounts); + if (nxt_slow_path(mnt == NULL)) { + return NXT_ERROR; + } - mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/proc") + 1); - if (nxt_slow_path(mnt->dst == NULL)) { - return NXT_ERROR; - } + mnt->name = (u_char *) "proc"; + mnt->type = NXT_FS_PROC; + mnt->src = (u_char *) "none"; + mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/proc") + 1); + if (nxt_slow_path(mnt->dst == NULL)) { + return NXT_ERROR; + } - p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); - p = nxt_cpymem(p, "/proc", 5); - *p = '\0'; + p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); + p = nxt_cpymem(p, "/proc", 5); + *p = '\0'; - mnt->data = (u_char *) ""; - mnt->flags = 0; - } -#endif + mnt->data = (u_char *) ""; + mnt->flags = NXT_FS_FLAGS_NOEXEC | NXT_FS_FLAGS_NOSUID; + mnt->builtin = 1; + mnt->deps = 0; qsort(mounts->elts, mounts->nelts, sizeof(nxt_fs_mount_t), nxt_isolation_mount_compare); @@ -661,7 +673,7 @@ nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process) while (n > 0) { n--; - if (mnt[n].builtin && !automount->language_deps) { + if (mnt[n].deps && !automount->language_deps) { continue; } @@ -690,11 +702,11 @@ nxt_isolation_prepare_rootfs(nxt_task_t *task, nxt_process_t *process) for (i = 0; i < n; i++) { dst = mnt[i].dst; - if (mnt[i].builtin && !automount->language_deps) { + if (mnt[i].deps && !automount->language_deps) { continue; } - if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0 + if (nxt_slow_path(mnt[i].type == NXT_FS_BIND && stat((const char *) mnt[i].src, &st) != 0)) { nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src); diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index d2edab1d..e4ec8b57 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -1163,9 +1163,14 @@ static nxt_conf_map_t nxt_app_lang_mounts_map[] = { offsetof(nxt_fs_mount_t, dst), }, { - nxt_string("fstype"), + nxt_string("name"), NXT_CONF_MAP_CSTRZ, - offsetof(nxt_fs_mount_t, fstype), + offsetof(nxt_fs_mount_t, name), + }, + { + nxt_string("type"), + NXT_CONF_MAP_INT, + offsetof(nxt_fs_mount_t, type), }, { nxt_string("flags"), @@ -1297,6 +1302,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) } mnt->builtin = 1; + mnt->deps = 1; ret = nxt_conf_map_object(rt->mem_pool, value, nxt_app_lang_mounts_map, diff --git a/src/nxt_process.h b/src/nxt_process.h index d9b4dff1..ddadb08f 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -74,7 +74,7 @@ typedef struct { typedef struct { - uint8_t language_deps; /* 1-byte */ + uint8_t language_deps; /* 1-bit */ } nxt_process_automount_t; -- cgit From 50af47fd7c03aecb5bb9f248cac8ccbdb65c9b4c Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 30 Oct 2020 07:55:26 +0300 Subject: Isolation: fixed passing custom options to nmount(). The "iov" array was filled incorrectly when custom mounting options were set. --- src/nxt_fs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_fs.c b/src/nxt_fs.c index 87d3b4a5..71498f99 100644 --- a/src/nxt_fs.c +++ b/src/nxt_fs.c @@ -172,8 +172,10 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) *end = '\0'; - iov[iovlen++].iov_base = (void *) p; - iov[iovlen++].iov_len = (end - p) + 1; + iov[iovlen].iov_base = (void *) p; + iov[iovlen].iov_len = (end - p) + 1; + + iovlen++; p = end + 1; @@ -182,8 +184,10 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) *end = '\0'; } - iov[iovlen++].iov_base = (void *) p; - iov[iovlen++].iov_len = nxt_strlen(p) + 1; + iov[iovlen].iov_base = (void *) p; + iov[iovlen].iov_len = nxt_strlen(p) + 1; + + iovlen++; } while (end != NULL && nxt_nitems(iov) > (iovlen + 2)); } -- cgit From bbe4b97ca159bbff19e3c507635e0d81a68c695e Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Fri, 30 Oct 2020 17:33:36 +0300 Subject: Java: supporting jsp-file attribute for servlet. This closes #487 issue on GitHub. --- src/java/nginx/unit/Context.java | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'src') diff --git a/src/java/nginx/unit/Context.java b/src/java/nginx/unit/Context.java index 5f7ec22f..5aa3a982 100644 --- a/src/java/nginx/unit/Context.java +++ b/src/java/nginx/unit/Context.java @@ -1214,6 +1214,16 @@ public class Context implements ServletContext, InitParams processXmlInitParam(reg, (Element) child_node); continue; } + + if (tag_name.equals("filter-name") + || tag_name.equals("#text") + || tag_name.equals("#comment")) + { + continue; + } + + log("processWebXml: tag '" + tag_name + "' for filter '" + + filter_name + "' is ignored"); } filters_.add(reg); @@ -1306,6 +1316,22 @@ public class Context implements ServletContext, InitParams reg.setLoadOnStartup(Integer.parseInt(child_node.getTextContent().trim())); continue; } + + if (tag_name.equals("jsp-file")) { + reg.setJspFile(child_node.getTextContent().trim()); + continue; + } + + if (tag_name.equals("servlet-name") + || tag_name.equals("display-name") + || tag_name.equals("#text") + || tag_name.equals("#comment")) + { + continue; + } + + log("processWebXml: tag '" + tag_name + "' for servlet '" + + servlet_name + "' is ignored"); } servlets_.add(reg); @@ -1888,6 +1914,7 @@ public class Context implements ServletContext, InitParams private boolean initialized_ = false; private final List filters_ = new ArrayList<>(); private boolean system_jsp_servlet_ = false; + private String jsp_file_; private MultipartConfigElement multipart_config_; public ServletReg(String name, Class servlet_class) @@ -1921,6 +1948,21 @@ public class Context implements ServletContext, InitParams trace("ServletReg.init(): " + getName()); + if (jsp_file_ != null) { + setInitParameter("jspFile", jsp_file_); + jsp_file_ = null; + + ServletReg jsp_servlet = name2servlet_.get("jsp"); + + if (jsp_servlet.servlet_class_ != null) { + servlet_class_ = jsp_servlet.servlet_class_; + } else { + setClassName(jsp_servlet.getClassName()); + } + + system_jsp_servlet_ = jsp_servlet.system_jsp_servlet_; + } + if (system_jsp_servlet_) { JasperInitializer ji = new JasperInitializer(); @@ -1972,6 +2014,10 @@ public class Context implements ServletContext, InitParams throw new IllegalStateException("Class already initialized"); } + if (jsp_file_ != null) { + throw new IllegalStateException("jsp-file already initialized"); + } + super.setClassName(class_name); } @@ -1985,11 +2031,31 @@ public class Context implements ServletContext, InitParams throw new IllegalStateException("Class already initialized"); } + if (jsp_file_ != null) { + throw new IllegalStateException("jsp-file already initialized"); + } + super.setClassName(servlet_class.getName()); servlet_class_ = servlet_class; getAnnotationMultipartConfig(); } + public void setJspFile(String jsp_file) throws IllegalStateException + { + if (servlet_ != null + || servlet_class_ != null + || getClassName() != null) + { + throw new IllegalStateException("Class already initialized"); + } + + if (jsp_file_ != null) { + throw new IllegalStateException("jsp-file already initialized"); + } + + jsp_file_ = jsp_file; + } + private void getAnnotationMultipartConfig() { if (servlet_class_ == null) { return; -- cgit From d03b217f33db21d9af28d58d92ba02c2a2e48f5e Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Sun, 1 Nov 2020 13:22:11 +0300 Subject: Fixed building test app without debug. Compilers complained about unused variables after 37e2a3ea1bf1. --- src/test/nxt_unit_app_test.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c index 5b2521e8..b6dd13d2 100644 --- a/src/test/nxt_unit_app_test.c +++ b/src/test/nxt_unit_app_test.c @@ -72,7 +72,13 @@ main(int argc, char **argv) for (i = 0; i < thread_count - 1; i++) { err = pthread_join(threads[i], NULL); - nxt_unit_debug(ctx, "join thread #%d: %d", i, err); + if (nxt_fast_path(err == 0)) { + nxt_unit_debug(ctx, "join thread #%d", i); + + } else { + nxt_unit_alert(ctx, "pthread_join(#%d) failed: %s (%d)", + i, strerror(err), err); + } } nxt_unit_free(ctx, threads); @@ -132,7 +138,7 @@ worker(void *main_ctx) nxt_unit_done(ctx); - return NULL; + return (void *) (intptr_t) rc; } -- cgit From 4225361f0ea7d230c80209d76fbc67a932651380 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 00:04:58 +0300 Subject: Python: introducting macro to simplify minor version check. --- src/python/nxt_python.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index 3dd04e31..6d7eba8e 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -11,6 +11,8 @@ #include #include +#define NXT_PYTHON_VER(maj, min) ((maj << 24) | (min << 16)) + #if PY_MAJOR_VERSION == 3 #define NXT_PYTHON_BYTES_TYPE "bytestring" @@ -31,7 +33,7 @@ #define PyUnicode_GET_LENGTH PyUnicode_GET_SIZE #endif -#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5 +#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 5) #define NXT_HAVE_ASGI 1 #endif -- cgit From 8dcb0b9987033d0349a6ecf528014a9daa574787 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 00:04:59 +0300 Subject: Python: request processing in multiple threads. This closes #459 issue on GitHub. --- src/nxt_application.h | 2 + src/nxt_conf_validation.c | 58 ++++ src/nxt_main_process.c | 12 + src/python/nxt_python.c | 268 ++++++++++++++++-- src/python/nxt_python.h | 19 +- src/python/nxt_python_asgi.c | 490 ++++++++++++++++++++------------- src/python/nxt_python_asgi.h | 33 ++- src/python/nxt_python_asgi_http.c | 36 ++- src/python/nxt_python_asgi_lifespan.c | 102 +++---- src/python/nxt_python_asgi_str.c | 2 +- src/python/nxt_python_asgi_str.h | 2 +- src/python/nxt_python_asgi_websocket.c | 21 +- src/python/nxt_python_wsgi.c | 444 +++++++++++++++-------------- 13 files changed, 992 insertions(+), 497 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index cb49a033..7f8ec1ba 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -51,6 +51,8 @@ typedef struct { nxt_str_t path; nxt_str_t module; char *callable; + uint32_t threads; + uint32_t thread_stack_size; } nxt_python_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index b44509e3..97a13e38 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -95,6 +95,10 @@ 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_threads(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_routes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt, @@ -489,6 +493,14 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { }, { .name = nxt_string("callable"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("threads"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_threads, + }, { + .name = nxt_string("thread_stack_size"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_thread_stack_size, }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) @@ -1328,6 +1340,52 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } +static nxt_int_t +nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + int64_t threads; + + threads = nxt_conf_get_number(value); + + if (threads < 1) { + return nxt_conf_vldt_error(vldt, "The \"threads\" number must be " + "equal to or greater than 1."); + } + + if (threads > NXT_INT32_T_MAX) { + return nxt_conf_vldt_error(vldt, "The \"threads\" number must " + "not exceed %d.", NXT_INT32_T_MAX); + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + int64_t size; + + size = nxt_conf_get_number(value); + + if (size < PTHREAD_STACK_MIN) { + return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number " + "must be equal to or greater than %d.", + PTHREAD_STACK_MIN); + } + + if ((size % nxt_pagesize) != 0) { + return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number " + "must be a multiple of the system page size (%d).", + nxt_pagesize); + } + + return NXT_OK; +} + + static nxt_int_t nxt_conf_vldt_routes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index e4ec8b57..16c405ac 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -197,6 +197,18 @@ static nxt_conf_map_t nxt_python_app_conf[] = { NXT_CONF_MAP_CSTRZ, offsetof(nxt_common_app_conf_t, u.python.callable), }, + + { + nxt_string("threads"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.python.threads), + }, + + { + nxt_string("thread_stack_size"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.python.thread_stack_size), + }, }; diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 01534a47..26a6f093 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -15,8 +15,20 @@ #include NXT_PYTHON_MOUNTS_H +typedef struct { + pthread_t thread; + nxt_unit_ctx_t *ctx; + void *ctx_data; +} nxt_py_thread_info_t; + + static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data); +static int nxt_python_init_threads(nxt_python_app_conf_t *c); +static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx); +static void *nxt_python_thread_func(void *main_ctx); +static void nxt_python_join_threads(nxt_unit_ctx_t *ctx, + nxt_python_app_conf_t *c); static void nxt_python_atexit(void); static uint32_t compat[] = { @@ -44,11 +56,15 @@ static wchar_t *nxt_py_home; static char *nxt_py_home; #endif +static pthread_attr_t *nxt_py_thread_attr; +static nxt_py_thread_info_t *nxt_py_threads; +static nxt_python_proto_t nxt_py_proto; + static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) { - int rc, asgi; + int rc; char *nxt_py_module; size_t len; PyObject *obj, *pypath, *module; @@ -124,9 +140,17 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) Py_InitializeEx(0); +#if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7) + if (c->threads > 1) { + PyEval_InitThreads(); + } +#endif + module = NULL; obj = NULL; + python_init.ctx_data = NULL; + obj = PySys_GetObject((char *) "stderr"); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to get \"sys.stderr\" object"); @@ -216,35 +240,50 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) nxt_unit_default_init(task, &python_init); + python_init.data = c; python_init.shm_limit = data->app->shm_limit; + python_init.callbacks.ready_handler = nxt_python_ready_handler; - asgi = nxt_python_asgi_check(nxt_py_application); - - if (asgi) { - rc = nxt_python_asgi_init(task, &python_init); + if (nxt_python_asgi_check(nxt_py_application)) { + rc = nxt_python_asgi_init(&python_init, &nxt_py_proto); } else { - rc = nxt_python_wsgi_init(task, &python_init); + rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto); } - if (nxt_slow_path(rc == NXT_ERROR)) { + if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { goto fail; } + rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_python_init_threads(c); + if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { + goto fail; + } + + if (nxt_py_proto.startup != NULL) { + if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) { + goto fail; + } + } + unit_ctx = nxt_unit_init(&python_init); if (nxt_slow_path(unit_ctx == NULL)) { goto fail; } - if (asgi) { - rc = nxt_python_asgi_run(unit_ctx); + rc = nxt_py_proto.run(unit_ctx); - } else { - rc = nxt_python_wsgi_run(unit_ctx); - } + nxt_python_join_threads(unit_ctx, c); nxt_unit_done(unit_ctx); + nxt_py_proto.ctx_data_free(python_init.ctx_data); + nxt_python_atexit(); exit(rc); @@ -253,6 +292,12 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) fail: + nxt_python_join_threads(NULL, c); + + if (python_init.ctx_data != NULL) { + nxt_py_proto.ctx_data_free(python_init.ctx_data); + } + Py_XDECREF(obj); Py_XDECREF(module); @@ -262,7 +307,195 @@ fail: } -nxt_int_t +static int +nxt_python_init_threads(nxt_python_app_conf_t *c) +{ + int res; + uint32_t i; + nxt_py_thread_info_t *ti; + static pthread_attr_t attr; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + if (c->thread_stack_size > 0) { + res = pthread_attr_init(&attr); + if (nxt_slow_path(res != 0)) { + nxt_unit_alert(NULL, "thread attr init failed: %s (%d)", + strerror(res), res); + + return NXT_UNIT_ERROR; + } + + res = pthread_attr_setstacksize(&attr, c->thread_stack_size); + if (nxt_slow_path(res != 0)) { + nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)", + strerror(res), res); + + return NXT_UNIT_ERROR; + } + + nxt_py_thread_attr = &attr; + } + + nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t) + * (c->threads - 1)); + if (nxt_slow_path(nxt_py_threads == NULL)) { + nxt_unit_alert(NULL, "Failed to allocate thread info array"); + + return NXT_UNIT_ERROR; + } + + memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1)); + + for (i = 0; i < c->threads - 1; i++) { + ti = &nxt_py_threads[i]; + + res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data); + if (nxt_slow_path(res != NXT_UNIT_OK)) { + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + +static int +nxt_python_ready_handler(nxt_unit_ctx_t *ctx) +{ + int res; + uint32_t i; + nxt_py_thread_info_t *ti; + nxt_python_app_conf_t *c; + + if (nxt_py_proto.ready != NULL) { + res = nxt_py_proto.ready(ctx); + if (nxt_slow_path(res != NXT_UNIT_OK)) { + return NXT_UNIT_ERROR; + } + } + + /* Worker thread context. */ + if (!nxt_unit_is_main_ctx(ctx)) { + return NXT_UNIT_OK; + } + + c = ctx->unit->data; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + for (i = 0; i < c->threads - 1; i++) { + ti = &nxt_py_threads[i]; + + ti->ctx = ctx; + + res = pthread_create(&ti->thread, nxt_py_thread_attr, + nxt_python_thread_func, ti); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); + + } else { + nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)", + (int) (i + 1), strerror(res), res); + } + } + + return NXT_UNIT_OK; +} + + +static void * +nxt_python_thread_func(void *data) +{ + nxt_unit_ctx_t *ctx; + PyGILState_STATE gstate; + nxt_py_thread_info_t *ti; + + ti = data; + + nxt_unit_debug(ti->ctx, "worker thread #%d start", + (int) (ti - nxt_py_threads + 1)); + + gstate = PyGILState_Ensure(); + + if (nxt_py_proto.startup != NULL) { + if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) { + goto fail; + } + } + + ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data); + if (nxt_slow_path(ctx == NULL)) { + goto fail; + } + + (void) nxt_py_proto.run(ctx); + + nxt_unit_done(ctx); + +fail: + + PyGILState_Release(gstate); + + nxt_unit_debug(NULL, "worker thread #%d end", + (int) (ti - nxt_py_threads + 1)); + + return NULL; +} + + +static void +nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c) +{ + int res; + uint32_t i; + PyThreadState *thread_state; + nxt_py_thread_info_t *ti; + + if (nxt_py_threads == NULL) { + return; + } + + thread_state = PyEval_SaveThread(); + + for (i = 0; i < c->threads - 1; i++) { + ti = &nxt_py_threads[i]; + + if ((uintptr_t) ti->thread == 0) { + continue; + } + + res = pthread_join(ti->thread, NULL); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); + + } else { + nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)", + (int) (i + 1), strerror(res), res); + } + } + + PyEval_RestoreThread(thread_state); + + for (i = 0; i < c->threads - 1; i++) { + ti = &nxt_py_threads[i]; + + if (ti->ctx_data != NULL) { + nxt_py_proto.ctx_data_free(ti->ctx_data); + } + } + + nxt_unit_free(NULL, nxt_py_threads); +} + + +int nxt_python_init_strings(nxt_python_string_t *pstr) { PyObject *obj; @@ -271,7 +504,7 @@ nxt_python_init_strings(nxt_python_string_t *pstr) obj = PyString_FromStringAndSize((char *) pstr->string.start, pstr->string.length); if (nxt_slow_path(obj == NULL)) { - return NXT_ERROR; + return NXT_UNIT_ERROR; } PyUnicode_InternInPlace(&obj); @@ -281,7 +514,7 @@ nxt_python_init_strings(nxt_python_string_t *pstr) pstr++; } - return NXT_OK; + return NXT_UNIT_OK; } @@ -304,8 +537,9 @@ nxt_python_done_strings(nxt_python_string_t *pstr) static void nxt_python_atexit(void) { - nxt_python_wsgi_done(); - nxt_python_asgi_done(); + if (nxt_py_proto.done != NULL) { + nxt_py_proto.done(); + } Py_XDECREF(nxt_py_stderr_flush); Py_XDECREF(nxt_py_application); diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index 6d7eba8e..b581dd46 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -44,20 +44,25 @@ typedef struct { PyObject **object_p; } nxt_python_string_t; +typedef struct { + int (*ctx_data_alloc)(void **pdata); + void (*ctx_data_free)(void *data); + int (*startup)(void *data); + int (*run)(nxt_unit_ctx_t *ctx); + int (*ready)(nxt_unit_ctx_t *ctx); + void (*done)(void); +} nxt_python_proto_t; + -nxt_int_t nxt_python_init_strings(nxt_python_string_t *pstr); +int nxt_python_init_strings(nxt_python_string_t *pstr); void nxt_python_done_strings(nxt_python_string_t *pstr); void nxt_python_print_exception(void); -nxt_int_t nxt_python_wsgi_init(nxt_task_t *task, nxt_unit_init_t *init); -int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx); -void nxt_python_wsgi_done(void); +int nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto); int nxt_python_asgi_check(PyObject *obj); -nxt_int_t nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init); -nxt_int_t nxt_python_asgi_run(nxt_unit_ctx_t *ctx); -void nxt_python_asgi_done(void); +int nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto); #endif /* _NXT_PYTHON_H_INCLUDED_ */ diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 72408ea1..a188ff56 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -16,6 +16,13 @@ #include +static int nxt_python_asgi_ctx_data_alloc(void **pdata); +static void nxt_python_asgi_ctx_data_free(void *data); +static int nxt_python_asgi_startup(void *data); +static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx); + +static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, + nxt_unit_port_t *port); static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req); static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req); @@ -24,30 +31,32 @@ static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f); static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f); +static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx); + static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port); static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); 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); -PyObject *nxt_py_loop_run_until_complete; -PyObject *nxt_py_loop_create_future; -PyObject *nxt_py_loop_create_task; - -nxt_queue_t nxt_py_asgi_drain_queue; - -static PyObject *nxt_py_loop_call_soon; -static PyObject *nxt_py_quit_future; -static PyObject *nxt_py_quit_future_set_result; -static PyObject *nxt_py_loop_add_reader; -static PyObject *nxt_py_loop_remove_reader; -static PyObject *nxt_py_port_read; +static PyObject *nxt_py_port_read; +static nxt_unit_port_t *nxt_py_shared_port; static PyMethodDef nxt_py_port_read_method = {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""}; +static nxt_python_proto_t nxt_py_asgi_proto = { + .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc, + .ctx_data_free = nxt_python_asgi_ctx_data_free, + .startup = nxt_python_asgi_startup, + .run = nxt_python_asgi_run, + .ready = nxt_python_asgi_ready, + .done = nxt_python_asgi_done, +}; + #define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A @@ -102,202 +111,254 @@ nxt_python_asgi_check(PyObject *obj) } -nxt_int_t -nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init) +int +nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) { - PyObject *asyncio, *loop, *get_event_loop; - nxt_int_t rc; - - nxt_debug(task, "asgi_init"); + nxt_unit_debug(NULL, "asgi_init"); - if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_OK)) { - nxt_alert(task, "Python failed to init string objects"); - return NXT_ERROR; + if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) { + nxt_unit_alert(NULL, "Python failed to init string objects"); + return NXT_UNIT_ERROR; } - asyncio = PyImport_ImportModule("asyncio"); - if (nxt_slow_path(asyncio == NULL)) { - nxt_alert(task, "Python failed to import module 'asyncio'"); - nxt_python_print_exception(); - return NXT_ERROR; + nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL); + if (nxt_slow_path(nxt_py_port_read == NULL)) { + nxt_unit_alert(NULL, + "Python failed to initialize the 'port_read' function"); + return NXT_UNIT_ERROR; } - loop = NULL; - get_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), - "get_event_loop"); - if (nxt_slow_path(get_event_loop == NULL)) { - nxt_alert(task, - "Python failed to get 'get_event_loop' from module 'asyncio'"); - goto fail; + if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) { + return NXT_UNIT_ERROR; } - if (nxt_slow_path(PyCallable_Check(get_event_loop) == 0)) { - nxt_alert(task, "'asyncio.get_event_loop' is not a callable object"); - goto fail; + if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) { + return NXT_UNIT_ERROR; } - loop = PyObject_CallObject(get_event_loop, NULL); - if (nxt_slow_path(loop == NULL)) { - nxt_alert(task, "Python failed to call 'asyncio.get_event_loop'"); - goto fail; - } + 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; + init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler; + init->callbacks.quit = nxt_py_asgi_quit; + init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; + init->callbacks.add_port = nxt_py_asgi_add_port; + init->callbacks.remove_port = nxt_py_asgi_remove_port; - nxt_py_loop_create_task = PyObject_GetAttrString(loop, "create_task"); - if (nxt_slow_path(nxt_py_loop_create_task == NULL)) { - nxt_alert(task, "Python failed to get 'loop.create_task'"); - goto fail; - } + *proto = nxt_py_asgi_proto; - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_create_task) == 0)) { - nxt_alert(task, "'loop.create_task' is not a callable object"); - goto fail; - } + return NXT_UNIT_OK; +} - nxt_py_loop_add_reader = PyObject_GetAttrString(loop, "add_reader"); - if (nxt_slow_path(nxt_py_loop_add_reader == NULL)) { - nxt_alert(task, "Python failed to get 'loop.add_reader'"); - goto fail; - } - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_add_reader) == 0)) { - nxt_alert(task, "'loop.add_reader' is not a callable object"); - goto fail; - } +static int +nxt_python_asgi_ctx_data_alloc(void **pdata) +{ + uint32_t i; + PyObject *asyncio, *loop, *new_event_loop, *obj; + nxt_py_asgi_ctx_data_t *ctx_data; - nxt_py_loop_remove_reader = PyObject_GetAttrString(loop, "remove_reader"); - if (nxt_slow_path(nxt_py_loop_remove_reader == NULL)) { - nxt_alert(task, "Python failed to get 'loop.remove_reader'"); - goto fail; + ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); + if (nxt_slow_path(ctx_data == NULL)) { + nxt_unit_alert(NULL, "Failed to allocate context data"); + return NXT_UNIT_ERROR; } - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_remove_reader) == 0)) { - nxt_alert(task, "'loop.remove_reader' is not a callable object"); - goto fail; - } + memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t)); - nxt_py_loop_call_soon = PyObject_GetAttrString(loop, "call_soon"); - if (nxt_slow_path(nxt_py_loop_call_soon == NULL)) { - nxt_alert(task, "Python failed to get 'loop.call_soon'"); - goto fail; - } + nxt_queue_init(&ctx_data->drain_queue); - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_call_soon) == 0)) { - nxt_alert(task, "'loop.call_soon' is not a callable object"); - goto fail; - } + struct { + const char *key; + PyObject **handler; - nxt_py_loop_run_until_complete = PyObject_GetAttrString(loop, - "run_until_complete"); - if (nxt_slow_path(nxt_py_loop_run_until_complete == NULL)) { - nxt_alert(task, "Python failed to get 'loop.run_until_complete'"); - goto fail; - } + } handlers[] = { + { "create_task", &ctx_data->loop_create_task }, + { "add_reader", &ctx_data->loop_add_reader }, + { "remove_reader", &ctx_data->loop_remove_reader }, + { "call_soon", &ctx_data->loop_call_soon }, + { "run_until_complete", &ctx_data->loop_run_until_complete }, + { "create_future", &ctx_data->loop_create_future }, + }; - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_run_until_complete) == 0)) { - nxt_alert(task, "'loop.run_until_complete' is not a callable object"); - goto fail; - } + loop = NULL; - nxt_py_loop_create_future = PyObject_GetAttrString(loop, "create_future"); - if (nxt_slow_path(nxt_py_loop_create_future == NULL)) { - nxt_alert(task, "Python failed to get 'loop.create_future'"); + asyncio = PyImport_ImportModule("asyncio"); + if (nxt_slow_path(asyncio == NULL)) { + nxt_unit_alert(NULL, "Python failed to import module 'asyncio'"); + nxt_python_print_exception(); goto fail; } - if (nxt_slow_path(PyCallable_Check(nxt_py_loop_create_future) == 0)) { - nxt_alert(task, "'loop.create_future' is not a callable object"); + new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), + "new_event_loop"); + if (nxt_slow_path(new_event_loop == NULL)) { + nxt_unit_alert(NULL, + "Python failed to get 'new_event_loop' from module 'asyncio'"); goto fail; } - nxt_py_quit_future = PyObject_CallObject(nxt_py_loop_create_future, NULL); - if (nxt_slow_path(nxt_py_quit_future == NULL)) { - nxt_alert(task, "Python failed to create Future "); - nxt_python_print_exception(); + if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) { + nxt_unit_alert(NULL, + "'asyncio.new_event_loop' is not a callable object"); goto fail; } - nxt_py_quit_future_set_result = PyObject_GetAttrString(nxt_py_quit_future, - "set_result"); - if (nxt_slow_path(nxt_py_quit_future_set_result == NULL)) { - nxt_alert(task, "Python failed to get 'future.set_result'"); + loop = PyObject_CallObject(new_event_loop, NULL); + if (nxt_slow_path(loop == NULL)) { + nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'"); goto fail; } - if (nxt_slow_path(PyCallable_Check(nxt_py_quit_future_set_result) == 0)) { - nxt_alert(task, "'future.set_result' is not a callable object"); - goto fail; + for (i = 0; i < nxt_nitems(handlers); i++) { + obj = PyObject_GetAttrString(loop, handlers[i].key); + if (nxt_slow_path(obj == NULL)) { + nxt_unit_alert(NULL, "Python failed to get 'loop.%s'", + handlers[i].key); + goto fail; + } + + *handlers[i].handler = obj; + + if (nxt_slow_path(PyCallable_Check(obj) == 0)) { + nxt_unit_alert(NULL, "'loop.%s' is not a callable object", + handlers[i].key); + goto fail; + } } - nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL); - if (nxt_slow_path(nxt_py_port_read == NULL)) { - nxt_alert(task, "Python failed to initialize the 'port_read' function"); + obj = PyObject_CallObject(ctx_data->loop_create_future, NULL); + if (nxt_slow_path(obj == NULL)) { + nxt_unit_alert(NULL, "Python failed to create Future "); + nxt_python_print_exception(); goto fail; } - nxt_queue_init(&nxt_py_asgi_drain_queue); + ctx_data->quit_future = obj; - if (nxt_slow_path(nxt_py_asgi_http_init(task) == NXT_ERROR)) { + obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result"); + if (nxt_slow_path(obj == NULL)) { + nxt_unit_alert(NULL, "Python failed to get 'future.set_result'"); goto fail; } - if (nxt_slow_path(nxt_py_asgi_websocket_init(task) == NXT_ERROR)) { - goto fail; - } + ctx_data->quit_future_set_result = obj; - rc = nxt_py_asgi_lifespan_startup(task); - if (nxt_slow_path(rc == NXT_ERROR)) { + if (nxt_slow_path(PyCallable_Check(obj) == 0)) { + nxt_unit_alert(NULL, "'future.set_result' is not a callable object"); goto fail; } - 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; - init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler; - init->callbacks.quit = nxt_py_asgi_quit; - init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; - init->callbacks.add_port = nxt_py_asgi_add_port; - init->callbacks.remove_port = nxt_py_asgi_remove_port; - Py_DECREF(loop); Py_DECREF(asyncio); - return NXT_OK; + *pdata = ctx_data; + + return NXT_UNIT_OK; fail: + nxt_python_asgi_ctx_data_free(ctx_data); + Py_XDECREF(loop); - Py_DECREF(asyncio); + Py_XDECREF(asyncio); + + return NXT_UNIT_ERROR; +} + - return NXT_ERROR; +static void +nxt_python_asgi_ctx_data_free(void *data) +{ + nxt_py_asgi_ctx_data_t *ctx_data; + + ctx_data = data; + + Py_XDECREF(ctx_data->loop_run_until_complete); + Py_XDECREF(ctx_data->loop_create_future); + Py_XDECREF(ctx_data->loop_create_task); + Py_XDECREF(ctx_data->loop_call_soon); + Py_XDECREF(ctx_data->loop_add_reader); + Py_XDECREF(ctx_data->loop_remove_reader); + Py_XDECREF(ctx_data->quit_future); + Py_XDECREF(ctx_data->quit_future_set_result); + + nxt_unit_free(NULL, ctx_data); } -nxt_int_t +static int +nxt_python_asgi_startup(void *data) +{ + return nxt_py_asgi_lifespan_startup(data); +} + + +static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx) { - PyObject *res; + PyObject *res; + nxt_py_asgi_ctx_data_t *ctx_data; + + ctx_data = ctx->data; - res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete, - nxt_py_quit_future, NULL); + res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, + ctx_data->quit_future, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete"); nxt_python_print_exception(); - return NXT_ERROR; + return NXT_UNIT_ERROR; } Py_DECREF(res); - nxt_py_asgi_lifespan_shutdown(); + nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port); + nxt_py_asgi_remove_reader(ctx, ctx_data->port); - return NXT_OK; + if (ctx_data->port != NULL) { + ctx_data->port->data = NULL; + ctx_data->port = NULL; + } + + nxt_py_asgi_lifespan_shutdown(ctx); + + return NXT_UNIT_OK; +} + + +static void +nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) +{ + PyObject *res; + nxt_py_asgi_ctx_data_t *ctx_data; + + if (port == NULL || port->in_fd == -1) { + return; + } + + ctx_data = ctx->data; + + nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port); + + res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, + PyLong_FromLong(port->in_fd), NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_alert(ctx, "Python failed to remove_reader"); + nxt_python_print_exception(); + + return; + } + + Py_DECREF(res); } static void 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; + nxt_py_asgi_ctx_data_t *ctx_data; if (req->request->websocket_handshake) { asgi = nxt_py_asgi_websocket_create(req); @@ -365,7 +426,9 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) goto release_scope; } - task = PyObject_CallFunctionObjArgs(nxt_py_loop_create_task, res, NULL); + ctx_data = req->ctx->data; + + task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); if (nxt_slow_path(task == NULL)) { nxt_unit_req_error(req, "Python failed to call the create_task"); nxt_python_print_exception(); @@ -723,11 +786,47 @@ fail: } +static int +nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) +{ + PyObject *res; + nxt_unit_port_t *port; + nxt_py_asgi_ctx_data_t *ctx_data; + + if (nxt_slow_path(nxt_py_shared_port == NULL)) { + return NXT_UNIT_ERROR; + } + + port = nxt_py_shared_port; + + nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port); + + ctx_data = ctx->data; + + res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, + PyLong_FromLong(port->in_fd), + nxt_py_port_read, + PyLong_FromVoidPtr(ctx), + PyLong_FromVoidPtr(port), NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_alert(ctx, "Python failed to add_reader"); + nxt_python_print_exception(); + + return NXT_UNIT_ERROR; + } + + Py_DECREF(res); + + return NXT_UNIT_OK; +} + + static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { - int nb; - PyObject *res; + int nb; + PyObject *res; + nxt_py_asgi_ctx_data_t *ctx_data; if (port->in_fd == -1) { return NXT_UNIT_OK; @@ -744,13 +843,25 @@ nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); - res = PyObject_CallFunctionObjArgs(nxt_py_loop_add_reader, + if (port->id.id == NXT_UNIT_SHARED_PORT_ID) { + nxt_py_shared_port = port; + + return NXT_UNIT_OK; + } + + ctx_data = ctx->data; + + ctx_data->port = port; + port->data = ctx_data; + + res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, PyLong_FromLong(port->in_fd), nxt_py_port_read, PyLong_FromVoidPtr(ctx), PyLong_FromVoidPtr(port), NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to add_reader"); + nxt_python_print_exception(); return NXT_UNIT_ERROR; } @@ -764,53 +875,67 @@ nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) { - PyObject *res; - - nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); - if (port->in_fd == -1) { return; } - res = PyObject_CallFunctionObjArgs(nxt_py_loop_remove_reader, - PyLong_FromLong(port->in_fd), NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_alert(NULL, "Python failed to remove_reader"); - } + nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); - Py_DECREF(res); + if (nxt_py_shared_port == port) { + nxt_py_shared_port = NULL; + } } static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) { - PyObject *res; + PyObject *res; + nxt_py_asgi_ctx_data_t *ctx_data; nxt_unit_debug(ctx, "asgi_quit %p", ctx); - res = PyObject_CallFunctionObjArgs(nxt_py_quit_future_set_result, + ctx_data = ctx->data; + + if (nxt_py_shared_port != NULL) { + res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, + PyLong_FromLong(nxt_py_shared_port->in_fd), NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_alert(NULL, "Python failed to remove_reader"); + nxt_python_print_exception(); + + } else { + Py_DECREF(res); + } + } + + res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, PyLong_FromLong(0), NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to set_result"); - } + nxt_python_print_exception(); - Py_DECREF(res); + } else { + Py_DECREF(res); + } } static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx) { - int rc; - nxt_queue_link_t *lnk; + int rc; + nxt_queue_link_t *lnk; + nxt_py_asgi_ctx_data_t *ctx_data; + + ctx_data = ctx->data; - while (!nxt_queue_is_empty(&nxt_py_asgi_drain_queue)) { - lnk = nxt_queue_first(&nxt_py_asgi_drain_queue); + while (!nxt_queue_is_empty(&ctx_data->drain_queue)) { + lnk = nxt_queue_first(&ctx_data->drain_queue); rc = nxt_py_asgi_http_drain(lnk); if (rc == NXT_UNIT_AGAIN) { - break; + return; } nxt_queue_remove(lnk); @@ -859,7 +984,7 @@ nxt_py_asgi_port_read(PyObject *self, PyObject *args) if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { return PyErr_Format(PyExc_RuntimeError, - "error processing port message"); + "error processing port %d message", port->id.id); } Py_RETURN_NONE; @@ -996,8 +1121,8 @@ nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val) PyObject * -nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future, - PyObject *result) +nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, + nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result) { PyObject *set_result, *res; @@ -1013,7 +1138,7 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future, Py_CLEAR(future); - goto cleanup; + goto cleanup_result; } if (nxt_slow_path(PyCallable_Check(set_result) == 0)) { @@ -1024,7 +1149,7 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future, goto cleanup; } - res = PyObject_CallFunctionObjArgs(nxt_py_loop_call_soon, set_result, + res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result, result, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'"); @@ -1038,6 +1163,9 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future, cleanup: Py_DECREF(set_result); + +cleanup_result: + Py_DECREF(result); return future; @@ -1147,6 +1275,17 @@ nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, } +void +nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link) +{ + nxt_py_asgi_ctx_data_t *ctx_data; + + ctx_data = req->ctx->data; + + nxt_queue_insert_tail(&ctx_data->drain_queue, link); +} + + void nxt_py_asgi_dealloc(PyObject *self) { @@ -1177,19 +1316,11 @@ nxt_py_asgi_next(PyObject *self) } -void +static void nxt_python_asgi_done(void) { nxt_py_asgi_str_done(); - Py_XDECREF(nxt_py_quit_future); - Py_XDECREF(nxt_py_quit_future_set_result); - Py_XDECREF(nxt_py_loop_run_until_complete); - Py_XDECREF(nxt_py_loop_create_future); - Py_XDECREF(nxt_py_loop_create_task); - Py_XDECREF(nxt_py_loop_call_soon); - Py_XDECREF(nxt_py_loop_add_reader); - Py_XDECREF(nxt_py_loop_remove_reader); Py_XDECREF(nxt_py_port_read); } @@ -1203,25 +1334,12 @@ nxt_python_asgi_check(PyObject *obj) } -nxt_int_t -nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init) -{ - nxt_alert(task, "ASGI not implemented"); - return NXT_ERROR; -} - - -nxt_int_t -nxt_python_asgi_run(nxt_unit_ctx_t *ctx) +int +nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) { - nxt_unit_alert(ctx, "ASGI not implemented"); - return NXT_ERROR; + nxt_unit_alert(NULL, "ASGI not implemented"); + return NXT_UNIT_ERROR; } -void -nxt_python_asgi_done(void) -{ -} - #endif /* NXT_HAVE_ASGI */ diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h index 24337c37..69d58477 100644 --- a/src/python/nxt_python_asgi.h +++ b/src/python/nxt_python_asgi.h @@ -10,6 +10,9 @@ typedef PyObject * (*nxt_py_asgi_enum_header_cb)(void *ctx, int i, PyObject *name, PyObject *val); +void nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, + nxt_queue_link_t *link); + typedef struct { uint32_t fields_count; uint32_t fields_size; @@ -20,6 +23,20 @@ typedef struct { uint64_t content_length; } nxt_py_asgi_add_field_ctx_t; +typedef struct { + nxt_queue_t drain_queue; + PyObject *loop_run_until_complete; + PyObject *loop_create_future; + PyObject *loop_create_task; + PyObject *loop_call_soon; + PyObject *loop_add_reader; + PyObject *loop_remove_reader; + PyObject *quit_future; + PyObject *quit_future_set_result; + PyObject *lifespan; + nxt_unit_port_t *port; +} nxt_py_asgi_ctx_data_t; + PyObject *nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb, void *data); @@ -27,7 +44,7 @@ PyObject *nxt_py_asgi_calc_size(void *data, int i, PyObject *n, PyObject *v); PyObject *nxt_py_asgi_add_field(void *data, int i, PyObject *n, PyObject *v); PyObject *nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, - PyObject *future, PyObject *result); + nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result); PyObject *nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type); PyObject *nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, PyObject *spec_version); @@ -37,24 +54,18 @@ PyObject *nxt_py_asgi_await(PyObject *self); PyObject *nxt_py_asgi_iter(PyObject *self); PyObject *nxt_py_asgi_next(PyObject *self); -nxt_int_t nxt_py_asgi_http_init(nxt_task_t *task); +int nxt_py_asgi_http_init(void); PyObject *nxt_py_asgi_http_create(nxt_unit_request_info_t *req); void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req); int nxt_py_asgi_http_drain(nxt_queue_link_t *lnk); -nxt_int_t nxt_py_asgi_websocket_init(nxt_task_t *task); +int nxt_py_asgi_websocket_init(void); PyObject *nxt_py_asgi_websocket_create(nxt_unit_request_info_t *req); void nxt_py_asgi_websocket_handler(nxt_unit_websocket_frame_t *ws); void nxt_py_asgi_websocket_close_handler(nxt_unit_request_info_t *req); -nxt_int_t nxt_py_asgi_lifespan_startup(nxt_task_t *task); -nxt_int_t nxt_py_asgi_lifespan_shutdown(void); - -extern PyObject *nxt_py_loop_run_until_complete; -extern PyObject *nxt_py_loop_create_future; -extern PyObject *nxt_py_loop_create_task; - -extern nxt_queue_t nxt_py_asgi_drain_queue; +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); #endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */ diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index b07d61d6..a5034ea6 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -67,15 +67,16 @@ static PyTypeObject nxt_py_asgi_http_type = { static Py_ssize_t nxt_py_asgi_http_body_buf_size = 32 * 1024 * 1024; -nxt_int_t -nxt_py_asgi_http_init(nxt_task_t *task) +int +nxt_py_asgi_http_init(void) { if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_http_type) != 0)) { - nxt_alert(task, "Python failed to initialize the 'http' type object"); - return NXT_ERROR; + nxt_unit_alert(NULL, + "Python failed to initialize the 'http' type object"); + return NXT_UNIT_ERROR; } - return NXT_OK; + return NXT_UNIT_OK; } @@ -106,6 +107,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) { PyObject *msg, *future; nxt_py_asgi_http_t *http; + nxt_py_asgi_ctx_data_t *ctx_data; nxt_unit_request_info_t *req; http = (nxt_py_asgi_http_t *) self; @@ -118,7 +120,9 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) return NULL; } - future = PyObject_CallObject(nxt_py_loop_create_future, NULL); + ctx_data = req->ctx->data; + + future = PyObject_CallObject(ctx_data->loop_create_future, NULL); if (nxt_slow_path(future == NULL)) { nxt_unit_req_alert(req, "Python failed to create Future object"); nxt_python_print_exception(); @@ -130,7 +134,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) } if (msg != Py_None) { - return nxt_py_asgi_set_result_soon(req, future, msg); + return nxt_py_asgi_set_result_soon(req, ctx_data, future, msg); } http->receive_future = future; @@ -329,11 +333,12 @@ nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict) static PyObject * nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) { - int rc; - char *body_str; - ssize_t sent; - PyObject *body, *more_body, *future; - Py_ssize_t body_len, body_off; + int rc; + char *body_str; + ssize_t sent; + PyObject *body, *more_body, *future; + Py_ssize_t body_len, body_off; + nxt_py_asgi_ctx_data_t *ctx_data; body = PyDict_GetItem(dict, nxt_py_body_str); if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) { @@ -371,6 +376,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) body_off = 0; + ctx_data = http->req->ctx->data; + while (body_len > 0) { sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0); if (nxt_slow_path(sent < 0)) { @@ -382,7 +389,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) "out of shared memory, %d", (int) body_len); - future = PyObject_CallObject(nxt_py_loop_create_future, NULL); + future = PyObject_CallObject(ctx_data->loop_create_future, + NULL); if (nxt_slow_path(future == NULL)) { nxt_unit_req_alert(http->req, "Python failed to create Future object"); @@ -396,7 +404,7 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) Py_INCREF(http->send_body); http->send_body_off = body_off; - nxt_queue_insert_tail(&nxt_py_asgi_drain_queue, &http->link); + nxt_py_asgi_drain_wait(http->req, &http->link); http->send_future = future; Py_INCREF(http->send_future); diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c index 14d0ee97..6910d2e8 100644 --- a/src/python/nxt_python_asgi_lifespan.c +++ b/src/python/nxt_python_asgi_lifespan.c @@ -15,15 +15,16 @@ typedef struct { PyObject_HEAD - int disabled; - int startup_received; - int startup_sent; - int shutdown_received; - int shutdown_sent; - int shutdown_called; - PyObject *startup_future; - PyObject *shutdown_future; - PyObject *receive_future; + nxt_py_asgi_ctx_data_t *ctx_data; + int disabled; + int startup_received; + int startup_sent; + int shutdown_received; + int shutdown_sent; + int shutdown_called; + PyObject *startup_future; + PyObject *shutdown_future; + PyObject *receive_future; } nxt_py_asgi_lifespan_t; @@ -39,8 +40,6 @@ static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan); static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future); -static nxt_py_asgi_lifespan_t *nxt_py_lifespan; - static PyMethodDef nxt_py_asgi_lifespan_methods[] = { { "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 }, { "send", nxt_py_asgi_lifespan_send, METH_O, 0 }, @@ -67,46 +66,46 @@ static PyTypeObject nxt_py_asgi_lifespan_type = { }; -nxt_int_t -nxt_py_asgi_lifespan_startup(nxt_task_t *task) +int +nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) { + int rc; PyObject *scope, *res, *py_task, *receive, *send, *done; - nxt_int_t rc; nxt_py_asgi_lifespan_t *lifespan; if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to initialize the 'asgi_lifespan' type object"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type); if (nxt_slow_path(lifespan == NULL)) { - nxt_alert(task, "Python failed to create lifespan object"); - return NXT_ERROR; + nxt_unit_alert(NULL, "Python failed to create lifespan object"); + return NXT_UNIT_ERROR; } - rc = NXT_ERROR; + rc = NXT_UNIT_ERROR; receive = PyObject_GetAttrString((PyObject *) lifespan, "receive"); if (nxt_slow_path(receive == NULL)) { - nxt_alert(task, "Python failed to get 'receive' method"); + nxt_unit_alert(NULL, "Python failed to get 'receive' method"); goto release_lifespan; } send = PyObject_GetAttrString((PyObject *) lifespan, "send"); if (nxt_slow_path(receive == NULL)) { - nxt_alert(task, "Python failed to get 'send' method"); + nxt_unit_alert(NULL, "Python failed to get 'send' method"); goto release_receive; } done = PyObject_GetAttrString((PyObject *) lifespan, "_done"); if (nxt_slow_path(receive == NULL)) { - nxt_alert(task, "Python failed to get '_done' method"); + nxt_unit_alert(NULL, "Python failed to get '_done' method"); goto release_send; } - lifespan->startup_future = PyObject_CallObject(nxt_py_loop_create_future, + lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future, NULL); if (nxt_slow_path(lifespan->startup_future == NULL)) { nxt_unit_alert(NULL, "Python failed to create Future object"); @@ -115,6 +114,7 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task) goto release_done; } + lifespan->ctx_data = ctx_data; lifespan->disabled = 0; lifespan->startup_received = 0; lifespan->startup_sent = 0; @@ -132,21 +132,20 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task) res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, receive, send, NULL); if (nxt_slow_path(res == NULL)) { - nxt_log(task, NXT_LOG_ERR, "Python failed to call the application"); + nxt_unit_error(NULL, "Python failed to call the application"); nxt_python_print_exception(); goto release_scope; } if (nxt_slow_path(!PyCoro_CheckExact(res))) { - nxt_log(task, NXT_LOG_ERR, - "Application result type is not a coroutine"); + nxt_unit_error(NULL, "Application result type is not a coroutine"); Py_DECREF(res); goto release_scope; } - py_task = PyObject_CallFunctionObjArgs(nxt_py_loop_create_task, res, NULL); + py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); if (nxt_slow_path(py_task == NULL)) { - nxt_log(task, NXT_LOG_ERR, "Python failed to call the create_task"); + nxt_unit_alert(NULL, "Python failed to call the create_task"); nxt_python_print_exception(); Py_DECREF(res); goto release_scope; @@ -157,18 +156,17 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task) res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str, done, NULL); if (nxt_slow_path(res == NULL)) { - nxt_log(task, NXT_LOG_ERR, - "Python failed to call 'task.add_done_callback'"); + nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'"); nxt_python_print_exception(); goto release_task; } Py_DECREF(res); - res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete, + res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, lifespan->startup_future, NULL); if (nxt_slow_path(res == NULL)) { - nxt_alert(task, "Python failed to call loop.run_until_complete"); + nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete"); nxt_python_print_exception(); goto release_task; } @@ -176,10 +174,10 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task) Py_DECREF(res); if (lifespan->startup_sent == 1 || lifespan->disabled) { - nxt_py_lifespan = lifespan; - Py_INCREF(nxt_py_lifespan); + ctx_data->lifespan = (PyObject *) lifespan; + Py_INCREF(ctx_data->lifespan); - rc = NXT_OK; + rc = NXT_UNIT_OK; } release_task: @@ -201,17 +199,21 @@ release_lifespan: } -nxt_int_t -nxt_py_asgi_lifespan_shutdown(void) +int +nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx) { PyObject *msg, *future, *res; 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; - if (nxt_slow_path(nxt_py_lifespan == NULL || nxt_py_lifespan->disabled)) { - return NXT_OK; + if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) { + return NXT_UNIT_OK; } - lifespan = nxt_py_lifespan; lifespan->shutdown_called = 1; if (lifespan->receive_future != NULL) { @@ -231,29 +233,29 @@ nxt_py_asgi_lifespan_shutdown(void) } if (lifespan->shutdown_sent) { - return NXT_OK; + return NXT_UNIT_OK; } - lifespan->shutdown_future = PyObject_CallObject(nxt_py_loop_create_future, + lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future, NULL); if (nxt_slow_path(lifespan->shutdown_future == NULL)) { nxt_unit_alert(NULL, "Python failed to create Future object"); nxt_python_print_exception(); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete, + res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, lifespan->shutdown_future, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete"); nxt_python_print_exception(); - return NXT_ERROR; + return NXT_UNIT_ERROR; } Py_DECREF(res); Py_CLEAR(lifespan->shutdown_future); - return NXT_OK; + return NXT_UNIT_OK; } @@ -262,12 +264,14 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none) { PyObject *msg, *future; nxt_py_asgi_lifespan_t *lifespan; + nxt_py_asgi_ctx_data_t *ctx_data; lifespan = (nxt_py_asgi_lifespan_t *) self; + ctx_data = lifespan->ctx_data; nxt_unit_debug(NULL, "asgi_lifespan_receive"); - future = PyObject_CallObject(nxt_py_loop_create_future, NULL); + future = PyObject_CallObject(ctx_data->loop_create_future, NULL); if (nxt_slow_path(future == NULL)) { nxt_unit_alert(NULL, "Python failed to create Future object"); nxt_python_print_exception(); @@ -281,7 +285,7 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none) msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str); - return nxt_py_asgi_set_result_soon(NULL, future, msg); + return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg); } if (lifespan->shutdown_called && !lifespan->shutdown_received) { @@ -289,7 +293,7 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none) msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str); - return nxt_py_asgi_set_result_soon(NULL, future, msg); + return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg); } Py_INCREF(future); diff --git a/src/python/nxt_python_asgi_str.c b/src/python/nxt_python_asgi_str.c index 37fa7f04..34422973 100644 --- a/src/python/nxt_python_asgi_str.c +++ b/src/python/nxt_python_asgi_str.c @@ -124,7 +124,7 @@ static nxt_python_string_t nxt_py_asgi_strings[] = { }; -nxt_int_t +int nxt_py_asgi_str_init(void) { return nxt_python_init_strings(nxt_py_asgi_strings); diff --git a/src/python/nxt_python_asgi_str.h b/src/python/nxt_python_asgi_str.h index 3f389c62..92969fd2 100644 --- a/src/python/nxt_python_asgi_str.h +++ b/src/python/nxt_python_asgi_str.h @@ -62,7 +62,7 @@ extern PyObject *nxt_py_ws_str; extern PyObject *nxt_py_wss_str; -nxt_int_t nxt_py_asgi_str_init(void); +int nxt_py_asgi_str_init(void); void nxt_py_asgi_str_done(void); diff --git a/src/python/nxt_python_asgi_websocket.c b/src/python/nxt_python_asgi_websocket.c index 5a27b588..fc7d9fa4 100644 --- a/src/python/nxt_python_asgi_websocket.c +++ b/src/python/nxt_python_asgi_websocket.c @@ -98,16 +98,16 @@ static uint64_t nxt_py_asgi_ws_max_frame_size = 1024 * 1024; static uint64_t nxt_py_asgi_ws_max_buffer_size = 10 * 1024 * 1024; -nxt_int_t -nxt_py_asgi_websocket_init(nxt_task_t *task) +int +nxt_py_asgi_websocket_init(void) { if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_websocket_type) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to initialize the \"asgi_websocket\" type object"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - return NXT_OK; + return NXT_UNIT_OK; } @@ -137,6 +137,7 @@ static PyObject * nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none) { PyObject *future, *msg; + nxt_py_asgi_ctx_data_t *ctx_data; nxt_py_asgi_websocket_t *ws; ws = (nxt_py_asgi_websocket_t *) self; @@ -160,7 +161,9 @@ nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none) "WebSocket already closed"); } - future = PyObject_CallObject(nxt_py_loop_create_future, NULL); + ctx_data = ws->req->ctx->data; + + future = PyObject_CallObject(ctx_data->loop_create_future, NULL); if (nxt_slow_path(future == NULL)) { nxt_unit_req_alert(ws->req, "Python failed to create Future object"); nxt_python_print_exception(); @@ -174,19 +177,19 @@ nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none) msg = nxt_py_asgi_new_msg(ws->req, nxt_py_websocket_connect_str); - return nxt_py_asgi_set_result_soon(ws->req, future, msg); + return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg); } if (ws->pending_fins > 0) { msg = nxt_py_asgi_websocket_pop_msg(ws, NULL); - return nxt_py_asgi_set_result_soon(ws->req, future, msg); + return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg); } if (nxt_slow_path(ws->state == NXT_WS_DISCONNECTED)) { msg = nxt_py_asgi_websocket_disconnect_msg(ws); - return nxt_py_asgi_set_result_soon(ws->req, future, msg); + return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg); } ws->receive_future = future; diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 8f692941..da7b183c 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -38,55 +38,57 @@ */ -typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t; - typedef struct { PyObject_HEAD -} nxt_py_input_t; + uint64_t content_length; + uint64_t bytes_sent; + PyObject *environ; + PyObject *start_resp; + PyObject *write; + nxt_unit_request_info_t *req; + PyThreadState *thread_state; +} nxt_python_ctx_t; -typedef struct { - PyObject_HEAD -} nxt_py_error_t; + +static int nxt_python_wsgi_ctx_data_alloc(void **pdata); +static void nxt_python_wsgi_ctx_data_free(void *data); +static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx); +static void nxt_python_wsgi_done(void); static void nxt_python_request_handler(nxt_unit_request_info_t *req); -static PyObject *nxt_python_create_environ(nxt_task_t *task); -static PyObject *nxt_python_get_environ(nxt_python_run_ctx_t *ctx); -static int nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name, +static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c); +static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx); +static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, nxt_unit_sptr_t *sptr, uint32_t size); -static int nxt_python_add_field(nxt_python_run_ctx_t *ctx, +static int nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n, uint32_t vl); static PyObject *nxt_python_field_name(const char *name, uint8_t len); static PyObject *nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl); -static int nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, +static int nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value); static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args); -static int nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, +static int nxt_python_response_add_field(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value, int i); static int nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, PyObject **bytes); static PyObject *nxt_py_write(PyObject *self, PyObject *args); -static void nxt_py_input_dealloc(nxt_py_input_t *self); -static PyObject *nxt_py_input_read(nxt_py_input_t *self, PyObject *args); -static PyObject *nxt_py_input_readline(nxt_py_input_t *self, PyObject *args); -static PyObject *nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size); -static PyObject *nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args); +static void nxt_py_input_dealloc(nxt_python_ctx_t *pctx); +static PyObject *nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args); +static PyObject *nxt_py_input_readline(nxt_python_ctx_t *pctx, + PyObject *args); +static PyObject *nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size); +static PyObject *nxt_py_input_readlines(nxt_python_ctx_t *self, + PyObject *args); -static PyObject *nxt_py_input_iter(PyObject *self); -static PyObject *nxt_py_input_next(PyObject *self); +static PyObject *nxt_py_input_iter(PyObject *pctx); +static PyObject *nxt_py_input_next(PyObject *pctx); -static int nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes); - -struct nxt_python_run_ctx_s { - uint64_t content_length; - uint64_t bytes_sent; - PyObject *environ; - nxt_unit_request_info_t *req; -}; +static int nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes); static PyMethodDef nxt_py_start_resp_method[] = { @@ -111,7 +113,7 @@ static PyTypeObject nxt_py_input_type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "unit._input", - .tp_basicsize = sizeof(nxt_py_input_t), + .tp_basicsize = sizeof(nxt_python_ctx_t), .tp_dealloc = (destructor) nxt_py_input_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "unit input object.", @@ -121,12 +123,7 @@ static PyTypeObject nxt_py_input_type = { }; -static PyObject *nxt_py_start_resp_obj; -static PyObject *nxt_py_write_obj; -static PyObject *nxt_py_environ_ptyp; - -static PyThreadState *nxt_python_thread_state; -static nxt_python_run_ctx_t *nxt_python_run_ctx; +static PyObject *nxt_py_environ_ptyp; static PyObject *nxt_py_80_str; static PyObject *nxt_py_close_str; @@ -143,6 +140,7 @@ static PyObject *nxt_py_server_addr_str; static PyObject *nxt_py_server_name_str; static PyObject *nxt_py_server_port_str; static PyObject *nxt_py_server_protocol_str; +static PyObject *nxt_py_wsgi_input_str; static PyObject *nxt_py_wsgi_uri_scheme_str; static nxt_python_string_t nxt_python_strings[] = { @@ -161,82 +159,132 @@ static nxt_python_string_t nxt_python_strings[] = { { nxt_string("SERVER_NAME"), &nxt_py_server_name_str }, { nxt_string("SERVER_PORT"), &nxt_py_server_port_str }, { nxt_string("SERVER_PROTOCOL"), &nxt_py_server_protocol_str }, + { nxt_string("wsgi.input"), &nxt_py_wsgi_input_str }, { nxt_string("wsgi.url_scheme"), &nxt_py_wsgi_uri_scheme_str }, { nxt_null_string, NULL }, }; +static nxt_python_proto_t nxt_py_wsgi_proto = { + .ctx_data_alloc = nxt_python_wsgi_ctx_data_alloc, + .ctx_data_free = nxt_python_wsgi_ctx_data_free, + .run = nxt_python_wsgi_run, + .done = nxt_python_wsgi_done, +}; + -nxt_int_t -nxt_python_wsgi_init(nxt_task_t *task, nxt_unit_init_t *init) +int +nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) { PyObject *obj; obj = NULL; - if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings) != NXT_OK)) { - nxt_alert(task, "Python failed to init string objects"); + if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings) + != NXT_UNIT_OK)) + { + nxt_unit_alert(NULL, "Python failed to init string objects"); goto fail; } - obj = PyCFunction_New(nxt_py_start_resp_method, NULL); + obj = nxt_python_create_environ(init->data); if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, - "Python failed to initialize the \"start_response\" function"); goto fail; } - nxt_py_start_resp_obj = obj; + nxt_py_environ_ptyp = obj; + obj = NULL; + + init->callbacks.request_handler = nxt_python_request_handler; + + *proto = nxt_py_wsgi_proto; - obj = PyCFunction_New(nxt_py_write_method, NULL); - if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, "Python failed to initialize the \"write\" function"); - goto fail; + return NXT_UNIT_OK; + +fail: + + Py_XDECREF(obj); + + return NXT_UNIT_ERROR; +} + + +static int +nxt_python_wsgi_ctx_data_alloc(void **pdata) +{ + nxt_python_ctx_t *pctx; + + pctx = PyObject_New(nxt_python_ctx_t, &nxt_py_input_type); + if (nxt_slow_path(pctx == NULL)) { + nxt_unit_alert(NULL, + "Python failed to create the \"wsgi.input\" object"); + return NXT_UNIT_ERROR; } - nxt_py_write_obj = obj; + pctx->write = NULL; - obj = nxt_python_create_environ(task); - if (nxt_slow_path(obj == NULL)) { + pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method, + (PyObject *) pctx); + if (nxt_slow_path(pctx->start_resp == NULL)) { + nxt_unit_alert(NULL, + "Python failed to initialize the \"start_response\" function"); goto fail; } - nxt_py_environ_ptyp = obj; - obj = NULL; + pctx->write = PyCFunction_New(nxt_py_write_method, (PyObject *) pctx); + if (nxt_slow_path(pctx->write == NULL)) { + nxt_unit_alert(NULL, + "Python failed to initialize the \"write\" function"); + goto fail; + } - init->callbacks.request_handler = nxt_python_request_handler; + *pdata = pctx; - return NXT_OK; + return NXT_UNIT_OK; fail: - Py_XDECREF(obj); + nxt_python_wsgi_ctx_data_free(pctx); - return NXT_ERROR; + return NXT_UNIT_ERROR; } -int +static void +nxt_python_wsgi_ctx_data_free(void *data) +{ + nxt_python_ctx_t *pctx; + + pctx = data; + + Py_XDECREF(pctx->start_resp); + Py_XDECREF(pctx->write); + Py_XDECREF(pctx); +} + + +static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx) { - int rc; + int rc; + nxt_python_ctx_t *pctx; - nxt_python_thread_state = PyEval_SaveThread(); + pctx = ctx->data; + + pctx->thread_state = PyEval_SaveThread(); rc = nxt_unit_run(ctx); - PyEval_RestoreThread(nxt_python_thread_state); + PyEval_RestoreThread(pctx->thread_state); return rc; } -void +static void nxt_python_wsgi_done(void) { nxt_python_done_strings(nxt_python_strings); - Py_XDECREF(nxt_py_start_resp_obj); - Py_XDECREF(nxt_py_write_obj); Py_XDECREF(nxt_py_environ_ptyp); } @@ -244,14 +292,20 @@ nxt_python_wsgi_done(void) static void nxt_python_request_handler(nxt_unit_request_info_t *req) { - int rc; - PyObject *environ, *args, *response, *iterator, *item; - PyObject *close, *result; - nxt_python_run_ctx_t run_ctx = {-1, 0, NULL, req}; + int rc; + PyObject *environ, *args, *response, *iterator, *item; + PyObject *close, *result; + nxt_python_ctx_t *pctx; - PyEval_RestoreThread(nxt_python_thread_state); + pctx = req->ctx->data; - environ = nxt_python_get_environ(&run_ctx); + pctx->content_length = -1; + pctx->bytes_sent = 0; + pctx->req = req; + + PyEval_RestoreThread(pctx->thread_state); + + environ = nxt_python_get_environ(pctx); if (nxt_slow_path(environ == NULL)) { rc = NXT_UNIT_ERROR; goto done; @@ -269,10 +323,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) PyTuple_SET_ITEM(args, 0, environ); - Py_INCREF(nxt_py_start_resp_obj); - PyTuple_SET_ITEM(args, 1, nxt_py_start_resp_obj); - - nxt_python_run_ctx = &run_ctx; + Py_INCREF(pctx->start_resp); + PyTuple_SET_ITEM(args, 1, pctx->start_resp); response = PyObject_CallObject(nxt_py_application, args); @@ -288,7 +340,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) /* Shortcut: avoid iterate over response string symbols. */ if (PyBytes_Check(response)) { - rc = nxt_python_write(&run_ctx, response); + rc = nxt_python_write(pctx, response); } else { iterator = PyObject_GetIter(response); @@ -296,7 +348,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) if (nxt_fast_path(iterator != NULL)) { rc = NXT_UNIT_OK; - while (run_ctx.bytes_sent < run_ctx.content_length) { + while (pctx->bytes_sent < pctx->content_length) { item = PyIter_Next(iterator); if (item == NULL) { @@ -312,7 +364,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) } if (nxt_fast_path(PyBytes_Check(item))) { - rc = nxt_python_write(&run_ctx, item); + rc = nxt_python_write(pctx, item); } else { nxt_unit_req_error(req, "the application returned " @@ -361,29 +413,31 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) done: - nxt_python_thread_state = PyEval_SaveThread(); + pctx->thread_state = PyEval_SaveThread(); + + pctx->req = NULL; - nxt_python_run_ctx = NULL; nxt_unit_request_done(req, rc); } static PyObject * -nxt_python_create_environ(nxt_task_t *task) +nxt_python_create_environ(nxt_python_app_conf_t *c) { PyObject *obj, *err, *environ; environ = PyDict_New(); if (nxt_slow_path(environ == NULL)) { - nxt_alert(task, "Python failed to create the \"environ\" dictionary"); + nxt_unit_alert(NULL, + "Python failed to create the \"environ\" dictionary"); return NULL; } obj = PyString_FromStringAndSize((char *) nxt_server.start, nxt_server.length); if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to create the \"SERVER_SOFTWARE\" environ value"); goto fail; } @@ -391,7 +445,7 @@ nxt_python_create_environ(nxt_task_t *task) if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_SOFTWARE", obj) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to set the \"SERVER_SOFTWARE\" environ value"); goto fail; } @@ -401,15 +455,15 @@ nxt_python_create_environ(nxt_task_t *task) obj = Py_BuildValue("(ii)", 1, 0); if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to build the \"wsgi.version\" environ value"); goto fail; } if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.version", obj) != 0)) { - nxt_alert(task, - "Python failed to set the \"wsgi.version\" environ value"); + nxt_unit_alert(NULL, + "Python failed to set the \"wsgi.version\" environ value"); goto fail; } @@ -418,10 +472,10 @@ nxt_python_create_environ(nxt_task_t *task) if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multithread", - Py_False) + c->threads > 1 ? Py_True : Py_False) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to set the \"wsgi.multithread\" environ value"); goto fail; } @@ -430,7 +484,7 @@ nxt_python_create_environ(nxt_task_t *task) Py_True) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to set the \"wsgi.multiprocess\" environ value"); goto fail; } @@ -439,46 +493,30 @@ nxt_python_create_environ(nxt_task_t *task) Py_False) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to set the \"wsgi.run_once\" environ value"); goto fail; } if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) { - nxt_alert(task, + nxt_unit_alert(NULL, "Python failed to initialize the \"wsgi.input\" type object"); goto fail; } - obj = (PyObject *) PyObject_New(nxt_py_input_t, &nxt_py_input_type); - - if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, "Python failed to create the \"wsgi.input\" object"); - goto fail; - } - - if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.input", obj) != 0)) { - nxt_alert(task, - "Python failed to set the \"wsgi.input\" environ value"); - goto fail; - } - - Py_DECREF(obj); - obj = NULL; - err = PySys_GetObject((char *) "stderr"); if (nxt_slow_path(err == NULL)) { - nxt_alert(task, "Python failed to get \"sys.stderr\" object"); + nxt_unit_alert(NULL, "Python failed to get \"sys.stderr\" object"); goto fail; } if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.errors", err) != 0)) { - nxt_alert(task, - "Python failed to set the \"wsgi.errors\" environ value"); + nxt_unit_alert(NULL, + "Python failed to set the \"wsgi.errors\" environ value"); goto fail; } @@ -494,7 +532,7 @@ fail: static PyObject * -nxt_python_get_environ(nxt_python_run_ctx_t *ctx) +nxt_python_get_environ(nxt_python_ctx_t *pctx) { int rc; uint32_t i, j, vl; @@ -504,15 +542,15 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx) environ = PyDict_Copy(nxt_py_environ_ptyp); if (nxt_slow_path(environ == NULL)) { - nxt_unit_req_error(ctx->req, + nxt_unit_req_error(pctx->req, "Python failed to copy the \"environ\" dictionary"); return NULL; } - ctx->environ = environ; + pctx->environ = environ; - r = ctx->req->request; + r = pctx->req->request; #define RC(S) \ do { \ @@ -522,36 +560,36 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx) } \ } while(0) - RC(nxt_python_add_sptr(ctx, nxt_py_request_method_str, &r->method, + RC(nxt_python_add_sptr(pctx, nxt_py_request_method_str, &r->method, r->method_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_request_uri_str, &r->target, + RC(nxt_python_add_sptr(pctx, nxt_py_request_uri_str, &r->target, r->target_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_query_string_str, &r->query, + RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query, r->query_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_path_info_str, &r->path, + RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path, r->path_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_remote_addr_str, &r->remote, + RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote, r->remote_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_server_addr_str, &r->local, + RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local, r->local_length)); if (r->tls) { - RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str, + RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str, nxt_py_https_str)); } else { - RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str, + RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str, nxt_py_http_str)); } - RC(nxt_python_add_sptr(ctx, nxt_py_server_protocol_str, &r->version, + RC(nxt_python_add_sptr(pctx, nxt_py_server_protocol_str, &r->version, r->version_length)); - RC(nxt_python_add_sptr(ctx, nxt_py_server_name_str, &r->server_name, + RC(nxt_python_add_sptr(pctx, nxt_py_server_name_str, &r->server_name, r->server_name_length)); - RC(nxt_python_add_obj(ctx, nxt_py_server_port_str, nxt_py_80_str)); + RC(nxt_python_add_obj(pctx, nxt_py_server_port_str, nxt_py_80_str)); - nxt_unit_request_group_dup_fields(ctx->req); + nxt_unit_request_group_dup_fields(pctx->req); for (i = 0; i < r->fields_count;) { f = r->fields + i; @@ -569,7 +607,7 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx) vl += 2 + f2->value_length; } - RC(nxt_python_add_field(ctx, f, j - i, vl)); + RC(nxt_python_add_field(pctx, f, j - i, vl)); i = j; } @@ -577,19 +615,27 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx) if (r->content_length_field != NXT_UNIT_NONE_FIELD) { f = r->fields + r->content_length_field; - RC(nxt_python_add_sptr(ctx, nxt_py_content_length_str, &f->value, + RC(nxt_python_add_sptr(pctx, nxt_py_content_length_str, &f->value, f->value_length)); } if (r->content_type_field != NXT_UNIT_NONE_FIELD) { f = r->fields + r->content_type_field; - RC(nxt_python_add_sptr(ctx, nxt_py_content_type_str, &f->value, + RC(nxt_python_add_sptr(pctx, nxt_py_content_type_str, &f->value, f->value_length)); } #undef RC + if (nxt_slow_path(PyDict_SetItem(environ, nxt_py_wsgi_input_str, + (PyObject *) pctx) != 0)) + { + nxt_unit_req_error(pctx->req, + "Python failed to set the \"wsgi.input\" environ value"); + goto fail; + } + return environ; fail: @@ -601,7 +647,7 @@ fail: static int -nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name, +nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, nxt_unit_sptr_t *sptr, uint32_t size) { char *src; @@ -611,7 +657,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name, value = PyString_FromStringAndSize(src, size); if (nxt_slow_path(value == NULL)) { - nxt_unit_req_error(ctx->req, + nxt_unit_req_error(pctx->req, "Python failed to create value string \"%.*s\"", (int) size, src); nxt_python_print_exception(); @@ -619,8 +665,8 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name, return NXT_UNIT_ERROR; } - if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) { - nxt_unit_req_error(ctx->req, + if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { + nxt_unit_req_error(pctx->req, "Python failed to set the \"%s\" environ value", PyUnicode_AsUTF8(name)); Py_DECREF(value); @@ -635,7 +681,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name, static int -nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n, +nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n, uint32_t vl) { char *src; @@ -645,7 +691,7 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n, name = nxt_python_field_name(src, field->name_length); if (nxt_slow_path(name == NULL)) { - nxt_unit_req_error(ctx->req, + nxt_unit_req_error(pctx->req, "Python failed to create name string \"%.*s\"", (int) field->name_length, src); nxt_python_print_exception(); @@ -656,7 +702,7 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n, value = nxt_python_field_value(field, n, vl); if (nxt_slow_path(value == NULL)) { - nxt_unit_req_error(ctx->req, + nxt_unit_req_error(pctx->req, "Python failed to create value string \"%.*s\"", (int) field->value_length, (char *) nxt_unit_sptr_get(&field->value)); @@ -665,8 +711,8 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n, goto fail; } - if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) { - nxt_unit_req_error(ctx->req, + if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { + nxt_unit_req_error(pctx->req, "Python failed to set the \"%s\" environ value", PyUnicode_AsUTF8(name)); goto fail; @@ -761,10 +807,10 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) static int -nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, PyObject *value) +nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value) { - if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) { - nxt_unit_req_error(ctx->req, + if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { + nxt_unit_req_error(pctx->req, "Python failed to set the \"%s\" environ value", PyUnicode_AsUTF8(name)); @@ -778,15 +824,15 @@ nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, PyObject *value) static PyObject * nxt_py_start_resp(PyObject *self, PyObject *args) { - int rc, status; - char *status_str, *space_ptr; - uint32_t status_len; - PyObject *headers, *tuple, *string, *status_bytes; - Py_ssize_t i, n, fields_size, fields_count; - nxt_python_run_ctx_t *ctx; - - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { + int rc, status; + char *status_str, *space_ptr; + uint32_t status_len; + PyObject *headers, *tuple, *string, *status_bytes; + Py_ssize_t i, n, fields_size, fields_count; + nxt_python_ctx_t *pctx; + + pctx = (nxt_python_ctx_t *) self; + if (nxt_slow_path(pctx->req == NULL)) { return PyErr_Format(PyExc_RuntimeError, "start_response() is called " "outside of WSGI request processing"); @@ -851,7 +897,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args) } } - ctx->content_length = -1; + pctx->content_length = -1; string = PyTuple_GET_ITEM(args, 0); rc = nxt_python_str_buf(string, &status_str, &status_len, &status_bytes); @@ -877,7 +923,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args) * ... applications can replace their originally intended output with error * output, up until the last possible moment. */ - rc = nxt_unit_response_init(ctx->req, status, fields_count, fields_size); + rc = nxt_unit_response_init(pctx->req, status, fields_count, fields_size); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return PyErr_Format(PyExc_RuntimeError, "failed to allocate response object"); @@ -886,7 +932,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args) for (i = 0; i < fields_count; i++) { tuple = PyList_GET_ITEM(headers, i); - rc = nxt_python_response_add_field(ctx, PyTuple_GET_ITEM(tuple, 0), + rc = nxt_python_response_add_field(pctx, PyTuple_GET_ITEM(tuple, 0), PyTuple_GET_ITEM(tuple, 1), i); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return PyErr_Format(PyExc_RuntimeError, @@ -907,21 +953,21 @@ nxt_py_start_resp(PyObject *self, PyObject *args) * possible exception to this rule is if the response headers explicitly * include a Content-Length of zero.) */ - if (ctx->content_length == 0) { - rc = nxt_unit_response_send(ctx->req); + if (pctx->content_length == 0) { + rc = nxt_unit_response_send(pctx->req); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return PyErr_Format(PyExc_RuntimeError, "failed to send response headers"); } } - Py_INCREF(nxt_py_write_obj); - return nxt_py_write_obj; + Py_INCREF(pctx->write); + return pctx->write; } static int -nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, PyObject *name, +nxt_python_response_add_field(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value, int i) { int rc; @@ -943,20 +989,20 @@ nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, PyObject *name, goto fail; } - rc = nxt_unit_response_add_field(ctx->req, name_str, name_length, + rc = nxt_unit_response_add_field(pctx->req, name_str, name_length, value_str, value_length); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } - if (ctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { + if (pctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { content_length = nxt_off_t_parse((u_char *) value_str, value_length); if (nxt_slow_path(content_length < 0)) { - nxt_unit_req_error(ctx->req, "failed to parse Content-Length " + nxt_unit_req_error(pctx->req, "failed to parse Content-Length " "value %.*s", (int) value_length, value_str); } else { - ctx->content_length = content_length; + pctx->content_length = content_length; } } @@ -1001,7 +1047,7 @@ nxt_py_write(PyObject *self, PyObject *str) NXT_PYTHON_BYTES_TYPE); } - rc = nxt_python_write(nxt_python_run_ctx, str); + rc = nxt_python_write((nxt_python_ctx_t *) self, str); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return PyErr_Format(PyExc_RuntimeError, "failed to write response value"); @@ -1012,28 +1058,26 @@ nxt_py_write(PyObject *self, PyObject *str) static void -nxt_py_input_dealloc(nxt_py_input_t *self) +nxt_py_input_dealloc(nxt_python_ctx_t *self) { PyObject_Del(self); } static PyObject * -nxt_py_input_read(nxt_py_input_t *self, PyObject *args) +nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args) { - char *buf; - PyObject *content, *obj; - Py_ssize_t size, n; - nxt_python_run_ctx_t *ctx; + char *buf; + PyObject *content, *obj; + Py_ssize_t size, n; - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { + if (nxt_slow_path(pctx->req == NULL)) { return PyErr_Format(PyExc_RuntimeError, "wsgi.input.read() is called " "outside of WSGI request processing"); } - size = ctx->req->content_length; + size = pctx->req->content_length; n = PyTuple_GET_SIZE(args); @@ -1057,8 +1101,8 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) } } - if (size == -1 || size > (Py_ssize_t) ctx->req->content_length) { - size = ctx->req->content_length; + if (size == -1 || size > (Py_ssize_t) pctx->req->content_length) { + size = pctx->req->content_length; } } @@ -1069,22 +1113,20 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) buf = PyBytes_AS_STRING(content); - size = nxt_unit_request_read(ctx->req, buf, size); + size = nxt_unit_request_read(pctx->req, buf, size); return content; } static PyObject * -nxt_py_input_readline(nxt_py_input_t *self, PyObject *args) +nxt_py_input_readline(nxt_python_ctx_t *pctx, PyObject *args) { - ssize_t ssize; - PyObject *obj; - Py_ssize_t n; - nxt_python_run_ctx_t *ctx; + ssize_t ssize; + PyObject *obj; + Py_ssize_t n; - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { + if (nxt_slow_path(pctx->req == NULL)) { return PyErr_Format(PyExc_RuntimeError, "wsgi.input.readline() is called " "outside of WSGI request processing"); @@ -1102,7 +1144,7 @@ nxt_py_input_readline(nxt_py_input_t *self, PyObject *args) ssize = PyNumber_AsSsize_t(obj, PyExc_OverflowError); if (nxt_fast_path(ssize > 0)) { - return nxt_py_input_getline(ctx, ssize); + return nxt_py_input_getline(pctx, ssize); } if (ssize == 0) { @@ -1119,18 +1161,18 @@ nxt_py_input_readline(nxt_py_input_t *self, PyObject *args) } } - return nxt_py_input_getline(ctx, SSIZE_MAX); + return nxt_py_input_getline(pctx, SSIZE_MAX); } static PyObject * -nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size) +nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size) { void *buf; ssize_t res; PyObject *content; - res = nxt_unit_request_readline_size(ctx->req, size); + res = nxt_unit_request_readline_size(pctx->req, size); if (nxt_slow_path(res < 0)) { return NULL; } @@ -1146,20 +1188,18 @@ nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size) buf = PyBytes_AS_STRING(content); - res = nxt_unit_request_read(ctx->req, buf, res); + res = nxt_unit_request_read(pctx->req, buf, res); return content; } static PyObject * -nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args) +nxt_py_input_readlines(nxt_python_ctx_t *pctx, PyObject *args) { - PyObject *res; - nxt_python_run_ctx_t *ctx; + PyObject *res; - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { + if (nxt_slow_path(pctx->req == NULL)) { return PyErr_Format(PyExc_RuntimeError, "wsgi.input.readlines() is called " "outside of WSGI request processing"); @@ -1171,7 +1211,7 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args) } for ( ;; ) { - PyObject *line = nxt_py_input_getline(ctx, SSIZE_MAX); + PyObject *line = nxt_py_input_getline(pctx, SSIZE_MAX); if (nxt_slow_path(line == NULL)) { Py_DECREF(res); return NULL; @@ -1201,17 +1241,17 @@ nxt_py_input_iter(PyObject *self) static PyObject * nxt_py_input_next(PyObject *self) { - PyObject *line; - nxt_python_run_ctx_t *ctx; + PyObject *line; + nxt_python_ctx_t *pctx; - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { + pctx = (nxt_python_ctx_t *) self; + if (nxt_slow_path(pctx->req == NULL)) { return PyErr_Format(PyExc_RuntimeError, "wsgi.input.next() is called " "outside of WSGI request processing"); } - line = nxt_py_input_getline(ctx, SSIZE_MAX); + line = nxt_py_input_getline(pctx, SSIZE_MAX); if (nxt_slow_path(line == NULL)) { return NULL; } @@ -1227,7 +1267,7 @@ nxt_py_input_next(PyObject *self) static int -nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes) +nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes) { int rc; char *str_buf; @@ -1248,16 +1288,16 @@ nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes) * stop iterating over the response when enough data has been sent, or raise * an error if the application tries to write() past that point. */ - if (nxt_slow_path(str_length > ctx->content_length - ctx->bytes_sent)) { - nxt_unit_req_error(ctx->req, "content length %"PRIu64" exceeded", - ctx->content_length); + if (nxt_slow_path(str_length > pctx->content_length - pctx->bytes_sent)) { + nxt_unit_req_error(pctx->req, "content length %"PRIu64" exceeded", + pctx->content_length); return NXT_UNIT_ERROR; } - rc = nxt_unit_response_write(ctx->req, str_buf, str_length); + rc = nxt_unit_response_write(pctx->req, str_buf, str_length); if (nxt_fast_path(rc == NXT_UNIT_OK)) { - ctx->bytes_sent += str_length; + pctx->bytes_sent += str_length; } return rc; -- cgit From 8e37b1cbf5ad2935c1777992ebded183e6454ba4 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 00:05:00 +0300 Subject: Python: fixing some arguments reference counting. --- src/python/nxt_python_asgi.c | 163 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index a188ff56..cab73239 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -330,7 +330,7 @@ nxt_python_asgi_run(nxt_unit_ctx_t *ctx) static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { - PyObject *res; + PyObject *res, *fd; nxt_py_asgi_ctx_data_t *ctx_data; if (port == NULL || port->in_fd == -1) { @@ -341,16 +341,24 @@ nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port); - res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, - PyLong_FromLong(port->in_fd), NULL); + fd = PyLong_FromLong(port->in_fd); + if (nxt_slow_path(fd == NULL)) { + nxt_unit_alert(ctx, "Python failed to create Long object"); + nxt_python_print_exception(); + + return; + } + + res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to remove_reader"); nxt_python_print_exception(); - return; + } else { + Py_DECREF(res); } - Py_DECREF(res); + Py_DECREF(fd); } @@ -789,7 +797,8 @@ fail: static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) { - PyObject *res; + int rc; + PyObject *res, *fd, *py_ctx, *py_port; nxt_unit_port_t *port; nxt_py_asgi_ctx_data_t *ctx_data; @@ -803,29 +812,64 @@ nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) ctx_data = ctx->data; + rc = NXT_UNIT_ERROR; + + fd = PyLong_FromLong(port->in_fd); + if (nxt_slow_path(fd == NULL)) { + nxt_unit_alert(ctx, "Python failed to create fd"); + nxt_python_print_exception(); + + return rc; + } + + py_ctx = PyLong_FromVoidPtr(ctx); + if (nxt_slow_path(py_ctx == NULL)) { + nxt_unit_alert(ctx, "Python failed to create py_ctx"); + nxt_python_print_exception(); + + goto clean_fd; + } + + py_port = PyLong_FromVoidPtr(port); + if (nxt_slow_path(py_port == NULL)) { + nxt_unit_alert(ctx, "Python failed to create py_port"); + nxt_python_print_exception(); + + goto clean_py_ctx; + } + res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, - PyLong_FromLong(port->in_fd), - nxt_py_port_read, - PyLong_FromVoidPtr(ctx), - PyLong_FromVoidPtr(port), NULL); + fd, nxt_py_port_read, + py_ctx, py_port, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to add_reader"); nxt_python_print_exception(); - return NXT_UNIT_ERROR; + } else { + Py_DECREF(res); + + rc = NXT_UNIT_OK; } - Py_DECREF(res); + Py_DECREF(py_port); - return NXT_UNIT_OK; +clean_py_ctx: + + Py_DECREF(py_ctx); + +clean_fd: + + Py_DECREF(fd); + + return rc; } static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { - int nb; - PyObject *res; + int nb, rc; + PyObject *res, *fd, *py_ctx, *py_port; nxt_py_asgi_ctx_data_t *ctx_data; if (port->in_fd == -1) { @@ -854,21 +898,56 @@ nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) ctx_data->port = port; port->data = ctx_data; + rc = NXT_UNIT_ERROR; + + fd = PyLong_FromLong(port->in_fd); + if (nxt_slow_path(fd == NULL)) { + nxt_unit_alert(ctx, "Python failed to create fd"); + nxt_python_print_exception(); + + return rc; + } + + py_ctx = PyLong_FromVoidPtr(ctx); + if (nxt_slow_path(py_ctx == NULL)) { + nxt_unit_alert(ctx, "Python failed to create py_ctx"); + nxt_python_print_exception(); + + goto clean_fd; + } + + py_port = PyLong_FromVoidPtr(port); + if (nxt_slow_path(py_port == NULL)) { + nxt_unit_alert(ctx, "Python failed to create py_port"); + nxt_python_print_exception(); + + goto clean_py_ctx; + } + res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, - PyLong_FromLong(port->in_fd), - nxt_py_port_read, - PyLong_FromVoidPtr(ctx), - PyLong_FromVoidPtr(port), NULL); + fd, nxt_py_port_read, + py_ctx, py_port, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_alert(ctx, "Python failed to add_reader"); nxt_python_print_exception(); - return NXT_UNIT_ERROR; + } else { + Py_DECREF(res); + + rc = NXT_UNIT_OK; } - Py_DECREF(res); + Py_DECREF(py_port); - return NXT_UNIT_OK; +clean_py_ctx: + + Py_DECREF(py_ctx); + +clean_fd: + + Py_DECREF(fd); + + return rc; } @@ -890,7 +969,7 @@ nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) { - PyObject *res; + PyObject *res, *p; nxt_py_asgi_ctx_data_t *ctx_data; nxt_unit_debug(ctx, "asgi_quit %p", ctx); @@ -898,25 +977,43 @@ nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) ctx_data = ctx->data; if (nxt_py_shared_port != NULL) { - res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, - PyLong_FromLong(nxt_py_shared_port->in_fd), NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_alert(NULL, "Python failed to remove_reader"); + p = PyLong_FromLong(nxt_py_shared_port->in_fd); + if (nxt_slow_path(p == NULL)) { + nxt_unit_alert(NULL, "Python failed to create Long"); nxt_python_print_exception(); } else { - Py_DECREF(res); + res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, + p, NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_alert(NULL, "Python failed to remove_reader"); + nxt_python_print_exception(); + + } else { + Py_DECREF(res); + } + + Py_DECREF(p); } } - res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, - PyLong_FromLong(0), NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_alert(ctx, "Python failed to set_result"); + p = PyLong_FromLong(0); + if (nxt_slow_path(p == NULL)) { + nxt_unit_alert(NULL, "Python failed to create Long"); nxt_python_print_exception(); } else { - Py_DECREF(res); + res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, + p, NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_alert(ctx, "Python failed to set_result"); + nxt_python_print_exception(); + + } else { + Py_DECREF(res); + } + + Py_DECREF(p); } } -- cgit From 29db46c52ba0f05706d83ed75d88e4b57bac36e5 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 00:06:10 +0300 Subject: Java: request processing in multiple threads. This closes #458 issue on GitHub. --- src/nxt_application.h | 2 + src/nxt_conf_validation.c | 8 ++ src/nxt_java.c | 199 ++++++++++++++++++++++++++++++++++++++++++---- src/nxt_main_process.c | 10 +++ 4 files changed, 205 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 7f8ec1ba..08de0fce 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -77,6 +77,8 @@ typedef struct { char *webapp; nxt_conf_value_t *options; char *unit_jars; + uint32_t threads; + uint32_t thread_stack_size; } nxt_java_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 97a13e38..1ce2d9ea 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -639,6 +639,14 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = { }, { .name = nxt_string("unit_jars"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("threads"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_threads, + }, { + .name = nxt_string("thread_stack_size"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_thread_stack_size, }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) diff --git a/src/nxt_java.c b/src/nxt_java.c index 1f8864bd..ac715c0b 100644 --- a/src/nxt_java.c +++ b/src/nxt_java.c @@ -36,6 +36,11 @@ static nxt_int_t nxt_java_start(nxt_task_t *task, static void nxt_java_request_handler(nxt_unit_request_info_t *req); static void nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws); static void nxt_java_close_handler(nxt_unit_request_info_t *req); +static int nxt_java_ready_handler(nxt_unit_ctx_t *ctx); +static void *nxt_java_thread_func(void *main_ctx); +static int nxt_java_init_threads(nxt_java_app_conf_t *c); +static void nxt_java_join_threads(nxt_unit_ctx_t *ctx, + nxt_java_app_conf_t *c); static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, @@ -43,6 +48,9 @@ static uint32_t compat[] = { char *nxt_java_modules; +static pthread_t *nxt_java_threads; +static pthread_attr_t *nxt_java_thread_attr; + #define NXT_STRING(x) _NXT_STRING(x) #define _NXT_STRING(x) #x @@ -59,8 +67,10 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { }; typedef struct { - JNIEnv *env; - jobject ctx; + JavaVM *jvm; + jobject cl; + jobject ctx; + nxt_java_app_conf_t *conf; } nxt_java_data_t; @@ -402,8 +412,10 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) goto env_failed; } - java_data.env = env; + java_data.jvm = jvm; + java_data.cl = cl; java_data.ctx = nxt_java_startContext(env, c->webapp, classpath); + java_data.conf = c; if ((*env)->ExceptionCheck(env)) { nxt_alert(task, "Unhandled exception in application start"); @@ -411,13 +423,20 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) return NXT_ERROR; } + rc = nxt_java_init_threads(c); + if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { + return NXT_ERROR; + } + nxt_unit_default_init(task, &java_init); java_init.callbacks.request_handler = nxt_java_request_handler; java_init.callbacks.websocket_handler = nxt_java_websocket_handler; java_init.callbacks.close_handler = nxt_java_close_handler; + java_init.callbacks.ready_handler = nxt_java_ready_handler; java_init.request_data_size = sizeof(nxt_java_request_data_t); java_init.data = &java_data; + java_init.ctx_data = env; java_init.shm_limit = app_conf->shm_limit; ctx = nxt_unit_init(&java_init); @@ -427,9 +446,8 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) } rc = nxt_unit_run(ctx); - if (nxt_slow_path(rc != NXT_UNIT_OK)) { - /* TODO report error */ - } + + nxt_java_join_threads(ctx, c); nxt_java_stopContext(env, java_data.ctx); @@ -441,7 +459,7 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) (*jvm)->DestroyJavaVM(jvm); - exit(0); + exit(rc); return NXT_OK; @@ -464,7 +482,7 @@ nxt_java_request_handler(nxt_unit_request_info_t *req) nxt_java_request_data_t *data; java_data = req->unit->data; - env = java_data->env; + env = req->ctx->data; data = req->data; jreq = nxt_java_newRequest(env, java_data->ctx, req); @@ -543,11 +561,9 @@ nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws) void *b; JNIEnv *env; jobject jbuf; - nxt_java_data_t *java_data; nxt_java_request_data_t *data; - java_data = ws->req->unit->data; - env = java_data->env; + env = ws->req->ctx->data; data = ws->req->data; b = malloc(ws->payload_len); @@ -578,11 +594,9 @@ static void nxt_java_close_handler(nxt_unit_request_info_t *req) { JNIEnv *env; - nxt_java_data_t *java_data; nxt_java_request_data_t *data; - java_data = req->unit->data; - env = java_data->env; + env = req->ctx->data; data = req->data; nxt_java_Request_close(env, data->jreq); @@ -593,3 +607,160 @@ nxt_java_close_handler(nxt_unit_request_info_t *req) nxt_unit_request_done(req, NXT_UNIT_OK); } + +static int +nxt_java_ready_handler(nxt_unit_ctx_t *ctx) +{ + int res; + uint32_t i; + nxt_java_data_t *java_data; + nxt_java_app_conf_t *c; + + /* Worker thread context. */ + if (!nxt_unit_is_main_ctx(ctx)) { + return NXT_UNIT_OK; + } + + java_data = ctx->unit->data; + c = java_data->conf; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + for (i = 0; i < c->threads - 1; i++) { + res = pthread_create(&nxt_java_threads[i], nxt_java_thread_attr, + nxt_java_thread_func, ctx); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); + + } else { + nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)", + (int) (i + 1), strerror(res), res); + + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + +static void * +nxt_java_thread_func(void *data) +{ + int rc; + JavaVM *jvm; + JNIEnv *env; + nxt_unit_ctx_t *main_ctx, *ctx; + nxt_java_data_t *java_data; + + main_ctx = data; + + nxt_unit_debug(main_ctx, "worker thread start"); + + java_data = main_ctx->unit->data; + jvm = java_data->jvm; + + rc = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL); + if (rc != JNI_OK) { + nxt_unit_alert(main_ctx, "failed to attach Java VM: %d", (int) rc); + return NULL; + } + + nxt_java_setContextClassLoader(env, java_data->cl); + + ctx = nxt_unit_ctx_alloc(main_ctx, env); + if (nxt_slow_path(ctx == NULL)) { + goto fail; + } + + (void) nxt_unit_run(ctx); + + nxt_unit_done(ctx); + +fail: + + (*jvm)->DetachCurrentThread(jvm); + + nxt_unit_debug(NULL, "worker thread end"); + + return NULL; +} + + +static int +nxt_java_init_threads(nxt_java_app_conf_t *c) +{ + int res; + static pthread_attr_t attr; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + if (c->thread_stack_size > 0) { + res = pthread_attr_init(&attr); + if (nxt_slow_path(res != 0)) { + nxt_unit_alert(NULL, "thread attr init failed: %s (%d)", + strerror(res), res); + + return NXT_UNIT_ERROR; + } + + res = pthread_attr_setstacksize(&attr, c->thread_stack_size); + if (nxt_slow_path(res != 0)) { + nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)", + strerror(res), res); + + return NXT_UNIT_ERROR; + } + + nxt_java_thread_attr = &attr; + } + + nxt_java_threads = nxt_unit_malloc(NULL, + sizeof(pthread_t) * (c->threads - 1)); + if (nxt_slow_path(nxt_java_threads == NULL)) { + nxt_unit_alert(NULL, "Failed to allocate thread id array"); + + return NXT_UNIT_ERROR; + } + + memset(nxt_java_threads, 0, sizeof(pthread_t) * (c->threads - 1)); + + return NXT_UNIT_OK; +} + + +static void +nxt_java_join_threads(nxt_unit_ctx_t *ctx, nxt_java_app_conf_t *c) +{ + int res; + uint32_t i; + + if (nxt_java_threads == NULL) { + return; + } + + for (i = 0; i < c->threads - 1; i++) { + if ((uintptr_t) nxt_java_threads[i] == 0) { + continue; + } + + res = pthread_join(nxt_java_threads[i], NULL); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d joined", (int) i); + + } else { + nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)", + (int) i, strerror(res), res); + } + } + + nxt_unit_free(ctx, nxt_java_threads); +} + + diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 16c405ac..ce8d6916 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -266,6 +266,16 @@ static nxt_conf_map_t nxt_java_app_conf[] = { NXT_CONF_MAP_CSTRZ, offsetof(nxt_common_app_conf_t, u.java.unit_jars), }, + { + nxt_string("threads"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.java.threads), + }, + { + nxt_string("thread_stack_size"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.java.thread_stack_size), + }, }; -- cgit From 9f8b746e776031e6eef6dea84c8dafbb8c24c725 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 12:45:08 +0300 Subject: Ruby: reusing static constant references to string objects. This shall save a couple of CPU cycles in request processing. --- src/ruby/nxt_ruby.c | 158 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 743bf646..7b383557 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -14,14 +14,6 @@ #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 #define NXT_RUBY_RACK_API_VERSION_MINOR 3 -#define NXT_RUBY_STRINGIZE_HELPER(x) #x -#define NXT_RUBY_STRINGIZE(x) NXT_RUBY_STRINGIZE_HELPER(x) - -#define NXT_RUBY_LIB_VERSION \ - NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MAJOR) \ - "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MINOR) \ - "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_TEENY) - typedef struct { nxt_task_t *task; @@ -45,10 +37,8 @@ static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); static VALUE nxt_ruby_rack_app_run(VALUE arg); static int nxt_ruby_read_request(VALUE hash_env); -nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, - const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len); -nxt_inline void nxt_ruby_add_str(VALUE hash_env, - const char *name, uint32_t name_len, const char *str, uint32_t len); +nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, + nxt_unit_sptr_t *sptr, uint32_t len); static nxt_int_t nxt_ruby_rack_result_status(VALUE result); static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status); static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); @@ -86,6 +76,93 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_ruby_start, }; +typedef struct { + nxt_str_t string; + VALUE *v; +} nxt_ruby_string_t; + +static VALUE nxt_rb_80_str; +static VALUE nxt_rb_content_length_str; +static VALUE nxt_rb_content_type_str; +static VALUE nxt_rb_http_str; +static VALUE nxt_rb_https_str; +static VALUE nxt_rb_path_info_str; +static VALUE nxt_rb_query_string_str; +static VALUE nxt_rb_rack_url_scheme_str; +static VALUE nxt_rb_remote_addr_str; +static VALUE nxt_rb_request_method_str; +static VALUE nxt_rb_request_uri_str; +static VALUE nxt_rb_server_addr_str; +static VALUE nxt_rb_server_name_str; +static VALUE nxt_rb_server_port_str; +static VALUE nxt_rb_server_protocol_str; + +static nxt_ruby_string_t nxt_rb_strings[] = { + { nxt_string("80"), &nxt_rb_80_str }, + { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str }, + { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str }, + { nxt_string("http"), &nxt_rb_http_str }, + { nxt_string("https"), &nxt_rb_https_str }, + { nxt_string("PATH_INFO"), &nxt_rb_path_info_str }, + { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str }, + { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str }, + { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str }, + { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str }, + { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str }, + { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str }, + { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, + { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, + { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, + { nxt_null_string, NULL }, +}; + + +static int +nxt_ruby_init_strings(void) +{ + VALUE v; + nxt_ruby_string_t *pstr; + + pstr = nxt_rb_strings; + + while (pstr->string.start != NULL) { + v = rb_str_new_static((char *) pstr->string.start, pstr->string.length); + + if (nxt_slow_path(v == Qnil)) { + nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'", + (int) pstr->string.length, + (char *) pstr->string.start); + + return NXT_UNIT_ERROR; + } + + *pstr->v = v; + + rb_gc_register_address(pstr->v); + + pstr++; + } + + return NXT_UNIT_OK; +} + + +static void +nxt_ruby_done_strings(void) +{ + nxt_ruby_string_t *pstr; + + pstr = nxt_rb_strings; + + while (pstr->string.start != NULL) { + rb_gc_unregister_address(pstr->v); + + *pstr->v = Qnil; + + pstr++; + } +} + static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) @@ -109,6 +186,8 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) rack_init.task = task; rack_init.script = &conf->u.ruby.script; + nxt_ruby_init_strings(); + res = rb_protect(nxt_ruby_init_basic, (VALUE) (uintptr_t) &rack_init, &state); if (nxt_slow_path(res == Qnil || state != 0)) { @@ -440,78 +519,69 @@ fail: static int nxt_ruby_read_request(VALUE hash_env) { + VALUE name; uint32_t i; nxt_unit_field_t *f; nxt_unit_request_t *r; r = nxt_ruby_run_ctx.req->request; -#define NL(S) (S), sizeof(S)-1 - - nxt_ruby_add_sptr(hash_env, NL("REQUEST_METHOD"), &r->method, + nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, r->method_length); - nxt_ruby_add_sptr(hash_env, NL("REQUEST_URI"), &r->target, + nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target, r->target_length); - nxt_ruby_add_sptr(hash_env, NL("PATH_INFO"), &r->path, r->path_length); - nxt_ruby_add_sptr(hash_env, NL("QUERY_STRING"), &r->query, + nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length); + nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query, r->query_length); - nxt_ruby_add_sptr(hash_env, NL("SERVER_PROTOCOL"), &r->version, + nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version, r->version_length); - nxt_ruby_add_sptr(hash_env, NL("REMOTE_ADDR"), &r->remote, + nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, r->remote_length); - nxt_ruby_add_sptr(hash_env, NL("SERVER_ADDR"), &r->local, r->local_length); - - nxt_ruby_add_sptr(hash_env, NL("SERVER_NAME"), &r->server_name, + nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, + r->local_length); + nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, r->server_name_length); - nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), "80", 2); - rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), - r->tls ? rb_str_new2("https") : rb_str_new2("http")); + rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str); + + rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str, + r->tls ? nxt_rb_https_str : nxt_rb_http_str); for (i = 0; i < r->fields_count; i++) { f = r->fields + i; - nxt_ruby_add_sptr(hash_env, nxt_unit_sptr_get(&f->name), f->name_length, - &f->value, f->value_length); + name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length); + + nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length); } if (r->content_length_field != NXT_UNIT_NONE_FIELD) { f = r->fields + r->content_length_field; - nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"), + nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str, &f->value, f->value_length); } if (r->content_type_field != NXT_UNIT_NONE_FIELD) { f = r->fields + r->content_type_field; - nxt_ruby_add_sptr(hash_env, NL("CONTENT_TYPE"), + nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str, &f->value, f->value_length); } -#undef NL - return NXT_UNIT_OK; } nxt_inline void -nxt_ruby_add_sptr(VALUE hash_env, - const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len) +nxt_ruby_add_sptr(VALUE hash_env, VALUE name, + nxt_unit_sptr_t *sptr, uint32_t len) { char *str; str = nxt_unit_sptr_get(sptr); - rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); -} - - -nxt_inline void -nxt_ruby_add_str(VALUE hash_env, - const char *name, uint32_t name_len, const char *str, uint32_t len) -{ - rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); + rb_hash_aset(hash_env, name, rb_str_new(str, len)); } @@ -901,5 +971,7 @@ nxt_ruby_atexit(void) rb_gc_unregister_address(&nxt_ruby_call); rb_gc_unregister_address(&nxt_ruby_env); + nxt_ruby_done_strings(); + ruby_cleanup(0); } -- cgit From b6475df79cd14d80c794abfc9d1edbcebbe86f2c Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 12:45:10 +0300 Subject: Ruby: request processing in multiple threads. This closes #482 issue on GitHub. --- src/nxt_application.h | 1 + src/nxt_conf_validation.c | 4 + src/nxt_main_process.c | 5 + src/ruby/nxt_ruby.c | 647 ++++++++++++++++++++++++++++++------------ src/ruby/nxt_ruby.h | 8 +- src/ruby/nxt_ruby_stream_io.c | 51 ++-- 6 files changed, 510 insertions(+), 206 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 08de0fce..b8e18a23 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -69,6 +69,7 @@ typedef struct { typedef struct { nxt_str_t script; + uint32_t threads; } nxt_ruby_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 1ce2d9ea..8dd9082d 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -615,6 +615,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { .name = nxt_string("script"), .type = NXT_CONF_VLDT_STRING, .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("threads"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_threads, }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index ce8d6916..631b3146 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -242,6 +242,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = { NXT_CONF_MAP_STR, offsetof(nxt_common_app_conf_t, u.ruby.script), }, + { + nxt_string("threads"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.ruby.threads), + }, }; diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 7b383557..ffc1469a 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -8,6 +8,8 @@ #include #include +#include + #include NXT_RUBY_MOUNTS_H @@ -16,16 +18,15 @@ typedef struct { - nxt_task_t *task; - nxt_str_t *script; - VALUE builder; + nxt_task_t *task; + nxt_str_t *script; + nxt_ruby_ctx_t *rctx; } nxt_ruby_rack_init_t; static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data); static VALUE nxt_ruby_init_basic(VALUE arg); -static nxt_int_t nxt_ruby_init_io(nxt_task_t *task); static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); static VALUE nxt_ruby_require_rubygems(VALUE arg); @@ -33,24 +34,40 @@ static VALUE nxt_ruby_bundler_setup(VALUE arg); static VALUE nxt_ruby_require_rack(VALUE arg); static VALUE nxt_ruby_rack_parse_script(VALUE ctx); static VALUE nxt_ruby_rack_env_create(VALUE arg); +static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx); static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); +static void *nxt_ruby_request_handler_gvl(void *req); +static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx); +static VALUE nxt_ruby_thread_func(VALUE arg); +static void *nxt_ruby_unit_run(void *ctx); +static void nxt_ruby_ubf(void *ctx); +static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c); +static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, + nxt_ruby_app_conf_t *c); static VALUE nxt_ruby_rack_app_run(VALUE arg); -static int nxt_ruby_read_request(VALUE hash_env); +static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env); nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, nxt_unit_sptr_t *sptr, uint32_t len); -static nxt_int_t nxt_ruby_rack_result_status(VALUE result); -static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status); +static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, + VALUE result); +static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, + VALUE result, nxt_int_t status); static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); -static int nxt_ruby_rack_result_body(VALUE result); -static int nxt_ruby_rack_result_body_file_write(VALUE filepath); +static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, + VALUE result); +static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, + VALUE filepath); +static void *nxt_ruby_response_write_cb(void *read_info); static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, const VALUE *argv, VALUE blockarg); +static void *nxt_ruby_response_write(void *body); -static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, - const char *desc); +static void nxt_ruby_exception_log(nxt_unit_request_info_t *req, + uint32_t level, const char *desc); +static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx); static void nxt_ruby_atexit(void); @@ -58,12 +75,11 @@ static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; -static VALUE nxt_ruby_rackup; -static VALUE nxt_ruby_call; -static VALUE nxt_ruby_env; -static VALUE nxt_ruby_io_input; -static VALUE nxt_ruby_io_error; -static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; +static VALUE nxt_ruby_rackup; +static VALUE nxt_ruby_call; + +static uint32_t nxt_ruby_threads; +static nxt_ruby_ctx_t *nxt_ruby_ctxs; NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(compat), @@ -169,79 +185,114 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) { int state, rc; VALUE res; + nxt_ruby_ctx_t ruby_ctx; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t ruby_unit_init; + nxt_ruby_app_conf_t *c; nxt_ruby_rack_init_t rack_init; nxt_common_app_conf_t *conf; static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; conf = data->app; + c = &conf->u.ruby; + + nxt_ruby_threads = c->threads; RUBY_INIT_STACK ruby_init(); ruby_options(2, argv); ruby_script("NGINX_Unit"); + ruby_ctx.env = Qnil; + ruby_ctx.io_input = Qnil; + ruby_ctx.io_error = Qnil; + ruby_ctx.thread = Qnil; + ruby_ctx.ctx = NULL; + ruby_ctx.req = NULL; + rack_init.task = task; - rack_init.script = &conf->u.ruby.script; + rack_init.script = &c->script; + rack_init.rctx = &ruby_ctx; nxt_ruby_init_strings(); res = rb_protect(nxt_ruby_init_basic, (VALUE) (uintptr_t) &rack_init, &state); if (nxt_slow_path(res == Qnil || state != 0)) { - nxt_ruby_exception_log(task, NXT_LOG_ALERT, + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to init basic variables"); return NXT_ERROR; } + nxt_ruby_call = Qnil; + nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { return NXT_ERROR; } + rb_gc_register_address(&nxt_ruby_rackup); + nxt_ruby_call = rb_intern("call"); if (nxt_slow_path(nxt_ruby_call == Qnil)) { nxt_alert(task, "Ruby: Unable to find rack entry point"); - return NXT_ERROR; + goto fail; } - nxt_ruby_env = rb_protect(nxt_ruby_rack_env_create, Qnil, &state); - if (nxt_slow_path(state != 0)) { - nxt_ruby_exception_log(task, NXT_LOG_ALERT, + rb_gc_register_address(&nxt_ruby_call); + + ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create, + (VALUE) (uintptr_t) &ruby_ctx, &state); + if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to create 'environ' variable"); - return NXT_ERROR; + goto fail; } - rb_gc_register_address(&nxt_ruby_rackup); - rb_gc_register_address(&nxt_ruby_call); - rb_gc_register_address(&nxt_ruby_env); + rc = nxt_ruby_init_threads(c); + if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { + goto fail; + } nxt_unit_default_init(task, &ruby_unit_init); ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; + ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler; ruby_unit_init.shm_limit = conf->shm_limit; + ruby_unit_init.data = c; + ruby_unit_init.ctx_data = &ruby_ctx; unit_ctx = nxt_unit_init(&ruby_unit_init); if (nxt_slow_path(unit_ctx == NULL)) { - return NXT_ERROR; + goto fail; } - nxt_ruby_run_ctx.unit_ctx = unit_ctx; + rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, + nxt_ruby_ubf, unit_ctx); - rc = nxt_unit_run(unit_ctx); + nxt_ruby_join_threads(unit_ctx, c); - nxt_ruby_atexit(); + nxt_unit_done(unit_ctx); - nxt_ruby_run_ctx.unit_ctx = NULL; + nxt_ruby_ctx_done(&ruby_ctx); - nxt_unit_done(unit_ctx); + nxt_ruby_atexit(); exit(rc); return NXT_OK; + +fail: + + nxt_ruby_join_threads(NULL, c); + + nxt_ruby_ctx_done(&ruby_ctx); + + nxt_ruby_atexit(); + + return NXT_ERROR; } @@ -249,7 +300,6 @@ static VALUE nxt_ruby_init_basic(VALUE arg) { int state; - nxt_int_t rc; nxt_ruby_rack_init_t *rack_init; rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; @@ -265,56 +315,19 @@ nxt_ruby_init_basic(VALUE arg) rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("enc/trans/transdb")); - rc = nxt_ruby_init_io(rack_init->task); - if (nxt_slow_path(rc != NXT_OK)) { - return Qnil; - } - return arg; } -static nxt_int_t -nxt_ruby_init_io(nxt_task_t *task) -{ - VALUE rb, io_input, io_error; - - io_input = nxt_ruby_stream_io_input_init(); - rb = Data_Wrap_Struct(io_input, 0, 0, &nxt_ruby_run_ctx); - - nxt_ruby_io_input = rb_funcall(io_input, rb_intern("new"), 1, rb); - if (nxt_slow_path(nxt_ruby_io_input == Qnil)) { - nxt_alert(task, "Ruby: Failed to create environment 'rack.input' var"); - - return NXT_ERROR; - } - - io_error = nxt_ruby_stream_io_error_init(); - rb = Data_Wrap_Struct(io_error, 0, 0, &nxt_ruby_run_ctx); - - nxt_ruby_io_error = rb_funcall(io_error, rb_intern("new"), 1, rb); - if (nxt_slow_path(nxt_ruby_io_error == Qnil)) { - nxt_alert(task, "Ruby: Failed to create environment 'rack.error' var"); - - return NXT_ERROR; - } - - rb_gc_register_address(&nxt_ruby_io_input); - rb_gc_register_address(&nxt_ruby_io_error); - - return NXT_OK; -} - - static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) { int state; - VALUE rack, rackup, err; + VALUE rackup, err; rb_protect(nxt_ruby_require_rubygems, Qnil, &state); if (nxt_slow_path(state != 0)) { - nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to require 'rubygems' package"); return Qnil; } @@ -324,7 +337,7 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) err = rb_errinfo(); if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { - nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to require 'bundler/setup' package"); return Qnil; } @@ -334,18 +347,15 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) rb_protect(nxt_ruby_require_rack, Qnil, &state); if (nxt_slow_path(state != 0)) { - nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to require 'rack' package"); return Qnil; } - rack = rb_const_get(rb_cObject, rb_intern("Rack")); - rack_init->builder = rb_const_get(rack, rb_intern("Builder")); - rackup = rb_protect(nxt_ruby_rack_parse_script, (VALUE) (uintptr_t) rack_init, &state); if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { - nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to parse rack script"); return Qnil; } @@ -385,15 +395,18 @@ nxt_ruby_require_rack(VALUE arg) static VALUE nxt_ruby_rack_parse_script(VALUE ctx) { - VALUE script, res; + VALUE script, res, rack, builder; nxt_ruby_rack_init_t *rack_init; rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; + rack = rb_const_get(rb_cObject, rb_intern("Rack")); + builder = rb_const_get(rack, rb_intern("Builder")); + script = rb_str_new((const char *) rack_init->script->start, (long) rack_init->script->length); - res = rb_funcall(rack_init->builder, rb_intern("parse_file"), 1, script); + res = rb_funcall(builder, rb_intern("parse_file"), 1, script); rb_str_free(script); @@ -404,7 +417,16 @@ nxt_ruby_rack_parse_script(VALUE ctx) static VALUE nxt_ruby_rack_env_create(VALUE arg) { - VALUE hash_env, version; + int rc; + VALUE hash_env, version; + nxt_ruby_ctx_t *rctx; + + rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; + + rc = nxt_ruby_init_io(rctx); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return Qnil; + } hash_env = rb_hash_new(); @@ -418,47 +440,114 @@ nxt_ruby_rack_env_create(VALUE arg) rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); - rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input); - rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error); - rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse); + rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input); + rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error); + rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), + nxt_ruby_threads > 1 ? Qtrue : Qfalse); rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); + rctx->env = hash_env; + + rb_gc_register_address(&rctx->env); + return hash_env; } +static int +nxt_ruby_init_io(nxt_ruby_ctx_t *rctx) +{ + VALUE io_input, io_error; + + io_input = nxt_ruby_stream_io_input_init(); + + rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1, + (VALUE) (uintptr_t) rctx); + if (nxt_slow_path(rctx->io_input == Qnil)) { + nxt_unit_alert(NULL, + "Ruby: Failed to create environment 'rack.input' var"); + + return NXT_UNIT_ERROR; + } + + rb_gc_register_address(&rctx->io_input); + + io_error = nxt_ruby_stream_io_error_init(); + + rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1, + (VALUE) (uintptr_t) rctx); + if (nxt_slow_path(rctx->io_error == Qnil)) { + nxt_unit_alert(NULL, + "Ruby: Failed to create environment 'rack.error' var"); + + return NXT_UNIT_ERROR; + } + + rb_gc_register_address(&rctx->io_error); + + return NXT_UNIT_OK; +} + + static void nxt_ruby_request_handler(nxt_unit_request_info_t *req) { - int state; - VALUE res; + (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req); +} + + +static void * +nxt_ruby_request_handler_gvl(void *data) +{ + int state; + VALUE res; + nxt_ruby_ctx_t *rctx; + nxt_unit_request_info_t *req; + + req = data; - nxt_ruby_run_ctx.req = req; + rctx = req->ctx->data; + rctx->req = req; - res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); + res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state); if (nxt_slow_path(res == Qnil || state != 0)) { - nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + nxt_ruby_exception_log(req, NXT_LOG_ERR, "Failed to run ruby script"); + + nxt_unit_request_done(req, NXT_UNIT_ERROR); + + } else { + nxt_unit_request_done(req, NXT_UNIT_OK); } + + rctx->req = NULL; + + return NULL; } static VALUE nxt_ruby_rack_app_run(VALUE arg) { - int rc; - VALUE env, result; - nxt_int_t status; + int rc; + VALUE env, result; + nxt_int_t status; + nxt_ruby_ctx_t *rctx; + nxt_unit_request_info_t *req; - env = rb_hash_dup(nxt_ruby_env); + req = (nxt_unit_request_info_t *) arg; - rc = nxt_ruby_read_request(env); + rctx = req->ctx->data; + + env = rb_hash_dup(rctx->env); + + rc = nxt_ruby_read_request(req, env); if (nxt_slow_path(rc != NXT_UNIT_OK)) { - nxt_unit_req_alert(nxt_ruby_run_ctx.req, + nxt_unit_req_alert(req, "Ruby: Failed to process incoming request"); goto fail; @@ -466,50 +555,44 @@ nxt_ruby_rack_app_run(VALUE arg) result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); if (nxt_slow_path(TYPE(result) != T_ARRAY)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Invalid response format from application"); goto fail; } if (nxt_slow_path(RARRAY_LEN(result) != 3)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Invalid response format from application. " "Need 3 entries [Status, Headers, Body]"); goto fail; } - status = nxt_ruby_rack_result_status(result); + status = nxt_ruby_rack_result_status(req, result); if (nxt_slow_path(status < 0)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Invalid response status from application."); goto fail; } - rc = nxt_ruby_rack_result_headers(result, status); + rc = nxt_ruby_rack_result_headers(req, result, status); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } - rc = nxt_ruby_rack_result_body(result); + rc = nxt_ruby_rack_result_body(req, result); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } - nxt_unit_request_done(nxt_ruby_run_ctx.req, rc); - nxt_ruby_run_ctx.req = NULL; - rb_hash_delete(env, rb_obj_id(env)); return result; fail: - nxt_unit_request_done(nxt_ruby_run_ctx.req, NXT_UNIT_ERROR); - nxt_ruby_run_ctx.req = NULL; - rb_hash_delete(env, rb_obj_id(env)); return Qnil; @@ -517,14 +600,14 @@ fail: static int -nxt_ruby_read_request(VALUE hash_env) +nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) { VALUE name; uint32_t i; nxt_unit_field_t *f; nxt_unit_request_t *r; - r = nxt_ruby_run_ctx.req->request; + r = req->request; nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, r->method_length); @@ -586,7 +669,7 @@ nxt_ruby_add_sptr(VALUE hash_env, VALUE name, static nxt_int_t -nxt_ruby_rack_result_status(VALUE result) +nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result) { VALUE status; @@ -601,7 +684,7 @@ nxt_ruby_rack_result_status(VALUE result) RSTRING_LEN(status)); } - nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' " + nxt_unit_req_error(req, "Ruby: Invalid response 'status' " "format from application"); return -2; @@ -609,14 +692,16 @@ nxt_ruby_rack_result_status(VALUE result) typedef struct { - int rc; - uint32_t fields; - uint32_t size; + int rc; + uint32_t fields; + uint32_t size; + nxt_unit_request_info_t *req; } nxt_ruby_headers_info_t; static int -nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) +nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result, + nxt_int_t status) { int rc; VALUE headers; @@ -624,7 +709,7 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) headers = rb_ary_entry(result, 1); if (nxt_slow_path(TYPE(headers) != T_HASH)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Invalid response 'headers' format from " "application"); @@ -636,6 +721,7 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) headers_info.rc = NXT_UNIT_OK; headers_info.fields = 0; headers_info.size = 0; + headers_info.req = req; rb_hash_foreach(headers, nxt_ruby_hash_info, (VALUE) (uintptr_t) &headers_info); @@ -643,13 +729,14 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) return headers_info.rc; } - rc = nxt_unit_response_init(nxt_ruby_run_ctx.req, status, + rc = nxt_unit_response_init(req, status, headers_info.fields, headers_info.size); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } - rb_hash_foreach(headers, nxt_ruby_hash_add, (VALUE) (uintptr_t) &rc); + rb_hash_foreach(headers, nxt_ruby_hash_add, + (VALUE) (uintptr_t) &headers_info); return rc; } @@ -664,14 +751,14 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) headers_info = (void *) (uintptr_t) arg; if (nxt_slow_path(TYPE(r_key) != T_STRING)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(headers_info->req, "Ruby: Wrong header entry 'key' from application"); goto fail; } if (nxt_slow_path(TYPE(r_value) != T_STRING)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(headers_info->req, "Ruby: Wrong header entry 'value' from application"); goto fail; @@ -714,11 +801,13 @@ fail: static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) { - int *rc; - uint32_t key_len; - const char *value, *value_end, *pos; + int *rc; + uint32_t key_len; + const char *value, *value_end, *pos; + nxt_ruby_headers_info_t *headers_info; - rc = (int *) (uintptr_t) arg; + headers_info = (void *) (uintptr_t) arg; + rc = &headers_info->rc; value = RSTRING_PTR(r_value); value_end = value + RSTRING_LEN(r_value); @@ -734,7 +823,7 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) break; } - *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, + *rc = nxt_unit_response_add_field(headers_info->req, RSTRING_PTR(r_key), key_len, value, pos - value); if (nxt_slow_path(*rc != NXT_UNIT_OK)) { @@ -746,7 +835,7 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) } if (value <= value_end) { - *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, + *rc = nxt_unit_response_add_field(headers_info->req, RSTRING_PTR(r_key), key_len, value, value_end - value); if (nxt_slow_path(*rc != NXT_UNIT_OK)) { @@ -765,7 +854,7 @@ fail: static int -nxt_ruby_rack_result_body(VALUE result) +nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result) { int rc; VALUE fn, body; @@ -776,24 +865,24 @@ nxt_ruby_rack_result_body(VALUE result) fn = rb_funcall(body, rb_intern("to_path"), 0); if (nxt_slow_path(TYPE(fn) != T_STRING)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Failed to get 'body' file path from " "application"); return NXT_UNIT_ERROR; } - rc = nxt_ruby_rack_result_body_file_write(fn); + rc = nxt_ruby_rack_result_body_file_write(req, fn); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } } else if (rb_respond_to(body, rb_intern("each"))) { rb_block_call(body, rb_intern("each"), 0, 0, - nxt_ruby_rack_result_body_each, 0); + nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req); } else { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Invalid response 'body' format " "from application"); @@ -842,17 +931,24 @@ nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) } +typedef struct { + nxt_unit_read_info_t read_info; + nxt_unit_request_info_t *req; +} nxt_ruby_read_info_t; + + static int -nxt_ruby_rack_result_body_file_write(VALUE filepath) +nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, + VALUE filepath) { int fd, rc; struct stat finfo; nxt_ruby_rack_file_t ruby_file; - nxt_unit_read_info_t read_info; + nxt_ruby_read_info_t ri; fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); if (nxt_slow_path(fd == -1)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Failed to open content file \"%s\": %s (%d)", RSTRING_PTR(filepath), strerror(errno), errno); @@ -861,7 +957,7 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath) rc = fstat(fd, &finfo); if (nxt_slow_path(rc == -1)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(req, "Ruby: Content file fstat(\"%s\") failed: %s (%d)", RSTRING_PTR(filepath), strerror(errno), errno); @@ -874,16 +970,16 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath) ruby_file.pos = 0; ruby_file.rest = finfo.st_size; - read_info.read = nxt_ruby_rack_file_read; - read_info.eof = ruby_file.rest == 0; - read_info.buf_size = ruby_file.rest; - read_info.data = &ruby_file; + ri.read_info.read = nxt_ruby_rack_file_read; + ri.read_info.eof = ruby_file.rest == 0; + ri.read_info.buf_size = ruby_file.rest; + ri.read_info.data = &ruby_file; + ri.req = req; - rc = nxt_unit_response_write_cb(nxt_ruby_run_ctx.req, &read_info); - if (nxt_slow_path(rc != NXT_UNIT_OK)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, - "Ruby: Failed to write content file."); - } + rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb, + &ri, + nxt_ruby_ubf, + req->ctx); close(fd); @@ -891,39 +987,77 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath) } +static void * +nxt_ruby_response_write_cb(void *data) +{ + int rc; + nxt_ruby_read_info_t *ri; + + ri = data; + + rc = nxt_unit_response_write_cb(ri->req, &ri->read_info); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(ri->req, "Ruby: Failed to write content file."); + } + + return (void *) (intptr_t) rc; +} + + +typedef struct { + VALUE body; + nxt_unit_request_info_t *req; +} nxt_ruby_write_info_t; + + static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, const VALUE *argv, VALUE blockarg) { - int rc; + nxt_ruby_write_info_t wi; if (TYPE(body) != T_STRING) { return Qnil; } - rc = nxt_unit_response_write(nxt_ruby_run_ctx.req, RSTRING_PTR(body), - RSTRING_LEN(body)); + wi.body = body; + wi.req = (void *) (uintptr_t) arg; + + (void) rb_thread_call_without_gvl(nxt_ruby_response_write, + (void *) (uintptr_t) &wi, + nxt_ruby_ubf, wi.req->ctx); + + return Qnil; +} + + +static void * +nxt_ruby_response_write(void *data) +{ + int rc; + nxt_ruby_write_info_t *wi; + + wi = data; + + rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body), + RSTRING_LEN(wi->body)); if (nxt_slow_path(rc != NXT_UNIT_OK)) { - nxt_unit_req_error(nxt_ruby_run_ctx.req, + nxt_unit_req_error(wi->req, "Ruby: Failed to write 'body' from application"); } - return Qnil; + return (void *) (intptr_t) rc; } static void -nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) +nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, + const char *desc) { int i; VALUE err, ary, eclass, msg; - if (task != NULL) { - nxt_log(task, level, "Ruby: %s", desc); - - } else { - nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s", desc); - } + nxt_unit_req_log(req, level, "Ruby: %s", desc); err = rb_errinfo(); if (nxt_slow_path(err == Qnil)) { @@ -938,25 +1072,30 @@ nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) eclass = rb_class_name(rb_class_of(err)); msg = rb_funcall(err, rb_intern("message"), 0); - if (task != NULL) { - nxt_log(task, level, "Ruby: %s: %s (%s)", - RSTRING_PTR(RARRAY_PTR(ary)[0]), - RSTRING_PTR(msg), RSTRING_PTR(eclass)); - - } else { - nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s: %s (%s)", + nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", RSTRING_PTR(RARRAY_PTR(ary)[0]), RSTRING_PTR(msg), RSTRING_PTR(eclass)); - } for (i = 1; i < RARRAY_LEN(ary); i++) { - if (task != NULL) { - nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); - - } else { - nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "from %s", + nxt_unit_req_log(req, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); - } + } +} + + +static void +nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx) +{ + if (rctx->io_input != Qnil) { + rb_gc_unregister_address(&rctx->io_input); + } + + if (rctx->io_error != Qnil) { + rb_gc_unregister_address(&rctx->io_error); + } + + if (rctx->env != Qnil) { + rb_gc_unregister_address(&rctx->env); } } @@ -964,14 +1103,170 @@ nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) static void nxt_ruby_atexit(void) { - rb_gc_unregister_address(&nxt_ruby_io_input); - rb_gc_unregister_address(&nxt_ruby_io_error); + if (nxt_ruby_rackup != Qnil) { + rb_gc_unregister_address(&nxt_ruby_rackup); + } - rb_gc_unregister_address(&nxt_ruby_rackup); - rb_gc_unregister_address(&nxt_ruby_call); - rb_gc_unregister_address(&nxt_ruby_env); + if (nxt_ruby_call != Qnil) { + rb_gc_unregister_address(&nxt_ruby_call); + } nxt_ruby_done_strings(); ruby_cleanup(0); } + + +static int +nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) +{ + VALUE res; + uint32_t i; + nxt_ruby_ctx_t *rctx; + nxt_ruby_app_conf_t *c; + + /* Worker thread context. */ + if (!nxt_unit_is_main_ctx(ctx)) { + return NXT_UNIT_OK; + } + + c = ctx->unit->data; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + for (i = 0; i < c->threads - 1; i++) { + rctx = &nxt_ruby_ctxs[i]; + + rctx->ctx = ctx; + + res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx); + + if (nxt_fast_path(res != Qnil)) { + nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); + + rctx->thread = res; + + } else { + nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1)); + } + } + + return NXT_UNIT_OK; +} + + +static VALUE +nxt_ruby_thread_func(VALUE arg) +{ + nxt_unit_ctx_t *ctx; + nxt_ruby_ctx_t *rctx; + + rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; + + nxt_unit_debug(rctx->ctx, "worker thread start"); + + ctx = nxt_unit_ctx_alloc(rctx->ctx, rctx); + if (nxt_slow_path(ctx == NULL)) { + goto fail; + } + + (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx, + nxt_ruby_ubf, ctx); + + nxt_unit_done(ctx); + +fail: + + nxt_unit_debug(NULL, "worker thread end"); + + return Qnil; +} + + +static void * +nxt_ruby_unit_run(void *ctx) +{ + return (void *) (intptr_t) nxt_unit_run(ctx); +} + + +static void +nxt_ruby_ubf(void *ctx) +{ + nxt_unit_warn(ctx, "Ruby: UBF"); +} + + +static int +nxt_ruby_init_threads(nxt_ruby_app_conf_t *c) +{ + int state; + uint32_t i; + nxt_ruby_ctx_t *rctx; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + nxt_ruby_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_ruby_ctx_t) + * (c->threads - 1)); + if (nxt_slow_path(nxt_ruby_ctxs == NULL)) { + nxt_unit_alert(NULL, "Failed to allocate run contexts array"); + + return NXT_UNIT_ERROR; + } + + for (i = 0; i < c->threads - 1; i++) { + rctx = &nxt_ruby_ctxs[i]; + + rctx->env = Qnil; + rctx->io_input = Qnil; + rctx->io_error = Qnil; + rctx->thread = Qnil; + } + + for (i = 0; i < c->threads - 1; i++) { + rctx = &nxt_ruby_ctxs[i]; + + rctx->env = rb_protect(nxt_ruby_rack_env_create, + (VALUE) (uintptr_t) rctx, &state); + if (nxt_slow_path(rctx->env == Qnil || state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, + "Failed to create 'environ' variable"); + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + +static void +nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c) +{ + uint32_t i; + nxt_ruby_ctx_t *rctx; + + if (nxt_ruby_ctxs == NULL) { + return; + } + + for(i = 0; i < c->threads - 1; i++) { + rctx = &nxt_ruby_ctxs[i]; + + if (rctx->thread != Qnil) { + rb_funcall(rctx->thread, rb_intern("join"), 0); + + nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); + + } else { + nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1)); + } + + nxt_ruby_ctx_done(rctx); + } + + nxt_unit_free(ctx, nxt_ruby_ctxs); +} diff --git a/src/ruby/nxt_ruby.h b/src/ruby/nxt_ruby.h index 77a65894..26430021 100644 --- a/src/ruby/nxt_ruby.h +++ b/src/ruby/nxt_ruby.h @@ -21,9 +21,13 @@ typedef struct { - nxt_unit_ctx_t *unit_ctx; + VALUE env; + VALUE io_input; + VALUE io_error; + VALUE thread; + nxt_unit_ctx_t *ctx; nxt_unit_request_info_t *req; -} nxt_ruby_run_ctx_t; +} nxt_ruby_ctx_t; VALUE nxt_ruby_stream_io_input_init(void); diff --git a/src/ruby/nxt_ruby_stream_io.c b/src/ruby/nxt_ruby_stream_io.c index cc110035..69bf289e 100644 --- a/src/ruby/nxt_ruby_stream_io.c +++ b/src/ruby/nxt_ruby_stream_io.c @@ -8,7 +8,7 @@ #include -static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE wrap); +static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE arg); static VALUE nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self); static VALUE nxt_ruby_stream_io_gets(VALUE obj); static VALUE nxt_ruby_stream_io_each(VALUE obj); @@ -16,8 +16,7 @@ static VALUE nxt_ruby_stream_io_read(VALUE obj, VALUE args); static VALUE nxt_ruby_stream_io_rewind(VALUE obj); static VALUE nxt_ruby_stream_io_puts(VALUE obj, VALUE args); static VALUE nxt_ruby_stream_io_write(VALUE obj, VALUE args); -nxt_inline long nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, - VALUE val); +nxt_inline long nxt_ruby_stream_io_s_write(nxt_ruby_ctx_t *rctx, VALUE val); static VALUE nxt_ruby_stream_io_flush(VALUE obj); @@ -63,13 +62,11 @@ nxt_ruby_stream_io_error_init(void) static VALUE -nxt_ruby_stream_io_new(VALUE class, VALUE wrap) +nxt_ruby_stream_io_new(VALUE class, VALUE arg) { - VALUE self; - nxt_ruby_run_ctx_t *run_ctx; + VALUE self; - Data_Get_Struct(wrap, nxt_ruby_run_ctx_t, run_ctx); - self = Data_Wrap_Struct(class, 0, 0, run_ctx); + self = Data_Wrap_Struct(class, 0, 0, (void *) (uintptr_t) arg); rb_obj_call_init(self, 0, NULL); @@ -89,12 +86,11 @@ nxt_ruby_stream_io_gets(VALUE obj) { VALUE buf; ssize_t res; - nxt_ruby_run_ctx_t *run_ctx; + nxt_ruby_ctx_t *rctx; nxt_unit_request_info_t *req; - Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); - - req = run_ctx->req; + Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx); + req = rctx->req; if (req->content_length == 0) { return Qnil; @@ -145,13 +141,13 @@ nxt_ruby_stream_io_each(VALUE obj) static VALUE nxt_ruby_stream_io_read(VALUE obj, VALUE args) { - VALUE buf; - long copy_size, u_size; - nxt_ruby_run_ctx_t *run_ctx; + VALUE buf; + long copy_size, u_size; + nxt_ruby_ctx_t *rctx; - Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); + Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx); - copy_size = run_ctx->req->content_length; + copy_size = rctx->req->content_length; if (RARRAY_LEN(args) > 0 && TYPE(RARRAY_PTR(args)[0]) == T_FIXNUM) { u_size = NUM2LONG(RARRAY_PTR(args)[0]); @@ -175,8 +171,7 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args) return Qnil; } - copy_size = nxt_unit_request_read(run_ctx->req, RSTRING_PTR(buf), - copy_size); + copy_size = nxt_unit_request_read(rctx->req, RSTRING_PTR(buf), copy_size); if (RARRAY_LEN(args) > 1 && TYPE(RARRAY_PTR(args)[1]) == T_STRING) { @@ -200,15 +195,15 @@ nxt_ruby_stream_io_rewind(VALUE obj) static VALUE nxt_ruby_stream_io_puts(VALUE obj, VALUE args) { - nxt_ruby_run_ctx_t *run_ctx; + nxt_ruby_ctx_t *rctx; if (RARRAY_LEN(args) != 1) { return Qnil; } - Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); + Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx); - nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]); + nxt_ruby_stream_io_s_write(rctx, RARRAY_PTR(args)[0]); return Qnil; } @@ -217,23 +212,23 @@ nxt_ruby_stream_io_puts(VALUE obj, VALUE args) static VALUE nxt_ruby_stream_io_write(VALUE obj, VALUE args) { - long len; - nxt_ruby_run_ctx_t *run_ctx; + long len; + nxt_ruby_ctx_t *rctx; if (RARRAY_LEN(args) != 1) { return Qnil; } - Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); + Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx); - len = nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]); + len = nxt_ruby_stream_io_s_write(rctx, RARRAY_PTR(args)[0]); return LONG2FIX(len); } nxt_inline long -nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val) +nxt_ruby_stream_io_s_write(nxt_ruby_ctx_t *rctx, VALUE val) { if (nxt_slow_path(val == Qnil)) { return 0; @@ -247,7 +242,7 @@ nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val) } } - nxt_unit_req_error(run_ctx->req, "Ruby: %s", RSTRING_PTR(val)); + nxt_unit_req_error(rctx->req, "Ruby: %s", RSTRING_PTR(val)); return RSTRING_LEN(val); } -- cgit From d321d454f9304b083d62280d0621f282a74047ee Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 16:10:59 +0300 Subject: Perl: request processing in multiple threads. This closes #486 issue on GitHub. --- src/nxt_application.h | 2 + src/nxt_conf_validation.c | 8 + src/nxt_main_process.c | 12 + src/perl/nxt_perl_psgi.c | 494 +++++++++++++++++++++++++++++------------ src/perl/nxt_perl_psgi_layer.h | 6 +- 5 files changed, 374 insertions(+), 148 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index b8e18a23..66c0e1f7 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -64,6 +64,8 @@ typedef struct { typedef struct { char *script; + uint32_t threads; + uint32_t thread_stack_size; } nxt_perl_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 8dd9082d..8e6279fa 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -604,6 +604,14 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { .name = nxt_string("script"), .type = NXT_CONF_VLDT_STRING, .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("threads"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_threads, + }, { + .name = nxt_string("thread_stack_size"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_thread_stack_size, }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 631b3146..99fc7995 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -233,6 +233,18 @@ static nxt_conf_map_t nxt_perl_app_conf[] = { NXT_CONF_MAP_CSTRZ, offsetof(nxt_common_app_conf_t, u.perl.script), }, + + { + nxt_string("threads"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.perl.threads), + }, + + { + nxt_string("thread_stack_size"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, u.perl.thread_stack_size), + }, }; diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 16079a38..5df1465d 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -18,14 +18,14 @@ typedef struct { PerlInterpreter *my_perl; - nxt_unit_request_info_t *req; -} nxt_perl_psgi_input_t; - - -typedef struct { - PerlInterpreter *my_perl; + nxt_perl_psgi_io_arg_t arg_input; + nxt_perl_psgi_io_arg_t arg_error; SV *app; -} nxt_perl_psgi_module_t; + CV *cb; + nxt_unit_request_info_t *req; + pthread_t thread; + nxt_unit_ctx_t *ctx; +} nxt_perl_psgi_ctx_t; static long nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl, @@ -62,11 +62,11 @@ static nxt_int_t nxt_perl_psgi_io_input_init(PerlInterpreter *my_perl, static nxt_int_t nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg); -static PerlInterpreter *nxt_perl_psgi_interpreter_init(nxt_task_t *task, - char *script, SV **app); +static int nxt_perl_psgi_ctx_init(const char *script, + nxt_perl_psgi_ctx_t *pctx); static SV *nxt_perl_psgi_env_create(PerlInterpreter *my_perl, - nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input); + nxt_unit_request_info_t *req); nxt_inline int nxt_perl_psgi_add_sptr(PerlInterpreter *my_perl, HV *hash_env, const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len); nxt_inline int nxt_perl_psgi_add_str(PerlInterpreter *my_perl, HV *hash_env, @@ -75,8 +75,7 @@ nxt_inline int nxt_perl_psgi_add_value(PerlInterpreter *my_perl, HV *hash_env, const char *name, uint32_t name_len, void *value); -static u_char *nxt_perl_psgi_module_create(nxt_task_t *task, - const char *script); +static char *nxt_perl_psgi_module_create(const char *script); static nxt_int_t nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result); @@ -96,18 +95,20 @@ static void nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, nxt_unit_request_info_t *req); static nxt_int_t nxt_perl_psgi_start(nxt_task_t *task, - nxt_process_data_t *conf); + nxt_process_data_t *data); static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req); -static void nxt_perl_psgi_atexit(void); - -typedef SV *(*nxt_perl_psgi_callback_f)(PerlInterpreter *my_perl, - SV *env, nxt_task_t *task); - -static CV *nxt_perl_psgi_cb; -static PerlInterpreter *nxt_perl_psgi; -static nxt_perl_psgi_io_arg_t nxt_perl_psgi_arg_input; -static nxt_perl_psgi_io_arg_t nxt_perl_psgi_arg_error; -static nxt_unit_request_info_t *nxt_perl_psgi_request; +static int nxt_perl_psgi_ready_handler(nxt_unit_ctx_t *ctx); +static void *nxt_perl_psgi_thread_func(void *main_ctx); +static int nxt_perl_psgi_init_threads(nxt_perl_app_conf_t *c); +static void nxt_perl_psgi_join_threads(nxt_unit_ctx_t *ctx, + nxt_perl_app_conf_t *c); +static void nxt_perl_psgi_ctx_free(nxt_perl_psgi_ctx_t *pctx); + +static CV *nxt_perl_psgi_write; +static CV *nxt_perl_psgi_close; +static CV *nxt_perl_psgi_cb; +static pthread_attr_t *nxt_perl_psgi_thread_attr; +static nxt_perl_psgi_ctx_t *nxt_perl_psgi_ctxs; static uint32_t nxt_perl_psgi_compat[] = { NXT_VERNUM, NXT_DEBUG, @@ -129,11 +130,11 @@ static long nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length) { - nxt_perl_psgi_input_t *input; + nxt_perl_psgi_ctx_t *pctx; - input = (nxt_perl_psgi_input_t *) arg->ctx; + pctx = arg->pctx; - return nxt_unit_request_read(input->req, vbuf, length); + return nxt_unit_request_read(pctx->req, vbuf, length); } @@ -165,10 +166,11 @@ static long nxt_perl_psgi_io_error_write(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg, const void *vbuf, size_t length) { - nxt_perl_psgi_input_t *input; + nxt_perl_psgi_ctx_t *pctx; - input = (nxt_perl_psgi_input_t *) arg->ctx; - nxt_unit_req_error(input->req, "Perl: %s", (const char*) vbuf); + pctx = arg->pctx; + + nxt_unit_req_error(pctx->req, "Perl: %s", (const char*) vbuf); return (long) length; } @@ -216,9 +218,10 @@ XS(XS_NGINX__Unit__PSGI_exit) XS(XS_NGINX__Unit__Sandbox_write); XS(XS_NGINX__Unit__Sandbox_write) { - int rc; - char *body; - size_t len; + int rc; + char *body; + size_t len; + nxt_perl_psgi_ctx_t *pctx; dXSARGS; @@ -230,7 +233,9 @@ XS(XS_NGINX__Unit__Sandbox_write) body = SvPV(ST(1), len); - rc = nxt_unit_response_write(nxt_perl_psgi_request, body, len); + pctx = CvXSUBANY(cv).any_ptr; + + rc = nxt_unit_response_write(pctx->req, body, len); if (nxt_slow_path(rc != NXT_UNIT_OK)) { Perl_croak(aTHX_ "Failed to write response body"); @@ -242,15 +247,11 @@ XS(XS_NGINX__Unit__Sandbox_write) nxt_inline void -nxt_perl_psgi_cb_request_done(nxt_int_t status) +nxt_perl_psgi_cb_request_done(nxt_perl_psgi_ctx_t *pctx, int status) { - nxt_unit_request_info_t *req; - - req = nxt_perl_psgi_request; - - if (req != NULL) { - nxt_unit_request_done(req, status); - nxt_perl_psgi_request = NULL; + if (pctx->req != NULL) { + nxt_unit_request_done(pctx->req, status); + pctx->req = NULL; } } @@ -262,7 +263,7 @@ XS(XS_NGINX__Unit__Sandbox_close) ax = POPMARK; - nxt_perl_psgi_cb_request_done(NXT_UNIT_OK); + nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_OK); XSRETURN_NO; } @@ -271,14 +272,15 @@ XS(XS_NGINX__Unit__Sandbox_close) XS(XS_NGINX__Unit__Sandbox_cb); XS(XS_NGINX__Unit__Sandbox_cb) { - SV *obj; - int rc; - long array_len; + SV *obj; + int rc; + long array_len; + nxt_perl_psgi_ctx_t *pctx; dXSARGS; if (nxt_slow_path(items != 1)) { - nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR); + nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR); Perl_croak(aTHX_ "Wrong number of arguments"); @@ -288,7 +290,7 @@ XS(XS_NGINX__Unit__Sandbox_cb) if (nxt_slow_path(SvOK(ST(0)) == 0 || SvROK(ST(0)) == 0 || SvTYPE(SvRV(ST(0))) != SVt_PVAV)) { - nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR); + nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR); Perl_croak(aTHX_ "PSGI: An unexpected response was received " "from Perl Application"); @@ -296,10 +298,11 @@ XS(XS_NGINX__Unit__Sandbox_cb) XSRETURN_EMPTY; } - rc = nxt_perl_psgi_result_array(PERL_GET_CONTEXT, ST(0), - nxt_perl_psgi_request); + pctx = CvXSUBANY(cv).any_ptr; + + rc = nxt_perl_psgi_result_array(PERL_GET_CONTEXT, ST(0), pctx->req); if (nxt_slow_path(rc != NXT_UNIT_OK)) { - nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR); + nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR); Perl_croak(aTHX_ (char *) NULL); @@ -316,7 +319,7 @@ XS(XS_NGINX__Unit__Sandbox_cb) XSRETURN(1); } - nxt_perl_psgi_cb_request_done(NXT_UNIT_OK); + nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_OK); XSRETURN_EMPTY; } @@ -335,10 +338,11 @@ nxt_perl_psgi_xs_init(pTHX) /* DynaLoader for Perl modules who use XS */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__); - newXS("NGINX::Unit::Sandbox::write", XS_NGINX__Unit__Sandbox_write, - __FILE__); - newXS("NGINX::Unit::Sandbox::close", XS_NGINX__Unit__Sandbox_close, - __FILE__); + nxt_perl_psgi_write = newXS("NGINX::Unit::Sandbox::write", + XS_NGINX__Unit__Sandbox_write, __FILE__); + + nxt_perl_psgi_close = newXS("NGINX::Unit::Sandbox::close", + XS_NGINX__Unit__Sandbox_close, __FILE__); nxt_perl_psgi_cb = newXS("NGINX::Unit::Sandbox::cb", XS_NGINX__Unit__Sandbox_cb, __FILE__); @@ -416,10 +420,10 @@ nxt_perl_psgi_call_method(PerlInterpreter *my_perl, SV *obj, const char *method, } -static u_char * -nxt_perl_psgi_module_create(nxt_task_t *task, const char *script) +static char * +nxt_perl_psgi_module_create(const char *script) { - u_char *buf, *p; + char *buf, *p; size_t length; static nxt_str_t prefix = nxt_string( @@ -441,12 +445,11 @@ nxt_perl_psgi_module_create(nxt_task_t *task, const char *script) length = strlen(script); - buf = nxt_malloc(prefix.length + length + suffix.length); - + buf = nxt_unit_malloc(NULL, prefix.length + length + suffix.length); if (nxt_slow_path(buf == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to allocate memory " - "for Perl script file %s", script); + nxt_unit_alert(NULL, "PSGI: Failed to allocate memory " + "for Perl script file %s", script); + return NULL; } @@ -518,30 +521,27 @@ nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl, } -static PerlInterpreter * -nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script, SV **app) +static int +nxt_perl_psgi_ctx_init(const char *script, nxt_perl_psgi_ctx_t *pctx) { - int status, pargc; - char **pargv, **penv; - u_char *run_module; + int status; + char *run_module; PerlInterpreter *my_perl; static char argv[] = "\0""-e\0""0"; static char *embedding[] = { &argv[0], &argv[1], &argv[4] }; - pargc = 0; - pargv = NULL; - penv = NULL; - - PERL_SYS_INIT3(&pargc, &pargv, &penv); - my_perl = perl_alloc(); if (nxt_slow_path(my_perl == NULL)) { - nxt_alert(task, "PSGI: Failed to allocate memory for Perl interpreter"); - return NULL; + nxt_unit_alert(NULL, + "PSGI: Failed to allocate memory for Perl interpreter"); + + return NXT_UNIT_ERROR; } + pctx->my_perl = my_perl; + run_module = NULL; perl_construct(my_perl); @@ -550,77 +550,86 @@ nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script, SV **app) status = perl_parse(my_perl, nxt_perl_psgi_xs_init, 3, embedding, NULL); if (nxt_slow_path(status != 0)) { - nxt_alert(task, "PSGI: Failed to parse Perl Script"); + nxt_unit_alert(NULL, "PSGI: Failed to parse Perl Script"); goto fail; } + CvXSUBANY(nxt_perl_psgi_write).any_ptr = pctx; + CvXSUBANY(nxt_perl_psgi_close).any_ptr = pctx; + CvXSUBANY(nxt_perl_psgi_cb).any_ptr = pctx; + + pctx->cb = nxt_perl_psgi_cb; + PL_exit_flags |= PERL_EXIT_DESTRUCT_END; PL_origalen = 1; status = perl_run(my_perl); if (nxt_slow_path(status != 0)) { - nxt_alert(task, "PSGI: Failed to run Perl"); + nxt_unit_alert(NULL, "PSGI: Failed to run Perl"); goto fail; } sv_setsv(get_sv("0", 0), newSVpv(script, 0)); - run_module = nxt_perl_psgi_module_create(task, script); - + run_module = nxt_perl_psgi_module_create(script); if (nxt_slow_path(run_module == NULL)) { goto fail; } - status = nxt_perl_psgi_io_input_init(my_perl, &nxt_perl_psgi_arg_input); + pctx->arg_input.pctx = pctx; + status = nxt_perl_psgi_io_input_init(my_perl, &pctx->arg_input); if (nxt_slow_path(status != NXT_OK)) { - nxt_alert(task, "PSGI: Failed to init io.psgi.input"); + nxt_unit_alert(NULL, "PSGI: Failed to init io.psgi.input"); goto fail; } - status = nxt_perl_psgi_io_error_init(my_perl, &nxt_perl_psgi_arg_error); + pctx->arg_error.pctx = pctx; + status = nxt_perl_psgi_io_error_init(my_perl, &pctx->arg_error); if (nxt_slow_path(status != NXT_OK)) { - nxt_alert(task, "PSGI: Failed to init io.psgi.errors"); + nxt_unit_alert(NULL, "PSGI: Failed to init io.psgi.errors"); goto fail; } - *app = eval_pv((const char *) run_module, FALSE); + pctx->app = eval_pv(run_module, FALSE); if (SvTRUE(ERRSV)) { - nxt_alert(task, "PSGI: Failed to parse script: %s\n%s", - script, SvPV_nolen(ERRSV)); + nxt_unit_alert(NULL, "PSGI: Failed to parse script: %s\n%s", + script, SvPV_nolen(ERRSV)); goto fail; } - nxt_free(run_module); + nxt_unit_free(NULL, run_module); - return my_perl; + return NXT_UNIT_OK; fail: if (run_module != NULL) { - nxt_free(run_module); + nxt_unit_free(NULL, run_module); } perl_destruct(my_perl); perl_free(my_perl); - PERL_SYS_TERM(); - return NULL; + return NXT_UNIT_ERROR; } static SV * nxt_perl_psgi_env_create(PerlInterpreter *my_perl, - nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input) + nxt_unit_request_info_t *req) { - HV *hash_env; - AV *array_version; - uint32_t i; - nxt_unit_field_t *f; - nxt_unit_request_t *r; + HV *hash_env; + AV *array_version; + uint32_t i; + nxt_unit_field_t *f; + nxt_unit_request_t *r; + nxt_perl_psgi_ctx_t *pctx; + + pctx = req->ctx->data; hash_env = newHV(); if (nxt_slow_path(hash_env == NULL)) { @@ -664,11 +673,12 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl, : newSVpv("http", 4))); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.input"), - SvREFCNT_inc(nxt_perl_psgi_arg_input.io))); + SvREFCNT_inc(pctx->arg_input.io))); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.errors"), - SvREFCNT_inc(nxt_perl_psgi_arg_error.io))); + SvREFCNT_inc(pctx->arg_error.io))); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multithread"), - &PL_sv_no)); + nxt_perl_psgi_ctxs != NULL + ? &PL_sv_yes : &PL_sv_no)); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multiprocess"), &PL_sv_yes)); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.run_once"), @@ -1109,13 +1119,17 @@ static void nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, nxt_unit_request_info_t *req) { + nxt_perl_psgi_ctx_t *pctx; + dSP; + pctx = req->ctx->data; + ENTER; SAVETMPS; PUSHMARK(sp); - XPUSHs(newRV_noinc((SV*) nxt_perl_psgi_cb)); + XPUSHs(newRV_noinc((SV*) pctx->cb)); PUTBACK; call_sv(result, G_EVAL|G_SCALAR); @@ -1126,7 +1140,7 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, nxt_unit_error(NULL, "PSGI: Failed to execute result callback: \n%s", SvPV_nolen(ERRSV)); - nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR); + nxt_perl_psgi_cb_request_done(pctx, NXT_UNIT_ERROR); } PUTBACK; @@ -1138,90 +1152,116 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, static nxt_int_t nxt_perl_psgi_start(nxt_task_t *task, nxt_process_data_t *data) { - int rc; - nxt_unit_ctx_t *unit_ctx; - nxt_unit_init_t perl_init; - PerlInterpreter *my_perl; - nxt_common_app_conf_t *conf; - nxt_perl_psgi_module_t module; + int rc, pargc; + char **pargv, **penv; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t perl_init; + nxt_perl_psgi_ctx_t pctx; + nxt_perl_app_conf_t *c; + nxt_common_app_conf_t *common_conf; + + common_conf = data->app; + c = &common_conf->u.perl; + + pargc = 0; + pargv = NULL; + penv = NULL; - conf = data->app; + PERL_SYS_INIT3(&pargc, &pargv, &penv); - my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script, - &module.app); + memset(&pctx, 0, sizeof(nxt_perl_psgi_ctx_t)); - if (nxt_slow_path(my_perl == NULL)) { - return NXT_ERROR; + rc = nxt_perl_psgi_ctx_init(c->script, &pctx); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; } - module.my_perl = my_perl; - nxt_perl_psgi = my_perl; + rc = nxt_perl_psgi_init_threads(c); + + PERL_SET_CONTEXT(pctx.my_perl); + + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } nxt_unit_default_init(task, &perl_init); perl_init.callbacks.request_handler = nxt_perl_psgi_request_handler; - perl_init.data = &module; - perl_init.shm_limit = conf->shm_limit; + perl_init.callbacks.ready_handler = nxt_perl_psgi_ready_handler; + perl_init.data = c; + perl_init.ctx_data = &pctx; + perl_init.shm_limit = common_conf->shm_limit; unit_ctx = nxt_unit_init(&perl_init); if (nxt_slow_path(unit_ctx == NULL)) { - return NXT_ERROR; + goto fail; } rc = nxt_unit_run(unit_ctx); + nxt_perl_psgi_join_threads(unit_ctx, c); + nxt_unit_done(unit_ctx); - nxt_perl_psgi_atexit(); + nxt_perl_psgi_ctx_free(&pctx); + + PERL_SYS_TERM(); exit(rc); return NXT_OK; + +fail: + + nxt_perl_psgi_join_threads(NULL, c); + + nxt_perl_psgi_ctx_free(&pctx); + + PERL_SYS_TERM(); + + return NXT_ERROR; } static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req) { - SV *env, *result; - nxt_int_t rc; - PerlInterpreter *my_perl; - nxt_perl_psgi_input_t input; - nxt_perl_psgi_module_t *module; - - module = req->unit->data; - my_perl = module->my_perl; + SV *env, *result; + nxt_int_t rc; + PerlInterpreter *my_perl; + nxt_perl_psgi_ctx_t *pctx; - input.my_perl = my_perl; - input.req = req; + pctx = req->ctx->data; + my_perl = pctx->my_perl; - nxt_perl_psgi_request = req; + pctx->req = req; /* * Create environ variable for perl sub "application". * > sub application { * > my ($environ) = @_; */ - env = nxt_perl_psgi_env_create(my_perl, req, &input); + env = nxt_perl_psgi_env_create(my_perl, req); if (nxt_slow_path(env == NULL)) { nxt_unit_req_error(req, "PSGI: Failed to create 'env' for Perl Application"); nxt_unit_request_done(req, NXT_UNIT_ERROR); + pctx->req = NULL; return; } - nxt_perl_psgi_arg_input.ctx = &input; - nxt_perl_psgi_arg_error.ctx = &input; - /* Call perl sub and get result as SV*. */ - result = nxt_perl_psgi_call_var_application(my_perl, env, module->app, req); + result = nxt_perl_psgi_call_var_application(my_perl, env, pctx->app, + req); if (nxt_fast_path(SvOK(result) != 0 && SvROK(result) != 0)) { if (SvTYPE(SvRV(result)) == SVt_PVAV) { rc = nxt_perl_psgi_result_array(my_perl, result, req); nxt_unit_request_done(req, rc); + pctx->req = NULL; + goto release; } @@ -1235,6 +1275,7 @@ nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req) "from Perl Application"); nxt_unit_request_done(req, NXT_UNIT_ERROR); + pctx->req = NULL; release: @@ -1243,18 +1284,181 @@ release: } +static int +nxt_perl_psgi_ready_handler(nxt_unit_ctx_t *ctx) +{ + int res; + uint32_t i; + nxt_perl_app_conf_t *c; + nxt_perl_psgi_ctx_t *pctx; + + /* Worker thread context. */ + if (!nxt_unit_is_main_ctx(ctx)) { + return NXT_UNIT_OK; + } + + c = ctx->unit->data; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + for (i = 0; i < c->threads - 1; i++) { + pctx = &nxt_perl_psgi_ctxs[i]; + + pctx->ctx = ctx; + + res = pthread_create(&pctx->thread, nxt_perl_psgi_thread_attr, + nxt_perl_psgi_thread_func, pctx); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); + + } else { + nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)", + (int) (i + 1), strerror(res), res); + + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + +static void * +nxt_perl_psgi_thread_func(void *data) +{ + nxt_unit_ctx_t *ctx; + nxt_perl_psgi_ctx_t *pctx; + + pctx = data; + + nxt_unit_debug(pctx->ctx, "worker thread start"); + + ctx = nxt_unit_ctx_alloc(pctx->ctx, pctx); + if (nxt_slow_path(ctx == NULL)) { + return NULL; + } + + pctx->ctx = ctx; + + PERL_SET_CONTEXT(pctx->my_perl); + + (void) nxt_unit_run(ctx); + + nxt_unit_done(ctx); + + nxt_unit_debug(NULL, "worker thread end"); + + return NULL; +} + + +static int +nxt_perl_psgi_init_threads(nxt_perl_app_conf_t *c) +{ + int rc; + uint32_t i; + static pthread_attr_t attr; + + if (c->threads <= 1) { + return NXT_UNIT_OK; + } + + if (c->thread_stack_size > 0) { + rc = pthread_attr_init(&attr); + if (nxt_slow_path(rc != 0)) { + nxt_unit_alert(NULL, "thread attr init failed: %s (%d)", + strerror(rc), rc); + + return NXT_UNIT_ERROR; + } + + rc = pthread_attr_setstacksize(&attr, c->thread_stack_size); + if (nxt_slow_path(rc != 0)) { + nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)", + strerror(rc), rc); + + return NXT_UNIT_ERROR; + } + + nxt_perl_psgi_thread_attr = &attr; + } + + nxt_perl_psgi_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_perl_psgi_ctx_t) + * (c->threads - 1)); + if (nxt_slow_path(nxt_perl_psgi_ctxs == NULL)) { + return NXT_UNIT_ERROR; + } + + memset(nxt_perl_psgi_ctxs, 0, sizeof(nxt_perl_psgi_ctx_t) + * (c->threads - 1)); + + for (i = 0; i < c->threads - 1; i++) { + rc = nxt_perl_psgi_ctx_init(c->script, &nxt_perl_psgi_ctxs[i]); + + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return NXT_UNIT_ERROR; + } + } + + return NXT_UNIT_OK; +} + + static void -nxt_perl_psgi_atexit(void) +nxt_perl_psgi_join_threads(nxt_unit_ctx_t *ctx, nxt_perl_app_conf_t *c) { - dTHXa(nxt_perl_psgi); + int res; + uint32_t i; + nxt_perl_psgi_ctx_t *pctx; - nxt_perl_psgi_layer_stream_io_destroy(aTHX_ nxt_perl_psgi_arg_input.io); - nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ nxt_perl_psgi_arg_input.fp); + if (nxt_perl_psgi_ctxs == NULL) { + return; + } - nxt_perl_psgi_layer_stream_io_destroy(aTHX_ nxt_perl_psgi_arg_error.io); - nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ nxt_perl_psgi_arg_error.fp); + for (i = 0; i < c->threads - 1; i++) { + pctx = &nxt_perl_psgi_ctxs[i]; - perl_destruct(nxt_perl_psgi); - perl_free(nxt_perl_psgi); - PERL_SYS_TERM(); + res = pthread_join(pctx->thread, NULL); + + if (nxt_fast_path(res == 0)) { + nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); + + } else { + nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)", + (int) (i + 1), strerror(res), res); + } + } + + for (i = 0; i < c->threads - 1; i++) { + nxt_perl_psgi_ctx_free(&nxt_perl_psgi_ctxs[i]); + } + + nxt_unit_free(NULL, nxt_perl_psgi_ctxs); +} + + +static void +nxt_perl_psgi_ctx_free(nxt_perl_psgi_ctx_t *pctx) +{ + PerlInterpreter *my_perl; + + my_perl = pctx->my_perl; + + if (nxt_slow_path(my_perl == NULL)) { + return; + } + + PERL_SET_CONTEXT(my_perl); + + nxt_perl_psgi_layer_stream_io_destroy(aTHX_ pctx->arg_input.io); + nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ pctx->arg_input.fp); + + nxt_perl_psgi_layer_stream_io_destroy(aTHX_ pctx->arg_error.io); + nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ pctx->arg_error.fp); + + perl_destruct(my_perl); + perl_free(my_perl); } diff --git a/src/perl/nxt_perl_psgi_layer.h b/src/perl/nxt_perl_psgi_layer.h index 3fa349c0..af18ad0d 100644 --- a/src/perl/nxt_perl_psgi_layer.h +++ b/src/perl/nxt_perl_psgi_layer.h @@ -14,7 +14,7 @@ #include -typedef struct nxt_perl_psgi_io_arg nxt_perl_psgi_io_arg_t; +typedef struct nxt_perl_psgi_io_arg_s nxt_perl_psgi_io_arg_t; typedef long (*nxt_perl_psgi_io_read_f)(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length); @@ -24,7 +24,7 @@ typedef long (*nxt_perl_psgi_io_arg_f)(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg); -struct nxt_perl_psgi_io_arg { +struct nxt_perl_psgi_io_arg_s { SV *io; PerlIO *fp; @@ -32,7 +32,7 @@ struct nxt_perl_psgi_io_arg { nxt_perl_psgi_io_read_f read; nxt_perl_psgi_io_write_f write; - void *ctx; + void *pctx; }; -- cgit From 2220b8258f116d12f9cf6eb4133f41ec374b75f5 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 5 Nov 2020 17:02:55 +0300 Subject: Ruby: error checking during thread creation. Application terminates in case of thread creation failure. --- src/ruby/nxt_ruby.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index ffc1469a..698d4a43 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -1150,6 +1150,8 @@ nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) } else { nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1)); + + return NXT_UNIT_ERROR; } } @@ -1253,7 +1255,7 @@ nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c) return; } - for(i = 0; i < c->threads - 1; i++) { + for (i = 0; i < c->threads - 1; i++) { rctx = &nxt_ruby_ctxs[i]; if (rctx->thread != Qnil) { @@ -1264,8 +1266,10 @@ nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c) } else { nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1)); } + } - nxt_ruby_ctx_done(rctx); + for (i = 0; i < c->threads - 1; i++) { + nxt_ruby_ctx_done(&nxt_ruby_ctxs[i]); } nxt_unit_free(ctx, nxt_ruby_ctxs); -- cgit From 702e7bcc894adaac5f409a733eaf6fd2ab822f47 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Fri, 6 Nov 2020 20:41:56 +0300 Subject: Java: fixing ClassGraph deprecated API call. The issue (deprecated API warning) introduced by ClassGraph upgrade in ccd5c695b739 commit. --- src/java/nginx/unit/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/java/nginx/unit/Context.java b/src/java/nginx/unit/Context.java index 5aa3a982..0197858b 100644 --- a/src/java/nginx/unit/Context.java +++ b/src/java/nginx/unit/Context.java @@ -443,7 +443,7 @@ public class Context implements ServletContext, InitParams .enableClassInfo() .enableAnnotationInfo() //.enableSystemPackages() - .whitelistModules("javax.*") + .acceptModules("javax.*") //.enableAllInfo() ; -- cgit From 5fd2933d2e54a7b5781698a670abf89b1031db44 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 10 Nov 2020 22:27:08 +0300 Subject: Python: supporting ASGI legacy protocol. Introducing manual protocol selection for 'universal' apps and frameworks. --- src/nxt_application.h | 1 + src/nxt_conf_validation.c | 26 ++++++++ src/nxt_main_process.c | 6 ++ src/python/nxt_python.c | 12 +++- src/python/nxt_python_asgi.c | 121 +++++++++++++++++++++++++++------- src/python/nxt_python_asgi.h | 2 + src/python/nxt_python_asgi_lifespan.c | 43 +++++++++++- 7 files changed, 184 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 66c0e1f7..5632f56f 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -51,6 +51,7 @@ typedef struct { nxt_str_t path; nxt_str_t module; char *callable; + nxt_str_t protocol; uint32_t threads; uint32_t thread_stack_size; } nxt_python_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 8e6279fa..fc521016 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -95,6 +95,8 @@ 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_protocol(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, @@ -493,6 +495,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { }, { .name = nxt_string("callable"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("protocol"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_python_protocol, }, { .name = nxt_string("threads"), .type = NXT_CONF_VLDT_INTEGER, @@ -1360,6 +1366,26 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } +static nxt_int_t +nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + nxt_str_t proto; + + static const nxt_str_t wsgi = nxt_string("wsgi"); + static const nxt_str_t asgi = nxt_string("asgi"); + + nxt_conf_get_string(value, &proto); + + if (nxt_strstr_eq(&proto, &wsgi) || nxt_strstr_eq(&proto, &asgi)) { + return NXT_OK; + } + + return nxt_conf_vldt_error(vldt, "The \"protocol\" can either be " + "\"wsgi\" or \"asgi\"."); +} + + static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 99fc7995..0cde435b 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -198,6 +198,12 @@ static nxt_conf_map_t nxt_python_app_conf[] = { offsetof(nxt_common_app_conf_t, u.python.callable), }, + { + nxt_string("protocol"), + NXT_CONF_MAP_STR, + offsetof(nxt_common_app_conf_t, u.python.protocol), + }, + { nxt_string("threads"), NXT_CONF_MAP_INT32, diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 26a6f093..faf0c0e1 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -68,6 +68,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) char *nxt_py_module; size_t len; PyObject *obj, *pypath, *module; + nxt_str_t proto; const char *callable; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t python_init; @@ -82,6 +83,9 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) static const char bin_python[] = "/bin/python"; #endif + static const nxt_str_t wsgi = nxt_string("wsgi"); + static const nxt_str_t asgi = nxt_string("asgi"); + app_conf = data->app; c = &app_conf->u.python; @@ -244,7 +248,13 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) python_init.shm_limit = data->app->shm_limit; python_init.callbacks.ready_handler = nxt_python_ready_handler; - if (nxt_python_asgi_check(nxt_py_application)) { + proto = c->protocol; + + if (proto.length == 0) { + proto = nxt_python_asgi_check(nxt_py_application) ? asgi : wsgi; + } + + if (nxt_strstr_eq(&proto, &asgi)) { rc = nxt_python_asgi_init(&python_init, &nxt_py_proto); } else { diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index cab73239..e11a3b6c 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -16,6 +16,7 @@ #include +static PyObject *nxt_python_asgi_get_func(PyObject *obj); static int nxt_python_asgi_ctx_data_alloc(void **pdata); static void nxt_python_asgi_ctx_data_free(void *data); static int nxt_python_asgi_startup(void *data); @@ -42,6 +43,7 @@ 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; @@ -64,56 +66,78 @@ int nxt_python_asgi_check(PyObject *obj) { int res; - PyObject *call; + PyObject *func; PyCodeObject *code; - if (PyFunction_Check(obj)) { - code = (PyCodeObject *) PyFunction_GET_CODE(obj); + func = nxt_python_asgi_get_func(obj); + + if (func == NULL) { + return 0; + } + + code = (PyCodeObject *) PyFunction_GET_CODE(func); + + nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with " + "%d argument(s)", + (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ", + code->co_argcount); + + res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1; + + Py_DECREF(func); + + return res; +} + + +static PyObject * +nxt_python_asgi_get_func(PyObject *obj) +{ + PyObject *call; - return (code->co_flags & CO_COROUTINE) != 0; + if (PyFunction_Check(obj)) { + Py_INCREF(obj); + return obj; } if (PyMethod_Check(obj)) { obj = PyMethod_GET_FUNCTION(obj); - code = (PyCodeObject *) PyFunction_GET_CODE(obj); - - return (code->co_flags & CO_COROUTINE) != 0; + Py_INCREF(obj); + return obj; } call = PyObject_GetAttrString(obj, "__call__"); if (call == NULL) { - return 0; + return NULL; } if (PyFunction_Check(call)) { - code = (PyCodeObject *) PyFunction_GET_CODE(call); - - res = (code->co_flags & CO_COROUTINE) != 0; - - } else { - if (PyMethod_Check(call)) { - obj = PyMethod_GET_FUNCTION(call); + return call; + } - code = (PyCodeObject *) PyFunction_GET_CODE(obj); + if (PyMethod_Check(call)) { + obj = PyMethod_GET_FUNCTION(call); - res = (code->co_flags & CO_COROUTINE) != 0; + Py_INCREF(obj); + Py_DECREF(call); - } else { - res = 0; - } + return obj; } Py_DECREF(call); - return res; + return NULL; } int nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) { + PyObject *func; + PyCodeObject *code; + nxt_unit_debug(NULL, "asgi_init"); if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) { @@ -136,6 +160,22 @@ 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; + } + + 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; @@ -366,6 +406,7 @@ static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) { PyObject *scope, *res, *task, *receive, *send, *done, *asgi; + PyObject *stage2; nxt_py_asgi_ctx_data_t *ctx_data; if (req->request->websocket_handshake) { @@ -415,8 +456,42 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) req->data = asgi; - res = PyObject_CallFunctionObjArgs(nxt_py_application, - scope, receive, send, NULL); + if (!nxt_py_asgi_legacy) { + nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); + + res = PyObject_CallFunctionObjArgs(nxt_py_application, + scope, receive, send, NULL); + + } else { + nxt_unit_req_debug(req, "Python call legacy application"); + + res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL); + + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_error(req, "Python failed to call legacy app stage1"); + nxt_python_print_exception(); + nxt_unit_request_done(req, NXT_UNIT_ERROR); + + goto release_scope; + } + + if (nxt_slow_path(PyCallable_Check(res) == 0)) { + nxt_unit_req_error(req, + "Legacy ASGI application returns not a callable"); + nxt_unit_request_done(req, NXT_UNIT_ERROR); + + Py_DECREF(res); + + goto release_scope; + } + + stage2 = res; + + res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); + + Py_DECREF(stage2); + } + if (nxt_slow_path(res == NULL)) { nxt_unit_req_error(req, "Python failed to call the application"); nxt_python_print_exception(); diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h index 69d58477..c3c3e17a 100644 --- a/src/python/nxt_python_asgi.h +++ b/src/python/nxt_python_asgi.h @@ -68,4 +68,6 @@ 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_ */ diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c index 6910d2e8..506eaf4d 100644 --- a/src/python/nxt_python_asgi_lifespan.c +++ b/src/python/nxt_python_asgi_lifespan.c @@ -71,6 +71,7 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) { int rc; PyObject *scope, *res, *py_task, *receive, *send, *done; + PyObject *stage2; nxt_py_asgi_lifespan_t *lifespan; if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) { @@ -129,8 +130,43 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) goto release_future; } - res = PyObject_CallFunctionObjArgs(nxt_py_application, - scope, receive, send, NULL); + if (!nxt_py_asgi_legacy) { + nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application"); + + res = PyObject_CallFunctionObjArgs(nxt_py_application, + scope, receive, send, NULL); + + } else { + nxt_unit_req_debug(NULL, "Python call legacy application"); + + res = PyObject_CallFunctionObjArgs(nxt_py_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; + + goto release_scope; + } + + if (nxt_slow_path(PyCallable_Check(res) == 0)) { + nxt_unit_req_error(NULL, + "Legacy ASGI application returns not a callable"); + + Py_DECREF(res); + + goto release_scope; + } + + stage2 = res; + + res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); + + Py_DECREF(stage2); + } + if (nxt_slow_path(res == NULL)) { nxt_unit_error(NULL, "Python failed to call the application"); nxt_python_print_exception(); @@ -143,7 +179,8 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) goto release_scope; } - py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); + py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, + NULL); if (nxt_slow_path(py_task == NULL)) { nxt_unit_alert(NULL, "Python failed to call the create_task"); nxt_python_print_exception(); -- cgit From 896d8e8bfb3d8649db467d92e06c789b789d3feb Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 10 Nov 2020 22:27:08 +0300 Subject: Fixing multi-buffer body send to application. Application shared queue only capable to pass one shared memory buffer. The rest buffers in chain needs to be send directly to application in response to REQ_HEADERS_AC message. The issue can be reproduced for configurations where 'body_buffer_size' is greater than memory segment size (10 Mb). Requests with body size greater than 10 Mb are just `stuck` e.g. not passed to application awaiting for more data from router. The bug was introduced in 1d84b9e4b459 (v1.19.0). --- src/nxt_router.c | 16 +++++++++++++--- src/nxt_unit.c | 10 +++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index fbc9a4c8..95c8c160 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3912,6 +3912,7 @@ nxt_router_req_headers_ack_handler(nxt_task_t *task, { int res; nxt_app_t *app; + nxt_buf_t *b; nxt_bool_t start_process, unlinked; nxt_port_t *app_port, *main_app_port, *idle_port; nxt_queue_link_t *idle_lnk; @@ -4009,16 +4010,25 @@ nxt_router_req_headers_ack_handler(nxt_task_t *task, req_rpc_data->app_port = app_port; - if (req_rpc_data->msg_info.body_fd != -1) { + b = req_rpc_data->msg_info.buf; + + if (b != NULL) { + /* First buffer is already sent. Start from second. */ + b = b->next; + } + + if (req_rpc_data->msg_info.body_fd != -1 || b != NULL) { nxt_debug(task, "stream #%uD: send body fd %d", req_rpc_data->stream, req_rpc_data->msg_info.body_fd); - lseek(req_rpc_data->msg_info.body_fd, 0, SEEK_SET); + if (req_rpc_data->msg_info.body_fd != -1) { + lseek(req_rpc_data->msg_info.body_fd, 0, SEEK_SET); + } res = nxt_port_socket_write(task, app_port, NXT_PORT_MSG_REQ_BODY, req_rpc_data->msg_info.body_fd, req_rpc_data->stream, - task->thread->engine->port->id, NULL); + task->thread->engine->port->id, b); if (nxt_slow_path(res != NXT_OK)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 23848f5b..7230552d 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -1357,8 +1357,14 @@ nxt_unit_process_req_body(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) if (recv_msg->incoming_buf != NULL) { b = nxt_container_of(req->content_buf, nxt_unit_mmap_buf_t, buf); + while (b->next != NULL) { + b = b->next; + } + /* "Move" incoming buffer list to req_impl. */ - nxt_unit_mmap_buf_insert_tail(&b->next, recv_msg->incoming_buf); + b->next = recv_msg->incoming_buf; + b->next->prev = &b->next; + recv_msg->incoming_buf = NULL; } @@ -2988,8 +2994,6 @@ nxt_unit_request_read(nxt_unit_request_info_t *req, void *dst, size_t size) buf_res = nxt_unit_buf_read(&req->content_buf, &req->content_length, dst, size); - nxt_unit_req_debug(req, "read: %d", (int) buf_res); - if (buf_res < (ssize_t) size && req->content_fd != -1) { res = read(req->content_fd, dst, size); if (nxt_slow_path(res < 0)) { -- cgit From cb28b41311479424cdf3b217a2252518da34c23d Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 11 Nov 2020 12:09:49 +0300 Subject: PHP: prevention of consuming unread request body on finalization. The php_request_shutdown() function calls sapi_deactivate() that tries to read request body into a dummy buffer. In our case it's just waste of CPU cycles. This change is also required for the following implementation of the fastcgi_finish_request() function, where the request context can be cleared by the time of finalization. --- src/nxt_php_sapi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 234ceef8..f6539af5 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -934,6 +934,9 @@ nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) { +#if (PHP_VERSION_ID < 50600) + void *read_post; +#endif nxt_unit_field_t *f; zend_file_handle file_handle; @@ -990,9 +993,21 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) php_execute_script(&file_handle TSRMLS_CC); + /* Prevention of consuming possible unread request body. */ +#if (PHP_VERSION_ID < 50600) + read_post = sapi_module.read_post; + sapi_module.read_post = NULL; +#else + SG(post_read) = 1; +#endif + php_request_shutdown(NULL); nxt_unit_request_done(ctx->req, NXT_UNIT_OK); + +#if (PHP_VERSION_ID < 50600) + sapi_module.read_post = read_post; +#endif } -- cgit From e30db59168eb83ba65b55df22b021085ae700605 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 11 Nov 2020 12:09:54 +0300 Subject: PHP: implementation of the fastcgi_finish_request() function. This closes #219 issue on GitHub. --- src/nxt_php_sapi.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index f6539af5..d2fbdd27 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -8,6 +8,7 @@ #include "SAPI.h" #include "php_main.h" #include "php_variables.h" +#include "ext/standard/php_standard.h" #include #include @@ -137,16 +138,38 @@ static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); #endif +#ifdef NXT_PHP7 +#if PHP_VERSION_ID < 70200 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, + _IS_BOOL, NULL, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, + _IS_BOOL, 0) +#endif +#else /* PHP5 */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 0) +#endif +ZEND_END_ARG_INFO() + +ZEND_FUNCTION(fastcgi_finish_request); + PHP_MINIT_FUNCTION(nxt_php_ext); ZEND_NAMED_FUNCTION(nxt_php_chdir); + +static const zend_function_entry nxt_php_ext_functions[] = { + ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request) + ZEND_FE_END +}; + + zif_handler nxt_php_chdir_handler; static zend_module_entry nxt_php_unit_module = { STANDARD_MODULE_HEADER, "unit", - NULL, /* function table */ + nxt_php_ext_functions, /* function table */ PHP_MINIT(nxt_php_ext), /* initialization */ NULL, /* shutdown */ NULL, /* request initialization */ @@ -186,6 +209,55 @@ ZEND_NAMED_FUNCTION(nxt_php_chdir) } +PHP_FUNCTION(fastcgi_finish_request) +{ + nxt_php_run_ctx_t *ctx; + + if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) { +#ifdef NXT_PHP8 + RETURN_THROWS(); +#else + return; +#endif + } + + ctx = SG(server_context); + + if (nxt_slow_path(ctx->req == NULL)) { + RETURN_FALSE; + } + +#ifdef NXT_PHP7 + php_output_end_all(); + php_header(); +#else +#ifdef PHP_OUTPUT_NEWAPI + php_output_end_all(TSRMLS_C); +#else + php_end_ob_buffers(1 TSRMLS_CC); +#endif + + php_header(TSRMLS_C); +#endif + + nxt_unit_request_done(ctx->req, NXT_UNIT_OK); + ctx->req = NULL; + + PG(connection_status) = PHP_CONNECTION_ABORTED; +#ifdef NXT_PHP7 + php_output_set_status(PHP_OUTPUT_DISABLED); +#else +#ifdef PHP_OUTPUT_NEWAPI + php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); +#else + php_output_set_status(0 TSRMLS_CC); +#endif +#endif + + RETURN_TRUE; +} + + static sapi_module_struct nxt_php_sapi_module = { (char *) "cli-server", @@ -1003,7 +1075,9 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) php_request_shutdown(NULL); - nxt_unit_request_done(ctx->req, NXT_UNIT_OK); + if (ctx->req != NULL) { + nxt_unit_request_done(ctx->req, NXT_UNIT_OK); + } #if (PHP_VERSION_ID < 50600) sapi_module.read_post = read_post; -- cgit From 3837d28f9b7c5d0840d2e4b26f4867b66838d31b Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Fri, 13 Nov 2020 10:48:32 +0000 Subject: Isolation: added option to disable tmpfs mount. Now users can disable the default tmpfs mount point in the rootfs. { "isolation": { "automount": { "tmpfs": false } } } --- src/nxt_conf_validation.c | 3 +++ src/nxt_isolation.c | 48 ++++++++++++++++++++++++++++------------------- src/nxt_process.h | 1 + 3 files changed, 33 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index fc521016..69a47274 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -841,6 +841,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { { .name = nxt_string("language_deps"), .type = NXT_CONF_VLDT_BOOLEAN, + }, { + .name = nxt_string("tmpfs"), + .type = NXT_CONF_VLDT_BOOLEAN, }, NXT_CONF_VLDT_END diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index e0f169aa..f0ef625f 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -484,10 +484,12 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation, static nxt_str_t automount_name = nxt_string("automount"); static nxt_str_t langdeps_name = nxt_string("language_deps"); + static nxt_str_t tmp_name = nxt_string("tmpfs"); automount = &process->isolation.automount; automount->language_deps = 1; + automount->tmpfs = 1; conf = nxt_conf_get_object_member(isolation, &automount_name, NULL); if (conf != NULL) { @@ -495,6 +497,11 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation, if (value != NULL) { automount->language_deps = nxt_conf_get_boolean(value); } + + value = nxt_conf_get_object_member(conf, &tmp_name, NULL); + if (value != NULL) { + automount->tmpfs = nxt_conf_get_boolean(value); + } } return NXT_OK; @@ -576,29 +583,32 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, *p = '\0'; } - mnt = nxt_array_add(mounts); - if (nxt_slow_path(mnt == NULL)) { - return NXT_ERROR; - } + if (process->isolation.automount.tmpfs) { + mnt = nxt_array_add(mounts); + if (nxt_slow_path(mnt == NULL)) { + return NXT_ERROR; + } - mnt->src = (u_char *) "tmpfs"; - mnt->name = (u_char *) "tmpfs"; - mnt->type = NXT_FS_TMP; - mnt->flags = (NXT_FS_FLAGS_NOSUID | NXT_FS_FLAGS_NODEV - | NXT_FS_FLAGS_NOEXEC); - mnt->data = (u_char *) "size=1m,mode=777"; - mnt->builtin = 1; - mnt->deps = 0; + mnt->src = (u_char *) "tmpfs"; + mnt->name = (u_char *) "tmpfs"; + mnt->type = NXT_FS_TMP; + mnt->flags = (NXT_FS_FLAGS_NOSUID + | NXT_FS_FLAGS_NODEV + | NXT_FS_FLAGS_NOEXEC); + mnt->data = (u_char *) "size=1m,mode=777"; + mnt->builtin = 1; + mnt->deps = 0; + + mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/tmp") + 1); + if (nxt_slow_path(mnt->dst == NULL)) { + return NXT_ERROR; + } - mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/tmp") + 1); - if (nxt_slow_path(mnt->dst == NULL)) { - return NXT_ERROR; + p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); + p = nxt_cpymem(p, "/tmp", 4); + *p = '\0'; } - p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); - p = nxt_cpymem(p, "/tmp", 4); - *p = '\0'; - mnt = nxt_array_add(mounts); if (nxt_slow_path(mnt == NULL)) { return NXT_ERROR; diff --git a/src/nxt_process.h b/src/nxt_process.h index ddadb08f..99ba8022 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -75,6 +75,7 @@ typedef struct { typedef struct { uint8_t language_deps; /* 1-bit */ + uint8_t tmpfs; /* 1-bit */ } nxt_process_automount_t; -- cgit From e7d66acda726490fb7b8da03f0d4788857918d5a Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Mon, 16 Nov 2020 17:56:12 +0000 Subject: Isolation: added option to disable "procfs" mount. Now users can disable the default procfs mount point in the rootfs. { "isolation": { "automount": { "procfs": false } } } --- src/nxt_conf_validation.c | 3 +++ src/nxt_isolation.c | 45 +++++++++++++++++++++++++++------------------ src/nxt_process.h | 1 + 3 files changed, 31 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 69a47274..dca56881 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -844,6 +844,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { }, { .name = nxt_string("tmpfs"), .type = NXT_CONF_VLDT_BOOLEAN, + }, { + .name = nxt_string("procfs"), + .type = NXT_CONF_VLDT_BOOLEAN, }, NXT_CONF_VLDT_END diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index f0ef625f..1e6323bc 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -485,11 +485,13 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation, static nxt_str_t automount_name = nxt_string("automount"); static nxt_str_t langdeps_name = nxt_string("language_deps"); static nxt_str_t tmp_name = nxt_string("tmpfs"); + static nxt_str_t proc_name = nxt_string("procfs"); automount = &process->isolation.automount; automount->language_deps = 1; automount->tmpfs = 1; + automount->procfs = 1; conf = nxt_conf_get_object_member(isolation, &automount_name, NULL); if (conf != NULL) { @@ -502,6 +504,11 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation, if (value != NULL) { automount->tmpfs = nxt_conf_get_boolean(value); } + + value = nxt_conf_get_object_member(conf, &proc_name, NULL); + if (value != NULL) { + automount->procfs = nxt_conf_get_boolean(value); + } } return NXT_OK; @@ -609,27 +616,29 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process, *p = '\0'; } - mnt = nxt_array_add(mounts); - if (nxt_slow_path(mnt == NULL)) { - return NXT_ERROR; - } + if (process->isolation.automount.procfs) { + mnt = nxt_array_add(mounts); + if (nxt_slow_path(mnt == NULL)) { + return NXT_ERROR; + } - mnt->name = (u_char *) "proc"; - mnt->type = NXT_FS_PROC; - mnt->src = (u_char *) "none"; - mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/proc") + 1); - if (nxt_slow_path(mnt->dst == NULL)) { - return NXT_ERROR; - } + mnt->name = (u_char *) "proc"; + mnt->type = NXT_FS_PROC; + mnt->src = (u_char *) "none"; + mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/proc") + 1); + if (nxt_slow_path(mnt->dst == NULL)) { + return NXT_ERROR; + } - p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); - p = nxt_cpymem(p, "/proc", 5); - *p = '\0'; + p = nxt_cpymem(mnt->dst, rootfs, rootfs_len); + p = nxt_cpymem(p, "/proc", 5); + *p = '\0'; - mnt->data = (u_char *) ""; - mnt->flags = NXT_FS_FLAGS_NOEXEC | NXT_FS_FLAGS_NOSUID; - mnt->builtin = 1; - mnt->deps = 0; + mnt->data = (u_char *) ""; + mnt->flags = NXT_FS_FLAGS_NOEXEC | NXT_FS_FLAGS_NOSUID; + mnt->builtin = 1; + mnt->deps = 0; + } qsort(mounts->elts, mounts->nelts, sizeof(nxt_fs_mount_t), nxt_isolation_mount_compare); diff --git a/src/nxt_process.h b/src/nxt_process.h index 99ba8022..7afb8803 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -76,6 +76,7 @@ typedef struct { typedef struct { uint8_t language_deps; /* 1-bit */ uint8_t tmpfs; /* 1-bit */ + uint8_t procfs; /* 1-bit */ } nxt_process_automount_t; -- cgit From fb80502513bf0140c5e595714967f75ea3e1e5d3 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 17 Nov 2020 16:50:06 +0300 Subject: HTTP parser: allowed more characters in header field names. Previously, all requests that contained in header field names characters other than alphanumeric, or "-", or "_" were rejected with a 400 "Bad Request" error response. Now, the parser allows the same set of characters as specified in RFC 7230, including: "!", "#", "$", "%", "&", "'", "*", "+", ".", "^", "`", "|", and "~". Header field names that contain only these characters are considered valid. Also, there's a new option introduced: "discard_unsafe_fields". It accepts boolean value and it is set to "true" by default. When this option is "true", all header field names that contain characters in valid range, but other than alphanumeric or "-" are skipped during parsing. When the option is "false", these header fields aren't skipped. Requests with non-valid characters in header field names according to RFC 7230 are rejected regardless of "discard_unsafe_fields" setting. This closes #422 issue on GitHub. --- src/nxt_conf_validation.c | 3 ++ src/nxt_h1proto.c | 6 +++- src/nxt_http_parse.c | 67 +++++++++++++++++++++++++++--------------- src/nxt_http_parse.h | 14 +++++---- src/nxt_router.c | 7 +++++ src/nxt_router.h | 2 ++ src/test/nxt_http_parse_test.c | 38 +++++++++++++++++++----- 7 files changed, 100 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index dca56881..f4e7c358 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -273,6 +273,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { }, { .name = nxt_string("body_temp_path"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("discard_unsafe_fields"), + .type = NXT_CONF_VLDT_BOOLEAN, }, { .name = nxt_string("websocket"), .type = NXT_CONF_VLDT_OBJECT, diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index dc23d7c4..dccbe56c 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -467,6 +467,7 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data) nxt_int_t ret; nxt_conn_t *c; nxt_h1proto_t *h1p; + nxt_socket_conf_t *skcf; nxt_http_request_t *r; nxt_socket_conf_joint_t *joint; @@ -503,11 +504,14 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data) joint->count++; r->conf = joint; + skcf = joint->socket_conf; if (c->local == NULL) { - c->local = joint->socket_conf->sockaddr; + c->local = skcf->sockaddr; } + h1p->parser.discard_unsafe_fields = skcf->discard_unsafe_fields; + nxt_h1p_conn_request_header_parse(task, c, h1p); return; } diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c index 22004cc1..338b0a90 100644 --- a/src/nxt_http_parse.c +++ b/src/nxt_http_parse.c @@ -288,11 +288,13 @@ continue_target: case NXT_HTTP_TARGET_SPACE: rp->target_end = p; goto space_after_target; - +#if 0 case NXT_HTTP_TARGET_QUOTE_MARK: rp->quoted_target = 1; goto rest_of_target; - +#else + case NXT_HTTP_TARGET_QUOTE_MARK: +#endif case NXT_HTTP_TARGET_HASH: rp->complex_target = 1; goto rest_of_target; @@ -378,7 +380,7 @@ space_after_target: } } - rp->space_in_target = 1; + //rp->space_in_target = 1; if (rest) { goto rest_of_target; @@ -397,7 +399,7 @@ space_after_target: goto space_after_target; } - rp->space_in_target = 1; + //rp->space_in_target = 1; if (rest) { goto rest_of_target; @@ -432,7 +434,12 @@ space_after_target: *pos = p + 10; } - if (rp->complex_target != 0 || rp->quoted_target != 0) { + if (rp->complex_target != 0 +#if 0 + || rp->quoted_target != 0 +#endif + ) + { rc = nxt_http_parse_complex_target(rp); if (nxt_slow_path(rc != NXT_OK)) { @@ -518,11 +525,13 @@ nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, static const u_char normal[256] nxt_aligned(64) = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" + /* \s ! " # $ % & ' ( ) * + , . / : ; < = > ? */ + "\0\1\0\1\1\1\1\1\0\0\1\1\0" "-" "\1\0" "0123456789" "\0\0\0\0\0\0" - /* These 64 bytes should reside in one cache line. */ - "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_" - "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" + /* @ [ \ ] ^ _ */ + "\0" "abcdefghijklmnopqrstuvwxyz" "\0\0\0\1\1" + /* ` { | } ~ */ + "\1" "abcdefghijklmnopqrstuvwxyz" "\0\1\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" @@ -538,9 +547,14 @@ nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, \ c = normal[ch]; \ \ - if (nxt_slow_path(c == '\0')) { \ - p = &(ch); \ - goto name_end; \ + if (nxt_slow_path(c <= '\1')) { \ + if (c == '\0') { \ + p = &(ch); \ + goto name_end; \ + } \ + \ + rp->skip_field = rp->discard_unsafe_fields; \ + c = ch; \ } \ \ hash = nxt_http_field_hash_char(hash, c); @@ -777,20 +791,25 @@ nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos, *pos = p + 1; if (rp->field_name.length != 0) { - field = nxt_list_add(rp->fields); + if (rp->skip_field) { + rp->skip_field = 0; - if (nxt_slow_path(field == NULL)) { - return NXT_ERROR; - } + } else { + field = nxt_list_add(rp->fields); - field->hash = nxt_http_field_hash_end(rp->field_hash); - field->skip = 0; - field->hopbyhop = 0; + if (nxt_slow_path(field == NULL)) { + return NXT_ERROR; + } - field->name_length = rp->field_name.length; - field->value_length = rp->field_value.length; - field->name = rp->field_name.start; - field->value = rp->field_value.start; + field->hash = nxt_http_field_hash_end(rp->field_hash); + field->skip = 0; + field->hopbyhop = 0; + + field->name_length = rp->field_name.length; + field->value_length = rp->field_value.length; + field->name = rp->field_name.start; + field->value = rp->field_value.start; + } rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; @@ -1023,7 +1042,7 @@ nxt_http_parse_complex_target(nxt_http_request_parse_t *rp) break; case sw_quoted: - rp->quoted_target = 1; + //rp->quoted_target = 1; if (ch >= '0' && ch <= '9') { high = (u_char) (ch - '0'); diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h index cbfc8433..3cd9bd15 100644 --- a/src/nxt_http_parse.h +++ b/src/nxt_http_parse.h @@ -55,15 +55,19 @@ struct nxt_http_request_parse_s { uint32_t field_hash; + uint8_t skip_field; /* 1 bit */ + uint8_t discard_unsafe_fields; /* 1 bit */ + /* target with "/." */ - uint8_t complex_target; /* 1 bit */ + uint8_t complex_target; /* 1 bit */ +#if 0 /* target with "%" */ - uint8_t quoted_target; /* 1 bit */ + uint8_t quoted_target; /* 1 bit */ /* target with " " */ - uint8_t space_in_target; /* 1 bit */ - + uint8_t space_in_target; /* 1 bit */ +#endif /* Preserve encoded '/' (%2F) and '%' (%25). */ - uint8_t encoded_slashes; /* 1 bit */ + uint8_t encoded_slashes; /* 1 bit */ }; diff --git a/src/nxt_router.c b/src/nxt_router.c index 95c8c160..9dd5c30e 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1262,6 +1262,12 @@ static nxt_conf_map_t nxt_router_http_conf[] = { NXT_CONF_MAP_STR, offsetof(nxt_socket_conf_t, body_temp_path), }, + + { + nxt_string("discard_unsafe_fields"), + NXT_CONF_MAP_INT8, + offsetof(nxt_socket_conf_t, discard_unsafe_fields), + }, }; @@ -1649,6 +1655,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, skcf->header_buffer_size = 2048; skcf->large_header_buffer_size = 8192; skcf->large_header_buffers = 4; + skcf->discard_unsafe_fields = 1; skcf->body_buffer_size = 16 * 1024; skcf->max_body_size = 8 * 1024 * 1024; skcf->proxy_header_buffer_size = 64 * 1024; diff --git a/src/nxt_router.h b/src/nxt_router.h index 512f1810..5804840f 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -191,6 +191,8 @@ typedef struct { nxt_str_t body_temp_path; + uint8_t discard_unsafe_fields; /* 1 bit */ + #if (NXT_TLS) nxt_tls_conf_t *tls; #endif diff --git a/src/test/nxt_http_parse_test.c b/src/test/nxt_http_parse_test.c index 9630b21c..540309c1 100644 --- a/src/test/nxt_http_parse_test.c +++ b/src/test/nxt_http_parse_test.c @@ -23,9 +23,15 @@ typedef struct { } nxt_http_parse_test_request_line_t; +typedef struct { + nxt_int_t result; + unsigned discard_unsafe_fields:1; +} nxt_http_parse_test_fields_t; + + typedef union { void *pointer; - nxt_int_t result; + nxt_http_parse_test_fields_t fields; nxt_http_parse_test_request_line_t request_line; } nxt_http_parse_test_data_t; @@ -324,10 +330,11 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = { { nxt_string("GET / HTTP/1.1\r\n" "X-Unknown-Header: value\r\n" - "X-Good-Header: value\r\n\r\n"), + "X-Good-Header: value\r\n" + "!#$%&'*+.^_`|~: skipped\r\n\r\n"), NXT_DONE, &nxt_http_parse_test_fields, - { .result = NXT_OK } + { .fields = { NXT_OK, 1 } } }, { nxt_string("GET / HTTP/1.1\r\n" @@ -336,7 +343,14 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = { "X-Bad-Header: value\r\n\r\n"), NXT_DONE, &nxt_http_parse_test_fields, - { .result = NXT_ERROR } + { .fields = { NXT_ERROR, 1 } } + }, + { + nxt_string("GET / HTTP/1.1\r\n" + "!#$%&'*+.^_`|~: allowed\r\n\r\n"), + NXT_DONE, + &nxt_http_parse_test_fields, + { .fields = { NXT_ERROR, 0 } } }, }; @@ -349,6 +363,10 @@ static nxt_http_field_proc_t nxt_http_test_fields[] = { { nxt_string("X-Good-Header"), &nxt_http_test_header_return, NXT_OK }, + + { nxt_string("!#$%&'*+.^_`|~"), + &nxt_http_test_header_return, + NXT_ERROR }, }; @@ -540,6 +558,10 @@ nxt_http_parse_test(nxt_thread_t *thr) return NXT_ERROR; } + if (test->handler == &nxt_http_parse_test_fields) { + rp.discard_unsafe_fields = test->data.fields.discard_unsafe_fields; + } + rc = nxt_http_parse_test_run(&rp, &test->request); if (rc != test->result) { @@ -740,7 +762,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp, return NXT_ERROR; } - if (rp->complex_target != test->complex_target) { + if (rp->complex_target != (test->complex_target | test->quoted_target)) { nxt_log_alert(log, "http parse test case failed:\n" " - request:\n\"%V\"\n" " - complex_target: %d (expected: %d)", @@ -748,6 +770,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp, return NXT_ERROR; } +#if 0 if (rp->quoted_target != test->quoted_target) { nxt_log_alert(log, "http parse test case failed:\n" " - request:\n\"%V\"\n" @@ -763,6 +786,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp, request, rp->space_in_target, test->space_in_target); return NXT_ERROR; } +#endif return NXT_OK; } @@ -776,11 +800,11 @@ nxt_http_parse_test_fields(nxt_http_request_parse_t *rp, rc = nxt_http_fields_process(rp->fields, &nxt_http_test_fields_hash, NULL); - if (rc != data->result) { + if (rc != data->fields.result) { nxt_log_alert(log, "http parse test hash failed:\n" " - request:\n\"%V\"\n" " - result: %i (expected: %i)", - request, rc, data->result); + request, rc, data->fields.result); return NXT_ERROR; } -- cgit From 8340ca0b9c7ad4109033ccb028f87cc1b73396bc Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: improving logging consistency. Debug logging depends on macros defined in nxt_auto_config.h. --- src/nxt_unit.c | 4 ++++ src/nxt_unit.h | 1 + src/test/nxt_unit_app_test.c | 2 -- src/test/nxt_unit_websocket_chat.c | 8 ++++---- 4 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 7230552d..564fd094 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -6527,7 +6527,9 @@ nxt_unit_malloc(nxt_unit_ctx_t *ctx, size_t size) p = malloc(size); if (nxt_fast_path(p != NULL)) { +#if (NXT_DEBUG_ALLOC) nxt_unit_debug(ctx, "malloc(%d): %p", (int) size, p); +#endif } else { nxt_unit_alert(ctx, "malloc(%d) failed: %s (%d)", @@ -6541,7 +6543,9 @@ nxt_unit_malloc(nxt_unit_ctx_t *ctx, size_t size) void nxt_unit_free(nxt_unit_ctx_t *ctx, void *p) { +#if (NXT_DEBUG_ALLOC) nxt_unit_debug(ctx, "free(%p)", p); +#endif free(p); } diff --git a/src/nxt_unit.h b/src/nxt_unit.h index cb78e862..90cba2a3 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -12,6 +12,7 @@ #include #include +#include "nxt_auto_config.h" #include "nxt_version.h" #include "nxt_unit_typedefs.h" diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c index b6dd13d2..a5f3728c 100644 --- a/src/test/nxt_unit_app_test.c +++ b/src/test/nxt_unit_app_test.c @@ -3,8 +3,6 @@ * Copyright (C) NGINX, Inc. */ -#include -#include #include #include #include diff --git a/src/test/nxt_unit_websocket_chat.c b/src/test/nxt_unit_websocket_chat.c index 6e274722..39f8a440 100644 --- a/src/test/nxt_unit_websocket_chat.c +++ b/src/test/nxt_unit_websocket_chat.c @@ -30,7 +30,7 @@ typedef struct { static int ws_chat_root(nxt_unit_request_info_t *req); -static void ws_chat_broadcast(const void *buf, size_t size); +static void ws_chat_broadcast(const char *buf, size_t size); static const char ws_chat_index_html[]; @@ -139,18 +139,18 @@ ws_chat_root(nxt_unit_request_info_t *req) static void -ws_chat_broadcast(const void *buf, size_t size) +ws_chat_broadcast(const char *buf, size_t size) { ws_chat_request_data_t *data; nxt_unit_request_info_t *req; - nxt_unit_debug(NULL, "broadcast: %s", buf); + nxt_unit_debug(NULL, "broadcast: %*.s", (int) size, buf); nxt_queue_each(data, &ws_chat_sessions, ws_chat_request_data_t, link) { req = nxt_unit_get_request_info_from_data(data); - nxt_unit_req_debug(req, "broadcast: %s", buf); + nxt_unit_req_debug(req, "send: %*.s", (int) size, buf); nxt_unit_websocket_send(req, NXT_WEBSOCKET_OP_TEXT, 1, buf, size); } nxt_queue_loop; -- cgit From 0ec69aa46e3577ef44060b9dd8576faab4a863ce Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: fixing racing condition for port add / state change. The issue only occurred in Go applications because "port_send" is overloaded only in Go. To reproduce it, send multiple concurrent requests to the application after it has initialised. The warning message "[unit] [go] port NNN:dd not found" is the first visible aspect of the issue; the second and more valuable one is a closed connection, an error response, or a hanging response to some requests. When the application starts, it is unaware of the router's worker thread ports, so it requests the ports from the router after receiving requests from the corresponding router worker threads. When multiple requests are processed simultaneously, the router port may be required by several requests, so request processing starts only after the application receives the required port information. The port should be added to the Go port repository after its 'ready' flag is updated. Otherwise, Unit may start processing some requests and use the port before it is in the repository. The issue was introduced in changeset 78836321a126. --- src/nxt_unit.c | 126 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 564fd094..69cae8bb 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -147,6 +147,8 @@ nxt_inline void nxt_unit_port_use(nxt_unit_port_t *port); nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port); static nxt_unit_port_t *nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue); +static void nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx, + nxt_queue_t *awaiting_req); static void nxt_unit_remove_port(nxt_unit_impl_t *lib, nxt_unit_port_id_t *port_id); static nxt_unit_port_t *nxt_unit_remove_port_unsafe(nxt_unit_impl_t *lib, @@ -5340,14 +5342,12 @@ nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port) static nxt_unit_port_t * nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue) { - int rc; - nxt_queue_t awaiting_req; - nxt_unit_impl_t *lib; - nxt_unit_port_t *old_port; - nxt_unit_process_t *process; - nxt_unit_ctx_impl_t *ctx_impl; - nxt_unit_port_impl_t *new_port, *old_port_impl; - nxt_unit_request_info_impl_t *req_impl; + int rc, ready; + nxt_queue_t awaiting_req; + nxt_unit_impl_t *lib; + nxt_unit_port_t *old_port; + nxt_unit_process_t *process; + nxt_unit_port_impl_t *new_port, *old_port_impl; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); @@ -5396,46 +5396,45 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue) old_port_impl->queue = queue; } - if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) { - nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req); - nxt_queue_init(&old_port_impl->awaiting_req); - } - - old_port_impl->ready = (port->in_fd != -1 || port->out_fd != -1); + ready = (port->in_fd != -1 || port->out_fd != -1); - pthread_mutex_unlock(&lib->mutex); + /* + * Port can be market as 'ready' only after callbacks.add_port() call. + * Otherwise, request may try to use the port before callback. + */ + if (lib->callbacks.add_port == NULL && ready) { + old_port_impl->ready = ready; - if (lib->callbacks.add_port != NULL - && (port->in_fd != -1 || port->out_fd != -1)) - { - lib->callbacks.add_port(ctx, old_port); + if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) { + nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req); + nxt_queue_init(&old_port_impl->awaiting_req); + } } - nxt_queue_each(req_impl, &awaiting_req, - nxt_unit_request_info_impl_t, port_wait_link) - { - nxt_queue_remove(&req_impl->port_wait_link); - - ctx_impl = nxt_container_of(req_impl->req.ctx, nxt_unit_ctx_impl_t, - ctx); + pthread_mutex_unlock(&lib->mutex); - pthread_mutex_lock(&ctx_impl->mutex); + if (lib->callbacks.add_port != NULL && ready) { + lib->callbacks.add_port(ctx, old_port); - nxt_queue_insert_tail(&ctx_impl->ready_req, - &req_impl->port_wait_link); + pthread_mutex_lock(&lib->mutex); - pthread_mutex_unlock(&ctx_impl->mutex); + old_port_impl->ready = ready; - nxt_atomic_fetch_add(&ctx_impl->wait_items, -1); + if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) { + nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req); + nxt_queue_init(&old_port_impl->awaiting_req); + } - nxt_unit_awake_ctx(ctx, ctx_impl); + pthread_mutex_unlock(&lib->mutex); + } - } nxt_queue_loop; + nxt_unit_process_awaiting_req(ctx, &awaiting_req); return old_port; } new_port = NULL; + ready = 0; nxt_unit_debug(ctx, "add_port: port{%d,%d} in_fd %d out_fd %d queue %p", port->id.pid, port->id.id, @@ -5478,13 +5477,21 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue) new_port->use_count = 2; new_port->process = process; - new_port->ready = (port->in_fd != -1 || port->out_fd != -1); new_port->queue = queue; new_port->from_socket = 0; new_port->socket_rbuf = NULL; nxt_queue_init(&new_port->awaiting_req); + ready = (port->in_fd != -1 || port->out_fd != -1); + + if (lib->callbacks.add_port == NULL) { + new_port->ready = ready; + + } else { + new_port->ready = 0; + } + process = NULL; unlock: @@ -5495,14 +5502,55 @@ unlock: nxt_unit_process_release(process); } - if (lib->callbacks.add_port != NULL - && new_port != NULL - && (port->in_fd != -1 || port->out_fd != -1)) - { + if (lib->callbacks.add_port != NULL && new_port != NULL && ready) { lib->callbacks.add_port(ctx, &new_port->port); + + nxt_queue_init(&awaiting_req); + + pthread_mutex_lock(&lib->mutex); + + new_port->ready = 1; + + if (!nxt_queue_is_empty(&new_port->awaiting_req)) { + nxt_queue_add(&awaiting_req, &new_port->awaiting_req); + nxt_queue_init(&new_port->awaiting_req); + } + + pthread_mutex_unlock(&lib->mutex); + + nxt_unit_process_awaiting_req(ctx, &awaiting_req); } - return &new_port->port; + return (new_port == NULL) ? NULL : &new_port->port; +} + + +static void +nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx, nxt_queue_t *awaiting_req) +{ + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_request_info_impl_t *req_impl; + + nxt_queue_each(req_impl, awaiting_req, + nxt_unit_request_info_impl_t, port_wait_link) + { + nxt_queue_remove(&req_impl->port_wait_link); + + ctx_impl = nxt_container_of(req_impl->req.ctx, nxt_unit_ctx_impl_t, + ctx); + + pthread_mutex_lock(&ctx_impl->mutex); + + nxt_queue_insert_tail(&ctx_impl->ready_req, + &req_impl->port_wait_link); + + pthread_mutex_unlock(&ctx_impl->mutex); + + nxt_atomic_fetch_add(&ctx_impl->wait_items, -1); + + nxt_unit_awake_ctx(ctx, ctx_impl); + + } nxt_queue_loop; } -- cgit From d26afcb481d97cd71db014b16bde44e807043a2b Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: fixing racing condition in request struct recycling. The issue occurred under highly concurrent request load in Go applications. Such applications are multi-threaded but use a single libunit context; any thread-safe code in the libunit context is only required for Go applications. As a result of improper request state reset, the recycled request structure was recovered in the released state, so further operations with this request resulted in 'response already sent' warnings. However, the actual response was never delivered to the router and the client. --- src/nxt_unit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 69cae8bb..f0c68374 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -1751,6 +1751,8 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req) req->response_port = NULL; } + req_impl->state = NXT_UNIT_RS_RELEASED; + pthread_mutex_lock(&ctx_impl->mutex); nxt_queue_remove(&req_impl->link); @@ -1758,8 +1760,6 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req) nxt_queue_insert_tail(&ctx_impl->free_req, &req_impl->link); pthread_mutex_unlock(&ctx_impl->mutex); - - req_impl->state = NXT_UNIT_RS_RELEASED; } -- cgit From 8132e1f700934a32bc9e3fb0ab66f550a335a326 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Go: removing C proxy functions and re-using goroutines. --- src/nxt_unit.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++---------- src/nxt_unit.h | 2 ++ 2 files changed, 65 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index f0c68374..44525d04 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -54,12 +54,13 @@ static int nxt_unit_read_env(nxt_unit_port_t *ready_port, int *log_fd, uint32_t *stream, uint32_t *shm_limit); static int nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd); -static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf); +static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, + nxt_unit_request_info_t **preq); static int nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg); static int nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx); static int nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, - nxt_unit_recv_msg_t *recv_msg); + nxt_unit_recv_msg_t *recv_msg, nxt_unit_request_info_t **preq); static int nxt_unit_process_req_body(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg); static int nxt_unit_request_check_response_port(nxt_unit_request_info_t *req, @@ -904,7 +905,8 @@ nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd) static int -nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) +nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, + nxt_unit_request_info_t **preq) { int rc; pid_t pid; @@ -1040,7 +1042,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) break; case _NXT_PORT_MSG_REQ_HEADERS: - rc = nxt_unit_process_req_headers(ctx, &recv_msg); + rc = nxt_unit_process_req_headers(ctx, &recv_msg, preq); break; case _NXT_PORT_MSG_REQ_BODY: @@ -1213,7 +1215,8 @@ nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx) static int -nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) +nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg, + nxt_unit_request_info_t **preq) { int res; nxt_unit_impl_t *lib; @@ -1329,7 +1332,12 @@ nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) } } - lib->callbacks.request_handler(req); + if (preq == NULL) { + lib->callbacks.request_handler(req); + + } else { + *preq = req; + } } return NXT_UNIT_OK; @@ -2179,7 +2187,8 @@ nxt_unit_response_add_field(nxt_unit_request_info_t *req, resp = req->response; if (nxt_slow_path(resp->fields_count >= req->response_max_fields)) { - nxt_unit_req_warn(req, "add_field: too many response fields"); + nxt_unit_req_warn(req, "add_field: too many response fields (%d)", + (int) resp->fields_count); return NXT_UNIT_ERROR; } @@ -2356,6 +2365,8 @@ nxt_unit_response_buf_alloc(nxt_unit_request_info_t *req, uint32_t size) if (nxt_slow_path(rc != NXT_UNIT_OK)) { nxt_unit_mmap_buf_release(mmap_buf); + nxt_unit_req_alert(req, "response_buf_alloc: failed to get out buf"); + return NULL; } @@ -4537,7 +4548,7 @@ nxt_unit_run_once_impl(nxt_unit_ctx_t *ctx) return rc; } - rc = nxt_unit_process_msg(ctx, rbuf); + rc = nxt_unit_process_msg(ctx, rbuf, NULL); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { return NXT_UNIT_ERROR; } @@ -4686,7 +4697,7 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx) nxt_queue_each(rbuf, &pending_rbuf, nxt_unit_read_buf_t, link) { if (nxt_fast_path(rc != NXT_UNIT_ERROR)) { - rc = nxt_unit_process_msg(&ctx_impl->ctx, rbuf); + rc = nxt_unit_process_msg(&ctx_impl->ctx, rbuf, NULL); } else { nxt_unit_read_buf_release(ctx, rbuf); @@ -4793,7 +4804,7 @@ nxt_unit_run_ctx(nxt_unit_ctx_t *ctx) goto retry; } - rc = nxt_unit_process_msg(ctx, rbuf); + rc = nxt_unit_process_msg(ctx, rbuf, NULL); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { break; } @@ -4902,7 +4913,7 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) break; } - rc = nxt_unit_process_msg(ctx, rbuf); + rc = nxt_unit_process_msg(ctx, rbuf, NULL); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { break; } @@ -4921,6 +4932,46 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) } +nxt_unit_request_info_t * +nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx) +{ + int rc; + nxt_unit_impl_t *lib; + nxt_unit_read_buf_t *rbuf; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_request_info_t *req; + + nxt_unit_ctx_use(ctx); + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + req = NULL; + + if (nxt_slow_path(!ctx_impl->online)) { + goto done; + } + + rbuf = nxt_unit_read_buf_get(ctx); + if (nxt_slow_path(rbuf == NULL)) { + goto done; + } + + rc = nxt_unit_app_queue_recv(lib->shared_port, rbuf); + if (rc != NXT_UNIT_OK) { + goto done; + } + + (void) nxt_unit_process_msg(ctx, rbuf, &req); + +done: + + nxt_unit_ctx_release(ctx); + + return req; +} + + int nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx) { @@ -4977,7 +5028,7 @@ retry: return rc; } - rc = nxt_unit_process_msg(ctx, rbuf); + rc = nxt_unit_process_msg(ctx, rbuf, NULL); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { return NXT_UNIT_ERROR; } diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 90cba2a3..1e1a8dbe 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -213,6 +213,8 @@ int nxt_unit_run_ctx(nxt_unit_ctx_t *ctx); int nxt_unit_run_shared(nxt_unit_ctx_t *ctx); +nxt_unit_request_info_t *nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx); + int nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx); /* -- cgit From 300347a5cffa4187921384bdd02c5bb90875f9e5 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: making minor tweaks. Removing unnecessary context operations from shared queue processing loop. Initializing temporary queues only when required. --- src/nxt_unit.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 44525d04..e74c8370 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -4675,8 +4675,6 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx) nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_read_buf_t *rbuf; - nxt_queue_init(&pending_rbuf); - ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); pthread_mutex_lock(&ctx_impl->mutex); @@ -4687,6 +4685,8 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx) return NXT_UNIT_OK; } + nxt_queue_init(&pending_rbuf); + nxt_queue_add(&pending_rbuf, &ctx_impl->pending_rbuf); nxt_queue_init(&ctx_impl->pending_rbuf); @@ -4719,8 +4719,6 @@ nxt_unit_process_ready_req(nxt_unit_ctx_t *ctx) nxt_unit_request_info_t *req; nxt_unit_request_info_impl_t *req_impl; - nxt_queue_init(&ready_req); - ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); pthread_mutex_lock(&ctx_impl->mutex); @@ -4731,6 +4729,8 @@ nxt_unit_process_ready_req(nxt_unit_ctx_t *ctx) return; } + nxt_queue_init(&ready_req); + nxt_queue_add(&ready_req, &ctx_impl->ready_req); nxt_queue_init(&ctx_impl->ready_req); @@ -4917,13 +4917,6 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { break; } - - rc = nxt_unit_process_pending_rbuf(ctx); - if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { - break; - } - - nxt_unit_process_ready_req(ctx); } nxt_unit_ctx_release(ctx); -- cgit From 6c3c83561a97b91f18a771e0c582c5ed4013a9a6 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: closing active requests on quit. --- src/nodejs/unit-http/unit.cpp | 2 ++ src/nxt_unit.c | 37 +++++++++++++++++++++------- src/python/nxt_python_asgi.c | 15 ++++++++++- src/python/nxt_python_asgi.h | 1 + src/python/nxt_python_asgi_http.c | 52 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 96 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 1ee5b742..c5bca49a 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -307,6 +307,8 @@ Unit::close_handler(nxt_unit_request_info_t *req) } catch (exception &e) { nxt_unit_req_warn(req, "close_handler: %s", e.str); + nxt_unit_request_done(req, NXT_UNIT_ERROR); + return; } diff --git a/src/nxt_unit.c b/src/nxt_unit.c index e74c8370..69948954 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -1643,8 +1643,6 @@ nxt_unit_process_websocket(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) } if (recv_msg->last) { - req_impl->websocket = 0; - if (cb->close_handler) { nxt_unit_req_debug(req, "close_handler"); @@ -1737,8 +1735,6 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req) nxt_unit_request_hash_find(req->ctx, req_impl->stream, 1); } - req_impl->websocket = 0; - while (req_impl->outgoing_buf != NULL) { nxt_unit_mmap_buf_free(req_impl->outgoing_buf); } @@ -5708,9 +5704,12 @@ nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process) static void nxt_unit_quit(nxt_unit_ctx_t *ctx) { - nxt_port_msg_t msg; - nxt_unit_impl_t *lib; - nxt_unit_ctx_impl_t *ctx_impl; + nxt_port_msg_t msg; + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_callbacks_t *cb; + nxt_unit_request_info_t *req; + nxt_unit_request_info_impl_t *req_impl; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); @@ -5721,10 +5720,30 @@ nxt_unit_quit(nxt_unit_ctx_t *ctx) ctx_impl->online = 0; - if (lib->callbacks.quit != NULL) { - lib->callbacks.quit(ctx); + cb = &lib->callbacks; + + if (cb->quit != NULL) { + cb->quit(ctx); } + nxt_queue_each(req_impl, &ctx_impl->active_req, + nxt_unit_request_info_impl_t, link) + { + req = &req_impl->req; + + nxt_unit_req_warn(req, "active request on ctx quit"); + + if (cb->close_handler) { + nxt_unit_req_debug(req, "close_handler"); + + cb->close_handler(req); + + } else { + nxt_unit_request_done(req, NXT_UNIT_ERROR); + } + + } nxt_queue_loop; + if (ctx != &lib->main_ctx.ctx) { return; } diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index e11a3b6c..98aeedf4 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -25,6 +25,7 @@ static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx); static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req); +static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req); static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req); static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, @@ -179,7 +180,7 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 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; - init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler; + init->callbacks.close_handler = nxt_py_asgi_close_handler; init->callbacks.quit = nxt_py_asgi_quit; init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; init->callbacks.add_port = nxt_py_asgi_add_port; @@ -551,6 +552,18 @@ release_asgi: } +static void +nxt_py_asgi_close_handler(nxt_unit_request_info_t *req) +{ + if (req->request->websocket_handshake) { + nxt_py_asgi_websocket_close_handler(req); + + } else { + nxt_py_asgi_http_close_handler(req); + } +} + + static PyObject * nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) { diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h index c3c3e17a..37f2a099 100644 --- a/src/python/nxt_python_asgi.h +++ b/src/python/nxt_python_asgi.h @@ -58,6 +58,7 @@ int nxt_py_asgi_http_init(void); PyObject *nxt_py_asgi_http_create(nxt_unit_request_info_t *req); void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req); int nxt_py_asgi_http_drain(nxt_queue_link_t *lnk); +void nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req); int nxt_py_asgi_websocket_init(void); PyObject *nxt_py_asgi_websocket_create(nxt_unit_request_info_t *req); diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index a5034ea6..5ea8e0a5 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -24,6 +24,7 @@ typedef struct { uint64_t content_length; uint64_t bytes_sent; int complete; + int closed; PyObject *send_body; Py_ssize_t send_body_off; } nxt_py_asgi_http_t; @@ -94,6 +95,7 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req) http->content_length = -1; http->bytes_sent = 0; http->complete = 0; + http->closed = 0; http->send_body = NULL; http->send_body_off = 0; } @@ -115,7 +117,13 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) nxt_unit_req_debug(req, "asgi_http_receive"); - msg = nxt_py_asgi_http_read_msg(http); + if (nxt_slow_path(http->closed || nxt_unit_response_is_sent(req))) { + msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); + + } else { + msg = nxt_py_asgi_http_read_msg(http); + } + if (nxt_slow_path(msg == NULL)) { return NULL; } @@ -561,6 +569,48 @@ fail: } +void +nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) +{ + PyObject *msg, *future, *res; + nxt_py_asgi_http_t *http; + + http = req->data; + + nxt_unit_req_debug(req, "asgi_http_close_handler"); + + http->closed = 1; + + if (http->receive_future == NULL) { + return; + } + + msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); + if (nxt_slow_path(msg == NULL)) { + return; + } + + if (msg == Py_None) { + Py_DECREF(msg); + return; + } + + future = http->receive_future; + http->receive_future = NULL; + + res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(req, "'set_result' call failed"); + nxt_python_print_exception(); + } + + Py_XDECREF(res); + Py_DECREF(future); + + Py_DECREF(msg); +} + + static PyObject * nxt_py_asgi_http_done(PyObject *self, PyObject *future) { -- cgit From 66bb41e8bbe81d82a66f0d7188ad89963b4cd251 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Libunit: fixing read buffer allocations on exit. --- src/nxt_unit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 69948954..0790afc4 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -5029,12 +5029,12 @@ retry: nxt_unit_process_ready_req(ctx); - rbuf = nxt_unit_read_buf_get(ctx); - if (nxt_slow_path(rbuf == NULL)) { - return NXT_UNIT_ERROR; - } - if (ctx_impl->online) { + rbuf = nxt_unit_read_buf_get(ctx); + if (nxt_slow_path(rbuf == NULL)) { + return NXT_UNIT_ERROR; + } + goto retry; } -- cgit From 25219a7ece30a5b21ac0674c557137f91b4b47fe Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 18 Nov 2020 22:33:53 +0300 Subject: Python: improving ASGI http send message processing. --- src/python/nxt_python_asgi_http.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index 5ea8e0a5..d88c4b00 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -262,22 +262,23 @@ nxt_py_asgi_http_send(PyObject *self, PyObject *dict) nxt_unit_req_debug(http->req, "asgi_http_send type is '%.*s'", (int) type_len, type_str); - if (type_len == (Py_ssize_t) response_start.length - && memcmp(type_str, response_start.start, type_len) == 0) - { - return nxt_py_asgi_http_response_start(http, dict); - } + if (nxt_unit_response_is_init(http->req)) { + if (nxt_str_eq(&response_body, type_str, (size_t) type_len)) { + return nxt_py_asgi_http_response_body(http, dict); + } - if (type_len == (Py_ssize_t) response_body.length - && memcmp(type_str, response_body.start, type_len) == 0) - { - return nxt_py_asgi_http_response_body(http, dict); + return PyErr_Format(PyExc_RuntimeError, + "Expected ASGI message 'http.response.body', " + "but got '%U'", type); } - nxt_unit_req_error(http->req, "asgi_http_send: unexpected 'type': '%.*s'", - (int) type_len, type_str); + if (nxt_str_eq(&response_start, type_str, (size_t) type_len)) { + return nxt_py_asgi_http_response_start(http, dict); + } - return PyErr_Format(PyExc_AssertionError, "unexpected 'type': '%U'", type); + return PyErr_Format(PyExc_RuntimeError, + "Expected ASGI message 'http.response.start', " + "but got '%U'", type); } -- cgit From 2a381a82a6e1bc2bd5d2f43a08fce50a1994f2e8 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 19 Nov 2020 13:49:12 +0300 Subject: Libunit: fixing read buffer leakage. If shared queue is empty, allocated read buffer should be explicitly released. Found by Coverity (CID 363943). The issue was introduced in f5ba5973a0a3. --- src/nxt_unit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 0790afc4..097f50d6 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -4948,6 +4948,7 @@ nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx) rc = nxt_unit_app_queue_recv(lib->shared_port, rbuf); if (rc != NXT_UNIT_OK) { + nxt_unit_read_buf_release(ctx, rbuf); goto done; } -- cgit From e3af18834d7cc32734cba7532d8864bb343b416b Mon Sep 17 00:00:00 2001 From: Axel Duch Date: Tue, 17 Nov 2020 15:03:30 +0000 Subject: Router: matching regular expressions support. --- src/nxt_conf_validation.c | 35 +++++++++- src/nxt_http.h | 6 ++ src/nxt_http_route.c | 99 ++++++++++++++++++++++++---- src/nxt_pcre.c | 135 ++++++++++++++++++++++++++++++++++++++ src/nxt_pcre2.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_regex.h | 41 ++++++++++++ src/nxt_runtime.c | 4 +- 7 files changed, 463 insertions(+), 18 deletions(-) create mode 100644 src/nxt_pcre.c create mode 100644 src/nxt_pcre2.c create mode 100644 src/nxt_regex.h (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f4e7c358..acb2e3de 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -11,6 +11,7 @@ #include #include #include +#include typedef enum { @@ -1504,8 +1505,12 @@ static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { - nxt_str_t pattern; - nxt_uint_t i, first, last; + nxt_str_t pattern; + nxt_uint_t i, first, last; +#if (NXT_HAVE_REGEX) + nxt_regex_t *re; + nxt_regex_err_t err; +#endif if (nxt_conf_type(value) != NXT_CONF_STRING) { return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", " @@ -1519,6 +1524,32 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } first = (pattern.start[0] == '!'); + + if (first < pattern.length && pattern.start[first] == '~') { +#if (NXT_HAVE_REGEX) + pattern.start += first + 1; + pattern.length -= first + 1; + + re = nxt_regex_compile(vldt->pool, &pattern, &err); + if (nxt_slow_path(re == NULL)) { + if (err.offset < pattern.length) { + return nxt_conf_vldt_error(vldt, "Invalid regular expression: " + "%s at offset %d", + err.msg, err.offset); + } + + return nxt_conf_vldt_error(vldt, "Invalid regular expression: %s", + err.msg); + } + + return NXT_OK; +#else + return nxt_conf_vldt_error(vldt, "Unit is built without support of " + "regular expressions: \"--no-regex\" " + "./configure option was set."); +#endif + } + last = pattern.length - 1; for (i = first; i < last; i++) { diff --git a/src/nxt_http.h b/src/nxt_http.h index 08181520..1418be95 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -7,6 +7,8 @@ #ifndef _NXT_HTTP_H_INCLUDED_ #define _NXT_HTTP_H_INCLUDED_ +#include + typedef enum { NXT_HTTP_UNSET = -1, @@ -168,6 +170,10 @@ struct nxt_http_request_s { void *req_rpc_data; +#if (NXT_HAVE_REGEX) + nxt_regex_match_t *regex_match; +#endif + nxt_http_peer_t *peer; nxt_buf_t *last; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index ae91076a..9aaa708e 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -8,6 +8,7 @@ #include #include #include +#include typedef enum { @@ -76,12 +77,20 @@ typedef struct { typedef struct { + union { + nxt_array_t *pattern_slices; +#if (NXT_HAVE_REGEX) + nxt_regex_t *regex; +#endif + } u; uint32_t min_length; - nxt_array_t *pattern_slices; uint8_t case_sensitive; /* 1 bit */ uint8_t negative; /* 1 bit */ uint8_t any; /* 1 bit */ +#if (NXT_HAVE_REGEX) + uint8_t regex; /* 1 bit */ +#endif } nxt_http_route_pattern_t; @@ -1060,24 +1069,24 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_str_t test, tmp; nxt_int_t ret; nxt_array_t *slices; +#if (NXT_HAVE_REGEX) + nxt_regex_t *re; + nxt_regex_err_t err; +#endif nxt_http_route_pattern_type_t type; - nxt_http_route_pattern_slice_t *slice; type = NXT_HTTP_ROUTE_PATTERN_EXACT; nxt_conf_get_string(cv, &test); - slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t)); - if (nxt_slow_path(slices == NULL)) { - return NXT_ERROR; - } - - pattern->pattern_slices = slices; - + pattern->u.pattern_slices = NULL; pattern->negative = 0; pattern->any = 1; pattern->min_length = 0; +#if (NXT_HAVE_REGEX) + pattern->regex = 0; +#endif if (test.length != 0 && test.start[0] == '!') { test.start++; @@ -1087,6 +1096,41 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, pattern->any = 0; } + if (test.length > 0 && test.start[0] == '~') { +#if (NXT_HAVE_REGEX) + test.start++; + test.length--; + + re = nxt_regex_compile(mp, &test, &err); + if (nxt_slow_path(re == NULL)) { + if (err.offset < test.length) { + nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d", + &test, err.msg, (int) err.offset); + return NXT_ERROR; + } + + nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg); + + return NXT_ERROR; + } + + pattern->u.regex = re; + pattern->regex = 1; + + return NXT_OK; + +#else + return NXT_ERROR; +#endif + } + + slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t)); + if (nxt_slow_path(slices == NULL)) { + return NXT_ERROR; + } + + pattern->u.pattern_slices = slices; + if (test.length == 0) { slice = nxt_array_add(slices); if (nxt_slow_path(slice == NULL)) { @@ -1980,6 +2024,9 @@ nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule) } ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } if (ret == 0) { return ret; @@ -2155,7 +2202,7 @@ static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array) { - nxt_bool_t ret; + nxt_int_t ret; nxt_http_name_value_t *nv, *end; ret = 0; @@ -2171,6 +2218,10 @@ nxt_http_route_test_argument(nxt_http_request_t *r, { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + if (ret == 0) { break; } @@ -2189,7 +2240,7 @@ nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule) nxt_bool_t tls, https; nxt_http_route_pattern_slice_t *pattern_slice; - pattern_slice = rule->pattern[0].pattern_slices->elts; + pattern_slice = rule->pattern[0].u.pattern_slices->elts; https = (pattern_slice->length == nxt_length("https")); tls = (r->tls != NULL); @@ -2337,7 +2388,7 @@ static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array) { - nxt_bool_t ret; + nxt_int_t ret; nxt_http_name_value_t *nv, *end; ret = 0; @@ -2353,6 +2404,10 @@ nxt_http_route_test_cookie(nxt_http_request_t *r, { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + if (ret == 0) { break; } @@ -2378,6 +2433,9 @@ nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, while (pattern < end) { ret = nxt_http_route_pattern(r, pattern, start, length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } /* nxt_http_route_pattern() returns either 1 or 0. */ ret ^= pattern->negative; @@ -2403,11 +2461,26 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, nxt_array_t *pattern_slices; nxt_http_route_pattern_slice_t *pattern_slice; +#if (NXT_HAVE_REGEX) + if (pattern->regex) { + if (r->regex_match == NULL) { + r->regex_match = nxt_regex_match_create(r->mem_pool, 0); + if (nxt_slow_path(r->regex_match == NULL)) { + return NXT_ERROR; + } + } + + return nxt_regex_match(pattern->u.regex, start, length, r->regex_match); + } +#endif + if (length < pattern->min_length) { return 0; } - pattern_slices = pattern->pattern_slices; + nxt_assert(pattern->u.pattern_slices != NULL); + + pattern_slices = pattern->u.pattern_slices; pattern_slice = pattern_slices->elts; end = start + length; diff --git a/src/nxt_pcre.c b/src/nxt_pcre.c new file mode 100644 index 00000000..737fc7cf --- /dev/null +++ b/src/nxt_pcre.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include + + +struct nxt_regex_s { + pcre *code; + pcre_extra *extra; + nxt_str_t pattern; +}; + +struct nxt_regex_match_s { + int ovecsize; + int ovec[]; +}; + + +static void *nxt_pcre_malloc(size_t size); +static void nxt_pcre_free(void *p); + +static nxt_mp_t *nxt_pcre_mp; + + +nxt_regex_t * +nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err) +{ + int erroffset; + char *pattern; + void *saved_malloc, *saved_free; + nxt_regex_t *re; + + err->offset = source->length; + + re = nxt_mp_get(mp, sizeof(nxt_regex_t) + source->length + 1); + if (nxt_slow_path(re == NULL)) { + err->msg = "memory allocation failed"; + return NULL; + } + + pattern = nxt_pointer_to(re, sizeof(nxt_regex_t)); + + nxt_memcpy(pattern, source->start, source->length); + pattern[source->length] = '\0'; + + re->pattern.length = source->length; + re->pattern.start = (u_char *) pattern; + + saved_malloc = pcre_malloc; + saved_free = pcre_free; + + pcre_malloc = nxt_pcre_malloc; + pcre_free = nxt_pcre_free; + nxt_pcre_mp = mp; + + re->code = pcre_compile(pattern, 0, &err->msg, &erroffset, NULL); + if (nxt_fast_path(re->code != NULL)) { +#if 0 + re->extra = pcre_study(re->code, PCRE_STUDY_JIT_COMPILE, &err->msg); + if (nxt_slow_path(re->extra == NULL && err->msg != NULL)) { + nxt_log_warn(thr->log, "pcre_study(%V) failed: %s", source, err->msg); + } +#else + re->extra = NULL; +#endif + + } else { + err->offset = erroffset; + re = NULL; + } + + pcre_malloc = saved_malloc; + pcre_free = saved_free; + + return re; +} + + +static void* +nxt_pcre_malloc(size_t size) +{ + if (nxt_slow_path(nxt_pcre_mp == NULL)) { + nxt_thread_log_alert("pcre_malloc(%uz) called without memory pool", + size); + return NULL; + } + + nxt_thread_log_debug("pcre_malloc(%uz), pool %p", size, nxt_pcre_mp); + + return nxt_mp_get(nxt_pcre_mp, size); +} + + +static void +nxt_pcre_free(void *p) +{ +} + + +nxt_regex_match_t * +nxt_regex_match_create(nxt_mp_t *mp, size_t size) +{ + nxt_regex_match_t *match; + + match = nxt_mp_get(mp, sizeof(nxt_regex_match_t) + sizeof(int) * size); + if (nxt_fast_path(match != NULL)) { + match->ovecsize = size; + } + + return match; +} + + +nxt_int_t +nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length, + nxt_regex_match_t *match) +{ + int ret; + + ret = pcre_exec(re->code, re->extra, (const char *) subject, length, 0, 0, + match->ovec, match->ovecsize); + if (nxt_slow_path(ret < PCRE_ERROR_NOMATCH)) { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre_exec() failed: %d on \"%*s\" using \"%V\"", + ret, length, subject, &re->pattern); + + return NXT_ERROR; + } + + return (ret != PCRE_ERROR_NOMATCH); +} diff --git a/src/nxt_pcre2.c b/src/nxt_pcre2.c new file mode 100644 index 00000000..22c4d2d4 --- /dev/null +++ b/src/nxt_pcre2.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include + + +static void *nxt_pcre2_malloc(PCRE2_SIZE size, void *memory_data); +static void nxt_pcre2_free(void *p, void *memory_data); + + +struct nxt_regex_s { + pcre2_code *code; + nxt_str_t pattern; +}; + + +nxt_regex_t * +nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err) +{ + int errcode; + nxt_int_t ret; + PCRE2_SIZE erroffset; + nxt_regex_t *re; + pcre2_general_context *general_ctx; + pcre2_compile_context *compile_ctx; + + static const u_char alloc_error[] = "memory allocation failed"; + + general_ctx = pcre2_general_context_create(nxt_pcre2_malloc, + nxt_pcre2_free, mp); + if (nxt_slow_path(general_ctx == NULL)) { + goto alloc_fail; + } + + compile_ctx = pcre2_compile_context_create(general_ctx); + if (nxt_slow_path(compile_ctx == NULL)) { + goto alloc_fail; + } + + re = nxt_mp_get(mp, sizeof(nxt_regex_t)); + if (nxt_slow_path(re == NULL)) { + goto alloc_fail; + } + + if (nxt_slow_path(nxt_str_dup(mp, &re->pattern, source) == NULL)) { + goto alloc_fail; + } + + re->code = pcre2_compile((PCRE2_SPTR) source->start, source->length, 0, + &errcode, &erroffset, compile_ctx); + if (nxt_slow_path(re->code == NULL)) { + err->offset = erroffset; + + ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg, + ERR_BUF_SIZE); + if (ret < 0) { + (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE, + "compilation failed with unknown " + "error code: %d%Z", errcode); + } + + return NULL; + } + +#if 0 + errcode = pcre2_jit_compile(re, PCRE2_JIT_COMPLETE); + if (nxt_slow_path(errcode != 0 && errcode != PCRE2_ERROR_JIT_BADOPTION)) { + ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg, + ERR_BUF_SIZE); + if (ret < 0) { + (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE, + "JIT compilation failed with unknown " + "error code: %d%Z", errcode); + } + + return NULL; + } +#endif + + return re; + +alloc_fail: + + err->offset = source->length; + nxt_memcpy(err->msg, alloc_error, sizeof(alloc_error)); + + return NULL; +} + + +static void * +nxt_pcre2_malloc(PCRE2_SIZE size, void *mp) +{ + return nxt_mp_get(mp, size); +} + + +static void +nxt_pcre2_free(void *p, void *mp) +{ +} + + +nxt_regex_match_t * +nxt_regex_match_create(nxt_mp_t *mp, size_t size) +{ + nxt_regex_match_t *match; + pcre2_general_context *ctx; + + ctx = pcre2_general_context_create(nxt_pcre2_malloc, nxt_pcre2_free, mp); + if (nxt_slow_path(ctx == NULL)) { + nxt_thread_log_alert("pcre2_general_context_create() failed"); + return NULL; + } + + match = pcre2_match_data_create(size, ctx); + if (nxt_slow_path(match == NULL)) { + nxt_thread_log_alert("pcre2_match_data_create(%uz) failed", size); + return NULL; + } + + return match; +} + + +nxt_int_t +nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length, + nxt_regex_match_t *match) +{ + nxt_int_t ret; + PCRE2_UCHAR errptr[ERR_BUF_SIZE]; + + ret = pcre2_match(re->code, (PCRE2_SPTR) subject, length, 0, 0, match, + NULL); + + if (nxt_slow_path(ret < PCRE2_ERROR_NOMATCH)) { + + if (pcre2_get_error_message(ret, errptr, ERR_BUF_SIZE) < 0) { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre2_match() failed: %d on \"%*s\" " + "using \"%V\"", ret, subject, length, subject, + &re->pattern); + + } else { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre2_match() failed: %s (%d) on \"%*s\" " + "using \"%V\"", errptr, ret, length, subject, + &re->pattern); + } + + return NXT_ERROR; + } + + return (ret != PCRE2_ERROR_NOMATCH); +} diff --git a/src/nxt_regex.h b/src/nxt_regex.h new file mode 100644 index 00000000..a24c50f8 --- /dev/null +++ b/src/nxt_regex.h @@ -0,0 +1,41 @@ + +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_REGEX_H_INCLUDED_ +#define _NXT_REGEX_H_INCLUDED_ + +#if (NXT_HAVE_REGEX) + +typedef struct nxt_regex_s nxt_regex_t; + + #if (NXT_HAVE_PCRE2) +typedef void nxt_regex_match_t; +#else +typedef struct nxt_regex_match_s nxt_regex_match_t; +#endif + +typedef struct { + size_t offset; + +#if (NXT_HAVE_PCRE2) +#define ERR_BUF_SIZE 256 + u_char msg[ERR_BUF_SIZE]; +#else + const char *msg; +#endif +} nxt_regex_err_t; + + +NXT_EXPORT void nxt_regex_init(void); +NXT_EXPORT nxt_regex_t *nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, + nxt_regex_err_t *err); +NXT_EXPORT nxt_regex_match_t *nxt_regex_match_create(nxt_mp_t *mp, size_t size); +NXT_EXPORT nxt_int_t nxt_regex_match(nxt_regex_t *re, u_char *subject, + size_t length, nxt_regex_match_t *match); + +#endif /* NXT_HAVE_REGEX */ + +#endif /* _NXT_REGEX_H_INCLUDED_ */ diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 44970b34..d9d986da 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -10,6 +10,7 @@ #include #include #include +#include static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task, @@ -702,9 +703,6 @@ nxt_runtime_thread_pool_destroy(nxt_task_t *task, nxt_runtime_t *rt, static void nxt_runtime_thread_pool_init(void) { -#if (NXT_REGEX) - nxt_regex_init(0); -#endif } -- cgit