summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKonstantin Pavlov <thresh@nginx.com>2022-12-15 08:17:39 -0800
committerKonstantin Pavlov <thresh@nginx.com>2022-12-15 08:17:39 -0800
commite22669f2728814aba82da14702d18bfa9685311e (patch)
treec9c9471dab359e8e33fca24c5d4f035ab5b278db
parenta1d28488f9df8e28ee25ea438c275b96b9afe5b6 (diff)
parent4409a10ff0bd6bb45fb88716bd383cd867958a8a (diff)
downloadunit-e22669f2728814aba82da14702d18bfa9685311e.tar.gz
unit-e22669f2728814aba82da14702d18bfa9685311e.tar.bz2
Merged with the default branch.
-rw-r--r--.gitattributes2
-rw-r--r--.hgtags2
-rw-r--r--.mailmap2
-rw-r--r--CHANGES40
-rw-r--r--README.md14
-rw-r--r--SECURITY.txt30
-rw-r--r--auto/atomic8
-rw-r--r--auto/capability2
-rw-r--r--auto/cgroup22
-rw-r--r--auto/clang20
-rw-r--r--auto/events16
-rw-r--r--auto/files8
-rw-r--r--auto/help2
-rw-r--r--auto/isolation16
-rw-r--r--auto/make15
-rw-r--r--auto/malloc16
-rw-r--r--auto/mmap8
-rw-r--r--auto/modules/java8
-rw-r--r--auto/modules/java_jar.sha51226
-rw-r--r--auto/modules/perl4
-rw-r--r--auto/modules/php12
-rw-r--r--auto/modules/python4
-rw-r--r--auto/modules/ruby12
-rw-r--r--auto/njs49
-rw-r--r--auto/options4
-rw-r--r--auto/pcre2
-rw-r--r--auto/sendfile12
-rw-r--r--auto/shmem8
-rw-r--r--auto/sockets32
-rw-r--r--auto/sources13
-rw-r--r--auto/ssltls18
-rw-r--r--auto/summary3
-rw-r--r--auto/threads16
-rw-r--r--auto/time20
-rw-r--r--auto/types14
-rw-r--r--auto/unix18
-rwxr-xr-xconfigure5
-rw-r--r--docs/changes.xml141
-rw-r--r--pkg/Makefile4
-rw-r--r--pkg/contrib/Makefile140
-rw-r--r--pkg/contrib/src/njs/Makefile19
-rw-r--r--pkg/contrib/src/njs/SHA512SUMS1
-rw-r--r--pkg/contrib/src/njs/version1
-rw-r--r--pkg/contrib/tarballs/.hgignore3
-rw-r--r--pkg/deb/Makefile26
-rw-r--r--pkg/deb/Makefile.jsc1971
-rw-r--r--pkg/deb/debian.module/control.in2
-rw-r--r--pkg/deb/debian.module/unit.example-jsc19-config15
-rw-r--r--pkg/deb/debian/control.in4
-rw-r--r--pkg/deb/debian/rules.in9
-rw-r--r--pkg/docker/Dockerfile.go1.192
-rw-r--r--pkg/docker/Dockerfile.jsc112
-rw-r--r--pkg/docker/Dockerfile.minimal2
-rw-r--r--pkg/docker/Dockerfile.node18 (renamed from pkg/docker/Dockerfile.node16)6
-rw-r--r--pkg/docker/Dockerfile.perl5.362
-rw-r--r--pkg/docker/Dockerfile.php8.12
-rw-r--r--pkg/docker/Dockerfile.python3.11 (renamed from pkg/docker/Dockerfile.python3.10)6
-rw-r--r--pkg/docker/Dockerfile.ruby3.12
-rw-r--r--pkg/docker/Makefile4
-rwxr-xr-xpkg/docker/docker-entrypoint.sh26
-rw-r--r--pkg/rpm/Makefile27
-rw-r--r--pkg/rpm/Makefile.python31155
-rw-r--r--pkg/rpm/Makefile.python3853
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python311-config16
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config16
-rw-r--r--pkg/rpm/unit.module.spec.in2
-rw-r--r--pkg/rpm/unit.spec.in8
-rw-r--r--src/java/nxt_jni_Request.c20
-rw-r--r--src/nodejs/unit-http/loader.mjs6
-rw-r--r--src/nodejs/unit-http/unit.cpp3
-rw-r--r--src/nxt_application.c2
-rw-r--r--src/nxt_application.h2
-rw-r--r--src/nxt_capability.c2
-rw-r--r--src/nxt_cgroup.c174
-rw-r--r--src/nxt_cgroup.h14
-rw-r--r--src/nxt_conf.c6
-rw-r--r--src/nxt_conf.h2
-rw-r--r--src/nxt_conf_validation.c175
-rw-r--r--src/nxt_conn.h3
-rw-r--r--src/nxt_conn_close.c8
-rw-r--r--src/nxt_controller.c14
-rw-r--r--src/nxt_errno.c6
-rw-r--r--src/nxt_file.c37
-rw-r--r--src/nxt_file.h4
-rw-r--r--src/nxt_fs.c250
-rw-r--r--src/nxt_fs.h33
-rw-r--r--src/nxt_fs_mount.c230
-rw-r--r--src/nxt_fs_mount.h48
-rw-r--r--src/nxt_h1proto.c8
-rw-r--r--src/nxt_http.h7
-rw-r--r--src/nxt_http_js.c273
-rw-r--r--src/nxt_http_parse.c6
-rw-r--r--src/nxt_http_request.c13
-rw-r--r--src/nxt_http_return.c30
-rw-r--r--src/nxt_http_route.c66
-rw-r--r--src/nxt_http_route_addr.c16
-rw-r--r--src/nxt_http_static.c52
-rw-r--r--src/nxt_http_variables.c43
-rw-r--r--src/nxt_isolation.c50
-rw-r--r--src/nxt_js.c299
-rw-r--r--src/nxt_js.h38
-rw-r--r--src/nxt_main.h2
-rw-r--r--src/nxt_main_process.c16
-rw-r--r--src/nxt_openssl.c20
-rw-r--r--src/nxt_php_sapi.c112
-rw-r--r--src/nxt_port.c4
-rw-r--r--src/nxt_process.c12
-rw-r--r--src/nxt_process.h13
-rw-r--r--src/nxt_router.c23
-rw-r--r--src/nxt_router.h6
-rw-r--r--src/nxt_router_access_log.c27
-rw-r--r--src/nxt_runtime.c2
-rw-r--r--src/nxt_runtime.h3
-rw-r--r--src/nxt_sockaddr.c14
-rw-r--r--src/nxt_string.c4
-rw-r--r--src/nxt_string.h14
-rw-r--r--src/nxt_tstr.c317
-rw-r--r--src/nxt_tstr.h79
-rw-r--r--src/nxt_unit.c42
-rw-r--r--src/nxt_unit_request.h6
-rw-r--r--src/nxt_var.c223
-rw-r--r--src/nxt_var.h34
-rw-r--r--src/perl/nxt_perl_psgi.c4
-rw-r--r--src/python/nxt_python.c133
-rw-r--r--src/python/nxt_python.h2
-rw-r--r--src/python/nxt_python_asgi.c70
-rw-r--r--src/python/nxt_python_asgi_str.c2
-rw-r--r--src/python/nxt_python_wsgi.c83
-rw-r--r--src/ruby/nxt_ruby.c13
-rw-r--r--src/test/nxt_http_parse_test.c2
-rw-r--r--src/test/nxt_tests.c2
-rw-r--r--src/test/nxt_unit_app_test.c2
-rw-r--r--src/test/nxt_unit_websocket_chat.c2
-rw-r--r--src/test/nxt_unit_websocket_echo.c2
-rw-r--r--src/test/nxt_utf8_file_name_test.c2
-rw-r--r--src/test/nxt_utf8_test.c2
-rw-r--r--test/conftest.py28
-rw-r--r--test/python/prefix/asgi.py15
-rw-r--r--test/python/prefix/wsgi.py10
-rw-r--r--test/python/targets/asgi.py17
-rw-r--r--test/python/targets/wsgi.py9
-rw-r--r--test/test_access_log.py22
-rw-r--r--test/test_asgi_application.py43
-rw-r--r--test/test_asgi_targets.py45
-rw-r--r--test/test_configuration.py86
-rw-r--r--test/test_java_application.py20
-rw-r--r--test/test_njs.py90
-rw-r--r--test/test_perl_application.py3
-rw-r--r--test/test_php_application.py44
-rw-r--r--test/test_proxy.py25
-rw-r--r--test/test_python_application.py41
-rw-r--r--test/test_python_isolation.py136
-rw-r--r--test/test_python_targets.py52
-rw-r--r--test/test_reconfigure.py5
-rw-r--r--test/test_routing.py16
-rw-r--r--test/test_ruby_application.py3
-rw-r--r--test/test_settings.py68
-rw-r--r--test/test_static.py50
-rw-r--r--test/test_status.py99
-rw-r--r--test/test_upstreams_rr.py8
-rw-r--r--test/test_variables.py492
-rw-r--r--test/unit/applications/lang/go.py6
-rw-r--r--test/unit/applications/lang/java.py2
-rw-r--r--test/unit/applications/lang/python.py1
-rw-r--r--test/unit/applications/websockets.py3
-rw-r--r--test/unit/check/go.py4
-rw-r--r--test/unit/check/njs.py6
-rw-r--r--test/unit/check/regex.py9
-rw-r--r--test/unit/check/tls.py13
-rw-r--r--test/unit/http.py7
-rw-r--r--test/unit/utils.py21
-rw-r--r--tools/README.md91
-rwxr-xr-xtools/setup-unit1497
-rwxr-xr-xtools/unitc235
-rw-r--r--version4
175 files changed, 6357 insertions, 1373 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..45ec5156
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+*.c diff=cpp
+*.h diff=cpp
diff --git a/.hgtags b/.hgtags
index dbf6692b..49215628 100644
--- a/.hgtags
+++ b/.hgtags
@@ -63,4 +63,6 @@ aa207ced9712132040e6153ceccdaf04c112d02c 1.25.0-1
069c16dd4ed34d49584028b25f5cba4a4a2eded6 1.26.1-1
8a9055cbe4ffd450fac4d7a849c00e0db5485ad3 1.27.0
e7df1cfec95945265a33f7085fbd313b4dd207fd 1.27.0-1
+ea073fb3cb75abfb4be5dc12402de73e0c20da60 1.28.0
16e01c5fead470d8f630613b973a50a33fd0d0f2 1.28.0-1
+37cac7fec92e5656d8a03a8594ade131c3391f45 1.29.0
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 00000000..b96dcabc
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+<a.clayton@nginx.com> <andrew@digital-domain.net>
+<a.clayton@nginx.com> <a.clayton@f5.com>
diff --git a/CHANGES b/CHANGES
index 29cd3301..959fdd32 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,44 @@
+Changes with Unit 1.29.0 15 Dec 2022
+
+ *) Change: removed $uri auto-append for "share" when loading
+ configuration.
+
+ *) Change: prefer system crypto policy instead of hardcoding a default.
+
+ *) Feature: njs support with the basic syntax of JS template literals.
+
+ *) Feature: support per-application cgroups on Linux.
+
+ *) Feature: the $request_time variable contains the request processing
+ time.
+
+ *) Feature: "prefix" option in Python applications to set WSGI
+ "SCRIPT_NAME" and ASGI root-path variables.
+
+ *) Feature: compatibility with Python 3.11.
+
+ *) Feature: compatibility with OpenSSL 3.
+
+ *) Feature: compatibility with PHP 8.2.
+
+ *) Feature: compatibility with Node.js 19.0.
+
+ *) Feature: Ruby Rack v3 support.
+
+ *) Bugfix: fix error in connection statistics when using proxy.
+
+ *) Bugfix: fix HTTP cookie parsing when the value contains an equals
+ sign.
+
+ *) Bugfix: PHP directory URLs without a trailing '/' would give a 503
+ error (fixed with a 301 re-direct).
+
+ *) Bugfix: missing error checks in the C API.
+
+ *) Bugfix: report the regex status in configure summary.
+
+
Changes with Unit 1.28.0 13 Sep 2022
*) Change: increased the applications' startup timeout.
diff --git a/README.md b/README.md
index 039200d6..9ec7eca8 100644
--- a/README.md
+++ b/README.md
@@ -50,8 +50,9 @@ For a description of image tags, see the
### Amazon Linux, Fedora, RedHat
``` console
-$ curl -sL 'https://unit.nginx.org/_downloads/setup-unit.sh' | sudo -E bash
-# yum install unit
+$ wget https://raw.githubusercontent.com/nginx/unit/master/tools/setup-unit && chmod +x setup-unit
+# ./setup-unit repo-config && yum install unit
+# ./setup-unit welcome
```
For details and available language packages, see the
@@ -61,8 +62,9 @@ For details and available language packages, see the
### Debian, Ubuntu
``` console
-$ curl -sL 'https://unit.nginx.org/_downloads/setup-unit.sh' | sudo -E bash
-# apt install unit
+$ wget https://raw.githubusercontent.com/nginx/unit/master/tools/setup-unit && chmod +x setup-unit
+# ./setup-unit repo-config && apt install unit
+# ./setup-unit welcome
```
For details and available language packages, see the
@@ -91,7 +93,7 @@ application object. Let's store our first config snippet in a file called
Saving it as a file isn't necessary, but can come in handy with larger objects.
-Now, `PUT` it into the `config/applications` section of Unit's control API,
+Now, `PUT` it into the `/config/applications` section of Unit's control API,
usually available by default via a Unix domain socket:
``` console
@@ -105,7 +107,7 @@ usually available by default via a Unix domain socket:
}
```
-Next, reference the app from a listener object in the `config/listeners`
+Next, reference the app from a listener object in the `/config/listeners`
section of the API. This time, we pass the config snippet straight from the
command line:
diff --git a/SECURITY.txt b/SECURITY.txt
new file mode 100644
index 00000000..a705bef7
--- /dev/null
+++ b/SECURITY.txt
@@ -0,0 +1,30 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+#
+# Please report security issues as below, specifically
+# mentioning NGINX Unit in the subject.
+#
+Canonical: https://unit.nginx.org/.well-known/security.txt
+Contact: mailto:security-alert@nginx.org
+Encryption: https://nginx.org/keys/maxim.key
+Encryption: https://nginx.org/keys/sb.key
+Encryption: https://nginx.org/keys/thresh.key
+Expires: 2024-01-01T00:00:00.000Z
+Policy: https://www.first.org/cvss/v3.1/specification-document
+Preferred-Languages: en
+
+-----BEGIN PGP SIGNATURE-----
+
+iQHEBAEBCAAuFiEEE8gqY7YDV2FW4wpOoOqYG2aw2WcFAmN7X/UQHGsucGF2bG92
+QGY1LmNvbQAKCRCg6pgbZrDZZ4wFDADIZz5UwVUaxQ6mAfi+3Gs28NLXQp5kBILJ
+PC9Rhjlksufbby5yd4lh+JMZ8U2YRQ8OWne6Kl0NvZHDcP2OyBOdiBUXvnE+ZcNz
+ujT3JMk15l1FxKbIitUzwZ+QcXOKTqsoavPs5hrGrrJNQWLhqAH8uESDdI7AUM5R
+BOQ9Z6ENw3rgEtrtMNMdwt+pt2/+1cuu/4PuIuFhjYyCuS7i7tyFtbkc9BTlx03I
+99g9bqKltWAvxGrMi+xfFVOnTgWp0b+oKsN8jgQji1zNMSx7UmrFq8uSpNV3eR5t
+a4iVZQsIRUVVSYh8VkZagtbiw4WXaEnbwUgxj/4K2rNvkn5jFk+NkzSALN8/7ocp
+U5R5ctku511bJiwFSUkTx8nkd58bzqqQ0EHr/3uTmfXSTTZYdUXuXXCSMzuUBEOi
+y9n+2JFRdlEXxqwhszJxAXhs6VH2su0laX2UOMMnw6X2GFF3CU4PK0qoalWLSh47
+6aiL99zgqrq0IFibRTXCo1a3RPqiYB0=
+=GiB/
+-----END PGP SIGNATURE-----
diff --git a/auto/atomic b/auto/atomic
index 31259fc5..f99adf7e 100644
--- a/auto/atomic
+++ b/auto/atomic
@@ -10,7 +10,7 @@ nxt_feature_name=NXT_HAVE_GCC_ATOMIC
nxt_feature_run=yes
nxt_feature_incs=
nxt_feature_libs=
-nxt_feature_test="int main() {
+nxt_feature_test="int main(void) {
long n = 0;
if (!__sync_bool_compare_and_swap(&n, 0, 3))
@@ -44,7 +44,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <atomic.h>
- int main() {
+ int main(void) {
ulong_t n = 0;
if (atomic_cas_ulong(&n, 0, 3) != 0)
@@ -70,7 +70,7 @@ fi
if [ $nxt_found = no ]; then
if [ $NXT_64BIT = 1 ]; then
- nxt_feature_test="int main() {
+ nxt_feature_test="int main(void) {
long n = 0;
long o = 0;
@@ -87,7 +87,7 @@ if [ $nxt_found = no ]; then
return 0;
}"
else
- nxt_feature_test="int main() {
+ nxt_feature_test="int main(void) {
int n = 0;
int o = 0;
diff --git a/auto/capability b/auto/capability
index 48777665..c0410b34 100644
--- a/auto/capability
+++ b/auto/capability
@@ -10,7 +10,7 @@ nxt_feature_test="#include <linux/capability.h>
#include <unistd.h>
#include <sys/syscall.h>
- int main() {
+ int main(void) {
struct __user_cap_header_struct hdr;
hdr.version = _LINUX_CAPABILITY_VERSION;
syscall(SYS_capget, &hdr, 0);
diff --git a/auto/cgroup b/auto/cgroup
new file mode 100644
index 00000000..2262b2ef
--- /dev/null
+++ b/auto/cgroup
@@ -0,0 +1,22 @@
+# Copyright (C) Andrew Clayton
+# Copyright (C) F5, Inc.
+
+NXT_HAVE_CGROUP=NO
+
+if [ -f "/proc/mounts" ]; then
+ CGROUP=$(grep cgroup2 /proc/mounts | head -n 1 | cut -d " " -f 2)
+
+ if [ "$CGROUP" ]; then
+ NXT_HAVE_CGROUP=YES
+
+ cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef NXT_HAVE_CGROUP
+#define NXT_HAVE_CGROUP 1
+#define NXT_CGROUP_ROOT "$CGROUP"
+#endif
+
+END
+
+ fi
+fi
diff --git a/auto/clang b/auto/clang
index 1a05b5a3..975a6468 100644
--- a/auto/clang
+++ b/auto/clang
@@ -14,7 +14,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#define set(dummy, ...) sprintf(__VA_ARGS__)
- int main() {
+ int main(void) {
char buf[4];
buf[0] = '0';
@@ -37,7 +37,7 @@ if [ $nxt_found = no ]; then
nxt_feature_test="#include <stdio.h>
#define set(dummy, args...) sprintf(args)
- int main() {
+ int main(void) {
char buf[4];
buf[0] = '0';
@@ -70,7 +70,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_UNREACHABLE
nxt_feature_run=no
nxt_feature_incs=
nxt_feature_libs=
-nxt_feature_test="int main() {
+nxt_feature_test="int main(void) {
__builtin_unreachable();
}"
. auto/feature
@@ -81,7 +81,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_PREFETCH
nxt_feature_run=no
nxt_feature_incs=
nxt_feature_libs=
-nxt_feature_test="int main() {
+nxt_feature_test="int main(void) {
__builtin_prefetch(0);
return 0;
}"
@@ -93,7 +93,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_CLZ
nxt_feature_run=
nxt_feature_incs=
nxt_feature_libs=
-nxt_feature_test="int main() {
+nxt_feature_test="int main(void) {
if (__builtin_clz(1) == 31)
return 0;
return 1;
@@ -106,7 +106,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_POPCOUNT
nxt_feature_run=
nxt_feature_incs=
nxt_feature_libs=
-nxt_feature_test="int main() {
+nxt_feature_test="int main(void) {
if (__builtin_popcount(5) == 2)
return 0;
return 1;
@@ -121,7 +121,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="int n __attribute__ ((visibility(\"default\")));
- int main() {
+ int main(void) {
return 1;
}"
. auto/feature
@@ -134,7 +134,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="int n __attribute__ ((aligned(64)));
- int main() {
+ int main(void) {
return 1;
}"
. auto/feature
@@ -153,7 +153,7 @@ nxt_feature_test="#include <stdlib.h>
return malloc(1);
}
- int main() {
+ int main(void) {
if (f() != NULL) {
return 1;
}
@@ -172,7 +172,7 @@ nxt_feature_test="struct s {
int i;
} __attribute__ ((__packed__));
- int main() {
+ int main(void) {
return 1;
}"
. auto/feature
diff --git a/auto/events b/auto/events
index 700dc20c..1ca6e7cd 100644
--- a/auto/events
+++ b/auto/events
@@ -13,7 +13,7 @@ nxt_feature_libs=
nxt_feature_test="#include <sys/epoll.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
n = epoll_create(1);
@@ -34,7 +34,7 @@ if [ $nxt_found = yes ]; then
#include <sys/signalfd.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
sigset_t mask;
@@ -54,7 +54,7 @@ if [ $nxt_found = yes ]; then
nxt_feature_test="#include <sys/eventfd.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
n = eventfd(0, 0);
@@ -79,7 +79,7 @@ nxt_feature_test="#include <sys/types.h>
#include <sys/event.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
n = kqueue();
@@ -100,7 +100,7 @@ if [ $nxt_found = yes ]; then
#include <sys/types.h>
#include <sys/event.h>
- int main() {
+ int main(void) {
struct kevent kev;
kev.filter = EVFILT_USER;
@@ -124,7 +124,7 @@ nxt_feature_libs=
nxt_feature_test="#include <port.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
n = port_create();
@@ -152,7 +152,7 @@ nxt_feature_test="#include <fcntl.h>
#include <sys/devpoll.h>
#include <unistd.h>
- int main() {
+ int main(void) {
int n;
n = open(\"/dev/poll\", O_RDWR);
@@ -180,7 +180,7 @@ nxt_feature_test="#include <fcntl.h>
#include <sys/pollset.h>
#include <unistd.h>
- int main() {
+ int main(void) {
pollset_t n;
n = pollset_create(-1);
diff --git a/auto/files b/auto/files
index 591c5ee1..1fa6ca28 100644
--- a/auto/files
+++ b/auto/files
@@ -12,7 +12,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <fcntl.h>
- int main() {
+ int main(void) {
(void) posix_fadvise(0, 0, 0, POSIX_FADV_WILLNEED);
return 0;
}"
@@ -28,7 +28,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <fcntl.h>
- int main() {
+ int main(void) {
(void) fcntl(0, F_READAHEAD, 1024);
return 0;
}"
@@ -44,7 +44,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <fcntl.h>
- int main() {
+ int main(void) {
(void) fcntl(0, F_RDAHEAD, 1);
return 0;
}"
@@ -62,7 +62,7 @@ nxt_feature_test="#include <fcntl.h>
#include <linux/openat2.h>
#include <string.h>
- int main() {
+ int main(void) {
struct open_how how;
memset(&how, 0, sizeof(how));
diff --git a/auto/help b/auto/help
index e2b81bc7..a3884679 100644
--- a/auto/help
+++ b/auto/help
@@ -41,6 +41,8 @@ cat << END
--openssl enable OpenSSL library usage
+ --njs enable NJS library usage
+
--debug enable debug logging
diff --git a/auto/isolation b/auto/isolation
index 384f7ef1..cbf42d9d 100644
--- a/auto/isolation
+++ b/auto/isolation
@@ -20,7 +20,7 @@ nxt_feature_libs=
nxt_feature_test="#include <sys/wait.h>
#include <sys/syscall.h>
- int main() {
+ int main(void) {
return SYS_clone | SIGCHLD;
}"
. auto/feature
@@ -40,7 +40,7 @@ if [ $nxt_found = yes ]; then
#include <sys/syscall.h>
#include <sched.h>
- int main() {
+ int main(void) {
return CLONE_NEW$flag;
}"
. auto/feature
@@ -70,7 +70,7 @@ nxt_feature_test="#include <sys/syscall.h>
# error
#endif
- int main() {
+ int main(void) {
return SYS_pivot_root;
}"
. auto/feature
@@ -96,7 +96,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <sys/prctl.h>
- int main() {
+ int main(void) {
return PR_SET_NO_NEW_PRIVS;
}"
. auto/feature
@@ -109,7 +109,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <sys/mount.h>
- int main() {
+ int main(void) {
return mount(\"/\", \"/\", \"bind\",
MS_BIND | MS_REC, \"\");
}"
@@ -128,7 +128,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <sys/mount.h>
- int main() {
+ int main(void) {
return nmount((void *)0, 0, 0);
}"
. auto/feature
@@ -146,7 +146,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <sys/mount.h>
- int main() {
+ int main(void) {
return umount2((void *)0, 0);
}"
. auto/feature
@@ -163,7 +163,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <sys/mount.h>
- int main() {
+ int main(void) {
return unmount((void *)0, 0);
}"
. auto/feature
diff --git a/auto/make b/auto/make
index cb1d1f49..5324f49c 100644
--- a/auto/make
+++ b/auto/make
@@ -363,12 +363,15 @@ install-check:
exit 1)
${NXT_DAEMON}-install: $NXT_DAEMON install-check
- install -d \$(DESTDIR)$NXT_SBINDIR
+ test -d \$(DESTDIR)$NXT_SBINDIR \
+ || install -d \$(DESTDIR)$NXT_SBINDIR
install -p $NXT_BUILD_DIR/$NXT_DAEMON \$(DESTDIR)$NXT_SBINDIR/
- install -d \$(DESTDIR)$NXT_STATE
+ test -d \$(DESTDIR)$NXT_STATE \
+ || install -d \$(DESTDIR)$NXT_STATE
manpage-install: manpage install-check
- install -d \$(DESTDIR)$NXT_MANDIR/man8
+ test -d \$(DESTDIR)$NXT_MANDIR/man8 \
+ || install -d \$(DESTDIR)$NXT_MANDIR/man8
install -p -m644 $NXT_BUILD_DIR/unitd.8 \$(DESTDIR)$NXT_MANDIR/man8/
.PHONY: uninstall ${NXT_DAEMON}-uninstall manpage-uninstall
@@ -390,10 +393,12 @@ cat << END >> $NXT_MAKEFILE
.PHONY: libunit-install libunit-uninstall
libunit-install: $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
- install -d \$(DESTDIR)$NXT_LIBDIR
+ test -d \$(DESTDIR)$NXT_LIBDIR \
+ || install -d \$(DESTDIR)$NXT_LIBDIR
install -p -m u=rw,go=r $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \
\$(DESTDIR)$NXT_LIBDIR/
- install -d \$(DESTDIR)$NXT_INCDIR
+ test -d \$(DESTDIR)$NXT_INCDIR \
+ || install -d \$(DESTDIR)$NXT_INCDIR
install -p -m u=rw,go=r src/nxt_unit.h \
src/nxt_unit_field.h \
src/nxt_unit_request.h \
diff --git a/auto/malloc b/auto/malloc
index 06a16b54..cf6fc59e 100644
--- a/auto/malloc
+++ b/auto/malloc
@@ -13,7 +13,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
- int main() {
+ int main(void) {
void *p;
if (posix_memalign(&p, 4096, 4096) != 0)
@@ -36,7 +36,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
- int main() {
+ int main(void) {
void *p;
p = memalign(4096, 4096);
@@ -59,7 +59,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <malloc.h>
- int main() {
+ int main(void) {
void *p;
p = malloc(4096);
@@ -82,7 +82,7 @@ if [ $nxt_found = no ]; then
nxt_feature_test="#include <stdlib.h>
#include <malloc_np.h>
- int main() {
+ int main(void) {
void *p;
p = malloc(4096);
@@ -105,7 +105,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <malloc/malloc.h>
- int main() {
+ int main(void) {
if (malloc_good_size(4096) < 4096)
return 1;
return 0;
@@ -125,7 +125,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
- int main() {
+ int main(void) {
void *p;
p = alloca(256);
@@ -147,7 +147,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=
nxt_feature_test="#include <alloca.h>
- int main() {
+ int main(void) {
void *p;
p = alloca(256);
@@ -168,7 +168,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <malloc.h>
- int main() {
+ int main(void) {
mallopt(M_PERTURB, 0x55);
return 0;
}"
diff --git a/auto/mmap b/auto/mmap
index 8ecdf670..e8ffac78 100644
--- a/auto/mmap
+++ b/auto/mmap
@@ -13,7 +13,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <sys/mman.h>
- int main() {
+ int main(void) {
if (mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0)
== MAP_FAILED)
@@ -35,7 +35,7 @@ if [ $nxt_found = no ]; then
nxt_feature_test="#include <stdlib.h>
#include <sys/mman.h>
- int main() {
+ int main(void) {
if (mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
== MAP_FAILED)
@@ -56,7 +56,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <sys/mman.h>
- int main() {
+ int main(void) {
if (mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, -1, 0)
== MAP_FAILED)
@@ -76,7 +76,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <sys/mman.h>
- int main() {
+ int main(void) {
if (mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_PREFAULT_READ,
-1, 0)
diff --git a/auto/modules/java b/auto/modules/java
index e8137217..bdf17022 100644
--- a/auto/modules/java
+++ b/auto/modules/java
@@ -191,7 +191,7 @@ nxt_feature_libs="${NXT_JAVA_LDFLAGS}"
nxt_feature_test="
#include <jni.h>
- int main() {
+ int main(void) {
JNI_CreateJavaVM(NULL, NULL, NULL);
return 0;
}"
@@ -238,7 +238,7 @@ cat << END > $NXT_JAVA_JARS
static const char *nxt_java_system_jars[] = {
END
-NXT_TOMCAT_VERSION=9.0.52
+NXT_TOMCAT_VERSION=9.0.70
NXT_JAR_VERSION=$NXT_TOMCAT_VERSION
@@ -284,7 +284,7 @@ static const char *nxt_java_unit_jars[] = {
"$NXT_UNIT_JAR",
END
-NXT_JAR_VERSION=9.4.43.v20210629
+NXT_JAR_VERSION=9.4.49.v20220914
NXT_JAR_NAMESPACE=org/eclipse/jetty/
NXT_JAR_NAME=jetty-util
@@ -297,7 +297,7 @@ NXT_JAR_NAME=jetty-http
. auto/modules/java_get_jar
NXT_JAR_NAME=classgraph
-NXT_JAR_VERSION=4.8.112
+NXT_JAR_VERSION=4.8.151
NXT_JAR_NAMESPACE=io/github/classgraph/
. auto/modules/java_get_jar
diff --git a/auto/modules/java_jar.sha512 b/auto/modules/java_jar.sha512
index 5289081c..d3e9016c 100644
--- a/auto/modules/java_jar.sha512
+++ b/auto/modules/java_jar.sha512
@@ -1,14 +1,14 @@
-e7ad5ee436f6befaddcdd1046022a2e30444a435d9419a33f8316f66e794cf710809dbcf7e408a087f434cd9cb724682965b5e4098e34803569241eb44288322 classgraph-4.8.112.jar
+4b47eabc83f3f672a7e91af6ae97bbdbc6f01ed7149540cb06b0f530f45a95d025cc7807a6640982d23d2da50bd973ad788a4c8fdfa025da7cf93c560abbe61e classgraph-4.8.151.jar
ab441acf5551a7dc81c353eaccb3b3df9e89a48987294d19e39acdb83a5b640fcdff7414cee29f5b96eaa8826647f1d5323e185018fe33a64c402d69c73c9158 ecj-3.26.0.jar
-a3ce1a5a41c9791ece4cbbf049ec4add1ec41330743d6757daea520f8b329299f5dd274f9e5741ba41fe49510f203efd19540d2404390eca53421132f5f46d4b jetty-http-9.4.43.v20210629.jar
-61a14e97baac9962bd68ece24f8b967eec8e32edfebfa27c6a13996a86650d82f8977bf1aa582fc9706a1b028cb3cec0057c97628235dfc130061939845229e6 jetty-server-9.4.43.v20210629.jar
-304fcdba2bdbf37e8f2ea69a3f5fbdffdfefd98d80fa78883b1dca1129a4907cef63eb2fa7c83eef359022a3b6a2f3ff742d8d23075c83d049ac01f1402e97f8 jetty-util-9.4.43.v20210629.jar
-f14ac948559c0b6e20f6d84e5177fea46ea1321a7a401f280ee9323f3a07e70e141d2b12c8c466c446efb55a58743af931b0584f56494f17311cab511bcd214a tomcat-api-9.0.52.jar
-a5ca293732267854a296ccc79b25051acf76fa8dea6118d43aa2e95b6d1951dfaffb941430b5080d7ab62d82d2c82c9385baf99e3c4a2bb6bf4a04372149169d tomcat-el-api-9.0.52.jar
-8660e11dd0f994de43b19ba251a789dc3194a6b82d674085fed510200c789b402b27ab97bcecfec0720f563bb0dd18c2631cd8bb5c35e604c1460d7357492123 tomcat-jasper-9.0.52.jar
-5b0b3e0edb47e3c4269736542d66d6fc099a74965fdcd5d3a6382db3f75bec7329e81f0719aaafccd318a058ec8fbba113a6ae9234ca94a00c8c39e5c8885568 tomcat-jasper-el-9.0.52.jar
-a4368d1073d79a4f8a1cb8967a5e39af87723a17b2d94466554e8e4d3e8bb2dec3ee37db9f606e0c775dd4028604d4e87921f0dda764c8ef138aa50acf03d549 tomcat-jsp-api-9.0.52.jar
-8687e108489996226a83e8310c73a2a176fac9ce365a7bd3fc23955fe968c3858a24c047cb5c7fbd1f3a890c893dcdf55e88180eefe61b98c1a3bf4e316fb07e tomcat-juli-9.0.52.jar
-f9929f433e2b2f93897a87d117af2519e44020b44e3a475dfc81662b08d08e010b14a3dd6df2d4800196cdba7cbb8db2b879341c5a0ef1d11e5abe63d872bc34 tomcat-servlet-api-9.0.52.jar
-c21ccf969378f2cad0ead32451c2527ea944207b5a894b642ee554042fe87eb0ce647aacbf8a51d12b4ecf2bf13e9380da78d8f7792486909daba72e8d0f83f2 tomcat-util-9.0.52.jar
-14b4eb31c124d22c7ea7f05808cd6a46076f9d72648afd76e2d11924874117266771a455686d704225d2eff94656f024293140a3259b108857fa6b8b218ddd63 tomcat-util-scan-9.0.52.jar
+82c6985f0d7c76459bf0638fdc24f3692a11804a95845b1a45203dfcc1205ab7bf67934f6babf7eb2f2b87d637a8fcbd87eae297e4403511bf73f359b1957e09 jetty-http-9.4.49.v20220914.jar
+2f199729ad9b46fda968b4bfafd657971fc9d90371852f8ad7afdae6d5752d2b84648734eabb6ffbf084800253d1da97d4bb9ad60f799ee6ae38a80c2d881fc4 jetty-server-9.4.49.v20220914.jar
+e207d93ef5bc98ad2b1a43393231bdacfb3ab642b6197a8b72d819f8ad30357c4daa0a76a0459340563fcdee0fdfc111e719a2db5be778d6b1e10f1ccbe77fc9 jetty-util-9.4.49.v20220914.jar
+a2cd93ccaa58191475df9aa40a11c8b3f14f77e78b6b2dc9e5fbebf07297e318d60c5cc5aca37e61bd748456b01491a0e6702b9e4d3ec3ef43d9b1a93f9b733e tomcat-api-9.0.70.jar
+4b2b33f6bdcb3fbff6de7da6f7558e4a21335c5c08dbc2adba1be90ddcaa4be1ba053d9021a4891edef975759a562b46a58da6c5acc2209ae8b942e4058b7022 tomcat-el-api-9.0.70.jar
+7ee837f218220022bf2543e4b3191c0a948c7f8bbd4f2e7202cc29196e5f4a8264aee027bc3521b79775b1ab0b3f8a4bef8982be9c0b2c5f95b77f36d5e5930f tomcat-jasper-9.0.70.jar
+f92cdddd3aae8d1b0b861afc67344fc6544c413d78e2e810f804632e68a3667b2b1929ac4995b582af03774ad024632e820143cd53273e06a796484ce2f0a73e tomcat-jasper-el-9.0.70.jar
+5ec6985740e7a5873f56430b1f0fd6e55a625fac8f5618d846072117f5ed8ccc69665fd6ebde40381099cf42ab9525f5da3cd16dd0b50a267734bfdf7f2e168d tomcat-jsp-api-9.0.70.jar
+33cf08f10bad572c9e7085b3ba8e91b38a293f8838a39483b01d07d9c1b9d0e67492343e0523da24af47782ec4a5d639db49679d951ccbe1da9d1309346cc693 tomcat-juli-9.0.70.jar
+0c8ee46dc49828720cd431e4e6bcb2a9d7409b3bae3d3427640b159985a27de22181151c8fa15a1f44f607730977c4ae2512c63a19c070b92e38438ad0ba8138 tomcat-servlet-api-9.0.70.jar
+e882c47acdb9e5612a0810503cb8900570b68aec5dd33dd6439884b15723a67cbf982c9cf546e7cd6d67b731df3d64ec5347500ab8a987d7cb1e11a74f819325 tomcat-util-9.0.70.jar
+0a562e8a40e406966ae2be5587dcad0ceae3143b03ef9b9f7dd77c6a2db522c31ed82b9c38b4464f9f80c1d8ca418ce6a09f9fecb3e0209a962da01e2f9bd626 tomcat-util-scan-9.0.70.jar
diff --git a/auto/modules/perl b/auto/modules/perl
index e9d7e109..2daebd0d 100644
--- a/auto/modules/perl
+++ b/auto/modules/perl
@@ -79,7 +79,7 @@ if /bin/sh -c "$NXT_PERL -MConfig -e 'print \"Perl version: \",
static PerlInterpreter *my_perl;
- int main() {
+ int main(void) {
char argv[] = \"\\0-e\\00\";
char *embedding[] = { &argv[0], &argv[1], &argv[4] };
@@ -124,7 +124,7 @@ nxt_feature_test="
#include <EXTERN.h>
#include <perl.h>
- int main() {
+ int main(void) {
printf(\"%s\", PERL_VERSION_STRING);
return 0;
}"
diff --git a/auto/modules/php b/auto/modules/php
index e92a67cd..f0ecb709 100644
--- a/auto/modules/php
+++ b/auto/modules/php
@@ -131,7 +131,7 @@ nxt_feature_libs="${NXT_PHP_LIB} ${NXT_PHP_LDFLAGS}"
nxt_feature_test="
#include <php.h>
- int main() {
+ int main(void) {
printf(\"%s\", PHP_VERSION);
return 0;
}"
@@ -148,8 +148,12 @@ nxt_feature_test="
#include <php.h>
#include <php_main.h>
- int main() {
+ int main(void) {
+ #if (PHP_VERSION_ID < 80200)
php_module_startup(NULL, NULL, 0);
+ #else
+ php_module_startup(NULL, NULL);
+ #endif
return 0;
}"
@@ -172,7 +176,7 @@ nxt_feature_test="
#include <php.h>
#include <php_main.h>
- int main() {
+ int main(void) {
#ifndef ZTS
#error ZTS is not defined.
#endif
@@ -193,7 +197,7 @@ nxt_feature_test="
#include <php.h>
#include <php_main.h>
- int main() {
+ int main(void) {
zend_signal_startup();
return 0;
}"
diff --git a/auto/modules/python b/auto/modules/python
index 9be6b370..480ae1da 100644
--- a/auto/modules/python
+++ b/auto/modules/python
@@ -86,7 +86,7 @@ if /bin/sh -c "$NXT_PYTHON_CONFIG --prefix" >> $NXT_AUTOCONF_ERR 2>&1; then
nxt_feature_test="
#include <Python.h>
- int main() {
+ int main(void) {
Py_Initialize();
return 0;
}"
@@ -114,7 +114,7 @@ nxt_feature_test="
#include <Python.h>
#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%s\", PY_VERSION);
return 0;
}"
diff --git a/auto/modules/ruby b/auto/modules/ruby
index dbedfd72..608193a6 100644
--- a/auto/modules/ruby
+++ b/auto/modules/ruby
@@ -101,12 +101,12 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then
nxt_feature="Ruby library"
nxt_feature_name=""
nxt_feature_run=value
- nxt_feature_incs="${NXT_RUBY_INCPATH}"
+ nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}"
nxt_feature_libs="${NXT_RUBY_LIBS}"
nxt_feature_test="
#include <ruby.h>
- int main() {
+ int main(void) {
static const char *argv[3] = {
\"NGINX_Unit\", \"-rrbconfig\",
\"-eprint RbConfig::CONFIG['libdir']\"
@@ -125,12 +125,12 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then
nxt_feature="Ruby library in $NXT_RUBY_LIBPATH"
nxt_feature_name=""
nxt_feature_run=no
- nxt_feature_incs="${NXT_RUBY_INCPATH}"
+ nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}"
nxt_feature_libs="${NXT_RUBY_LIBS}"
nxt_feature_test="
#include <ruby.h>
- int main() {
+ int main(void) {
ruby_init();
return ruby_cleanup(0);
}"
@@ -153,13 +153,13 @@ fi
nxt_feature="Ruby version"
nxt_feature_name=""
nxt_feature_run=value
-nxt_feature_incs="${NXT_RUBY_INCPATH}"
+nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}"
nxt_feature_libs="${NXT_RUBY_LIBS}"
nxt_feature_test="
#include <ruby.h>
#include <ruby/version.h>
- int main() {
+ int main(void) {
printf(\"%s\", ruby_version);
return 0;
}"
diff --git a/auto/njs b/auto/njs
new file mode 100644
index 00000000..c0c43f19
--- /dev/null
+++ b/auto/njs
@@ -0,0 +1,49 @@
+
+# Copyright (C) NGINX, Inc.
+
+
+nxt_found=no
+NXT_HAVE_NJS=NO
+
+if /bin/sh -c "(pkg-config njs --exists)" >> $NXT_AUTOCONF_ERR 2>&1;
+then
+ NXT_NJS_AUX_CFLAGS=
+ NXT_NJS_AUX_LIBS=
+ NXT_NJS_CFLAGS=`pkg-config njs --cflags`
+ NXT_NJS_LIBS=`pkg-config njs --libs`
+else
+ NXT_NJS_AUX_CFLAGS=
+ NXT_NJS_AUX_LIBS="$NXT_LIBM $NXT_LIB_AUX_LIBS"
+ NXT_NJS_CFLAGS=
+ NXT_NJS_LIBS="-lnjs"
+fi
+
+nxt_feature="NJS"
+nxt_feature_name=NXT_HAVE_NJS
+nxt_feature_run=no
+nxt_feature_incs="$NXT_NJS_CFLAGS $NXT_NJS_AUX_CFLAGS"
+nxt_feature_libs="$NXT_NJS_LIBS $NXT_NJS_AUX_LIBS"
+nxt_feature_test="#include <njs.h>
+
+ int main(void) {
+ njs_vm_t *vm;
+ njs_vm_opt_t opts;
+
+ njs_vm_opt_init(&opts);
+
+ vm = njs_vm_create(&opts);
+ if (vm == NULL)
+ return 1;
+ return 0;
+ }"
+. auto/feature
+
+if [ $nxt_found = no ]; then
+ $echo
+ $echo $0: error: no NJS library found.
+ $echo
+ exit 1;
+fi
+
+NXT_LIB_AUX_CFLAGS="$NXT_LIB_AUX_CFLAGS $NXT_NJS_CFLAGS"
+NXT_LIB_AUX_LIBS="$NXT_NJS_LIBS $NXT_LIB_AUX_LIBS"
diff --git a/auto/options b/auto/options
index 572d8a9b..abcf531d 100644
--- a/auto/options
+++ b/auto/options
@@ -28,6 +28,8 @@ NXT_GNUTLS=NO
NXT_CYASSL=NO
NXT_POLARSSL=NO
+NXT_NJS=NO
+
NXT_TEST_BUILD_EPOLL=NO
NXT_TEST_BUILD_EVENTPORT=NO
NXT_TEST_BUILD_DEVPOLL=NO
@@ -85,6 +87,8 @@ do
--cyassl) NXT_CYASSL=YES ;;
--polarssl) NXT_POLARSSL=YES ;;
+ --njs) NXT_NJS=YES ;;
+
--test-build-epoll) NXT_TEST_BUILD_EPOLL=YES ;;
--test-build-eventport) NXT_TEST_BUILD_EVENTPORT=YES ;;
--test-build-devpoll) NXT_TEST_BUILD_DEVPOLL=YES ;;
diff --git a/auto/pcre b/auto/pcre
index 955e4baf..27205118 100644
--- a/auto/pcre
+++ b/auto/pcre
@@ -50,7 +50,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=$NXT_PCRE_LIB
nxt_feature_test="#include <pcre.h>
- int main() {
+ int main(void) {
pcre *re;
re = pcre_compile(NULL, 0, NULL, 0, NULL);
diff --git a/auto/sendfile b/auto/sendfile
index 1c20db06..abbda6c7 100644
--- a/auto/sendfile
+++ b/auto/sendfile
@@ -17,7 +17,7 @@ nxt_feature="Linux sendfile()"
nxt_feature_name=NXT_HAVE_LINUX_SENDFILE
nxt_feature_test="#include <sys/sendfile.h>
- int main() {
+ int main(void) {
off_t offset;
sendfile(-1, -1, &offset, 0);
@@ -43,7 +43,7 @@ if [ $nxt_found = no ]; then
#include <sys/uio.h>
#include <stdlib.h>
- int main() {
+ int main(void) {
off_t sent;
sendfile(-1, -1, 0, 0, NULL, &sent, 0);
@@ -69,7 +69,7 @@ if [ $nxt_found = no ]; then
#include <sys/uio.h>
#include <stdlib.h>
- int main() {
+ int main(void) {
off_t sent;
sendfile(-1, -1, 0, &sent, NULL, 0);
@@ -100,7 +100,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs="-lsendfile"
nxt_feature_test="#include <sys/sendfile.h>
- int main() {
+ int main(void) {
size_t sent;
struct sendfilevec vec;
@@ -124,7 +124,7 @@ if [ $nxt_found = no ]; then
nxt_feature_name=NXT_HAVE_AIX_SEND_FILE
nxt_feature_test="#include <sys/socket.h>
- int main() {
+ int main(void) {
int s;
struct sf_parms sf_iobuf;
@@ -152,7 +152,7 @@ if [ $nxt_found = no ]; then
sbsize_t sendfile(int s, int fd, off_t offset,
bsize_t nbytes, const struct iovec *hdtrl, int flags);
- int main() {
+ int main(void) {
sendfile(-1, -1, 0, 0, NULL, 0);
return 0;
}"
diff --git a/auto/shmem b/auto/shmem
index bfe0ee4a..c434a58f 100644
--- a/auto/shmem
+++ b/auto/shmem
@@ -18,7 +18,7 @@ nxt_feature_test="#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
- int main() {
+ int main(void) {
int ret;
static char name[] = \"/unit.configure\";
@@ -62,7 +62,7 @@ if [ $nxt_found = no ]; then
#include <sys/stat.h>
#include <sys/types.h>
- int main() {
+ int main(void) {
static char name[] = \"/tmp/unit.configure\";
shm_unlink(name);
@@ -94,7 +94,7 @@ nxt_feature_test="#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
- int main() {
+ int main(void) {
int fd = shm_open(SHM_ANON, O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1)
return 1;
@@ -119,7 +119,7 @@ nxt_feature_test="#include <linux/memfd.h>
#include <unistd.h>
#include <sys/syscall.h>
- int main() {
+ int main(void) {
static char name[] = \"/unit.configure\";
int fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC);
diff --git a/auto/sockets b/auto/sockets
index e344a3db..241b88eb 100644
--- a/auto/sockets
+++ b/auto/sockets
@@ -15,7 +15,7 @@ if [ $NXT_INET6 = YES ]; then
#include <sys/types.h>
#include <netinet/in.h>
- int main() {
+ int main(void) {
struct sockaddr_in6 sin6;
sin6.sin6_family = AF_INET6;
@@ -36,7 +36,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
struct sockaddr sa;
sa.sa_len = 0;
@@ -54,7 +54,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(struct sockaddr));
return 0;
}"
@@ -70,7 +70,7 @@ nxt_feature_test="#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(struct sockaddr_in));
return 0;
}"
@@ -86,7 +86,7 @@ nxt_feature_test="#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(struct sockaddr_in6));
return 0;
}"
@@ -102,7 +102,7 @@ nxt_feature_test="#include <stdio.h>
#include <sys/types.h>
#include <sys/un.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(struct sockaddr_un));
return 0;
}"
@@ -117,7 +117,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(struct sockaddr_storage));
return 0;
}"
@@ -132,7 +132,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
int pair[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) != 0)
@@ -150,7 +150,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
struct msghdr msg;
printf(\"%d\", (int) sizeof(msg.msg_control));
@@ -175,7 +175,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then
nxt_feature_test="#define _GNU_SOURCE
#include <sys/socket.h>
- int main() {
+ int main(void) {
return SO_PASSCRED == 0;
}"
. auto/feature
@@ -191,7 +191,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then
#include <sys/socket.h>
#include <sys/un.h>
- int main() {
+ int main(void) {
return sizeof(struct ucred);
}"
. auto/feature
@@ -206,7 +206,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then
nxt_feature_test="#define _GNU_SOURCE
#include <sys/socket.h>
- int main() {
+ int main(void) {
return sizeof(struct cmsgcred);
}"
. auto/feature
@@ -220,7 +220,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <sys/filio.h>
- int main() {
+ int main(void) {
return 0;
}"
. auto/feature
@@ -235,7 +235,7 @@ nxt_feature_test="#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
- int main() {
+ int main(void) {
int nb;
nb = 0;
@@ -255,7 +255,7 @@ nxt_feature_libs=
nxt_feature_test="#define _GNU_SOURCE
#include <sys/socket.h>
- int main() {
+ int main(void) {
socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
return 0;
}"
@@ -273,7 +273,7 @@ nxt_feature_test="#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/socket.h>
- int main() {
+ int main(void) {
accept4(0, NULL, NULL, SOCK_NONBLOCK);
return 0;
}"
diff --git a/auto/sources b/auto/sources
index 8548f812..29f3c7b5 100644
--- a/auto/sources
+++ b/auto/sources
@@ -34,6 +34,7 @@ NXT_LIB_SRCS=" \
src/nxt_parse.c \
src/nxt_sprintf.c \
src/nxt_var.c \
+ src/nxt_tstr.c \
src/nxt_file_name.c \
src/nxt_log.c \
src/nxt_djb_hash.c \
@@ -104,6 +105,7 @@ NXT_LIB_SRCS=" \
src/nxt_websocket_accept.c \
src/nxt_http_websocket.c \
src/nxt_h1proto_websocket.c \
+ src/nxt_fs.c \
"
NXT_LIB_SRC0=" \
@@ -133,6 +135,10 @@ NXT_LIB_POLARSSL_SRCS="src/nxt_polarssl.c"
NXT_LIB_PCRE_SRCS="src/nxt_pcre.c"
NXT_LIB_PCRE2_SRCS="src/nxt_pcre2.c"
+if [ "$NXT_NJS" != "NO" ]; then
+ NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c src/nxt_http_js.c"
+fi
+
NXT_LIB_EPOLL_SRCS="src/nxt_epoll_engine.c"
NXT_LIB_KQUEUE_SRCS="src/nxt_kqueue_engine.c"
NXT_LIB_EVENTPORT_SRCS="src/nxt_eventport_engine.c"
@@ -187,7 +193,7 @@ NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
if [ $NXT_HAVE_ROOTFS = YES ]; then
- NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs.c"
+ NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs_mount.c"
fi
@@ -298,6 +304,11 @@ if [ "$NXT_HAVE_CLONE" = "YES" ]; then
fi
+if [ "$NXT_HAVE_CGROUP" = "YES" ]; then
+ NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_cgroup.c"
+fi
+
+
if [ "$NXT_TEST_BUILD" = "YES" ]; then
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_TEST_BUILD_SRCS"
fi
diff --git a/auto/ssltls b/auto/ssltls
index d678ba74..6512d330 100644
--- a/auto/ssltls
+++ b/auto/ssltls
@@ -23,7 +23,7 @@ if [ $NXT_OPENSSL = YES ]; then
nxt_feature_libs="-lssl -lcrypto"
nxt_feature_test="#include <openssl/ssl.h>
- int main() {
+ int main(void) {
SSL_library_init();
return 0;
}"
@@ -39,7 +39,7 @@ if [ $NXT_OPENSSL = YES ]; then
nxt_feature_run=value
nxt_feature_test="#include <openssl/ssl.h>
- int main() {
+ int main(void) {
printf(\"\\\"%s\\\"\",
SSLeay_version(SSLEAY_VERSION));
return 0;
@@ -61,7 +61,7 @@ if [ $NXT_OPENSSL = YES ]; then
nxt_feature_libs="$NXT_OPENSSL_LIBS"
nxt_feature_test="#include <openssl/ssl.h>
- int main() {
+ int main(void) {
SSL_CONF_cmd(NULL, NULL, NULL);
return 0;
}"
@@ -75,7 +75,7 @@ if [ $NXT_OPENSSL = YES ]; then
nxt_feature_libs="$NXT_OPENSSL_LIBS"
nxt_feature_test="#include <openssl/ssl.h>
- int main() {
+ int main(void) {
#if (OPENSSL_NO_TLSEXT)
#error OpenSSL: no tlsext support.
#else
@@ -100,7 +100,7 @@ if [ $NXT_GNUTLS = YES ]; then
nxt_feature_libs=$NXT_GNUTLS_LIBS
nxt_feature_test="#include <gnutls/gnutls.h>
- int main() {
+ int main(void) {
gnutls_global_init();
gnutls_global_deinit();
return 0;
@@ -121,7 +121,7 @@ if [ $NXT_GNUTLS = YES ]; then
nxt_feature_libs=$NXT_GNUTLS_LIBS
nxt_feature_test="#include <gnutls/gnutls.h>
- int main() {
+ int main(void) {
gnutls_transport_set_vec_push_function(NULL, NULL);
return 0;
}"
@@ -135,7 +135,7 @@ if [ $NXT_GNUTLS = YES ]; then
nxt_feature_libs=$NXT_GNUTLS_LIBS
nxt_feature_test="#include <gnutls/gnutls.h>
- int main() {
+ int main(void) {
gnutls_global_set_time_function(NULL);
return 0;
}"
@@ -160,7 +160,7 @@ if [ $NXT_CYASSL = YES ]; then
nxt_feature_libs="-lcyassl"
nxt_feature_test="#include <cyassl/ssl.h>
- int main() {
+ int main(void) {
CyaSSL_Init();
CyaSSL_Cleanup();
return 0;
@@ -191,7 +191,7 @@ if [ $NXT_POLARSSL = YES ]; then
nxt_feature_libs="-lpolarssl"
nxt_feature_test="#include <polarssl/ssl.h>
- int main() {
+ int main(void) {
ssl_context ssl;
memset(&ssl, '\0', sizeof(ssl));
ssl_init(&ssl);
diff --git a/auto/summary b/auto/summary
index 84bfbb7f..51db0eae 100644
--- a/auto/summary
+++ b/auto/summary
@@ -27,8 +27,11 @@ Unit configuration summary:
IPv6 support: .............. $NXT_INET6
Unix domain sockets support: $NXT_UNIX_DOMAIN
TLS support: ............... $NXT_OPENSSL
+ Regex support: ............. $NXT_REGEX
+ NJS support: ............... $NXT_NJS
process isolation: ......... $NXT_ISOLATION
+ cgroupv2: .................. $NXT_HAVE_CGROUP
debug logging: ............. $NXT_DEBUG
diff --git a/auto/threads b/auto/threads
index ff33eaac..67b46690 100644
--- a/auto/threads
+++ b/auto/threads
@@ -47,7 +47,7 @@ nxt_feature_libs=$NXT_PTHREAD
nxt_feature_test="#define _GNU_SOURCE
#include <pthread.h>
- int main() {
+ int main(void) {
pthread_yield();
return 0;
}"
@@ -65,7 +65,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=$NXT_PTHREAD
nxt_feature_test="#include <pthread.h>
- int main() {
+ int main(void) {
pthread_yield_np();
return 0;
}"
@@ -82,7 +82,7 @@ nxt_feature_incs=
nxt_feature_libs=$NXT_PTHREAD
nxt_feature_test="#include <pthread.h>
- int main() {
+ int main(void) {
pthread_spinlock_t lock;
if (pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) != 0)
@@ -112,7 +112,7 @@ if [ $nxt_found = yes ]; then
pthread_spinlock_t lock = 0;
- int main() {
+ int main(void) {
if (pthread_spin_trylock(&lock) != 0)
return 1;
if (pthread_spin_unlock(&lock) != 0)
@@ -130,7 +130,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <semaphore.h>
- int main() {
+ int main(void) {
sem_t sem;
struct timespec ts;
@@ -199,7 +199,7 @@ nxt_feature_test="#include <pthread.h>
return NULL;
}
- int main() {
+ int main(void) {
void *n;
pthread_t pt;
@@ -227,7 +227,7 @@ if [ $nxt_found = no ]; then
nxt_feature_libs=$NXT_PTHREAD
nxt_feature_test="#include <pthread.h>
- int main() {
+ int main(void) {
pthread_key_t key = -1;
if (pthread_key_create(&key, NULL))
@@ -250,7 +250,7 @@ if [ $nxt_found = no ]; then
#include <pthread.h>
#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", PTHREAD_KEYS_MAX);
return 0;
}"
diff --git a/auto/time b/auto/time
index 7663e62f..402a219c 100644
--- a/auto/time
+++ b/auto/time
@@ -13,7 +13,7 @@ nxt_feature_incs=
nxt_feature_libs="-lrt"
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) == -1)
@@ -36,7 +36,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME_FAST, &ts) == -1)
@@ -53,7 +53,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
@@ -87,7 +87,7 @@ nxt_feature_incs=
nxt_feature_libs="-lrt"
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == -1)
@@ -110,7 +110,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC_FAST, &ts) == -1)
@@ -127,7 +127,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
@@ -163,7 +163,7 @@ nxt_feature_libs="-lhg"
nxt_feature_test="#include <stdlib.h>
#include <sys/mercury.h>
- int main() {
+ int main(void) {
hg_gethrtime();
return 0;
}"
@@ -181,7 +181,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
time_t t;
struct tm tm;
@@ -199,7 +199,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
altzone = 0;
return 0;
}"
@@ -213,7 +213,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <time.h>
- int main() {
+ int main(void) {
time_t t;
struct tm tm;
diff --git a/auto/types b/auto/types
index 91d53b6f..c0a871dd 100644
--- a/auto/types
+++ b/auto/types
@@ -18,7 +18,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(int));
return 0;
}"
@@ -32,7 +32,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(long));
return 0;
}"
@@ -46,7 +46,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(long long));
return 0;
}"
@@ -60,7 +60,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(void *));
return 0;
}"
@@ -80,7 +80,7 @@ nxt_feature_incs=
nxt_feature_libs=
nxt_feature_test="#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(size_t));
return 0;
}"
@@ -96,7 +96,7 @@ nxt_feature_test="#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(off_t));
return 0;
}"
@@ -111,7 +111,7 @@ nxt_feature_libs=
nxt_feature_test="#include <time.h>
#include <stdio.h>
- int main() {
+ int main(void) {
printf(\"%d\", (int) sizeof(time_t));
return 0;
}"
diff --git a/auto/unix b/auto/unix
index 45c6a139..1307bdbd 100644
--- a/auto/unix
+++ b/auto/unix
@@ -13,7 +13,7 @@ nxt_feature_libs=
nxt_feature_test="#include <unistd.h>
#include <sys/random.h>
- int main() {
+ int main(void) {
char buf[4];
if (getrandom(buf, 4, 0) < 0) {
@@ -35,7 +35,7 @@ if [ $nxt_found = no ]; then
#include <sys/syscall.h>
#include <linux/random.h>
- int main() {
+ int main(void) {
char buf[4];
if (syscall(SYS_getrandom, buf, 4, 0) < 0) {
@@ -56,7 +56,7 @@ if [ $nxt_found = no ]; then
nxt_feature_name=NXT_HAVE_GETENTROPY
nxt_feature_test="#include <unistd.h>
- int main() {
+ int main(void) {
char buf[4];
if (getentropy(buf, 4) == -1) {
@@ -78,7 +78,7 @@ if [ $nxt_found = no ]; then
nxt_feature_test="#include <unistd.h>
#include <sys/random.h>
- int main() {
+ int main(void) {
char buf[4];
if (getentropy(buf, 4) == -1) {
@@ -99,7 +99,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <ucontext.h>
- int main() {
+ int main(void) {
ucontext_t uc;
if (getcontext(&uc) == 0) {
@@ -126,7 +126,7 @@ if [ $nxt_found = no ]; then
#include <stdlib.h>
#include <ucontext.h>
- int main() {
+ int main(void) {
ucontext_t uc;
if (getcontext(&uc) == 0) {
@@ -155,7 +155,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <dlfcn.h>
- int main() {
+ int main(void) {
void *h = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
dlsym(h, \"\");
dlclose(h);
@@ -188,7 +188,7 @@ nxt_feature_libs=
nxt_feature_test="#include <stdlib.h>
#include <unistd.h>
- int main() {
+ int main(void) {
setproctitle(\"%s\", \"title\");
return 0;
}"
@@ -204,7 +204,7 @@ nxt_feature_libs=
nxt_feature_test="#include <unistd.h>
#include <grp.h>
- int main() {
+ int main(void) {
getgrouplist(\"root\", 0, NULL, NULL);
return 0;
}"
diff --git a/configure b/configure
index ea86e18e..1d897f1d 100755
--- a/configure
+++ b/configure
@@ -129,6 +129,7 @@ if [ $NXT_REGEX = YES ]; then
. auto/pcre
fi
+. auto/cgroup
. auto/isolation
. auto/capability
@@ -171,5 +172,9 @@ NXT_LIB_AUX_LIBS="$NXT_OPENSSL_LIBS $NXT_GNUTLS_LIBS \\
$NXT_CYASSL_LIBS $NXT_POLARSSL_LIBS \\
$NXT_PCRE_LIB"
+if [ $NXT_NJS != NO ]; then
+ . auto/njs
+fi
+
. auto/make
. auto/summary
diff --git a/docs/changes.xml b/docs/changes.xml
index 0674fb54..8853bc2e 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -13,6 +13,147 @@
unit-perl
unit-ruby
unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13
+ unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18
+ unit-jsc19"
+ ver="1.29.0" rev="1"
+ date="2022-12-15" time="18:00:00 +0300"
+ packager="Nginx Packaging &lt;nginx-packaging@f5.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.29.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-jsc19" ver="1.29.0" rev="1"
+ date="2022-11-18" time="15:00:00 +0400"
+ packager="Nginx Packaging &lt;nginx-packaging@f5.com&gt;">
+
+<change>
+<para>
+Initial release of Java 19 module for NGINX Unit.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.29.0" rev="1"
+ date="2022-12-15" time="18:00:00 +0300"
+ packager="Nginx Packaging &lt;nginx-packaging@f5.com&gt;">
+
+<change type="change">
+<para>
+removed $uri auto-append for "share" when loading configuration.
+</para>
+</change>
+
+<change type="change">
+<para>
+prefer system crypto policy instead of hardcoding a default.
+</para>
+</change>
+
+<change type="feature">
+<para>
+njs support with the basic syntax of JS template literals.
+</para>
+</change>
+
+<change type="feature">
+<para>
+support per-application cgroups on Linux.
+</para>
+</change>
+
+<change type="feature">
+<para>
+the $request_time variable contains the request processing time.
+</para>
+</change>
+
+<change type="feature">
+<para>
+"prefix" option in Python applications to set WSGI "SCRIPT_NAME"
+and ASGI root-path variables.
+</para>
+</change>
+
+<change type="feature">
+<para>
+compatibility with Python 3.11.
+</para>
+</change>
+
+<change type="feature">
+<para>
+compatibility with OpenSSL 3.
+</para>
+</change>
+
+<change type="feature">
+<para>
+compatibility with PHP 8.2.
+</para>
+</change>
+
+<change type="feature">
+<para>
+compatibility with Node.js 19.0.
+</para>
+</change>
+
+<change type="feature">
+<para>
+Ruby Rack v3 support.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+fix error in connection statistics when using proxy.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+fix HTTP cookie parsing when the value contains an equals sign.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+PHP directory URLs without a trailing '/' would give a 503 error (fixed with
+a 301 re-direct).
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+missing error checks in the C API.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+report the regex status in configure summary.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7
+ unit-python3.8 unit-python3.9 unit-python3.10
+ unit-go
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13
unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18"
ver="1.28.0" rev="1"
date="2022-09-13" time="18:00:00 +0300"
diff --git a/pkg/Makefile b/pkg/Makefile
index 4cf9ff80..c252969b 100644
--- a/pkg/Makefile
+++ b/pkg/Makefile
@@ -29,11 +29,15 @@ docker:
npm:
@cd npm && VERSION=$(VERSION) RELEASE=$(RELEASE) make all
+njs:
+ @cd contrib && make .njs
+
clean:
@cd rpm && make clean
@cd deb && make clean
@cd docker && make clean
@cd npm && make clean
+ @cd contrib && make clean
rm -f unit-$(VERSION).tar.gz
rm -f unit-$(VERSION).tar.gz.sha512
diff --git a/pkg/contrib/Makefile b/pkg/contrib/Makefile
new file mode 100644
index 00000000..7e3b8b97
--- /dev/null
+++ b/pkg/contrib/Makefile
@@ -0,0 +1,140 @@
+all: install
+
+TOPSRC := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+SRC := $(TOPSRC)/src
+TARBALLS := $(TOPSRC)/tarballs
+VPATH := $(TARBALLS)
+PREFIX = $(TOPSRC)/local
+PREFIX := $(abspath $(PREFIX))
+
+PKGS_ALL := $(patsubst $(SRC)/%/Makefile,%,$(wildcard $(SRC)/*/Makefile))
+
+# Common download locations
+CONTRIB_NGINX := https://packages.nginx.org/contrib
+
+#
+# Tools
+#
+NPROC := $(shell getconf _NPROCESSORS_ONLN)
+_SMP_MFLAGS := -j$(NPROC)
+
+ifndef GIT
+ifeq ($(shell git --version >/dev/null 2>&1 || echo FAIL),)
+GIT = git
+endif
+endif
+GIT ?= $(error git not found)
+
+ifeq ($(shell curl --version >/dev/null 2>&1 || echo FAIL),)
+download = curl -f -L -- "$(1)" > "$@"
+else ifeq ($(shell wget --version >/dev/null 2>&1 || echo FAIL),)
+download = (rm -f $@.tmp && \
+ wget --passive -c -p -O $@.tmp "$(1)" && \
+ touch $@.tmp && \
+ mv $@.tmp $@ )
+else ifeq ($(which fetch >/dev/null 2>&1 || echo FAIL),)
+download = (rm -f $@.tmp && \
+ fetch -p -o $@.tmp "$(1)" && \
+ touch $@.tmp && \
+ mv $@.tmp $@)
+else
+download = $(error Neither curl nor wget found)
+endif
+
+download_pkg = $(call download,$(CONTRIB_NGINX)/$(2)/$(lastword $(subst /, ,$(@)))) || \
+ ( $(call download,$(1)) && echo "Please upload $(lastword $(subst /, ,$(@))) to $(CONTRIB_NGINX)" )
+
+ifeq ($(shell which xz >/dev/null 2>&1 || echo FAIL),)
+XZ = xz
+else
+XZ ?= $(error XZ (LZMA) compressor not found)
+endif
+
+ifeq ($(shell sha512sum --version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = sha512sum --check
+else ifeq ($(shell shasum --version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = shasum -a 512 --check
+else ifeq ($(shell openssl version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = openssl dgst -sha512
+else
+SHA512SUM = $(error SHA-512 checksumming not found)
+endif
+
+#
+# Common helpers
+#
+download_git = \
+ rm -Rf -- "$(@:.tar.xz=)" && \
+ $(GIT) init --bare "$(@:.tar.xz=)" && \
+ (cd "$(@:.tar.xz=)" && \
+ $(GIT) remote add origin "$(1)" && \
+ $(GIT) fetch origin "$(2)") && \
+ (cd "$(@:.tar.xz=)" && \
+ $(GIT) archive --prefix="$(notdir $(@:.tar.xz=))/" \
+ --format=tar "$(3)") > "$(@:.xz=)" && \
+ echo "$(3) $(@)" > "$(@:.tar.xz=.githash)" && \
+ rm -Rf -- "$(@:.tar.xz=)" && \
+ $(XZ) --stdout "$(@:.xz=)" > "$@.tmp" && \
+ rm -f "$(@:.xz=)" && \
+ mv -f -- "$@.tmp" "$@"
+check_githash = \
+ h=`sed -e "s,^\([0-9a-fA-F]\{40\}\) .*/$(notdir $<),\1,g" \
+ < "$(<:.tar.xz=.githash)"` && \
+ test "$$h" = "$1"
+
+checksum = \
+ $(foreach f,$(filter $(TARBALLS)/%,$^), \
+ grep -- " $(f:$(TARBALLS)/%=%)$$" \
+ "$(SRC)/$(patsubst $(3)%,%,$@)/$(2)SUMS" |) \
+ (cd $(TARBALLS) && $(1))
+CHECK_SHA512 = $(call checksum,$(SHA512SUM),SHA512,.sum-)
+UNPACK = $(RM) -R $@ \
+ $(foreach f,$(filter %.tar.gz %.tgz,$^), && tar xvzfo $(f)) \
+ $(foreach f,$(filter %.tar.bz2,$^), && tar xvjfo $(f)) \
+ $(foreach f,$(filter %.tar.xz,$^), && tar xvJfo $(f)) \
+ $(foreach f,$(filter %.zip,$^), && unzip $(f))
+UNPACK_DIR = $(patsubst %.tar,%,$(basename $(notdir $<)))
+APPLY = (cd $(UNPACK_DIR) && patch -fp1) <
+MOVE = mv $(UNPACK_DIR) $@ && touch $@
+
+# Per-package build rules
+include $(SRC)/*/Makefile
+
+# Targets
+PKGS_DEPS := $(sort $(foreach p,$(PKGS),$(DEPS_$(p))))
+
+fetch: $(PKGS:%=.sum-%)
+install: $(PKGS:%=.%)
+
+clean:
+ -$(RM) $(foreach p,$(PKGS),.$(p) .sum-$(p) .dep-$(p))
+ -$(RM) -R $(foreach p,$(PKGS),$(p))
+ -$(RM) -R "$(PREFIX)"
+ -$(RM) $(TARBALLS)/*.*
+
+list:
+ @echo Packages:
+ @echo ' $(PKGS)' | tr " " "\n" | sort | tr "\n" " " |fmt
+ @echo Depended-on packages:
+ @echo ' $(PKGS_DEPS)' | tr " " "\n" | sort | tr "\n" " " |fmt
+
+.PHONY: all fetch install clean list
+
+# Default pattern rules
+.sum-%: $(SRC)/%/SHA512SUMS
+ $(CHECK_SHA512)
+ touch $@
+
+.sum-%:
+ $(error Download and check target not defined for $*)
+
+# Real dependency on missing packages
+$(patsubst %,.dep-%,$(PKGS)): .dep-%: .%
+ touch -r $< $@
+
+.SECONDEXPANSION:
+
+# Dependency propagation (convert 'DEPS_foo = bar' to '.foo: .bar')
+$(foreach p,$(PKGS),.$(p)): .%: $$(foreach d,$$(DEPS_$$*),.dep-$$(d))
+
+.DELETE_ON_ERROR:
diff --git a/pkg/contrib/src/njs/Makefile b/pkg/contrib/src/njs/Makefile
new file mode 100644
index 00000000..54255aef
--- /dev/null
+++ b/pkg/contrib/src/njs/Makefile
@@ -0,0 +1,19 @@
+# njs
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/version
+NJS_URL := https://hg.nginx.org/njs/archive/$(NJS_VERSION).tar.gz
+
+PKGS += njs
+
+$(TARBALLS)/njs-$(NJS_VERSION).tar.gz:
+ $(call download_pkg,$(NJS_URL),njs)
+
+.sum-njs: njs-$(NJS_VERSION).tar.gz
+
+njs: njs-$(NJS_VERSION).tar.gz .sum-njs
+ $(UNPACK)
+ $(MOVE)
+
+.njs: njs
+ cd $< && ./configure && $(MAKE) libnjs
+ touch $@
diff --git a/pkg/contrib/src/njs/SHA512SUMS b/pkg/contrib/src/njs/SHA512SUMS
new file mode 100644
index 00000000..1bddec9b
--- /dev/null
+++ b/pkg/contrib/src/njs/SHA512SUMS
@@ -0,0 +1 @@
+dc73029e7b570a7fbc94e90deb1e17c9a3d85072dc0e060f11dd96bd173e11b7c823c57115369d3c68af7acd97fabe619b70dfd73280694f8b5dc8b7929d850b njs-0.7.9.tar.gz
diff --git a/pkg/contrib/src/njs/version b/pkg/contrib/src/njs/version
new file mode 100644
index 00000000..511715d0
--- /dev/null
+++ b/pkg/contrib/src/njs/version
@@ -0,0 +1 @@
+NJS_VERSION := 0.7.9
diff --git a/pkg/contrib/tarballs/.hgignore b/pkg/contrib/tarballs/.hgignore
new file mode 100644
index 00000000..8d876d7b
--- /dev/null
+++ b/pkg/contrib/tarballs/.hgignore
@@ -0,0 +1,3 @@
+syntax:glob
+*.tar.*
+*.githash
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index c4f085f8..580cb655 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -19,6 +19,21 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit)
MODULES=
+# Ubuntu 22.10
+ifeq ($(CODENAME),kinetic)
+include Makefile.php
+include Makefile.python27
+include Makefile.python310
+include Makefile.go
+include Makefile.perl
+include Makefile.ruby
+include Makefile.jsc-common
+include Makefile.jsc11
+include Makefile.jsc17
+include Makefile.jsc18
+include Makefile.jsc19
+endif
+
# Ubuntu 22.04
ifeq ($(CODENAME),jammy)
include Makefile.php
@@ -100,7 +115,7 @@ include Makefile.jsc-common
include Makefile.jsc11
endif
-CONFIGURE_ARGS=\
+CONFIGURE_ARGS_COMMON=\
--prefix=/usr \
--state=/var/lib/unit \
--control="unix:/var/run/control.unit.sock" \
@@ -112,6 +127,10 @@ CONFIGURE_ARGS=\
--tests \
--openssl
+CONFIGURE_ARGS=\
+ $(CONFIGURE_ARGS_COMMON) \
+ --njs
+
export CR=\\n
default:
@@ -156,6 +175,7 @@ debuild/$(SRCDIR)/debian:
echo '3.0 (quilt)' > debuild/$(SRCDIR)/debian/source/format ; \
cat debian/control.in | sed \
-e "s#%%PACKAGE_VENDOR%%#$(PACKAGE_VENDOR)#g" \
+ -e "s#%%UNIT_VERSION%%#$(VERSION)#g" \
> debuild/$(SRCDIR)/debian/control ; \
cat debian/rules.in | sed \
-e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \
@@ -179,7 +199,7 @@ endif
debuild/unit_$(VERSION).orig.tar.gz: | debuild/$(SRCDIR)/debian
cd ../.. && tar -czf pkg/deb/debuild/$(SRCDIR).tar.gz \
--transform "s#^#$(SRCDIR)/#" \
- LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go docs/man/unitd.8.in
+ LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go pkg/contrib docs/man/unitd.8.in
mv debuild/$(SRCDIR).tar.gz debuild/unit_$(VERSION).orig.tar.gz
cd debuild && tar zxf unit_$(VERSION).orig.tar.gz
@@ -241,7 +261,7 @@ endif
-e "s#%%CODENAME%%#$(CODENAME)#g" \
-e "s#%%UNIT_VERSION%%#$(VERSION)#g" \
-e "s#%%UNIT_RELEASE%%#$(RELEASE)#g" \
- -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \
+ -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS_COMMON)#g" \
-e "s#%%MODULE_CONFARGS%%#$(MODULE_CONFARGS_$*)#g" \
-e "s#%%MODULE_MAKEARGS%%#$(MODULE_MAKEARGS_$*)#g" \
-e "s#%%MODULE_INSTARGS%%#$(MODULE_INSTARGS_$*)#g" \
diff --git a/pkg/deb/Makefile.jsc19 b/pkg/deb/Makefile.jsc19
new file mode 100644
index 00000000..2a6ef7db
--- /dev/null
+++ b/pkg/deb/Makefile.jsc19
@@ -0,0 +1,71 @@
+MODULES+= jsc19
+MODULE_SUFFIX_jsc19= jsc19
+
+MODULE_SUMMARY_jsc19= Java 19 module for NGINX Unit
+
+MODULE_VERSION_jsc19= $(VERSION)
+MODULE_RELEASE_jsc19= 1
+
+MODULE_CONFARGS_jsc19= java --module=java19 --home=/usr/lib/jvm/java-19-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/
+MODULE_MAKEARGS_jsc19= java19
+MODULE_INSTARGS_jsc19= java19-install
+
+MODULE_SOURCES_jsc19= unit.example-jsc-app \
+ unit.example-jsc19-config
+
+BUILD_DEPENDS_jsc19= openjdk-19-jdk-headless openjdk-19-jre-headless
+BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc19)
+
+MODULE_BUILD_DEPENDS_jsc19=,openjdk-19-jdk-headless
+MODULE_DEPENDS_jsc19=,openjdk-19-jre-headless,unit-jsc-common (= $(MODULE_VERSION_jsc_common)-$(MODULE_RELEASE_jsc_common)~$(CODENAME))
+
+define MODULE_PREINSTALL_jsc19
+ mkdir -p debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/jsc-app
+ install -m 644 -p debian/unit.example-jsc-app debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/jsc-app/index.jsp
+ install -m 644 -p debian/unit.example-jsc19-config debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/unit.config
+ install -m 644 -p src/java/README.JSR-340 debian/unit-jsc19/usr/share/doc/unit-jsc19/
+endef
+export MODULE_PREINSTALL_jsc19
+
+define MODULE_POSTINSTALL_jsc19
+ cd $$\(BUILDDIR_unit\) \&\& \
+ DESTDIR=$$\(INSTALLDIR\) make java-shared-uninstall
+endef
+export MODULE_POSTINSTALL_jsc19
+
+define MODULE_POST_jsc19
+cat <<BANNER
+----------------------------------------------------------------------
+
+The $(MODULE_SUMMARY_jsc19) has been installed.
+
+To check out the sample app, run these commands:
+
+ sudo service unit restart
+ cd /usr/share/doc/unit-$(MODULE_SUFFIX_jsc19)/examples
+ sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
+ curl http://localhost:8800/
+
+Online documentation is available at https://unit.nginx.org
+
+NOTICE:
+
+This version of Unit code is made available in support of the open source
+development process. This is an intermediate build made available for
+testing purposes only. This Unit code is untested and presumed incompatible
+with the JSR 340 Java Servlet 3.1 specification. You should not deploy or
+write to this code. You should instead deploy and write production
+applications on pre-built binaries that have been tested and certified
+to meet the JSR-340 compatibility requirements such as certified binaries
+published for the JSR-340 reference implementation available at
+https://javaee.github.io/glassfish/.
+
+Redistribution of any Intermediate Build must retain this notice.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_jsc19
diff --git a/pkg/deb/debian.module/control.in b/pkg/deb/debian.module/control.in
index f5ce8ae4..f82362d1 100644
--- a/pkg/deb/debian.module/control.in
+++ b/pkg/deb/debian.module/control.in
@@ -14,7 +14,7 @@ Section: admin
Architecture: any
Depends: lsb-base,
${misc:Depends}, ${shlibs:Depends},
- unit (= %%UNIT_VERSION%%-%%UNIT_RELEASE%%~%%CODENAME%%)%%MODULE_DEPENDS%%
+ unit-r%%UNIT_VERSION%%%%MODULE_DEPENDS%%
Description: %%SUMMARY%%
NGINX Unit is a runtime and delivery environment for modern distributed
applications. It runs the application code in multiple languages
diff --git a/pkg/deb/debian.module/unit.example-jsc19-config b/pkg/deb/debian.module/unit.example-jsc19-config
new file mode 100644
index 00000000..106b694b
--- /dev/null
+++ b/pkg/deb/debian.module/unit.example-jsc19-config
@@ -0,0 +1,15 @@
+{
+ "applications": {
+ "example_java19": {
+ "processes": 1,
+ "type": "java 19",
+ "webapp": "/usr/share/doc/unit-jsc19/examples/jsc-app"
+ }
+ },
+
+ "listeners": {
+ "*:8800": {
+ "pass": "applications/example_java19"
+ }
+ }
+}
diff --git a/pkg/deb/debian/control.in b/pkg/deb/debian/control.in
index 691bafed..579f41e3 100644
--- a/pkg/deb/debian/control.in
+++ b/pkg/deb/debian/control.in
@@ -5,7 +5,8 @@ Maintainer: %%PACKAGE_VENDOR%%
Build-Depends: debhelper (>= 11),
linux-libc-dev,
libssl-dev,
- libpcre2-dev
+ libpcre2-dev,
+ pkg-config
Standards-Version: 4.1.4
Homepage: https://unit.nginx.org
@@ -14,6 +15,7 @@ Section: admin
Architecture: any
Depends: lsb-base,
${misc:Depends}, ${shlibs:Depends}
+Provides: unit-r%%UNIT_VERSION%%
Description: NGINX Unit
NGINX Unit is a runtime and delivery environment for modern distributed
applications. It runs the application code in multiple languages
diff --git a/pkg/deb/debian/rules.in b/pkg/deb/debian/rules.in
index d2e34796..23812926 100644
--- a/pkg/deb/debian/rules.in
+++ b/pkg/deb/debian/rules.in
@@ -20,7 +20,12 @@ BASEDIR = $(CURDIR)
DOTESTS = 0
-config.env.%:
+njs:
+ dh_testdir
+ cd pkg/contrib && make .njs
+ touch $@
+
+config.env.%: njs
dh_testdir
mkdir -p $(BUILDDIR_$*)
cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/
@@ -40,6 +45,7 @@ config.env.%:
configure.unit: config.env.unit
cd $(BUILDDIR_unit) && \
+ PKG_CONFIG_PATH=$(CURDIR)/pkg/contrib/njs/build \
CFLAGS= ./configure \
%%CONFIGURE_ARGS%% \
--modules=/usr/lib/unit/modules \
@@ -50,6 +56,7 @@ configure.unit: config.env.unit
configure.unit_debug: config.env.unit_debug
cd $(BUILDDIR_unit_debug) && \
+ PKG_CONFIG_PATH=$(CURDIR)/pkg/contrib/njs/build \
CFLAGS= ./configure \
%%CONFIGURE_ARGS%% \
--modules=/usr/lib/unit/debug-modules \
diff --git a/pkg/docker/Dockerfile.go1.19 b/pkg/docker/Dockerfile.go1.19
index 1625b64f..ec2b40da 100644
--- a/pkg/docker/Dockerfile.go1.19
+++ b/pkg/docker/Dockerfile.go1.19
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.jsc11 b/pkg/docker/Dockerfile.jsc11
index fe344d0e..b8391997 100644
--- a/pkg/docker/Dockerfile.jsc11
+++ b/pkg/docker/Dockerfile.jsc11
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index c57379f7..ca3dec01 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.node16 b/pkg/docker/Dockerfile.node18
index d341e43f..bdf968b2 100644
--- a/pkg/docker/Dockerfile.node16
+++ b/pkg/docker/Dockerfile.node18
@@ -1,4 +1,4 @@
-FROM node:16 as BUILDER
+FROM node:18 as BUILDER
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
@@ -40,7 +40,7 @@ RUN set -ex \
&& make -j $NCPU node node-install libunit-install \
&& ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
-FROM node:16
+FROM node:18
COPY docker-entrypoint.sh /usr/local/bin/
COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
diff --git a/pkg/docker/Dockerfile.perl5.36 b/pkg/docker/Dockerfile.perl5.36
index d0b8006f..9c398c30 100644
--- a/pkg/docker/Dockerfile.perl5.36
+++ b/pkg/docker/Dockerfile.perl5.36
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.php8.1 b/pkg/docker/Dockerfile.php8.1
index c63e708e..76c7c428 100644
--- a/pkg/docker/Dockerfile.php8.1
+++ b/pkg/docker/Dockerfile.php8.1
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.python3.10 b/pkg/docker/Dockerfile.python3.11
index 6502f8a8..3a83ec57 100644
--- a/pkg/docker/Dockerfile.python3.10
+++ b/pkg/docker/Dockerfile.python3.11
@@ -1,4 +1,4 @@
-FROM python:3.10 as BUILDER
+FROM python:3.11 as BUILDER
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
@@ -40,7 +40,7 @@ RUN set -ex \
&& make -j $NCPU python3-install \
&& ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
-FROM python:3.10
+FROM python:3.11
COPY docker-entrypoint.sh /usr/local/bin/
COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
diff --git a/pkg/docker/Dockerfile.ruby3.1 b/pkg/docker/Dockerfile.ruby3.1
index f365bc96..1eb6ce5c 100644
--- a/pkg/docker/Dockerfile.ruby3.1
+++ b/pkg/docker/Dockerfile.ruby3.1
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.28.0 \
+ && hg up 1.29.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile
index 03723d1d..b08e885f 100644
--- a/pkg/docker/Makefile
+++ b/pkg/docker/Makefile
@@ -34,7 +34,7 @@ CONFIGURE_jsc ?= java --jars=/usr/share/unit-jsc-common/
INSTALL_jsc ?= java-shared-install java-install
COPY_jsc = COPY --from=BUILDER /usr/share/unit-jsc-common/ /usr/share/unit-jsc-common/
-VERSION_node ?= 16
+VERSION_node ?= 18
CONTAINER_node ?= node:$(VERSION_node)
CONFIGURE_node ?= nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp
INSTALL_node ?= node node-install libunit-install
@@ -56,7 +56,7 @@ CONFIGURE_php ?= php
INSTALL_php ?= php-install
COPY_php = RUN ldconfig
-VERSION_python ?= 3.10
+VERSION_python ?= 3.11
CONTAINER_python ?= python:$(VERSION_python)
CONFIGURE_python ?= python --config=/usr/local/bin/python3-config
INSTALL_python ?= python3-install
diff --git a/pkg/docker/docker-entrypoint.sh b/pkg/docker/docker-entrypoint.sh
index 59529925..3d134ea2 100755
--- a/pkg/docker/docker-entrypoint.sh
+++ b/pkg/docker/docker-entrypoint.sh
@@ -1,11 +1,14 @@
-#!/usr/bin/env bash
+#!/bin/sh
set -e
+WAITLOOPS=5
+SLEEPSEC=1
+
curl_put()
{
- RET=`/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2`
- RET_BODY=${RET::-3}
+ RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2)
+ RET_BODY=$(echo $RET | /bin/sed '$ s/...$//')
RET_STATUS=$(echo $RET | /usr/bin/tail -c 4)
if [ "$RET_STATUS" -ne "200" ]; then
echo "$0: Error: HTTP response status code is '$RET_STATUS'"
@@ -18,7 +21,7 @@ curl_put()
return 0
}
-if [ "$1" = "unitd" -o "$1" = "unitd-debug" ]; then
+if [ "$1" = "unitd" ] || [ "$1" = "unitd-debug" ]; then
if /usr/bin/find "/var/lib/unit/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then
echo "$0: /var/lib/unit/ is not empty, skipping initial configuration..."
else
@@ -55,9 +58,20 @@ if [ "$1" = "unitd" -o "$1" = "unitd-debug" ]; then
done
echo "$0: Stopping Unit daemon after initial configuration..."
- kill -TERM `/bin/cat /var/run/unit.pid`
+ kill -TERM $(/bin/cat /var/run/unit.pid)
- while [ -S /var/run/control.unit.sock ]; do echo "$0: Waiting for control socket to be removed..."; /bin/sleep 0.1; done
+ for i in $(/usr/bin/seq $WAITLOOPS); do
+ if [ -S /var/run/control.unit.sock ]; then
+ echo "$0 Waiting for control socket to be removed..."
+ /bin/sleep $SLEEPSEC
+ else
+ break
+ fi
+ done
+ if [ -S /var/run/control.unit.sock ]; then
+ kill -KILL $(/bin/cat /var/run/unit.pid)
+ rm -f /var/run/control.unit.sock
+ fi
echo
echo "$0: Unit initial configuration complete; ready for start up..."
diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile
index bbe44fe5..d00a25ac 100644
--- a/pkg/rpm/Makefile
+++ b/pkg/rpm/Makefile
@@ -18,8 +18,10 @@ else ifeq ($(shell rpm --eval "%{?rhel}"), 9)
OSVER = centos9
else ifeq ($(shell rpm --eval "%{?amzn}"), 2)
OSVER = amazonlinux2
-else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35'`; echo $$?),0)
+else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35 -a 0%{?fedora} -le 36'`; echo $$?),0)
OSVER = fedora
+else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 37'`; echo $$?),0)
+OSVER = fedora37
endif
BUILD_DEPENDS_unit = gcc rpm-build rpmlint
@@ -53,6 +55,8 @@ ifeq ($(OSVER), centos8)
include Makefile.php
include Makefile.python27
include Makefile.python36
+include Makefile.python38
+include Makefile.python39
include Makefile.go
include Makefile.perl
include Makefile.jsc-common
@@ -91,7 +95,18 @@ include Makefile.jsc8
include Makefile.jsc11
endif
-CONFIGURE_ARGS=\
+ifeq ($(OSVER), fedora37)
+include Makefile.php
+include Makefile.python311
+include Makefile.go
+include Makefile.perl
+include Makefile.ruby
+include Makefile.jsc-common
+include Makefile.jsc8
+include Makefile.jsc11
+endif
+
+CONFIGURE_ARGS_COMMON=\
--prefix=/usr \
--state=%{_sharedstatedir}/unit \
--control="unix:/var/run/unit/control.sock" \
@@ -103,6 +118,10 @@ CONFIGURE_ARGS=\
--tests \
--openssl
+CONFIGURE_ARGS=\
+ $(CONFIGURE_ARGS_COMMON) \
+ --njs
+
export CR=\\n
default:
@@ -161,7 +180,7 @@ endif
rpmbuild/SOURCES/unit-$(VERSION).tar.gz:
cd ../.. && tar -czf pkg/rpm/rpmbuild/SOURCES/unit-$(VERSION).tar.gz \
--transform "s#^#unit-$(VERSION)/#" \
- LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go docs/man/unitd.8.in
+ LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go pkg/contrib docs/man/unitd.8.in
unit: check-build-depends-unit rpmbuild/SPECS/unit.spec rpmbuild/SOURCES/unit-$(VERSION).tar.gz
@echo "===> Building $@ package" ; \
@@ -197,7 +216,7 @@ rpmbuild/SPECS/unit-%.spec: unit.module.spec.in ../../docs/changes.xml | rpmbuil
-e "s#%%UNIT_RELEASE%%#$(RELEASE)#g" \
-e "s#%%PACKAGE_VENDOR%%#$(PACKAGE_VENDOR)#g" \
-e "s#%%MODULE_SOURCES%%#$${sources}#g" \
- -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \
+ -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS_COMMON)#g" \
-e "s#%%MODULE_CONFARGS%%#$(MODULE_CONFARGS_$*)#g" \
-e "s#%%MODULE_MAKEARGS%%#$(MODULE_MAKEARGS_$*)#g" \
-e "s#%%MODULE_INSTARGS%%#$(MODULE_INSTARGS_$*)#g" \
diff --git a/pkg/rpm/Makefile.python311 b/pkg/rpm/Makefile.python311
new file mode 100644
index 00000000..a8bee943
--- /dev/null
+++ b/pkg/rpm/Makefile.python311
@@ -0,0 +1,55 @@
+MODULES+= python311
+MODULE_SUFFIX_python311= python3.11
+
+MODULE_SUMMARY_python311= Python 3.11 module for NGINX Unit
+
+MODULE_VERSION_python311= $(VERSION)
+MODULE_RELEASE_python311= 1
+
+MODULE_CONFARGS_python311= python --config=python3.11-config
+MODULE_MAKEARGS_python311= python3.11
+MODULE_INSTARGS_python311= python3.11-install
+
+MODULE_SOURCES_python311= unit.example-python-app \
+ unit.example-python311-config
+
+ifneq (,$(findstring $(OSVER),fedora37))
+BUILD_DEPENDS_python311= python3-devel
+endif
+
+BUILD_DEPENDS+= $(BUILD_DEPENDS_python311)
+
+define MODULE_PREINSTALL_python311
+%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python311/examples/python-app
+%{__install} -m 644 -p %{SOURCE100} \
+ %{buildroot}%{_datadir}/doc/unit-python311/examples/python-app/wsgi.py
+%{__install} -m 644 -p %{SOURCE101} \
+ %{buildroot}%{_datadir}/doc/unit-python311/examples/unit.config
+endef
+export MODULE_PREINSTALL_python311
+
+define MODULE_FILES_python311
+%{_libdir}/unit/modules/*
+%{_libdir}/unit/debug-modules/*
+endef
+export MODULE_FILES_python311
+
+define MODULE_POST_python311
+cat <<BANNER
+----------------------------------------------------------------------
+
+The $(MODULE_SUMMARY_python311) has been installed.
+
+To check the sample app, run these commands:
+
+ sudo service unit start
+ cd /usr/share/doc/%{name}/examples
+ sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/unit/control.sock http://localhost/config
+ curl http://localhost:8400/
+
+Online documentation is available at https://unit.nginx.org
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_python311
diff --git a/pkg/rpm/Makefile.python38 b/pkg/rpm/Makefile.python38
new file mode 100644
index 00000000..3f3657e2
--- /dev/null
+++ b/pkg/rpm/Makefile.python38
@@ -0,0 +1,53 @@
+MODULES+= python38
+MODULE_SUFFIX_python38= python3.8
+
+MODULE_SUMMARY_python38= Python 3.8 module for NGINX Unit
+
+MODULE_VERSION_python38= $(VERSION)
+MODULE_RELEASE_python38= 1
+
+MODULE_CONFARGS_python38= python --config=python3.8-config
+MODULE_MAKEARGS_python38= python3.8
+MODULE_INSTARGS_python38= python3.8-install
+
+MODULE_SOURCES_python38= unit.example-python-app \
+ unit.example-python38-config
+
+BUILD_DEPENDS_python38= python38-devel
+
+BUILD_DEPENDS+= $(BUILD_DEPENDS_python38)
+
+define MODULE_PREINSTALL_python38
+%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python38/examples/python-app
+%{__install} -m 644 -p %{SOURCE100} \
+ %{buildroot}%{_datadir}/doc/unit-python38/examples/python-app/wsgi.py
+%{__install} -m 644 -p %{SOURCE101} \
+ %{buildroot}%{_datadir}/doc/unit-python38/examples/unit.config
+endef
+export MODULE_PREINSTALL_python38
+
+define MODULE_FILES_python38
+%{_libdir}/unit/modules/*
+%{_libdir}/unit/debug-modules/*
+endef
+export MODULE_FILES_python38
+
+define MODULE_POST_python38
+cat <<BANNER
+----------------------------------------------------------------------
+
+The $(MODULE_SUMMARY_python38) has been installed.
+
+To check the sample app, run these commands:
+
+ sudo service unit start
+ cd /usr/share/doc/%{name}/examples
+ sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/unit/control.sock http://localhost/config
+ curl http://localhost:8400/
+
+Online documentation is available at https://unit.nginx.org
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_python38
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python311-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python311-config
new file mode 100644
index 00000000..ee653db6
--- /dev/null
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python311-config
@@ -0,0 +1,16 @@
+{
+ "applications": {
+ "example_python": {
+ "type": "python 3.11",
+ "processes": 2,
+ "path": "/usr/share/doc/unit-python311/examples/python-app",
+ "module": "wsgi"
+ }
+ },
+
+ "listeners": {
+ "*:8400": {
+ "pass": "applications/example_python"
+ }
+ }
+}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config
new file mode 100644
index 00000000..c98d1a52
--- /dev/null
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config
@@ -0,0 +1,16 @@
+{
+ "applications": {
+ "example_python": {
+ "type": "python 3.8",
+ "processes": 2,
+ "path": "/usr/share/doc/unit-python38/examples/python-app",
+ "module": "wsgi"
+ }
+ },
+
+ "listeners": {
+ "*:8400": {
+ "pass": "applications/example_python"
+ }
+ }
+}
diff --git a/pkg/rpm/unit.module.spec.in b/pkg/rpm/unit.module.spec.in
index 88a1c33e..bc68a254 100644
--- a/pkg/rpm/unit.module.spec.in
+++ b/pkg/rpm/unit.module.spec.in
@@ -39,7 +39,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: pcre2-devel
-Requires: unit == %%UNIT_VERSION%%-%%UNIT_RELEASE%%%{?dist}.ngx
+Requires: unit-r%%UNIT_VERSION%%
%description
NGINX Unit is a runtime and delivery environment for modern distributed
diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in
index 01c08bb8..06880fcf 100644
--- a/pkg/rpm/unit.spec.in
+++ b/pkg/rpm/unit.spec.in
@@ -46,6 +46,9 @@ Requires(preun): systemd
Requires(postun): systemd
BuildRequires: pcre2-devel
+BuildRequires: pkgconfig
+
+Provides: unit-r%{version}
%description
NGINX Unit is a runtime and delivery environment for modern distributed
@@ -72,6 +75,9 @@ Library and include files required for NGINX Unit modules development.
%setup -q
%build
+%{__make} %{?_smp_mflags} -C pkg/contrib .njs
+
+PKG_CONFIG_PATH=%{bdir}/pkg/contrib/njs/build \
./configure \
%{CONFIGURE_ARGS} \
--modules=%{_libdir}/unit/debug-modules \
@@ -82,6 +88,8 @@ Library and include files required for NGINX Unit modules development.
%{__make} %{?_smp_mflags}
%{__make} %{?_smp_mflags} build/libunit.a
%{__mv} build build-debug
+
+PKG_CONFIG_PATH=%{bdir}/pkg/contrib/njs/build \
./configure \
%{CONFIGURE_ARGS} \
--modules=%{_libdir}/unit/modules \
diff --git a/src/java/nxt_jni_Request.c b/src/java/nxt_jni_Request.c
index 2e9dce67..980a26b6 100644
--- a/src/java/nxt_jni_Request.c
+++ b/src/java/nxt_jni_Request.c
@@ -461,8 +461,8 @@ nxt_java_Request_getLocalAddr(JNIEnv *env, jclass cls, jlong req_ptr)
r = nxt_jlong2ptr(req_ptr);
- return nxt_java_newString(env, nxt_unit_sptr_get(&r->local),
- r->local_length);
+ return nxt_java_newString(env, nxt_unit_sptr_get(&r->local_addr),
+ r->local_addr_length);
}
@@ -474,11 +474,11 @@ nxt_java_Request_getLocalName(JNIEnv *env, jclass cls, jlong req_ptr)
r = nxt_jlong2ptr(req_ptr);
- local = nxt_unit_sptr_get(&r->local);
- colon = memchr(local, ':', r->local_length);
+ local = nxt_unit_sptr_get(&r->local_addr);
+ colon = memchr(local, ':', r->local_addr_length);
if (colon == NULL) {
- colon = local + r->local_length;
+ colon = local + r->local_addr_length;
}
return nxt_java_newString(env, local, colon - local);
@@ -494,20 +494,20 @@ nxt_java_Request_getLocalPort(JNIEnv *env, jclass cls, jlong req_ptr)
r = nxt_jlong2ptr(req_ptr);
- local = nxt_unit_sptr_get(&r->local);
- colon = memchr(local, ':', r->local_length);
+ local = nxt_unit_sptr_get(&r->local_addr);
+ colon = memchr(local, ':', r->local_addr_length);
if (colon == NULL) {
return 80;
}
- tmp = local[r->local_length];
+ tmp = local[r->local_addr_length];
- local[r->local_length] = '\0';
+ local[r->local_addr_length] = '\0';
res = strtol(colon + 1, NULL, 10);
- local[r->local_length] = tmp;
+ local[r->local_addr_length] = tmp;
return res;
}
diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs
index 546548f5..83985b0f 100644
--- a/src/nodejs/unit-http/loader.mjs
+++ b/src/nodejs/unit-http/loader.mjs
@@ -4,13 +4,15 @@ export async function resolve(specifier, context, defaultResolver) {
case "websocket":
return {
url: new URL("./websocket.js", import.meta.url).href,
- format: "commonjs"
+ format: "commonjs",
+ shortCircuit: true,
}
case "http":
return {
url: new URL("./http.js", import.meta.url).href,
- format: "commonjs"
+ format: "commonjs",
+ shortCircuit: true,
}
}
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index ee5dc46f..7912d0ac 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -657,7 +657,8 @@ Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
req_data->sock_ref = wrap(res, req, sock_destroy);
set_named_property(res, "remoteAddress", r->remote, r->remote_length);
- set_named_property(res, "localAddress", r->local, r->local_length);
+ set_named_property(res, "localAddress", r->local_addr,
+ r->local_addr_length);
return res;
}
diff --git a/src/nxt_application.c b/src/nxt_application.c
index 556f1ffb..786c768b 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -366,7 +366,7 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
&app->type, app->version, name);
if (app->compat_length != sizeof(compat)
- || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0)
+ || memcmp(app->compat, compat, sizeof(compat)) != 0)
{
nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name);
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 30a1a12f..4d624448 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -49,8 +49,6 @@ typedef struct {
typedef struct {
char *home;
nxt_conf_value_t *path;
- nxt_str_t module;
- char *callable;
nxt_str_t protocol;
uint32_t threads;
uint32_t thread_stack_size;
diff --git a/src/nxt_capability.c b/src/nxt_capability.c
index 24fd55d0..9f36ab99 100644
--- a/src/nxt_capability.c
+++ b/src/nxt_capability.c
@@ -50,7 +50,7 @@ nxt_capability_set(nxt_task_t *task, nxt_capabilities_t *cap)
#if (NXT_HAVE_LINUX_CAPABILITY)
static uint32_t
-nxt_capability_linux_get_version()
+nxt_capability_linux_get_version(void)
{
struct __user_cap_header_struct hdr;
diff --git a/src/nxt_cgroup.c b/src/nxt_cgroup.c
new file mode 100644
index 00000000..2c404acc
--- /dev/null
+++ b/src/nxt_cgroup.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <nxt_main.h>
+
+#include <nxt_cgroup.h>
+
+
+static int nxt_mk_cgpath_relative(nxt_task_t *task, const char *dir,
+ char *cgpath);
+static nxt_int_t nxt_mk_cgpath(nxt_task_t *task, const char *dir,
+ char *cgpath);
+
+
+nxt_int_t
+nxt_cgroup_proc_add(nxt_task_t *task, nxt_process_t *process)
+{
+ int len;
+ char cgprocs[NXT_MAX_PATH_LEN];
+ FILE *fp;
+ nxt_int_t ret;
+
+ if (task->thread->runtime->type != NXT_PROCESS_MAIN
+ || nxt_process_type(process) != NXT_PROCESS_PROTOTYPE
+ || process->isolation.cgroup.path == NULL)
+ {
+ return NXT_OK;
+ }
+
+ ret = nxt_mk_cgpath(task, process->isolation.cgroup.path, cgprocs);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
+
+ ret = nxt_fs_mkdir_all((const u_char *) cgprocs, 0777);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
+
+ len = strlen(cgprocs);
+
+ len = snprintf(cgprocs + len, NXT_MAX_PATH_LEN - len, "/cgroup.procs");
+ if (nxt_slow_path(len >= NXT_MAX_PATH_LEN - len)) {
+ nxt_errno = ENAMETOOLONG;
+ return NXT_ERROR;
+ }
+
+ fp = nxt_file_fopen(task, cgprocs, "we");
+ if (nxt_slow_path(fp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ setvbuf(fp, NULL, _IONBF, 0);
+ len = fprintf(fp, "%d\n", process->pid);
+ nxt_file_fclose(task, fp);
+
+ if (nxt_slow_path(len < 0)) {
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+void
+nxt_cgroup_cleanup(nxt_task_t *task, const nxt_process_t *process)
+{
+ char *ptr;
+ char cgroot[NXT_MAX_PATH_LEN], cgpath[NXT_MAX_PATH_LEN];
+ nxt_int_t ret;
+
+ ret = nxt_mk_cgpath(task, "", cgroot);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return;
+ }
+
+ ret = nxt_mk_cgpath(task, process->isolation.cgroup.path, cgpath);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return;
+ }
+
+ while (*cgpath != '\0' && strcmp(cgroot, cgpath) != 0) {
+ rmdir(cgpath);
+ ptr = strrchr(cgpath, '/');
+ *ptr = '\0';
+ }
+}
+
+
+static int
+nxt_mk_cgpath_relative(nxt_task_t *task, const char *dir, char *cgpath)
+{
+ int i, len;
+ char *buf, *ptr;
+ FILE *fp;
+ size_t size;
+ ssize_t nread;
+ nxt_bool_t found;
+
+ fp = nxt_file_fopen(task, "/proc/self/cgroup", "re");
+ if (nxt_slow_path(fp == NULL)) {
+ return -1;
+ }
+
+ len = -1;
+ buf = NULL;
+ found = 0;
+ while ((nread = getline(&buf, &size, fp)) != -1) {
+ if (strncmp(buf, "0::", 3) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ nxt_file_fclose(task, fp);
+
+ if (!found) {
+ nxt_errno = ENODATA;
+ goto out_free_buf;
+ }
+
+ buf[nread - 1] = '\0'; /* lose the trailing '\n' */
+ ptr = buf;
+ for (i = 0; i < 2; i++) {
+ ptr = strchr(ptr, ':');
+ if (ptr == NULL) {
+ nxt_errno = ENODATA;
+ goto out_free_buf;
+ }
+
+ ptr++;
+ }
+
+ len = snprintf(cgpath, NXT_MAX_PATH_LEN, NXT_CGROUP_ROOT "%s/%s",
+ ptr, dir);
+
+out_free_buf:
+
+ nxt_free(buf);
+
+ return len;
+}
+
+
+static nxt_int_t
+nxt_mk_cgpath(nxt_task_t *task, const char *dir, char *cgpath)
+{
+ int len;
+
+ /*
+ * If the path from the config is relative, we need to make
+ * the cgroup path include the main unit processes cgroup. I.e
+ *
+ * NXT_CGROUP_ROOT/<main process cgroup>/<cgroup path>
+ */
+ if (dir[0] != '/') {
+ len = nxt_mk_cgpath_relative(task, dir, cgpath);
+ } else {
+ len = snprintf(cgpath, NXT_MAX_PATH_LEN, NXT_CGROUP_ROOT "%s", dir);
+ }
+
+ if (len == -1) {
+ return NXT_ERROR;
+ }
+
+ if (len >= NXT_MAX_PATH_LEN) {
+ nxt_errno = ENAMETOOLONG;
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
diff --git a/src/nxt_cgroup.h b/src/nxt_cgroup.h
new file mode 100644
index 00000000..0b9055d2
--- /dev/null
+++ b/src/nxt_cgroup.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#ifndef _NXT_CGROUP_H_INCLUDED_
+#define _NXT_CGROUP_H_INCLUDED_
+
+
+nxt_int_t nxt_cgroup_proc_add(nxt_task_t *task, nxt_process_t *process);
+void nxt_cgroup_cleanup(nxt_task_t *task, const nxt_process_t *process);
+
+
+#endif /* _NXT_CGROUP_H_INCLUDED_ */
diff --git a/src/nxt_conf.c b/src/nxt_conf.c
index c6312f3d..d04aa45c 100644
--- a/src/nxt_conf.c
+++ b/src/nxt_conf.c
@@ -1389,7 +1389,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
case 't':
if (nxt_fast_path(end - start >= 4
- && nxt_memcmp(start, "true", 4) == 0))
+ && memcmp(start, "true", 4) == 0))
{
value->u.boolean = 1;
value->type = NXT_CONF_VALUE_BOOLEAN;
@@ -1401,7 +1401,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
case 'f':
if (nxt_fast_path(end - start >= 5
- && nxt_memcmp(start, "false", 5) == 0))
+ && memcmp(start, "false", 5) == 0))
{
value->u.boolean = 0;
value->type = NXT_CONF_VALUE_BOOLEAN;
@@ -1413,7 +1413,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
case 'n':
if (nxt_fast_path(end - start >= 4
- && nxt_memcmp(start, "null", 4) == 0))
+ && memcmp(start, "null", 4) == 0))
{
value->type = NXT_CONF_VALUE_NULL;
return start + 4;
diff --git a/src/nxt_conf.h b/src/nxt_conf.h
index c8a276c0..1b13f5ae 100644
--- a/src/nxt_conf.h
+++ b/src/nxt_conf.h
@@ -72,7 +72,7 @@ typedef struct {
nxt_mp_t *pool;
nxt_str_t error;
void *ctx;
- nxt_array_t *var_fields; /* of nxt_var_field_t */
+ nxt_tstr_state_t *tstr_state;
nxt_mp_t *conf_pool;
nxt_uint_t ver;
} nxt_conf_validation_t;
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index fe6c22e5..bf8aa760 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -34,7 +34,7 @@ typedef enum {
typedef enum {
NXT_CONF_VLDT_REQUIRED = 1 << 0,
- NXT_CONF_VLDT_VAR = 1 << 1,
+ NXT_CONF_VLDT_TSTR = 1 << 1,
} nxt_conf_vldt_flags_t;
@@ -128,6 +128,8 @@ static nxt_int_t nxt_conf_vldt_python_path_element(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);
+static nxt_int_t nxt_conf_vldt_python_prefix(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,
@@ -219,6 +221,11 @@ static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
#endif
+#if (NXT_HAVE_CGROUP)
+static nxt_int_t nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+#endif
+
static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[];
@@ -240,6 +247,9 @@ 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_CGROUP)
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[];
+#endif
#if (NXT_HAVE_ISOLATION_ROOTFS)
static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[];
#endif
@@ -307,6 +317,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = {
.name = nxt_string("idle_timeout"),
.type = NXT_CONF_VLDT_INTEGER,
}, {
+ .name = nxt_string("large_header_buffer_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("large_header_buffers"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
.name = nxt_string("body_buffer_size"),
.type = NXT_CONF_VLDT_INTEGER,
}, {
@@ -367,7 +383,7 @@ 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,
- .flags = NXT_CONF_VLDT_VAR,
+ .flags = NXT_CONF_VLDT_TSTR,
}, {
.name = nxt_string("application"),
.type = NXT_CONF_VLDT_STRING,
@@ -652,7 +668,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,
- .flags = NXT_CONF_VLDT_VAR,
+ .flags = NXT_CONF_VLDT_TSTR,
},
NXT_CONF_VLDT_END
@@ -667,7 +683,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = {
}, {
.name = nxt_string("location"),
.type = NXT_CONF_VLDT_STRING,
- .flags = NXT_CONF_VLDT_VAR,
+ .flags = NXT_CONF_VLDT_TSTR,
},
NXT_CONF_VLDT_END
@@ -697,7 +713,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = {
.validator = nxt_conf_vldt_unsupported,
.u.string = "chroot",
#endif
- .flags = NXT_CONF_VLDT_VAR,
+ .flags = NXT_CONF_VLDT_TSTR,
}, {
.name = nxt_string("follow_symlinks"),
.type = NXT_CONF_VLDT_BOOLEAN,
@@ -782,6 +798,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.validator = nxt_conf_vldt_targets_exclusive,
.u.string = "callable",
}, {
+ .name = nxt_string("prefix"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_targets_exclusive,
+ .u.string = "prefix",
+ }, {
.name = nxt_string("targets"),
.type = NXT_CONF_VLDT_OBJECT,
.validator = nxt_conf_vldt_targets,
@@ -800,6 +821,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = {
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("prefix"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_python_prefix,
},
NXT_CONF_VLDT_END
@@ -814,6 +839,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = {
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("prefix"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_python_prefix,
},
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
@@ -1094,6 +1123,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = {
},
#endif
+#if (NXT_HAVE_CGROUP)
+ {
+ .name = nxt_string("cgroup"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_app_cgroup_members,
+ },
+#endif
+
NXT_CONF_VLDT_END
};
@@ -1166,6 +1204,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = {
#endif
+#if (NXT_HAVE_CGROUP)
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[] = {
+ {
+ .name = nxt_string("path"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ .validator = nxt_conf_vldt_cgroup_path,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+#endif
+
+
#if (NXT_HAVE_CLONE_NEWUSER)
static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = {
@@ -1226,9 +1280,10 @@ nxt_int_t
nxt_conf_validate(nxt_conf_validation_t *vldt)
{
nxt_int_t ret;
+ u_char error[NXT_MAX_ERROR_STR];
- vldt->var_fields = nxt_array_create(vldt->pool, 4, sizeof(nxt_var_field_t));
- if (nxt_slow_path(vldt->var_fields == NULL)) {
+ vldt->tstr_state = nxt_tstr_state_new(vldt->pool, 1);
+ if (nxt_slow_path(vldt->tstr_state == NULL)) {
return NXT_ERROR;
}
@@ -1237,7 +1292,17 @@ nxt_conf_validate(nxt_conf_validation_t *vldt)
return ret;
}
- return nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members);
+ ret = nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ ret = nxt_tstr_state_done(vldt->tstr_state, error);
+ if (ret != NXT_OK) {
+ return nxt_conf_vldt_error(vldt, "%s", error);
+ }
+
+ return NXT_OK;
}
@@ -1364,7 +1429,7 @@ nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name,
{
u_char error[NXT_MAX_ERROR_STR];
- if (nxt_var_test(value, vldt->var_fields, error) != NXT_OK) {
+ if (nxt_tstr_test(vldt->tstr_state, value, error) != NXT_OK) {
return nxt_conf_vldt_error(vldt, "%s in the \"%V\" value.",
error, name);
}
@@ -1690,11 +1755,6 @@ static nxt_int_t
nxt_conf_vldt_share(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
- u_char *p;
- nxt_str_t name, temp;
-
- static nxt_str_t uri = nxt_string("$uri");
-
if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
if (nxt_conf_array_elements_count(value) == 0) {
return nxt_conf_vldt_error(vldt, "The \"share\" array "
@@ -1707,22 +1767,6 @@ nxt_conf_vldt_share(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
/* NXT_CONF_STRING */
- if (vldt->ver < 12600) {
- nxt_conf_get_string(value, &name);
-
- temp.length = name.length + uri.length;
-
- temp.start = nxt_mp_get(vldt->conf_pool, temp.length);
- if (nxt_slow_path(temp.start == NULL)) {
- return NXT_ERROR;
- }
-
- p = nxt_cpymem(temp.start, name.start, name.length);
- nxt_memcpy(p, uri.start, uri.length);
-
- nxt_conf_set_string(value, &temp);
- }
-
return nxt_conf_vldt_share_element(vldt, value);
}
@@ -1742,7 +1786,7 @@ nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt,
nxt_conf_get_string(value, &str);
- if (nxt_is_var(&str)) {
+ if (nxt_is_tstr(&str)) {
return nxt_conf_vldt_var(vldt, &share, &str);
}
@@ -1842,6 +1886,28 @@ nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
static nxt_int_t
+nxt_conf_vldt_python_prefix(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ nxt_str_t prefix;
+
+ if (nxt_conf_type(value) != NXT_CONF_STRING) {
+ return nxt_conf_vldt_error(vldt, "The \"prefix\" must be a string "
+ "beginning with \"/\".");
+ }
+
+ nxt_conf_get_string(value, &prefix);
+
+ if (!nxt_strchr_start(&prefix, '/')) {
+ return nxt_conf_vldt_error(vldt, "The \"prefix\" must be a string "
+ "beginning with \"/\".");
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
@@ -2522,12 +2588,12 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
continue;
}
- if (vals->flags & NXT_CONF_VLDT_VAR
+ if (vals->flags & NXT_CONF_VLDT_TSTR
&& nxt_conf_type(member) == NXT_CONF_STRING)
{
nxt_conf_get_string(member, &var);
- if (nxt_is_var(&var)) {
+ if (nxt_is_tstr(&var)) {
ret = nxt_conf_vldt_var(vldt, &name, &var);
if (ret != NXT_OK) {
return ret;
@@ -2731,12 +2797,12 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
"The environment name must not be empty.");
}
- if (nxt_memchr(name->start, '\0', name->length) != NULL) {
+ if (memchr(name->start, '\0', name->length) != NULL) {
return nxt_conf_vldt_error(vldt, "The environment name must not "
"contain null character.");
}
- if (nxt_memchr(name->start, '=', name->length) != NULL) {
+ if (memchr(name->start, '=', name->length) != NULL) {
return nxt_conf_vldt_error(vldt, "The environment name must not "
"contain '=' character.");
}
@@ -2748,7 +2814,7 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_get_string(value, &str);
- if (nxt_memchr(str.start, '\0', str.length) != NULL) {
+ if (memchr(str.start, '\0', str.length) != NULL) {
return nxt_conf_vldt_error(vldt, "The \"%V\" environment value must "
"not contain null character.", name);
}
@@ -2808,6 +2874,35 @@ nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
}
+#if (NXT_HAVE_CGROUP)
+
+static nxt_int_t
+nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ char path[NXT_MAX_PATH_LEN];
+ nxt_str_t cgpath;
+
+ nxt_conf_get_string(value, &cgpath);
+ if (cgpath.length >= NXT_MAX_PATH_LEN - strlen(NXT_CGROUP_ROOT) - 1) {
+ return nxt_conf_vldt_error(vldt, "The cgroup path \"%V\" is too long.",
+ &cgpath);
+ }
+
+ sprintf(path, "/%*s/", (int) cgpath.length, cgpath.start);
+
+ if (cgpath.length == 0 || strstr(path, "/../") != NULL) {
+ return nxt_conf_vldt_error(vldt,
+ "The cgroup path \"%V\" is invalid.",
+ &cgpath);
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
static nxt_int_t
nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
@@ -2947,7 +3042,7 @@ nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
nxt_conf_get_string(value, &str);
- if (nxt_memchr(str.start, '\0', str.length) != NULL) {
+ if (memchr(str.start, '\0', str.length) != NULL) {
return nxt_conf_vldt_error(vldt, "The \"arguments\" array must not "
"contain strings with null character.");
}
@@ -3006,7 +3101,7 @@ nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
nxt_conf_get_string(value, &str);
- if (nxt_memchr(str.start, '\0', str.length) != NULL) {
+ if (memchr(str.start, '\0', str.length) != NULL) {
return nxt_conf_vldt_error(vldt, "The \"classpath\" array must not "
"contain strings with null character.");
}
@@ -3027,7 +3122,7 @@ nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
nxt_conf_get_string(value, &str);
- if (nxt_memchr(str.start, '\0', str.length) != NULL) {
+ if (memchr(str.start, '\0', str.length) != NULL) {
return nxt_conf_vldt_error(vldt, "The \"options\" array must not "
"contain strings with null character.");
}
@@ -3168,7 +3263,7 @@ nxt_conf_vldt_access_log(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
"The \"path\" string must not be empty.");
}
- if (nxt_is_var(&conf.format)) {
+ if (nxt_is_tstr(&conf.format)) {
return nxt_conf_vldt_var(vldt, &format_str, &conf.format);
}
diff --git a/src/nxt_conn.h b/src/nxt_conn.h
index 8a703e9a..5717d3c9 100644
--- a/src/nxt_conn.h
+++ b/src/nxt_conn.h
@@ -312,8 +312,7 @@ NXT_EXPORT void nxt_event_conn_job_sendfile(nxt_task_t *task,
\
nxt_queue_remove(&c->link); \
\
- c->idle = 0; \
- e->idle_conns_cnt--; \
+ e->idle_conns_cnt -= c->idle; \
} while (0)
diff --git a/src/nxt_conn_close.c b/src/nxt_conn_close.c
index 92bd8d1b..bdd66951 100644
--- a/src/nxt_conn_close.c
+++ b/src/nxt_conn_close.c
@@ -119,7 +119,9 @@ nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data)
nxt_socket_close(task, c->socket.fd);
c->socket.fd = -1;
- engine->closed_conns_cnt++;
+ if (c->idle) {
+ engine->closed_conns_cnt++;
+ }
if (timers_pending == 0) {
nxt_work_queue_add(&engine->fast_work_queue,
@@ -155,7 +157,9 @@ nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
nxt_socket_close(task, c->socket.fd);
c->socket.fd = -1;
- engine->closed_conns_cnt++;
+ if (c->idle) {
+ engine->closed_conns_cnt++;
+ }
}
nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler,
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index 09168821..b5e0d831 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -666,6 +666,14 @@ nxt_runtime_controller_socket(nxt_task_t *task, nxt_runtime_t *rt)
#endif
ls->handler = nxt_controller_conn_init;
+#if (NXT_HAVE_UNIX_DOMAIN)
+ if (ls->sockaddr->u.sockaddr.sa_family == AF_UNIX) {
+ const char *path = ls->sockaddr->u.sockaddr_un.sun_path;
+
+ nxt_fs_mkdir_parent((const u_char *) path, 0755);
+ }
+#endif
+
if (nxt_listen_socket_create(task, rt->mem_pool, ls) != NXT_OK) {
return NXT_ERROR;
}
@@ -1255,7 +1263,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
/* Skip UTF-8 BOM. */
if (nxt_buf_mem_used_size(mbuf) >= 3
- && nxt_memcmp(mbuf->pos, "\xEF\xBB\xBF", 3) == 0)
+ && memcmp(mbuf->pos, "\xEF\xBB\xBF", 3) == 0)
{
mbuf->pos += 3;
}
@@ -1629,7 +1637,7 @@ nxt_controller_process_cert(nxt_task_t *task,
name.length = path->length - 1;
name.start = path->start + 1;
- p = nxt_memchr(name.start, '/', name.length);
+ p = memchr(name.start, '/', name.length);
if (p != NULL) {
name.length = p - name.start;
@@ -1932,7 +1940,7 @@ nxt_controller_process_control(nxt_task_t *task,
}
if (!nxt_str_start(path, "applications/", 13)
- || nxt_memcmp(path->start + path->length - 8, "/restart", 8) != 0)
+ || memcmp(path->start + path->length - 8, "/restart", 8) != 0)
{
goto not_found;
}
diff --git a/src/nxt_errno.c b/src/nxt_errno.c
index 869a970f..47479a82 100644
--- a/src/nxt_errno.c
+++ b/src/nxt_errno.c
@@ -78,7 +78,7 @@ nxt_strerror_start(void)
if (length == 0 /* HP-UX empty strings. */
|| nxt_errno == NXT_EINVAL
- || nxt_memcmp(msg, "Unknown error", 13) == 0)
+ || memcmp(msg, "Unknown error", 13) == 0)
{
invalid++;
continue;
@@ -86,8 +86,8 @@ nxt_strerror_start(void)
#if (NXT_AIX)
- if (nxt_memcmp(msg, "Error ", 6) == 0
- && nxt_memcmp(msg + length - 10, " occurred.", 9) == 0)
+ if (memcmp(msg, "Error ", 6) == 0
+ && memcmp(msg + length - 10, " occurred.", 9) == 0)
{
invalid++;
continue;
diff --git a/src/nxt_file.c b/src/nxt_file.c
index 5d38d57e..a3fcda76 100644
--- a/src/nxt_file.c
+++ b/src/nxt_file.c
@@ -500,6 +500,43 @@ nxt_fd_close(nxt_fd_t fd)
}
+FILE *
+nxt_file_fopen(nxt_task_t *task, const char *pathname, const char *mode)
+{
+ int err;
+ FILE *fp;
+
+#if (NXT_DEBUG)
+ nxt_thread_time_update(task->thread);
+#endif
+
+ fp = fopen(pathname, mode);
+ err = (fp == NULL) ? nxt_errno : 0;
+
+ nxt_debug(task, "fopen(\"%s\", \"%s\"): fp:%p err:%d", pathname, mode, fp,
+ err);
+
+ if (nxt_fast_path(fp != NULL)) {
+ return fp;
+ }
+
+ nxt_alert(task, "fopen(\"%s\") failed %E", pathname, err);
+
+ return NULL;
+}
+
+
+void
+nxt_file_fclose(nxt_task_t *task, FILE *fp)
+{
+ nxt_debug(task, "fclose(%p)", fp);
+
+ if (nxt_slow_path(fclose(fp) == -1)) {
+ nxt_alert(task, "fclose() failed %E", nxt_errno);
+ }
+}
+
+
/*
* nxt_file_redirect() redirects the file to the fd descriptor.
* Then the fd descriptor is closed.
diff --git a/src/nxt_file.h b/src/nxt_file.h
index 07c7a22b..945717b3 100644
--- a/src/nxt_file.h
+++ b/src/nxt_file.h
@@ -186,6 +186,10 @@ NXT_EXPORT ssize_t nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size);
NXT_EXPORT ssize_t nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size);
NXT_EXPORT void nxt_fd_close(nxt_fd_t fd);
+NXT_EXPORT FILE *nxt_file_fopen(nxt_task_t *task, const char *pathname,
+ const char *mode);
+NXT_EXPORT void nxt_file_fclose(nxt_task_t *task, FILE *fp);
+
NXT_EXPORT nxt_int_t nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd);
NXT_EXPORT nxt_int_t nxt_file_stderr(nxt_file_t *file);
NXT_EXPORT nxt_int_t nxt_stderr_start(void);
diff --git a/src/nxt_fs.c b/src/nxt_fs.c
index 71498f99..a467da98 100644
--- a/src/nxt_fs.c
+++ b/src/nxt_fs.c
@@ -4,235 +4,10 @@
#include <nxt_main.h>
-#if (NXT_HAVE_FREEBSD_NMOUNT)
-#include <sys/param.h>
-#include <sys/uio.h>
-#endif
-
static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode);
-#if (NXT_HAVE_LINUX_MOUNT)
-
-nxt_int_t
-nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
-{
- int rc;
- const char *fsname;
- unsigned long flags;
-
- 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\", %ul, \"%s\") %E",
- mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno);
-
- return NXT_ERROR;
- }
-
- 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 *fsname;
- struct iovec iov[128];
- char errmsg[256];
-
- if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) {
- nxt_alert(task, "nmount(2) doesn't support \"nodev\" option");
-
- return NXT_ERROR;
- }
-
- flags = 0;
-
- 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 *) 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;
- iov[3].iov_len = nxt_strlen(mnt->dst) + 1;
- iov[4].iov_base = (void *) "target";
- iov[4].iov_len = 7;
- iov[5].iov_base = (void *) mnt->src;
- iov[5].iov_len = nxt_strlen(mnt->src) + 1;
- iov[6].iov_base = (void *) "errmsg";
- iov[6].iov_len = 7;
- iov[7].iov_base = (void *) errmsg;
- iov[7].iov_len = sizeof(errmsg);
-
- iovlen = 8;
-
- data = NULL;
-
- if (mnt->data != NULL) {
- data = (u_char *) nxt_strdup(mnt->data);
- if (nxt_slow_path(data == NULL)) {
- return NXT_ERROR;
- }
-
- end = data - 1;
-
- do {
- p = end + 1;
- end = nxt_strchr(p, '=');
- if (end == NULL) {
- break;
- }
-
- *end = '\0';
-
- iov[iovlen].iov_base = (void *) p;
- iov[iovlen].iov_len = (end - p) + 1;
-
- iovlen++;
-
- p = end + 1;
-
- end = nxt_strchr(p, ',');
- if (end != NULL) {
- *end = '\0';
- }
-
- iov[iovlen].iov_base = (void *) p;
- iov[iovlen].iov_len = nxt_strlen(p) + 1;
-
- iovlen++;
-
- } while (end != NULL && nxt_nitems(iov) > (iovlen + 2));
- }
-
- ret = NXT_OK;
-
- if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) {
- nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg);
- ret = NXT_ERROR;
- }
-
- if (data != NULL) {
- free(data);
- }
-
- return ret;
-}
-
-#endif
-
-
-#if (NXT_HAVE_LINUX_UMOUNT2)
-
-void
-nxt_fs_unmount(const u_char *path)
-{
- if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) {
- nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E",
- path, nxt_errno);
- }
-}
-
-#elif (NXT_HAVE_UNMOUNT)
-
-void
-nxt_fs_unmount(const u_char *path)
-{
- if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) {
- nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno);
- }
-}
-
-#endif
-
-
nxt_int_t
nxt_fs_mkdir_all(const u_char *dir, mode_t mode)
{
@@ -273,6 +48,31 @@ nxt_fs_mkdir_all(const u_char *dir, mode_t mode)
}
+nxt_int_t
+nxt_fs_mkdir_parent(const u_char *path, mode_t mode)
+{
+ char *ptr, *dir;
+ nxt_int_t ret;
+
+ dir = nxt_strdup(path);
+ if (nxt_slow_path(dir == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ret = NXT_OK;
+
+ ptr = strrchr(dir, '/');
+ if (nxt_fast_path(ptr != NULL)) {
+ *ptr = '\0';
+ ret = nxt_fs_mkdir((const u_char *) dir, mode);
+ }
+
+ nxt_free(dir);
+
+ return ret;
+}
+
+
static nxt_int_t
nxt_fs_mkdir(const u_char *dir, mode_t mode)
{
diff --git a/src/nxt_fs.h b/src/nxt_fs.h
index ff589979..c8868d80 100644
--- a/src/nxt_fs.h
+++ b/src/nxt_fs.h
@@ -6,39 +6,8 @@
#define _NXT_FS_H_INCLUDED_
-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;
- 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;
-
-
+nxt_int_t nxt_fs_mkdir_parent(const u_char *path, mode_t mode);
nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode);
-nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt);
-void nxt_fs_unmount(const u_char *path);
#endif /* _NXT_FS_H_INCLUDED_ */
diff --git a/src/nxt_fs_mount.c b/src/nxt_fs_mount.c
new file mode 100644
index 00000000..d9b384e4
--- /dev/null
+++ b/src/nxt_fs_mount.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+#if (NXT_HAVE_FREEBSD_NMOUNT)
+#include <sys/param.h>
+#include <sys/uio.h>
+#endif
+
+
+#if (NXT_HAVE_LINUX_MOUNT)
+
+nxt_int_t
+nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
+{
+ int rc;
+ const char *fsname;
+ unsigned long flags;
+
+ 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\", %ul, \"%s\") %E",
+ mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno);
+
+ return NXT_ERROR;
+ }
+
+ 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 *fsname;
+ struct iovec iov[128];
+ char errmsg[256];
+
+ if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) {
+ nxt_alert(task, "nmount(2) doesn't support \"nodev\" option");
+
+ return NXT_ERROR;
+ }
+
+ flags = 0;
+
+ 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 *) 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;
+ iov[3].iov_len = nxt_strlen(mnt->dst) + 1;
+ iov[4].iov_base = (void *) "target";
+ iov[4].iov_len = 7;
+ iov[5].iov_base = (void *) mnt->src;
+ iov[5].iov_len = nxt_strlen(mnt->src) + 1;
+ iov[6].iov_base = (void *) "errmsg";
+ iov[6].iov_len = 7;
+ iov[7].iov_base = (void *) errmsg;
+ iov[7].iov_len = sizeof(errmsg);
+
+ iovlen = 8;
+
+ data = NULL;
+
+ if (mnt->data != NULL) {
+ data = (u_char *) nxt_strdup(mnt->data);
+ if (nxt_slow_path(data == NULL)) {
+ return NXT_ERROR;
+ }
+
+ end = data - 1;
+
+ do {
+ p = end + 1;
+ end = nxt_strchr(p, '=');
+ if (end == NULL) {
+ break;
+ }
+
+ *end = '\0';
+
+ iov[iovlen].iov_base = (void *) p;
+ iov[iovlen].iov_len = (end - p) + 1;
+
+ iovlen++;
+
+ p = end + 1;
+
+ end = nxt_strchr(p, ',');
+ if (end != NULL) {
+ *end = '\0';
+ }
+
+ iov[iovlen].iov_base = (void *) p;
+ iov[iovlen].iov_len = nxt_strlen(p) + 1;
+
+ iovlen++;
+
+ } while (end != NULL && nxt_nitems(iov) > (iovlen + 2));
+ }
+
+ ret = NXT_OK;
+
+ if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) {
+ nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg);
+ ret = NXT_ERROR;
+ }
+
+ if (data != NULL) {
+ free(data);
+ }
+
+ return ret;
+}
+
+#endif
+
+
+#if (NXT_HAVE_LINUX_UMOUNT2)
+
+void
+nxt_fs_unmount(const u_char *path)
+{
+ if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) {
+ nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E",
+ path, nxt_errno);
+ }
+}
+
+#elif (NXT_HAVE_UNMOUNT)
+
+void
+nxt_fs_unmount(const u_char *path)
+{
+ if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) {
+ nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno);
+ }
+}
+
+#endif
diff --git a/src/nxt_fs_mount.h b/src/nxt_fs_mount.h
new file mode 100644
index 00000000..e0fe6eb2
--- /dev/null
+++ b/src/nxt_fs_mount.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_FS_MOUNT_H_INCLUDED_
+#define _NXT_FS_MOUNT_H_INCLUDED_
+
+
+typedef enum nxt_fs_type_s nxt_fs_type_t;
+typedef enum nxt_fs_flags_s nxt_fs_flags_t;
+typedef struct nxt_fs_mount_s nxt_fs_mount_t;
+
+
+enum nxt_fs_type_s {
+ NXT_FS_UNKNOWN = 0,
+ NXT_FS_BIND,
+ NXT_FS_TMP,
+ NXT_FS_PROC,
+ NXT_FS_LAST,
+};
+
+
+enum nxt_fs_flags_s {
+ 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,
+};
+
+
+struct nxt_fs_mount_s {
+ 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_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt);
+void nxt_fs_unmount(const u_char *path);
+
+
+#endif /* _NXT_FS_MOUNT_H_INCLUDED_ */
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index 852b4866..1e37273f 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -823,7 +823,7 @@ nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data)
field->hopbyhop = 1;
if (field->value_length == 7
- && nxt_memcmp(field->value, "chunked", 7) == 0)
+ && memcmp(field->value, "chunked", 7) == 0)
{
te = NXT_HTTP_TE_CHUNKED;
@@ -2594,7 +2594,7 @@ nxt_h1p_peer_header_parse(nxt_http_peer_t *peer, nxt_buf_mem_t *bm)
p = bm->pos;
- if (nxt_slow_path(nxt_memcmp(p, "HTTP/1.", 7) != 0
+ if (nxt_slow_path(memcmp(p, "HTTP/1.", 7) != 0
|| (p[7] != '0' && p[7] != '1')))
{
return NXT_ERROR;
@@ -2609,7 +2609,7 @@ nxt_h1p_peer_header_parse(nxt_http_peer_t *peer, nxt_buf_mem_t *bm)
p += 12;
length -= 12;
- p = nxt_memchr(p, '\n', length);
+ p = memchr(p, '\n', length);
if (nxt_slow_path(p == NULL)) {
return NXT_AGAIN;
@@ -2868,7 +2868,7 @@ nxt_h1p_peer_transfer_encoding(void *ctx, nxt_http_field_t *field,
field->skip = 1;
if (field->value_length == 7
- && nxt_memcmp(field->value, "chunked", 7) == 0)
+ && memcmp(field->value, "chunked", 7) == 0)
{
r->peer->proto.h1->chunked = 1;
}
diff --git a/src/nxt_http.h b/src/nxt_http.h
index c2e85840..a8725d9f 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -140,6 +140,8 @@ struct nxt_http_request_s {
nxt_buf_t *out;
const nxt_http_request_state_t *state;
+ nxt_nsec_t start_time;
+
nxt_str_t host;
nxt_str_t server_name;
nxt_str_t target;
@@ -167,7 +169,8 @@ struct nxt_http_request_s {
nxt_timer_t timer;
void *timer_data;
- nxt_var_query_t *var_query;
+ nxt_tstr_query_t *tstr_query;
+ nxt_tstr_cache_t tstr_cache;
void *req_rpc_data;
@@ -243,7 +246,7 @@ struct nxt_http_action_s {
nxt_http_route_t *route;
nxt_upstream_t *upstream;
uint32_t upstream_number;
- nxt_var_t *var;
+ nxt_tstr_t *tstr;
nxt_str_t *pass;
} u;
diff --git a/src/nxt_http_js.c b/src/nxt_http_js.c
new file mode 100644
index 00000000..5a08a309
--- /dev/null
+++ b/src/nxt_http_js.c
@@ -0,0 +1,273 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_router.h>
+#include <nxt_http.h>
+#include <njs.h>
+
+
+static njs_int_t nxt_http_js_ext_uri(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t nxt_http_js_ext_host(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t nxt_http_js_ext_remote_addr(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t nxt_http_js_ext_get_arg(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t nxt_http_js_ext_get_header(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+static njs_int_t nxt_http_js_ext_get_cookie(njs_vm_t *vm,
+ njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval);
+
+
+static njs_external_t nxt_http_js_proto[] = {
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("uri"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = nxt_http_js_ext_uri,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("host"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = nxt_http_js_ext_host,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_PROPERTY,
+ .name.string = njs_str("remoteAddr"),
+ .enumerable = 1,
+ .u.property = {
+ .handler = nxt_http_js_ext_remote_addr,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_OBJECT,
+ .name.string = njs_str("args"),
+ .enumerable = 1,
+ .u.object = {
+ .enumerable = 1,
+ .prop_handler = nxt_http_js_ext_get_arg,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_OBJECT,
+ .name.string = njs_str("headers"),
+ .enumerable = 1,
+ .u.object = {
+ .enumerable = 1,
+ .prop_handler = nxt_http_js_ext_get_header,
+ }
+ },
+
+ {
+ .flags = NJS_EXTERN_OBJECT,
+ .name.string = njs_str("cookies"),
+ .enumerable = 1,
+ .u.object = {
+ .enumerable = 1,
+ .prop_handler = nxt_http_js_ext_get_cookie,
+ }
+ },
+};
+
+
+void
+nxt_http_register_js_proto(nxt_js_conf_t *jcf)
+{
+ nxt_js_set_proto(jcf, nxt_http_js_proto, njs_nitems(nxt_http_js_proto));
+}
+
+
+static njs_int_t
+nxt_http_js_ext_uri(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ nxt_http_request_t *r;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ return njs_vm_value_string_set(vm, retval, r->path->start, r->path->length);
+}
+
+
+static njs_int_t
+nxt_http_js_ext_host(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ nxt_http_request_t *r;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ return njs_vm_value_string_set(vm, retval, r->host.start, r->host.length);
+}
+
+
+static njs_int_t
+nxt_http_js_ext_remote_addr(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ nxt_http_request_t *r;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ return njs_vm_value_string_set(vm, retval,
+ nxt_sockaddr_address(r->remote),
+ r->remote->address_length);
+}
+
+
+static njs_int_t
+nxt_http_js_ext_get_arg(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_int_t rc;
+ njs_str_t key;
+ nxt_array_t *args;
+ nxt_http_request_t *r;
+ nxt_http_name_value_t *nv, *start, *end;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ rc = njs_vm_prop_name(vm, prop, &key);
+ if (rc != NJS_OK) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ args = nxt_http_arguments_parse(r);
+ if (nxt_slow_path(args == NULL)) {
+ return NJS_ERROR;
+ }
+
+ start = args->elts;
+ end = start + args->nelts;
+
+ for (nv = start; nv < end; nv++) {
+
+ if (key.length == nv->name_length
+ && memcmp(key.start, nv->name, nv->name_length) == 0)
+ {
+ return njs_vm_value_string_set(vm, retval, nv->value,
+ nv->value_length);
+ }
+ }
+
+ njs_value_undefined_set(retval);
+
+ return NJS_DECLINED;
+}
+
+
+static njs_int_t
+nxt_http_js_ext_get_header(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_int_t rc;
+ njs_str_t key;
+ nxt_http_field_t *f;
+ nxt_http_request_t *r;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ rc = njs_vm_prop_name(vm, prop, &key);
+ if (rc != NJS_OK) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ nxt_list_each(f, r->fields) {
+
+ if (key.length == f->name_length
+ && memcmp(key.start, f->name, f->name_length) == 0)
+ {
+ return njs_vm_value_string_set(vm, retval, f->value,
+ f->value_length);
+ }
+
+ } nxt_list_loop;
+
+ njs_value_undefined_set(retval);
+
+ return NJS_DECLINED;
+}
+
+
+static njs_int_t
+nxt_http_js_ext_get_cookie(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_int_t rc;
+ njs_str_t key;
+ nxt_array_t *cookies;
+ nxt_http_request_t *r;
+ nxt_http_name_value_t *nv, *start, *end;
+
+ r = njs_vm_external(vm, nxt_js_proto_id, value);
+ if (r == NULL) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ rc = njs_vm_prop_name(vm, prop, &key);
+ if (rc != NJS_OK) {
+ njs_value_undefined_set(retval);
+ return NJS_DECLINED;
+ }
+
+ cookies = nxt_http_cookies_parse(r);
+ if (nxt_slow_path(cookies == NULL)) {
+ return NJS_ERROR;
+ }
+
+ start = cookies->elts;
+ end = start + cookies->nelts;
+
+ for (nv = start; nv < end; nv++) {
+
+ if (key.length == nv->name_length
+ && memcmp(key.start, nv->name, nv->name_length) == 0)
+ {
+ return njs_vm_value_string_set(vm, retval, nv->value,
+ nv->value_length);
+ }
+ }
+
+ njs_value_undefined_set(retval);
+
+ return NJS_DECLINED;
+}
diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c
index 1bb4291f..f39d8f67 100644
--- a/src/nxt_http_parse.c
+++ b/src/nxt_http_parse.c
@@ -357,7 +357,7 @@ space_after_target:
} while (*p == ' ');
- if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
+ if (memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
switch (end - p) {
case 8:
@@ -412,7 +412,7 @@ space_after_target:
if (nxt_fast_path(ver.ui64 == http11.ui64
|| ver.ui64 == http10.ui64
- || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0
+ || (memcmp(ver.str, "HTTP/1.", 7) == 0
&& ver.s.minor >= '0' && ver.s.minor <= '9')))
{
rp->version.ui64 = ver.ui64;
@@ -464,7 +464,7 @@ space_after_target:
return nxt_http_parse_field_name(rp, pos, end);
}
- if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0
+ if (memcmp(ver.s.prefix, "HTTP/", 5) == 0
&& ver.s.major >= '0' && ver.s.major <= '9'
&& ver.s.point == '.'
&& ver.s.minor >= '0' && ver.s.minor <= '9')
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 943ad82d..73ffd2f0 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -278,8 +278,12 @@ nxt_http_request_create(nxt_task_t *task)
r->resp.content_length_n = -1;
r->state = &nxt_http_request_init_state;
+ r->start_time = nxt_thread_monotonic_time(task->thread);
+
task->thread->engine->requests_cnt++;
+ r->tstr_cache.var.pool = mp;
+
return r;
fail:
@@ -793,7 +797,7 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data)
void
nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
{
- nxt_var_t *log_format;
+ nxt_tstr_t *log_format;
nxt_http_proto_t proto;
nxt_http_request_t *r;
nxt_http_protocol_t protocol;
@@ -1035,14 +1039,11 @@ nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, const u_char *end)
for (p = start; p < end; p++) {
c = *p;
- if (c == '=') {
+ if (c == '=' && name == NULL) {
while (start[0] == ' ') { start++; }
name_length = p - start;
-
- if (name_length != 0) {
- name = start;
- }
+ name = start;
start = p + 1;
diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c
index 9f3c4fc5..b50e4ad0 100644
--- a/src/nxt_http_return.c
+++ b/src/nxt_http_return.c
@@ -9,7 +9,7 @@
typedef struct {
nxt_http_status_t status;
- nxt_var_t *location;
+ nxt_tstr_t *location;
nxt_str_t encoded;
} nxt_http_return_conf_t;
@@ -25,7 +25,7 @@ static nxt_http_action_t *nxt_http_return(nxt_task_t *task,
static nxt_int_t nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
const nxt_str_t *location);
static void nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data);
static const nxt_http_request_state_t nxt_http_return_send_state;
@@ -57,13 +57,13 @@ nxt_http_return_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
nxt_conf_get_string(acf->location, &str);
- conf->location = nxt_var_compile(&str, mp, rtcf->var_fields, 0);
+ conf->location = nxt_tstr_compile(rtcf->tstr_state, &str, 0);
if (nxt_slow_path(conf->location == NULL)) {
return NXT_ERROR;
}
- if (nxt_var_is_const(conf->location)) {
- nxt_var_raw(conf->location, &str);
+ if (nxt_tstr_is_const(conf->location)) {
+ nxt_tstr_str(conf->location, &str);
return nxt_http_return_encode(mp, &conf->encoded, &str);
}
@@ -76,6 +76,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
nxt_int_t ret;
+ nxt_router_conf_t *rtcf;
nxt_http_return_ctx_t *ctx;
nxt_http_return_conf_t *conf;
@@ -88,7 +89,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_set(&loc, "");
} else {
- nxt_var_raw(conf->location, &loc);
+ nxt_tstr_str(conf->location, &loc);
}
nxt_debug(task, "http return: %d (loc: \"%V\")", conf->status, &loc);
@@ -114,7 +115,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
r->status = conf->status;
r->resp.content_length_n = 0;
- if (ctx == NULL || nxt_var_is_const(conf->location)) {
+ if (ctx == NULL || nxt_tstr_is_const(conf->location)) {
if (ctx != NULL) {
ctx->encoded = conf->encoded;
}
@@ -122,16 +123,19 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_return_send_ready(task, r, ctx);
} else {
- ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
+ rtcf = r->conf->socket_conf->router_conf;
+
+ ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
+ &r->tstr_cache, r, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
- nxt_var_query(task, r->var_query, conf->location, &ctx->location);
+ nxt_tstr_query(task, r->tstr_query, conf->location, &ctx->location);
- nxt_var_query_resolve(task, r->var_query, ctx,
- nxt_http_return_send_ready,
- nxt_http_return_var_error);
+ nxt_tstr_query_resolve(task, r->tstr_query, ctx,
+ nxt_http_return_send_ready,
+ nxt_http_return_send_error);
}
return NULL;
@@ -213,7 +217,7 @@ fail:
static void
-nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data)
+nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data)
{
nxt_http_request_t *r;
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index cdc9077f..7081ff7e 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -193,8 +193,8 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action);
static nxt_http_action_t *nxt_http_pass_var(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_action_t *action);
-static void nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data);
static nxt_int_t nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf,
nxt_str_t *pass, nxt_http_action_t *action);
static nxt_int_t nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
@@ -673,8 +673,8 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_get_string(acf.pass, &pass);
- action->u.var = nxt_var_compile(&pass, mp, rtcf->var_fields, 0);
- if (nxt_slow_path(action->u.var == NULL)) {
+ action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, &pass, 0);
+ if (nxt_slow_path(action->u.tstr == NULL)) {
return NXT_ERROR;
}
@@ -1016,7 +1016,7 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) {
tmp.start = test.start;
- p = nxt_memchr(test.start, '*', test.length);
+ p = memchr(test.start, '*', test.length);
if (p == NULL) {
/* No '*' found - EXACT pattern. */
@@ -1272,8 +1272,8 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NXT_OK;
}
- if (nxt_var_is_const(action->u.var)) {
- nxt_var_raw(action->u.var, &pass);
+ if (nxt_tstr_is_const(action->u.tstr)) {
+ nxt_tstr_str(action->u.tstr, &pass);
ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass,
action);
@@ -1293,17 +1293,21 @@ static nxt_http_action_t *
nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
- nxt_int_t ret;
- nxt_str_t str;
- nxt_var_t *var;
+ nxt_int_t ret;
+ nxt_str_t str;
+ nxt_tstr_t *tstr;
+ nxt_router_conf_t *rtcf;
- var = action->u.var;
+ tstr = action->u.tstr;
- nxt_var_raw(var, &str);
+ nxt_tstr_str(tstr, &str);
nxt_debug(task, "http pass: \"%V\"", &str);
- ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
+ rtcf = r->conf->socket_conf->router_conf;
+
+ ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->tstr_cache,
+ r, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -1316,10 +1320,10 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
- nxt_var_query(task, r->var_query, var, action->u.pass);
- nxt_var_query_resolve(task, r->var_query, action,
- nxt_http_pass_var_ready,
- nxt_http_pass_var_error);
+ nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass);
+ nxt_tstr_query_resolve(task, r->tstr_query, action,
+ nxt_http_pass_query_ready,
+ nxt_http_pass_query_error);
return NULL;
fail:
@@ -1330,7 +1334,7 @@ fail:
static void
-nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data)
+nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data)
{
nxt_int_t ret;
nxt_router_conf_t *rtcf;
@@ -1359,7 +1363,7 @@ nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data)
static void
-nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data)
+nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data)
{
nxt_http_request_t *r;
@@ -1414,7 +1418,7 @@ nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
nxt_memzero(segments, n * sizeof(nxt_str_t));
do {
- p = nxt_memchr(rest.start, '/', rest.length);
+ p = memchr(rest.start, '/', rest.length);
if (p != NULL) {
n--;
@@ -1497,8 +1501,8 @@ nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NULL;
}
- action->u.var = nxt_var_compile(pass, mp, rtcf->var_fields, 0);
- if (nxt_slow_path(action->u.var == NULL)) {
+ action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, pass, 0);
+ if (nxt_slow_path(action->u.tstr == NULL)) {
return NULL;
}
@@ -1740,15 +1744,15 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
break;
case NXT_HTTP_ROUTE_ADDR_EXACT:
- match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
sizeof(struct in_addr))
== 0);
break;
case NXT_HTTP_ROUTE_ADDR_RANGE:
- match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
sizeof(struct in_addr)) >= 0
- && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
+ && memcmp(&sin->sin_addr, &p->addr.v4.end,
sizeof(struct in_addr)) <= 0);
break;
@@ -1786,15 +1790,15 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
break;
case NXT_HTTP_ROUTE_ADDR_EXACT:
- match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
sizeof(struct in6_addr))
== 0);
break;
case NXT_HTTP_ROUTE_ADDR_RANGE:
- match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
sizeof(struct in6_addr)) >= 0
- && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
+ && memcmp(&sin6->sin6_addr, &p->addr.v6.end,
sizeof(struct in6_addr)) <= 0);
break;
@@ -1937,7 +1941,7 @@ nxt_http_route_test_argument(nxt_http_request_t *r,
if (rule->u.name.hash == nv->hash
&& rule->u.name.length == nv->name_length
- && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
+ && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
{
ret = nxt_http_route_test_rule(r, rule, nv->value,
nv->value_length);
@@ -2015,7 +2019,7 @@ nxt_http_route_test_cookie(nxt_http_request_t *r,
if (rule->u.name.hash == nv->hash
&& rule->u.name.length == nv->name_length
- && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
+ && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
{
ret = nxt_http_route_test_rule(r, rule, nv->value,
nv->value_length);
@@ -2158,7 +2162,7 @@ nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
nxt_int_t n;
if (case_sensitive) {
- n = nxt_memcmp(start, test, test_length);
+ n = memcmp(start, test, test_length);
} else {
n = nxt_memcasecmp(start, test, test_length);
diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c
index 34455af4..5a0d7679 100644
--- a/src/nxt_http_route_addr.c
+++ b/src/nxt_http_route_addr.c
@@ -93,7 +93,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
inet6 = &pattern->addr.v6;
- delim = nxt_memchr(addr.start, '-', addr.length);
+ delim = memchr(addr.start, '-', addr.length);
if (delim != NULL) {
len = delim - addr.start;
if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) {
@@ -115,7 +115,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
return NXT_ADDR_PATTERN_FORMAT_ERROR;
}
- if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end,
+ if (nxt_slow_path(memcmp(&inet6->start, &inet6->end,
sizeof(struct in6_addr)) > 0))
{
return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
@@ -126,7 +126,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
goto parse_port;
}
- delim = nxt_memchr(addr.start, '/', addr.length);
+ delim = memchr(addr.start, '/', addr.length);
if (delim != NULL) {
cidr_prefix = nxt_int_parse(delim + 1,
addr.start + addr.length - (delim + 1));
@@ -201,7 +201,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
base->addr_family = AF_INET;
- delim = nxt_memchr(addr.start, ':', addr.length);
+ delim = memchr(addr.start, ':', addr.length);
if (delim != NULL) {
port.start = delim + 1;
port.length = addr.start + addr.length - port.start;
@@ -210,7 +210,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
inet = &pattern->addr.v4;
- delim = nxt_memchr(addr.start, '-', addr.length);
+ delim = memchr(addr.start, '-', addr.length);
if (delim != NULL) {
inet->start = nxt_inet_addr(addr.start, delim - addr.start);
if (nxt_slow_path(inet->start == INADDR_NONE)) {
@@ -223,7 +223,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
return NXT_ADDR_PATTERN_FORMAT_ERROR;
}
- if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end,
+ if (nxt_slow_path(memcmp(&inet->start, &inet->end,
sizeof(struct in_addr)) > 0))
{
return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
@@ -234,7 +234,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
goto parse_port;
}
- delim = nxt_memchr(addr.start, '/', addr.length);
+ delim = memchr(addr.start, '/', addr.length);
if (delim != NULL) {
cidr_prefix = nxt_int_parse(delim + 1,
addr.start + addr.length - (delim + 1));
@@ -283,7 +283,7 @@ parse_port:
return NXT_OK;
}
- delim = nxt_memchr(port.start, '-', port.length - 1);
+ delim = memchr(port.start, '-', port.length - 1);
if (delim != NULL) {
ret = nxt_int_parse(port.start, delim - port.start);
if (nxt_slow_path(ret < 0 || ret > 65535)) {
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index 0507e038..68174b9d 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -8,7 +8,7 @@
typedef struct {
- nxt_var_t *var;
+ nxt_tstr_t *tstr;
#if (NXT_HAVE_OPENAT2)
u_char *fname;
#endif
@@ -21,7 +21,7 @@ typedef struct {
nxt_http_static_share_t *shares;
nxt_str_t index;
#if (NXT_HAVE_OPENAT2)
- nxt_var_t *chroot;
+ nxt_tstr_t *chroot;
nxt_uint_t resolve;
#endif
nxt_http_route_rule_t *types;
@@ -48,7 +48,7 @@ static nxt_http_action_t *nxt_http_static(nxt_task_t *task,
static void nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_static_ctx_t *ctx);
static void nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data);
static void nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_static_ctx_t *ctx, nxt_http_status_t status);
#if (NXT_HAVE_OPENAT2)
@@ -77,7 +77,7 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
uint32_t i;
nxt_mp_t *mp;
nxt_str_t str, *ret;
- nxt_var_t *var;
+ nxt_tstr_t *tstr;
nxt_conf_value_t *cv;
nxt_router_conf_t *rtcf;
nxt_http_static_conf_t *conf;
@@ -104,13 +104,13 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
cv = nxt_conf_get_array_element_or_itself(acf->share, i);
nxt_conf_get_string(cv, &str);
- var = nxt_var_compile(&str, mp, rtcf->var_fields, NXT_VAR_STRZ);
- if (nxt_slow_path(var == NULL)) {
+ tstr = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_STRZ);
+ if (nxt_slow_path(tstr == NULL)) {
return NXT_ERROR;
}
- conf->shares[i].var = var;
- conf->shares[i].is_const = nxt_var_is_const(var);
+ conf->shares[i].tstr = tstr;
+ conf->shares[i].is_const = nxt_tstr_is_const(tstr);
}
if (acf->index == NULL) {
@@ -130,20 +130,20 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_str_t chr, shr;
nxt_bool_t is_const;
- conf->chroot = nxt_var_compile(&acf->chroot, mp, rtcf->var_fields,
- NXT_VAR_STRZ);
+ conf->chroot = nxt_tstr_compile(rtcf->tstr_state, &acf->chroot,
+ NXT_TSTR_STRZ);
if (nxt_slow_path(conf->chroot == NULL)) {
return NXT_ERROR;
}
- is_const = nxt_var_is_const(conf->chroot);
+ is_const = nxt_tstr_is_const(conf->chroot);
for (i = 0; i < conf->nshares; i++) {
conf->shares[i].is_const &= is_const;
if (conf->shares[i].is_const) {
- nxt_var_raw(conf->chroot, &chr);
- nxt_var_raw(conf->shares[i].var, &shr);
+ nxt_tstr_str(conf->chroot, &chr);
+ nxt_tstr_str(conf->shares[i].tstr, &shr);
conf->shares[i].fname = nxt_http_static_chroot_match(chr.start,
shr.start);
@@ -229,6 +229,7 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_static_ctx_t *ctx)
{
nxt_int_t ret;
+ nxt_router_conf_t *rtcf;
nxt_http_static_conf_t *conf;
nxt_http_static_share_t *share;
@@ -240,14 +241,14 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_t shr;
nxt_str_t idx;
- nxt_var_raw(share->var, &shr);
+ nxt_tstr_str(share->tstr, &shr);
idx = conf->index;
#if (NXT_HAVE_OPENAT2)
nxt_str_t chr;
if (conf->chroot != NULL) {
- nxt_var_raw(conf->chroot, &chr);
+ nxt_tstr_str(conf->chroot, &chr);
} else {
nxt_str_set(&chr, "");
@@ -261,34 +262,37 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
#endif /* NXT_DEBUG */
if (share->is_const) {
- nxt_var_raw(share->var, &ctx->share);
+ nxt_tstr_str(share->tstr, &ctx->share);
#if (NXT_HAVE_OPENAT2)
if (conf->chroot != NULL && ctx->share_idx == 0) {
- nxt_var_raw(conf->chroot, &ctx->chroot);
+ nxt_tstr_str(conf->chroot, &ctx->chroot);
}
#endif
nxt_http_static_send_ready(task, r, ctx);
} else {
- ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
+ rtcf = r->conf->socket_conf->router_conf;
+
+ ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
+ &r->tstr_cache, r, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
return;
}
- nxt_var_query(task, r->var_query, share->var, &ctx->share);
+ nxt_tstr_query(task, r->tstr_query, share->tstr, &ctx->share);
#if (NXT_HAVE_OPENAT2)
if (conf->chroot != NULL && ctx->share_idx == 0) {
- nxt_var_query(task, r->var_query, conf->chroot, &ctx->chroot);
+ nxt_tstr_query(task, r->tstr_query, conf->chroot, &ctx->chroot);
}
#endif
- nxt_var_query_resolve(task, r->var_query, ctx,
- nxt_http_static_send_ready,
- nxt_http_static_var_error);
+ nxt_tstr_query_resolve(task, r->tstr_query, ctx,
+ nxt_http_static_send_ready,
+ nxt_http_static_send_error);
}
}
@@ -658,7 +662,7 @@ fail:
static void
-nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data)
+nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data)
{
nxt_http_request_t *r;
diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c
index 5a632b24..fa0244db 100644
--- a/src/nxt_http_variables.c
+++ b/src/nxt_http_variables.c
@@ -9,6 +9,8 @@
static nxt_int_t nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str,
void *ctx, uint16_t field);
+static nxt_int_t nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str,
+ void *ctx, uint16_t field);
static nxt_int_t nxt_http_var_method(nxt_task_t *task, nxt_str_t *str,
void *ctx, uint16_t field);
static nxt_int_t nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str,
@@ -46,6 +48,9 @@ static nxt_var_decl_t nxt_http_vars[] = {
.name = nxt_string("dollar"),
.handler = nxt_http_var_dollar,
}, {
+ .name = nxt_string("request_time"),
+ .handler = nxt_http_var_request_time,
+ }, {
.name = nxt_string("method"),
.handler = nxt_http_var_method,
}, {
@@ -111,6 +116,34 @@ nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
static nxt_int_t
+nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, void *ctx,
+ uint16_t field)
+{
+ u_char *p;
+ nxt_msec_t ms;
+ nxt_nsec_t now;
+ nxt_http_request_t *r;
+
+ r = ctx;
+
+ now = nxt_thread_monotonic_time(task->thread);
+ ms = (now - r->start_time) / 1000000;
+
+ str->start = nxt_mp_nget(r->mem_pool, NXT_TIME_T_LEN + 4);
+ if (nxt_slow_path(str->start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = nxt_sprintf(str->start, str->start + NXT_TIME_T_LEN, "%T.%03M",
+ (nxt_time_t) ms / 1000, ms % 1000);
+
+ str->length = p - str->start;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
{
nxt_http_request_t *r;
@@ -374,7 +407,7 @@ nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
rtcf = r->conf->socket_conf->router_conf;
- vf = nxt_var_field_get(rtcf->var_fields, field);
+ vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
args = nxt_http_arguments_parse(r);
if (nxt_slow_path(args == NULL)) {
@@ -388,7 +421,7 @@ nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
if (vf->hash == nv->hash
&& vf->name.length == nv->name_length
- && nxt_memcmp(vf->name.start, nv->name, nv->name_length) == 0)
+ && memcmp(vf->name.start, nv->name, nv->name_length) == 0)
{
str->start = nv->value;
str->length = nv->value_length;
@@ -417,7 +450,7 @@ nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
rtcf = r->conf->socket_conf->router_conf;
- vf = nxt_var_field_get(rtcf->var_fields, field);
+ vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
nxt_list_each(f, r->fields) {
@@ -452,7 +485,7 @@ nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
rtcf = r->conf->socket_conf->router_conf;
- vf = nxt_var_field_get(rtcf->var_fields, field);
+ vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field);
cookies = nxt_http_cookies_parse(r);
if (nxt_slow_path(cookies == NULL)) {
@@ -466,7 +499,7 @@ nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field)
if (vf->hash == nv->hash
&& vf->name.length == nv->name_length
- && nxt_memcmp(vf->name.start, nv->name, nv->name_length) == 0)
+ && memcmp(vf->name.start, nv->name, nv->name_length) == 0)
{
str->start = nv->value;
str->length = nv->value_length;
diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c
index 796da4c6..b6b13c59 100644
--- a/src/nxt_isolation.c
+++ b/src/nxt_isolation.c
@@ -6,6 +6,7 @@
#include <nxt_application.h>
#include <nxt_process.h>
#include <nxt_isolation.h>
+#include <nxt_cgroup.h>
#if (NXT_HAVE_MNTENT_H)
#include <mntent.h>
@@ -15,6 +16,11 @@
static nxt_int_t nxt_isolation_set(nxt_task_t *task,
nxt_conf_value_t *isolation, nxt_process_t *process);
+#if (NXT_HAVE_CGROUP)
+static nxt_int_t nxt_isolation_set_cgroup(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+#endif
+
#if (NXT_HAVE_CLONE)
static nxt_int_t nxt_isolation_set_namespaces(nxt_task_t *task,
nxt_conf_value_t *isolation, nxt_process_t *process);
@@ -155,6 +161,14 @@ static nxt_int_t
nxt_isolation_set(nxt_task_t *task, nxt_conf_value_t *isolation,
nxt_process_t *process)
{
+#if (NXT_HAVE_CGROUP)
+ if (nxt_slow_path(nxt_isolation_set_cgroup(task, isolation, process)
+ != NXT_OK))
+ {
+ return NXT_ERROR;
+ }
+#endif
+
#if (NXT_HAVE_CLONE)
if (nxt_slow_path(nxt_isolation_set_namespaces(task, isolation, process)
!= NXT_OK))
@@ -197,6 +211,42 @@ nxt_isolation_set(nxt_task_t *task, nxt_conf_value_t *isolation,
}
+#if (NXT_HAVE_CGROUP)
+
+static nxt_int_t
+nxt_isolation_set_cgroup(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+ nxt_str_t str;
+ nxt_conf_value_t *obj;
+
+ static nxt_str_t cgname = nxt_string("cgroup");
+ static nxt_str_t path = nxt_string("path");
+
+ obj = nxt_conf_get_object_member(isolation, &cgname, NULL);
+ if (obj == NULL) {
+ return NXT_OK;
+ }
+
+ obj = nxt_conf_get_object_member(obj, &path, NULL);
+ if (obj == NULL) {
+ return NXT_ERROR;
+ }
+
+ nxt_conf_get_string(obj, &str);
+ process->isolation.cgroup.path = nxt_mp_alloc(process->mem_pool,
+ str.length + 1);
+ nxt_memcpy(process->isolation.cgroup.path, str.start, str.length);
+ process->isolation.cgroup.path[str.length] = '\0';
+
+ process->isolation.cgroup_cleanup = nxt_cgroup_cleanup;
+
+ return NXT_OK;
+}
+
+#endif
+
+
#if (NXT_HAVE_CLONE)
static nxt_int_t
diff --git a/src/nxt_js.c b/src/nxt_js.c
new file mode 100644
index 00000000..aa3c4af5
--- /dev/null
+++ b/src/nxt_js.c
@@ -0,0 +1,299 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+struct nxt_js_s {
+ uint32_t index;
+ njs_vm_t *vm;
+};
+
+
+struct nxt_js_conf_s {
+ nxt_mp_t *pool;
+ njs_vm_t *vm;
+ njs_uint_t protos;
+ njs_external_t *proto;
+ nxt_array_t *funcs;
+};
+
+
+njs_int_t nxt_js_proto_id;
+
+
+nxt_js_conf_t *
+nxt_js_conf_new(nxt_mp_t *mp)
+{
+ njs_vm_opt_t opts;
+ nxt_js_conf_t *jcf;
+
+ jcf = nxt_mp_zget(mp, sizeof(nxt_js_conf_t));
+ if (nxt_slow_path(jcf == NULL)) {
+ return NULL;
+ }
+
+ jcf->pool = mp;
+
+ njs_vm_opt_init(&opts);
+
+ jcf->vm = njs_vm_create(&opts);
+ if (nxt_slow_path(jcf->vm == NULL)) {
+ return NULL;
+ }
+
+ jcf->funcs = nxt_array_create(mp, 4, sizeof(nxt_str_t));
+ if (nxt_slow_path(jcf->funcs == NULL)) {
+ return NULL;
+ }
+
+ return jcf;
+}
+
+
+void
+nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, njs_uint_t n)
+{
+ jcf->protos = n;
+ jcf->proto = proto;
+}
+
+
+nxt_js_t *
+nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz)
+{
+ size_t size;
+ u_char *p, *start;
+ nxt_js_t *js;
+ nxt_str_t *func;
+
+ static nxt_str_t func_str = nxt_string("function(uri, host, remoteAddr, "
+ "args, headers, cookies) {"
+ " return ");
+
+ /*
+ * Appending a terminating null character if strz is true.
+ */
+ static nxt_str_t strz_str = nxt_string(" + '\\x00'");
+
+ size = func_str.length + str->length + 1;
+
+ if (strz) {
+ size += strz_str.length;
+ }
+
+ start = nxt_mp_nget(jcf->pool, size);
+ if (nxt_slow_path(start == NULL)) {
+ return NULL;
+ }
+
+ p = start;
+
+ p = nxt_cpymem(p, func_str.start, func_str.length);
+ p = nxt_cpymem(p, str->start, str->length);
+
+ if (strz) {
+ p = nxt_cpymem(p, strz_str.start, strz_str.length);
+ }
+
+ *p++ = '}';
+
+ js = nxt_mp_get(jcf->pool, sizeof(nxt_js_t));
+ if (nxt_slow_path(js == NULL)) {
+ return NULL;
+ }
+
+ js->vm = jcf->vm;
+
+ func = nxt_array_add(jcf->funcs);
+ if (nxt_slow_path(func == NULL)) {
+ return NULL;
+ }
+
+ func->start = start;
+ func->length = p - start;
+
+ js->index = jcf->funcs->nelts - 1;
+
+ return js;
+}
+
+
+nxt_int_t
+nxt_js_compile(nxt_js_conf_t *jcf)
+{
+ size_t size;
+ u_char *p, *start;
+ njs_int_t ret;
+ nxt_str_t *func;
+ nxt_uint_t i;
+
+ size = 2;
+ func = jcf->funcs->elts;
+
+ for (i = 0; i < jcf->funcs->nelts; i++) {
+ size += func[i].length + 1;
+ }
+
+ start = nxt_mp_nget(jcf->pool, size);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = start;
+ *p++ = '[';
+
+ func = jcf->funcs->elts;
+
+ for (i = 0; i < jcf->funcs->nelts; i++) {
+ p = nxt_cpymem(p, func[i].start, func[i].length);
+ *p++ = ',';
+ }
+
+ *p++ = ']';
+
+ nxt_js_proto_id = njs_vm_external_prototype(jcf->vm, jcf->proto,
+ jcf->protos);
+ if (nxt_slow_path(nxt_js_proto_id < 0)) {
+ return NXT_ERROR;
+ }
+
+ ret = njs_vm_compile(jcf->vm, &start, p);
+
+ return (ret == NJS_OK) ? NXT_OK : NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error)
+{
+ u_char *start;
+ nxt_str_t err;
+ njs_int_t ret;
+ njs_str_t res;
+
+ start = nxt_mp_nget(jcf->pool, str->length);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(start, str->start, str->length);
+
+ ret = njs_vm_compile(jcf->vm, &start, start + str->length);
+
+ if (nxt_slow_path(ret != NJS_OK)) {
+ (void) njs_vm_retval_string(jcf->vm, &res);
+
+ err.start = res.start;
+ err.length = res.length;
+
+ nxt_sprintf(error, error + NXT_MAX_ERROR_STR, "\"%V\"%Z", &err);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js,
+ nxt_str_t *str, void *ctx)
+{
+ njs_vm_t *vm;
+ njs_int_t rc, ret;
+ njs_str_t res;
+ njs_value_t *array, *value;
+ njs_function_t *func;
+ njs_opaque_value_t opaque_value, arguments[6];
+
+ static const njs_str_t uri_str = njs_str("uri");
+ static const njs_str_t host_str = njs_str("host");
+ static const njs_str_t remote_addr_str = njs_str("remoteAddr");
+ static const njs_str_t args_str = njs_str("args");
+ static const njs_str_t headers_str = njs_str("headers");
+ static const njs_str_t cookies_str = njs_str("cookies");
+
+ vm = cache->vm;
+
+ if (vm == NULL) {
+ vm = njs_vm_clone(js->vm, ctx);
+ if (nxt_slow_path(vm == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ret = njs_vm_start(vm);
+ if (ret != NJS_OK) {
+ return NXT_ERROR;
+ }
+
+ array = njs_vm_retval(vm);
+
+ cache->vm = vm;
+ cache->array = *array;
+ }
+
+ value = njs_vm_array_prop(vm, &cache->array, js->index, &opaque_value);
+ func = njs_value_function(value);
+
+ ret = njs_vm_external_create(vm, njs_value_arg(&opaque_value),
+ nxt_js_proto_id, ctx, 0);
+ if (nxt_slow_path(ret != NJS_OK)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &uri_str,
+ &arguments[0]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &host_str,
+ &arguments[1]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value),
+ &remote_addr_str, &arguments[2]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &args_str,
+ &arguments[3]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &headers_str,
+ &arguments[4]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &cookies_str,
+ &arguments[5]);
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ret = njs_vm_call(vm, func, njs_value_arg(&arguments), 6);
+
+ rc = njs_vm_retval_string(vm, &res);
+ if (rc != NJS_OK) {
+ return NXT_ERROR;
+ }
+
+ if (ret != NJS_OK) {
+ nxt_alert(task, "js exception: %V", &res);
+ return NXT_ERROR;
+ }
+
+ str->length = res.length;
+ str->start = res.start;
+
+ return NXT_OK;
+}
diff --git a/src/nxt_js.h b/src/nxt_js.h
new file mode 100644
index 00000000..dea43fe3
--- /dev/null
+++ b/src/nxt_js.h
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_JS_H_INCLUDED_
+#define _NXT_JS_H_INCLUDED_
+
+#if (NXT_HAVE_NJS)
+
+#include <njs_main.h>
+
+
+typedef struct nxt_js_s nxt_js_t;
+typedef struct nxt_js_conf_s nxt_js_conf_t;
+
+
+typedef struct {
+ njs_vm_t *vm;
+ njs_value_t array;
+} nxt_js_cache_t;
+
+
+nxt_js_conf_t *nxt_js_conf_new(nxt_mp_t *mp);
+void nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, nxt_uint_t n);
+nxt_js_t *nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz);
+nxt_int_t nxt_js_compile(nxt_js_conf_t *jcf);
+nxt_int_t nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error);
+nxt_int_t nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js,
+ nxt_str_t *str, void *ctx);
+
+
+extern njs_int_t nxt_js_proto_id;
+
+
+#endif /* NXT_HAVE_NJS */
+
+#endif /* _NXT_JS_H_INCLUDED_ */
diff --git a/src/nxt_main.h b/src/nxt_main.h
index dca4b6dc..b0cdc2d3 100644
--- a/src/nxt_main.h
+++ b/src/nxt_main.h
@@ -59,6 +59,7 @@ typedef uint16_t nxt_port_id_t;
#include <nxt_process_type.h>
#include <nxt_capability.h>
#include <nxt_credential.h>
+#include <nxt_fs_mount.h>
#include <nxt_fs.h>
#include <nxt_process.h>
#include <nxt_utf8.h>
@@ -67,6 +68,7 @@ typedef uint16_t nxt_port_id_t;
#include <nxt_sprintf.h>
#include <nxt_parse.h>
#include <nxt_var.h>
+#include <nxt_tstr.h>
/* TODO: remove unused */
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 39a8e112..de41e8d7 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -191,18 +191,6 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
},
{
- nxt_string("module"),
- NXT_CONF_MAP_STR,
- offsetof(nxt_common_app_conf_t, u.python.module),
- },
-
- {
- nxt_string("callable"),
- NXT_CONF_MAP_CSTRZ,
- 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),
@@ -1019,6 +1007,10 @@ nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process)
if (process->isolation.cleanup != NULL) {
process->isolation.cleanup(task, process);
}
+
+ if (process->isolation.cgroup_cleanup != NULL) {
+ process->isolation.cgroup_cleanup(task, process);
+ }
}
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index e19b1381..f56135f3 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -295,7 +295,7 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
nxt_tls_init_t *tls_init, nxt_bool_t last)
{
SSL_CTX *ctx;
- const char *ciphers, *ca_certificate;
+ const char *ca_certificate;
nxt_tls_conf_t *conf;
STACK_OF(X509_NAME) *list;
nxt_tls_bundle_conf_t *bundle;
@@ -361,13 +361,13 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
}
*/
- ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
-
- if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
- nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ if (conf->ciphers) { /* else use system crypto policy */
+ if (SSL_CTX_set_cipher_list(ctx, conf->ciphers) == 0) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_CTX_set_cipher_list(\"%s\") failed",
- ciphers);
- goto fail;
+ conf->ciphers);
+ goto fail;
+ }
}
#if (NXT_HAVE_OPENSSL_CONF_CMD)
@@ -780,7 +780,7 @@ nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv,
/* decrypt session ticket */
do {
- if (nxt_memcmp(name, ticket[i].name, 16) == 0) {
+ if (memcmp(name, ticket[i].name, 16) == 0) {
goto found;
}
@@ -1777,7 +1777,11 @@ nxt_openssl_copy_error(u_char *p, u_char *end)
clear = 0;
for ( ;; ) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ err = ERR_get_error_all(NULL, NULL, NULL, &data, &flags);
+#else
err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
+#endif
if (err == 0) {
break;
}
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 68ef07eb..126a4684 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -14,22 +14,23 @@
#include <nxt_router.h>
#include <nxt_unit.h>
#include <nxt_unit_request.h>
+#include <nxt_http.h>
-#if PHP_VERSION_ID >= 50400
+#if (PHP_VERSION_ID >= 50400)
#define NXT_HAVE_PHP_IGNORE_CWD 1
#endif
-#if PHP_VERSION_ID >= 70100
+#if (PHP_VERSION_ID >= 70100)
#define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
#else
#define NXT_HAVE_PHP_INTERRUPTS 1
#endif
-#if PHP_VERSION_ID >= 70000
+#if (PHP_VERSION_ID >= 70000)
#define NXT_PHP7 1
#endif
-#if PHP_VERSION_ID >= 80000
+#if (PHP_VERSION_ID >= 80000)
#define NXT_PHP8 1
#endif
@@ -73,7 +74,7 @@ typedef int (*nxt_php_disable_t)(char *p, size_t size);
typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
#endif
-#if PHP_VERSION_ID < 70200
+#if (PHP_VERSION_ID < 70200)
typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
#endif
@@ -100,6 +101,8 @@ static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
nxt_inline u_char *nxt_realpath(const void *c);
+static nxt_int_t nxt_php_do_301(nxt_unit_request_info_t *req);
+
static void nxt_php_request_handler(nxt_unit_request_info_t *req);
static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx,
nxt_unit_request_t *r);
@@ -139,7 +142,7 @@ static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
#ifdef NXT_PHP7
-#if PHP_VERSION_ID < 70200
+#if (PHP_VERSION_ID < 70200)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0,
_IS_BOOL, NULL, 0)
#else
@@ -355,7 +358,7 @@ static nxt_php_target_t *nxt_php_targets;
static nxt_int_t nxt_php_last_target = -1;
static nxt_unit_ctx_t *nxt_php_unit_ctx;
-#if defined(ZTS) && PHP_VERSION_ID < 70400
+#if defined(ZTS) && (PHP_VERSION_ID < 70400)
static void ***tsrm_ls;
#endif
@@ -377,7 +380,7 @@ nxt_php_setup(nxt_task_t *task, nxt_process_t *process,
#ifdef ZTS
-#if PHP_VERSION_ID >= 70400
+#if (PHP_VERSION_ID >= 70400)
php_tsrm_startup();
#else
tsrm_startup(1, 1, 0, NULL);
@@ -920,6 +923,63 @@ nxt_realpath(const void *c)
}
+static nxt_int_t
+nxt_php_do_301(nxt_unit_request_info_t *req)
+{
+ char *p, *url, *port;
+ uint32_t size;
+ const char *proto;
+ nxt_unit_request_t *r;
+
+ r = req->request;
+
+ url = nxt_malloc(sizeof("https://") - 1
+ + r->server_name_length
+ + r->local_port_length + 1
+ + r->path_length + 1
+ + r->query_length + 1
+ + 1);
+ if (nxt_slow_path(url == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ proto = r->tls ? "https://" : "http://";
+ p = nxt_cpymem(url, proto, strlen(proto));
+ p = nxt_cpymem(p, nxt_unit_sptr_get(&r->server_name),
+ r->server_name_length);
+
+ port = nxt_unit_sptr_get(&r->local_port);
+ if (r->local_port_length > 0
+ && !(r->tls && strcmp(port, "443") == 0)
+ && !(!r->tls && strcmp(port, "80") == 0))
+ {
+ *p++ = ':';
+ p = nxt_cpymem(p, port, r->local_port_length);
+ }
+
+ p = nxt_cpymem(p, nxt_unit_sptr_get(&r->path), r->path_length);
+ *p++ = '/';
+
+ if (r->query_length > 0) {
+ *p++ = '?';
+ p = nxt_cpymem(p, nxt_unit_sptr_get(&r->query), r->query_length);
+ }
+
+ *p = '\0';
+
+ size = p - url;
+
+ nxt_unit_response_init(req, NXT_HTTP_MOVED_PERMANENTLY, 1,
+ nxt_length("Location") + size);
+ nxt_unit_response_add_field(req, "Location", nxt_length("Location"),
+ url, size);
+
+ nxt_free(url);
+
+ return NXT_UNIT_OK;
+}
+
+
static void
nxt_php_request_handler(nxt_unit_request_info_t *req)
{
@@ -975,15 +1035,33 @@ nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
} else if (path.start[path.length - 1] == '/') {
script_name = *ctx->index;
- } else {
- if (nxt_slow_path(path.length < 4
- || nxt_memcmp(path.start + (path.length - 4),
- ".php", 4)))
- {
- nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
+ } else if (path.length < 4
+ || memcmp(path.start + (path.length - 4), ".php", 4) != 0)
+ {
+ char tpath[PATH_MAX];
+ nxt_int_t ec;
+ struct stat sb;
+
+ ec = NXT_UNIT_ERROR;
+
+ if (ctx->root->length + path.length + 1 > PATH_MAX) {
+ nxt_unit_request_done(ctx->req, ec);
return;
}
+
+ p = nxt_cpymem(tpath, ctx->root->start, ctx->root->length);
+ p = nxt_cpymem(p, path.start, path.length);
+ *p = '\0';
+
+ ret = stat(tpath, &sb);
+ if (ret == 0 && S_ISDIR(sb.st_mode)) {
+ ec = nxt_php_do_301(ctx->req);
+ }
+
+ nxt_unit_request_done(ctx->req, ec);
+
+ return;
}
ctx->script_filename.length = ctx->root->length
@@ -1150,7 +1228,11 @@ nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir)
static int
nxt_php_startup(sapi_module_struct *sapi_module)
{
+#if (PHP_VERSION_ID < 80200)
return php_module_startup(sapi_module, &nxt_php_unit_module, 1);
+#else
+ return php_module_startup(sapi_module, &nxt_php_unit_module);
+#endif
}
@@ -1365,7 +1447,7 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
track_vars_array TSRMLS_CC);
- nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length,
+ nxt_php_set_sptr(req, "SERVER_ADDR", &r->local_addr, r->local_addr_length,
track_vars_array TSRMLS_CC);
nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length,
diff --git a/src/nxt_port.c b/src/nxt_port.c
index ed7050f3..b9964df4 100644
--- a/src/nxt_port.c
+++ b/src/nxt_port.c
@@ -144,14 +144,14 @@ nxt_port_release(nxt_task_t *task, nxt_port_t *port)
nxt_port_id_t
-nxt_port_get_next_id()
+nxt_port_get_next_id(void)
{
return nxt_atomic_fetch_add(&nxt_port_last_id, 1);
}
void
-nxt_port_reset_next_id()
+nxt_port_reset_next_id(void)
{
nxt_port_last_id = 1;
}
diff --git a/src/nxt_process.c b/src/nxt_process.c
index 738a03bf..d8836ad2 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -5,6 +5,7 @@
*/
#include <nxt_main.h>
+#include <nxt_cgroup.h>
#if (NXT_HAVE_CLONE)
#include <nxt_clone.h>
@@ -378,6 +379,17 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process)
nxt_runtime_process_add(task, process);
}
+#if (NXT_HAVE_CGROUP)
+ ret = nxt_cgroup_proc_add(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "cgroup: failed to add process %s to %s %E",
+ process->name, process->isolation.cgroup.path, nxt_errno);
+ nxt_cgroup_cleanup(task, process);
+ kill(pid, SIGTERM);
+ return -1;
+ }
+#endif
+
return pid;
}
diff --git a/src/nxt_process.h b/src/nxt_process.h
index 15fd4e7f..0db68d45 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -61,8 +61,11 @@ typedef enum {
typedef struct nxt_port_mmap_s nxt_port_mmap_t;
typedef struct nxt_process_s nxt_process_t;
+typedef struct nxt_cgroup_s nxt_cgroup_t;
typedef void (*nxt_isolation_cleanup_t)(nxt_task_t *task,
nxt_process_t *process);
+typedef void (*nxt_cgroup_cleanup_t)(nxt_task_t *task,
+ const nxt_process_t *process);
typedef struct {
@@ -80,6 +83,11 @@ typedef struct {
} nxt_process_automount_t;
+struct nxt_cgroup_s {
+ char *path;
+};
+
+
typedef struct {
u_char *rootfs;
nxt_process_automount_t automount;
@@ -87,6 +95,11 @@ typedef struct {
nxt_isolation_cleanup_t cleanup;
+ nxt_cgroup_cleanup_t cgroup_cleanup;
+#if (NXT_HAVE_CGROUP)
+ nxt_cgroup_t cgroup;
+#endif
+
#if (NXT_HAVE_CLONE)
nxt_clone_t clone;
#endif
diff --git a/src/nxt_router.c b/src/nxt_router.c
index f02bf3f2..edc015c5 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -1060,11 +1060,15 @@ nxt_router_temp_conf(nxt_task_t *task)
rtcf->mem_pool = mp;
- rtcf->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t));
- if (nxt_slow_path(rtcf->var_fields == NULL)) {
+ rtcf->tstr_state = nxt_tstr_state_new(mp, 0);
+ if (nxt_slow_path(rtcf->tstr_state == NULL)) {
goto fail;
}
+#if (NXT_HAVE_NJS)
+ nxt_http_register_js_proto(rtcf->tstr_state->jcf);
+#endif
+
tmp = nxt_mp_create(1024, 128, 256, 32);
if (nxt_slow_path(tmp == NULL)) {
goto fail;
@@ -2042,6 +2046,11 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
}
}
+ ret = nxt_tstr_state_done(rtcf->tstr_state, NULL);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
nxt_queue_add(&deleting_sockets, &router->sockets);
nxt_queue_init(&router->sockets);
@@ -5249,11 +5258,17 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
r->remote->address_length);
*p++ = '\0';
- req->local_length = r->local->address_length;
- nxt_unit_sptr_set(&req->local, p);
+ req->local_addr_length = r->local->address_length;
+ nxt_unit_sptr_set(&req->local_addr, p);
p = nxt_cpymem(p, nxt_sockaddr_address(r->local), r->local->address_length);
*p++ = '\0';
+ req->local_port_length = nxt_sockaddr_port_length(r->local);
+ nxt_unit_sptr_set(&req->local_port, p);
+ p = nxt_cpymem(p, nxt_sockaddr_port(r->local),
+ nxt_sockaddr_port_length(r->local));
+ *p++ = '\0';
+
req->tls = r->tls;
req->websocket_handshake = r->websocket_handshake;
diff --git a/src/nxt_router.h b/src/nxt_router.h
index a6add219..11094960 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -43,7 +43,7 @@ typedef struct {
uint32_t threads;
nxt_mp_t *mem_pool;
- nxt_array_t *var_fields; /* of nxt_var_field_t */
+ nxt_tstr_state_t *tstr_state;
nxt_router_t *router;
nxt_http_routes_t *routes;
@@ -53,7 +53,7 @@ typedef struct {
nxt_lvlhsh_t apps_hash;
nxt_router_access_log_t *access_log;
- nxt_var_t *log_format;
+ nxt_tstr_t *log_format;
} nxt_router_conf_t;
@@ -225,7 +225,7 @@ typedef struct {
struct nxt_router_access_log_s {
void (*handler)(nxt_task_t *task, nxt_http_request_t *r,
nxt_router_access_log_t *access_log,
- nxt_var_t *format);
+ nxt_tstr_t *format);
nxt_fd_t fd;
nxt_str_t path;
uint32_t count;
diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c
index dc2a6687..ccbddb96 100644
--- a/src/nxt_router_access_log.c
+++ b/src/nxt_router_access_log.c
@@ -24,7 +24,7 @@ typedef struct {
static void nxt_router_access_log_writer(nxt_task_t *task,
nxt_http_request_t *r, nxt_router_access_log_t *access_log,
- nxt_var_t *format);
+ nxt_tstr_t *format);
static void nxt_router_access_log_write_ready(nxt_task_t *task, void *obj,
void *data);
static void nxt_router_access_log_write_error(nxt_task_t *task, void *obj,
@@ -63,7 +63,7 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
u_char *p;
nxt_int_t ret;
nxt_str_t str;
- nxt_var_t *format;
+ nxt_tstr_t *format;
nxt_router_t *router;
nxt_router_access_log_t *access_log;
nxt_router_access_log_conf_t alcf;
@@ -125,8 +125,7 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
p = nxt_cpymem(str.start, alcf.format.start, alcf.format.length);
*p = '\n';
- format = nxt_var_compile(&str, rtcf->mem_pool, rtcf->var_fields,
- NXT_VAR_LOGGING);
+ format = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_LOGGING);
if (nxt_slow_path(format == NULL)) {
return NXT_ERROR;
}
@@ -140,9 +139,10 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
static void
nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
- nxt_router_access_log_t *access_log, nxt_var_t *format)
+ nxt_router_access_log_t *access_log, nxt_tstr_t *format)
{
nxt_int_t ret;
+ nxt_router_conf_t *rtcf;
nxt_router_access_log_ctx_t *ctx;
ctx = nxt_mp_get(r->mem_pool, sizeof(nxt_router_access_log_ctx_t));
@@ -152,21 +152,24 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
ctx->access_log = access_log;
- if (nxt_var_is_const(format)) {
- nxt_var_raw(format, &ctx->text);
+ if (nxt_tstr_is_const(format)) {
+ nxt_tstr_str(format, &ctx->text);
nxt_router_access_log_write_ready(task, r, ctx);
} else {
- ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
+ rtcf = r->conf->socket_conf->router_conf;
+
+ ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
+ &r->tstr_cache, r, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) {
return;
}
- nxt_var_query(task, r->var_query, format, &ctx->text);
- nxt_var_query_resolve(task, r->var_query, ctx,
- nxt_router_access_log_write_ready,
- nxt_router_access_log_write_error);
+ nxt_tstr_query(task, r->tstr_query, format, &ctx->text);
+ nxt_tstr_query_resolve(task, r->tstr_query, ctx,
+ nxt_router_access_log_write_ready,
+ nxt_router_access_log_write_error);
}
}
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index d9c9f2ef..c7e4455e 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -1369,6 +1369,8 @@ nxt_runtime_pid_file_create(nxt_task_t *task, nxt_file_name_t *pid_file)
file.name = pid_file;
+ nxt_fs_mkdir_parent(pid_file, 0755);
+
n = nxt_file_open(task, &file, O_WRONLY, O_CREAT | O_TRUNC,
NXT_FILE_DEFAULT_ACCESS);
diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h
index d7fe2f38..687914f0 100644
--- a/src/nxt_runtime.h
+++ b/src/nxt_runtime.h
@@ -138,6 +138,9 @@ void nxt_cdecl nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log,
void nxt_stream_connection_init(nxt_task_t *task, void *obj, void *data);
nxt_int_t nxt_http_register_variables(void);
+#if (NXT_HAVE_NJS)
+void nxt_http_register_js_proto(nxt_js_conf_t *jcf);
+#endif
#define nxt_runtime_process_each(rt, process) \
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c
index 86c3335e..32941893 100644
--- a/src/nxt_sockaddr.c
+++ b/src/nxt_sockaddr.c
@@ -382,7 +382,7 @@ nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
return 0;
}
- if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
+ if (memcmp(&sa1->u.sockaddr_in6.sin6_addr,
&sa2->u.sockaddr_in6.sin6_addr, 16)
!= 0)
{
@@ -401,7 +401,7 @@ nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
length = sa1->socklen - offsetof(struct sockaddr_un, sun_path);
- if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
+ if (memcmp(&sa1->u.sockaddr_un.sun_path,
&sa2->u.sockaddr_un.sun_path, length)
!= 0)
{
@@ -550,7 +550,7 @@ nxt_sockaddr_parse_optport(nxt_mp_t *mp, nxt_str_t *addr)
return NULL;
}
- if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) {
+ if (addr->length > 6 && memcmp(addr->start, "unix:", 5) == 0) {
sa = nxt_sockaddr_unix_parse(mp, addr);
} else if (addr->start[0] == '[' || nxt_inet6_probe(addr)) {
@@ -653,7 +653,7 @@ nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr)
length = addr->length - 1;
start = addr->start + 1;
- end = nxt_memchr(start, ']', length);
+ end = memchr(start, ']', length);
if (nxt_slow_path(end == NULL)) {
return NULL;
}
@@ -723,7 +723,7 @@ nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr)
in_addr_t inaddr;
nxt_sockaddr_t *sa;
- p = nxt_memchr(addr->start, ':', addr->length);
+ p = memchr(addr->start, ':', addr->length);
if (p == NULL) {
length = addr->length;
@@ -964,11 +964,11 @@ nxt_inet6_probe(nxt_str_t *str)
{
u_char *colon, *end;
- colon = nxt_memchr(str->start, ':', str->length);
+ colon = memchr(str->start, ':', str->length);
if (colon != NULL) {
end = str->start + str->length;
- colon = nxt_memchr(colon + 1, ':', end - (colon + 1));
+ colon = memchr(colon + 1, ':', end - (colon + 1));
}
return (colon != NULL);
diff --git a/src/nxt_string.c b/src/nxt_string.c
index 4d89c23c..1ca595a1 100644
--- a/src/nxt_string.c
+++ b/src/nxt_string.c
@@ -257,7 +257,7 @@ nxt_memstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
return NULL;
}
- if (nxt_memcmp(s, s2, length) == 0) {
+ if (memcmp(s, s2, length) == 0) {
return (u_char *) s - 1;
}
}
@@ -325,7 +325,7 @@ nxt_rmemstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
c1 = *s1;
if (c1 == c2) {
- if (nxt_memcmp(s1 + 1, s2, length) == 0) {
+ if (memcmp(s1 + 1, s2, length) == 0) {
return (u_char *) s1;
}
}
diff --git a/src/nxt_string.h b/src/nxt_string.h
index a8673c61..18ea5490 100644
--- a/src/nxt_string.h
+++ b/src/nxt_string.h
@@ -66,14 +66,6 @@ nxt_cpymem(void *dst, const void *src, size_t length)
(void) memmove(dst, src, length)
-#define nxt_memcmp(s1, s2, length) \
- memcmp((char *) s1, (char *) s2, length)
-
-
-#define nxt_memchr(s, c, length) \
- memchr((char *) s, c, length)
-
-
#define nxt_strcmp(s1, s2) \
strcmp((char *) s1, (char *) s2)
@@ -132,7 +124,7 @@ NXT_EXPORT char *nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src);
#define nxt_strstr_eq(s1, s2) \
(((s1)->length == (s2)->length) \
- && (nxt_memcmp((s1)->start, (s2)->start, (s1)->length) == 0))
+ && (memcmp((s1)->start, (s2)->start, (s1)->length) == 0))
#define nxt_strcasestr_eq(s1, s2) \
@@ -141,11 +133,11 @@ NXT_EXPORT char *nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src);
#define nxt_str_eq(s, p, _length) \
- (((s)->length == _length) && (nxt_memcmp((s)->start, p, _length) == 0))
+ (((s)->length == _length) && (memcmp((s)->start, p, _length) == 0))
#define nxt_str_start(s, p, _length) \
- (((s)->length >= _length) && (nxt_memcmp((s)->start, p, _length) == 0))
+ (((s)->length >= _length) && (memcmp((s)->start, p, _length) == 0))
#define nxt_strchr_eq(s, c) \
diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c
new file mode 100644
index 00000000..fd01797c
--- /dev/null
+++ b/src/nxt_tstr.c
@@ -0,0 +1,317 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+typedef enum {
+ NXT_TSTR_CONST = 0,
+ NXT_TSTR_VAR,
+#if (NXT_HAVE_NJS)
+ NXT_TSTR_JS,
+#endif
+} nxt_tstr_type_t;
+
+
+struct nxt_tstr_s {
+ nxt_str_t str;
+
+ union {
+ nxt_var_t *var;
+#if (NXT_HAVE_NJS)
+ nxt_js_t *js;
+#endif
+ } u;
+
+ nxt_tstr_flags_t flags;
+ nxt_tstr_type_t type;
+};
+
+
+struct nxt_tstr_query_s {
+ nxt_mp_t *pool;
+
+ nxt_tstr_state_t *state;
+ nxt_tstr_cache_t *cache;
+
+ nxt_uint_t waiting;
+ nxt_uint_t failed; /* 1 bit */
+
+ void *ctx;
+ void *data;
+
+ nxt_work_handler_t ready;
+ nxt_work_handler_t error;
+};
+
+
+#define nxt_tstr_is_js(str) \
+ nxt_strchr_start(str, '`')
+
+
+nxt_tstr_state_t *
+nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test)
+{
+ nxt_tstr_state_t *state;
+
+ state = nxt_mp_get(mp, sizeof(nxt_tstr_state_t));
+ if (nxt_slow_path(state == NULL)) {
+ return NULL;
+ }
+
+ state->pool = mp;
+ state->test = test;
+
+ state->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t));
+ if (nxt_slow_path(state->var_fields == NULL)) {
+ return NULL;
+ }
+
+#if (NXT_HAVE_NJS)
+ state->jcf = nxt_js_conf_new(mp);
+ if (nxt_slow_path(state->jcf == NULL)) {
+ return NULL;
+ }
+#endif
+
+ return state;
+}
+
+
+nxt_tstr_t *
+nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str,
+ nxt_tstr_flags_t flags)
+{
+ u_char *p;
+ nxt_tstr_t *tstr;
+ nxt_bool_t strz;
+
+ strz = (flags & NXT_TSTR_STRZ) != 0;
+
+ tstr = nxt_mp_get(state->pool, sizeof(nxt_tstr_t));
+ if (nxt_slow_path(tstr == NULL)) {
+ return NULL;
+ }
+
+ tstr->str.length = str->length + strz;
+
+ tstr->str.start = nxt_mp_nget(state->pool, tstr->str.length);
+ if (nxt_slow_path(tstr->str.start == NULL)) {
+ return NULL;
+ }
+
+ p = nxt_cpymem(tstr->str.start, str->start, str->length);
+
+ if (strz) {
+ *p = '\0';
+ }
+
+ tstr->flags = flags;
+
+ if (nxt_tstr_is_js(str)) {
+
+#if (NXT_HAVE_NJS)
+
+ nxt_str_t tpl;
+
+ tstr->type = NXT_TSTR_JS;
+
+ nxt_tstr_str(tstr, &tpl);
+
+ tstr->u.js = nxt_js_add_tpl(state->jcf, &tpl, strz);
+ if (nxt_slow_path(tstr->u.js == NULL)) {
+ return NULL;
+ }
+
+#endif
+
+ } else {
+ p = memchr(str->start, '$', str->length);
+
+ if (p != NULL) {
+ tstr->type = NXT_TSTR_VAR;
+
+ tstr->u.var = nxt_var_compile(&tstr->str, state->pool,
+ state->var_fields);
+ if (nxt_slow_path(tstr->u.var == NULL)) {
+ return NULL;
+ }
+
+ } else {
+ tstr->type = NXT_TSTR_CONST;
+ }
+ }
+
+ return tstr;
+}
+
+
+nxt_int_t
+nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error)
+{
+ u_char *p;
+
+ if (nxt_tstr_is_js(str)) {
+#if (NXT_HAVE_NJS)
+ return nxt_js_test(state->jcf, str, error);
+
+#else
+ nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
+ "Unit is built without support of njs: "
+ "\"--njs\" ./configure option is missing.");
+ return NXT_ERROR;
+#endif
+
+ } else {
+ p = memchr(str->start, '$', str->length);
+
+ if (p != NULL) {
+ return nxt_var_test(str, state->var_fields, error);
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_tstr_state_done(nxt_tstr_state_t *state, u_char *error)
+{
+#if (NXT_HAVE_NJS)
+ if (!state->test) {
+ nxt_int_t ret;
+
+ ret = nxt_js_compile(state->jcf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+nxt_bool_t
+nxt_tstr_is_const(nxt_tstr_t *tstr)
+{
+ return (tstr->type == NXT_TSTR_CONST);
+}
+
+
+void
+nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str)
+{
+ *str = tstr->str;
+
+ if (tstr->flags & NXT_TSTR_STRZ) {
+ str->length--;
+ }
+}
+
+
+nxt_int_t
+nxt_tstr_query_init(nxt_tstr_query_t **query_p, nxt_tstr_state_t *state,
+ nxt_tstr_cache_t *cache, void *ctx, nxt_mp_t *mp)
+{
+ nxt_tstr_query_t *query;
+
+ query = *query_p;
+
+ if (*query_p == NULL) {
+ query = nxt_mp_zget(mp, sizeof(nxt_tstr_query_t));
+ if (nxt_slow_path(query == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+
+ query->pool = mp;
+ query->state = state;
+ query->cache = cache;
+ query->ctx = ctx;
+
+ *query_p = query;
+
+ return NXT_OK;
+}
+
+
+void
+nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
+ nxt_str_t *val)
+{
+ nxt_int_t ret;
+
+ if (nxt_tstr_is_const(tstr)) {
+ nxt_tstr_str(tstr, val);
+ return;
+ }
+
+ if (nxt_slow_path(query->failed)) {
+ return;
+ }
+
+ if (tstr->type == NXT_TSTR_VAR) {
+ ret = nxt_var_interpreter(task, &query->cache->var, tstr->u.var, val,
+ query->ctx, tstr->flags & NXT_TSTR_LOGGING);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ query->failed = 1;
+ return;
+ }
+
+ } else {
+#if (NXT_HAVE_NJS)
+ ret = nxt_js_call(task, &query->cache->js, tstr->u.js, val, query->ctx);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ query->failed = 1;
+ return;
+ }
+#endif
+ }
+
+ if (tstr->flags & NXT_TSTR_STRZ) {
+ val->length--;
+ }
+
+#if (NXT_DEBUG)
+ nxt_str_t str;
+
+ nxt_tstr_str(tstr, &str);
+
+ nxt_debug(task, "tstr query: \"%V\", result: \"%V\"", &str, val);
+#endif
+}
+
+
+void
+nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query, void *data,
+ nxt_work_handler_t ready, nxt_work_handler_t error)
+{
+ query->data = data;
+ query->ready = ready;
+ query->error = error;
+
+ if (query->waiting == 0) {
+ nxt_work_queue_add(&task->thread->engine->fast_work_queue,
+ query->failed ? query->error : query->ready,
+ task, query->ctx, query->data);
+ }
+}
+
+
+void
+nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query,
+ nxt_bool_t failed)
+{
+ query->failed |= failed;
+
+ if (--query->waiting == 0) {
+ nxt_work_queue_add(&task->thread->engine->fast_work_queue,
+ query->failed ? query->error : query->ready,
+ task, query->ctx, query->data);
+ }
+}
diff --git a/src/nxt_tstr.h b/src/nxt_tstr.h
new file mode 100644
index 00000000..0cc24292
--- /dev/null
+++ b/src/nxt_tstr.h
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_TSTR_H_INCLUDED_
+#define _NXT_TSTR_H_INCLUDED_
+
+
+#include <nxt_js.h>
+
+typedef struct nxt_tstr_s nxt_tstr_t;
+typedef struct nxt_tstr_query_s nxt_tstr_query_t;
+
+
+typedef struct {
+ nxt_mp_t *pool;
+ nxt_array_t *var_fields;
+#if (NXT_HAVE_NJS)
+ nxt_js_conf_t *jcf;
+#endif
+ uint8_t test; /* 1 bit */
+} nxt_tstr_state_t;
+
+
+typedef struct {
+ nxt_var_cache_t var;
+#if (NXT_HAVE_NJS)
+ nxt_js_cache_t js;
+#endif
+} nxt_tstr_cache_t;
+
+
+typedef enum {
+ NXT_TSTR_STRZ = 1 << 0,
+ NXT_TSTR_LOGGING = 1 << 1,
+} nxt_tstr_flags_t;
+
+
+nxt_tstr_state_t *nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test);
+nxt_tstr_t *nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str,
+ nxt_tstr_flags_t flags);
+nxt_int_t nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error);
+nxt_int_t nxt_tstr_state_done(nxt_tstr_state_t *state, u_char *error);
+
+nxt_bool_t nxt_tstr_is_const(nxt_tstr_t *tstr);
+void nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str);
+
+nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p,
+ nxt_tstr_state_t *state, nxt_tstr_cache_t *cache, void *ctx,
+ nxt_mp_t *mp);
+void nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
+ nxt_str_t *val);
+void nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query,
+ void *data, nxt_work_handler_t ready, nxt_work_handler_t error);
+void nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query,
+ nxt_bool_t failed);
+
+
+nxt_inline nxt_bool_t
+nxt_is_tstr(nxt_str_t *str)
+{
+ u_char *p;
+
+ p = memchr(str->start, '`', str->length);
+ if (p != NULL) {
+ return 1;
+ }
+
+ p = memchr(str->start, '$', str->length);
+ if (p != NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#endif /* _NXT_TSTR_H_INCLUDED_ */
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index e5cb0b58..e1b1897a 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -116,7 +116,7 @@ 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);
+static int 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);
static void nxt_unit_mmaps_destroy(nxt_unit_mmaps_t *mmaps);
@@ -196,7 +196,7 @@ static int nxt_unit_request_hash_add(nxt_unit_ctx_t *ctx,
static nxt_unit_request_info_t *nxt_unit_request_hash_find(
nxt_unit_ctx_t *ctx, uint32_t stream, int remove);
-static char * nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid,
+static char * nxt_unit_snprint_prefix(char *p, char *end, pid_t pid,
int level);
static void *nxt_unit_lvlhsh_alloc(void *data, size_t size);
static void nxt_unit_lvlhsh_free(void *data, void *p);
@@ -606,7 +606,7 @@ nxt_unit_create(nxt_unit_init_t *init)
if (nxt_slow_path(rc != 0)) {
nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc);
- goto fail;
+ goto out_unit_free;
}
lib->unit.data = init->data;
@@ -631,17 +631,35 @@ nxt_unit_create(nxt_unit_init_t *init)
rc = nxt_unit_ctx_init(lib, &lib->main_ctx, init->ctx_data);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- pthread_mutex_destroy(&lib->mutex);
- goto fail;
+ goto out_mutex_destroy;
+ }
+
+ rc = nxt_unit_mmaps_init(&lib->incoming);
+ if (nxt_slow_path(rc != 0)) {
+ nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc);
+
+ goto out_ctx_free;
}
- nxt_unit_mmaps_init(&lib->incoming);
- nxt_unit_mmaps_init(&lib->outgoing);
+ rc = nxt_unit_mmaps_init(&lib->outgoing);
+ if (nxt_slow_path(rc != 0)) {
+ nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc);
+
+ goto out_mmaps_destroy;
+ }
return lib;
-fail:
+out_mmaps_destroy:
+ nxt_unit_mmaps_destroy(&lib->incoming);
+
+out_ctx_free:
+ nxt_unit_ctx_free(&lib->main_ctx);
+
+out_mutex_destroy:
+ pthread_mutex_destroy(&lib->mutex);
+out_unit_free:
nxt_unit_free(NULL, lib);
return NULL;
@@ -4093,15 +4111,15 @@ nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl)
}
-static void
+static int
nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps)
{
- pthread_mutex_init(&mmaps->mutex, NULL);
-
mmaps->size = 0;
mmaps->cap = 0;
mmaps->elts = NULL;
mmaps->allocated_chunks = 0;
+
+ return pthread_mutex_init(&mmaps->mutex, NULL);
}
@@ -6661,7 +6679,7 @@ static const char * nxt_unit_log_levels[] = {
static char *
-nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid, int level)
+nxt_unit_snprint_prefix(char *p, char *end, pid_t pid, int level)
{
struct tm tm;
struct timespec ts;
diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h
index 5dbf648d..a6ebf0b6 100644
--- a/src/nxt_unit_request.h
+++ b/src/nxt_unit_request.h
@@ -18,7 +18,8 @@ struct nxt_unit_request_s {
uint8_t method_length;
uint8_t version_length;
uint8_t remote_length;
- uint8_t local_length;
+ uint8_t local_addr_length;
+ uint8_t local_port_length;
uint8_t tls;
uint8_t websocket_handshake;
uint8_t app_target;
@@ -38,7 +39,8 @@ struct nxt_unit_request_s {
nxt_unit_sptr_t method;
nxt_unit_sptr_t version;
nxt_unit_sptr_t remote;
- nxt_unit_sptr_t local;
+ nxt_unit_sptr_t local_addr;
+ nxt_unit_sptr_t local_port;
nxt_unit_sptr_t server_name;
nxt_unit_sptr_t target;
nxt_unit_sptr_t path;
diff --git a/src/nxt_var.c b/src/nxt_var.c
index f55a2d30..e113969f 100644
--- a/src/nxt_var.c
+++ b/src/nxt_var.c
@@ -9,7 +9,6 @@
struct nxt_var_s {
size_t length;
nxt_uint_t vars;
- nxt_var_flags_t flags;
u_char data[];
/*
@@ -29,8 +28,7 @@ typedef struct {
struct nxt_var_query_s {
nxt_mp_t *pool;
- nxt_lvlhsh_t cache;
- nxt_str_t *spare;
+ nxt_var_cache_t cache;
nxt_uint_t waiting;
nxt_uint_t failed; /* 1 bit */
@@ -58,11 +56,10 @@ static nxt_var_field_t *nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name,
uint32_t hash);
static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data);
-static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query,
- uint32_t index);
+static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache,
+ uint32_t index, void *ctx);
-static u_char *nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
- nxt_bool_t *is_var);
+static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part);
static const nxt_lvlhsh_proto_t nxt_var_hash_proto nxt_aligned(64) = {
@@ -233,21 +230,22 @@ nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
static nxt_str_t *
-nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index)
+nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, uint32_t index,
+ void *ctx)
{
nxt_int_t ret;
nxt_str_t *value;
nxt_lvlhsh_query_t lhq;
- value = query->spare;
+ value = cache->spare;
if (value == NULL) {
- value = nxt_mp_zget(query->pool, sizeof(nxt_str_t));
+ value = nxt_mp_zget(cache->pool, sizeof(nxt_str_t));
if (nxt_slow_path(value == NULL)) {
return NULL;
}
- query->spare = value;
+ cache->spare = value;
}
lhq.key_hash = nxt_murmur_hash2_uint32(&index);
@@ -256,21 +254,20 @@ nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index)
lhq.key.start = (u_char *) &index;
lhq.value = value;
lhq.proto = &nxt_var_cache_proto;
- lhq.pool = query->pool;
+ lhq.pool = cache->pool;
- ret = nxt_lvlhsh_insert(&query->cache, &lhq);
+ ret = nxt_lvlhsh_insert(&cache->hash, &lhq);
if (nxt_slow_path(ret == NXT_ERROR)) {
return NULL;
}
if (ret == NXT_OK) {
- ret = nxt_var_index[index >> 16](task, value, query->ctx,
- index & 0xffff);
+ ret = nxt_var_index[index >> 16](task, value, ctx, index & 0xffff);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
}
- query->spare = NULL;
+ cache->spare = NULL;
}
return lhq.value;
@@ -330,65 +327,55 @@ nxt_var_index_init(void)
nxt_var_t *
-nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields,
- nxt_var_flags_t flags)
+nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields)
{
u_char *p, *end, *next, *src;
size_t size;
uint32_t index;
- nxt_bool_t strz;
nxt_var_t *var;
nxt_str_t part;
nxt_uint_t n;
- nxt_bool_t is_var;
nxt_var_sub_t *subs;
nxt_var_decl_t *decl;
- strz = (flags & NXT_VAR_STRZ) != 0;
-
n = 0;
p = str->start;
end = p + str->length;
while (p < end) {
- p = nxt_var_next_part(p, end - p, &part, &is_var);
+ p = nxt_var_next_part(p, end, &part);
if (nxt_slow_path(p == NULL)) {
return NULL;
}
- if (is_var) {
+ if (part.start != NULL) {
n++;
}
}
size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
- var = nxt_mp_get(mp, size + strz);
+ var = nxt_mp_get(mp, size);
if (nxt_slow_path(var == NULL)) {
return NULL;
}
var->length = str->length;
var->vars = n;
- var->flags = flags;
subs = nxt_var_subs(var);
src = nxt_var_raw_start(var);
nxt_memcpy(src, str->start, str->length);
- if (strz) {
- src[str->length] = '\0';
- }
-
n = 0;
p = str->start;
while (p < end) {
- next = nxt_var_next_part(p, end - p, &part, &is_var);
+ next = nxt_var_next_part(p, end, &part);
- if (is_var) {
+ if (part.start != NULL) {
decl = nxt_var_decl_get(&part, fields, &index);
if (nxt_slow_path(decl == NULL)) {
return NULL;
@@ -413,14 +400,13 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
{
u_char *p, *end, *next;
nxt_str_t part;
- nxt_bool_t is_var;
nxt_var_decl_t *decl;
p = str->start;
end = p + str->length;
while (p < end) {
- next = nxt_var_next_part(p, end - p, &part, &is_var);
+ next = nxt_var_next_part(p, end, &part);
if (next == NULL) {
nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
@@ -429,7 +415,7 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
return NXT_ERROR;
}
- if (is_var) {
+ if (part.start != NULL) {
decl = nxt_var_decl_get(&part, fields, NULL);
if (decl == NULL) {
@@ -448,19 +434,15 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
static u_char *
-nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
- nxt_bool_t *is_var)
+nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part)
{
- u_char *p, *end, ch, c;
+ size_t length;
+ u_char *p, ch, c;
nxt_bool_t bracket;
- end = start + length;
-
- p = nxt_memchr(start, '$', length);
+ p = memchr(start, '$', end - start);
if (p == start) {
- *is_var = 1;
-
p++;
if (p == end) {
@@ -480,129 +462,74 @@ nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
bracket = 0;
}
+ length = 0;
start = p;
- for ( ;; ) {
+ while (p < end) {
ch = *p;
c = (u_char) (ch | 0x20);
- if ((c < 'a' || c > 'z') && ch != '_') {
- if (bracket && ch != '}') {
- return NULL;
- }
-
- break;
+ if ((c >= 'a' && c <= 'z') || ch == '_') {
+ p++;
+ length++;
+ continue;
}
- p++;
+ if (bracket && ch == '}') {
+ p++;
+ bracket = 0;
+ }
- if (p == end) {
- if (bracket) {
- return NULL;
- }
+ break;
+ }
- break;
- }
+ if (bracket || length == 0) {
+ return NULL;
}
- length = p - start;
- end = p + bracket;
+ part->length = length;
+ part->start = start;
} else {
- *is_var = 0;
-
- if (p != NULL) {
- length = p - start;
- end = p;
+ if (p == NULL) {
+ p = end;
}
- }
-
- part->length = length;
- part->start = start;
-
- return end;
-}
-
-
-inline void
-nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
-{
- str->length = var->length;
- str->start = nxt_var_raw_start(var);
-}
-
-
-inline nxt_bool_t
-nxt_var_is_const(nxt_var_t *var)
-{
- return (var->vars == 0);
-}
-
-
-nxt_int_t
-nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp)
-{
- nxt_var_query_t *query;
-
- query = *query_p;
- if (*query_p == NULL) {
- query = nxt_mp_zget(mp, sizeof(nxt_var_query_t));
- if (nxt_slow_path(query == NULL)) {
- return NXT_ERROR;
- }
+ nxt_str_null(part);
}
- query->pool = mp;
- query->ctx = ctx;
-
- *query_p = query;
-
- return NXT_OK;
+ return p;
}
-void
-nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
- nxt_str_t *str)
+nxt_int_t
+nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, nxt_var_t *var,
+ nxt_str_t *str, void *ctx, nxt_bool_t logging)
{
u_char *p, *src;
size_t length, last, next;
nxt_str_t *value, **part;
nxt_uint_t i;
- nxt_bool_t strz, logging;
nxt_array_t parts;
nxt_var_sub_t *subs;
- if (nxt_var_is_const(var)) {
- nxt_var_raw(var, str);
- return;
- }
-
- if (nxt_slow_path(query->failed)) {
- return;
- }
-
nxt_memzero(&parts, sizeof(nxt_array_t));
- nxt_array_init(&parts, query->pool, sizeof(nxt_str_t *));
-
- strz = (var->flags & NXT_VAR_STRZ) != 0;
- logging = (var->flags & NXT_VAR_LOGGING) != 0;
+ nxt_array_init(&parts, cache->pool, sizeof(nxt_str_t *));
subs = nxt_var_subs(var);
length = var->length;
for (i = 0; i < var->vars; i++) {
- value = nxt_var_cache_value(task, query, subs[i].index);
+ value = nxt_var_cache_value(task, cache, subs[i].index, ctx);
if (nxt_slow_path(value == NULL)) {
- goto fail;
+ return NXT_ERROR;
}
part = nxt_array_add(&parts);
if (nxt_slow_path(part == NULL)) {
- goto fail;
+ return NXT_ERROR;
}
*part = value;
@@ -614,9 +541,9 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
}
}
- p = nxt_mp_nget(query->pool, length + strz);
+ p = nxt_mp_nget(cache->pool, length);
if (nxt_slow_path(p == NULL)) {
- goto fail;
+ return NXT_ERROR;
}
str->length = length;
@@ -647,45 +574,5 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
p = nxt_cpymem(p, &src[last], var->length - last);
}
- if (strz) {
- *p = '\0';
- }
-
- nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, str);
-
- return;
-
-fail:
-
- query->failed = 1;
-}
-
-
-void
-nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
- nxt_work_handler_t ready, nxt_work_handler_t error)
-{
- query->data = data;
- query->ready = ready;
- query->error = error;
-
- if (query->waiting == 0) {
- nxt_work_queue_add(&task->thread->engine->fast_work_queue,
- query->failed ? query->error : query->ready,
- task, query->ctx, query->data);
- }
-}
-
-
-void
-nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
- nxt_bool_t failed)
-{
- query->failed |= failed;
-
- if (--query->waiting == 0) {
- nxt_work_queue_add(&task->thread->engine->fast_work_queue,
- query->failed ? query->error : query->ready,
- task, query->ctx, query->data);
- }
+ return NXT_OK;
}
diff --git a/src/nxt_var.h b/src/nxt_var.h
index cc7ff502..ab25800d 100644
--- a/src/nxt_var.h
+++ b/src/nxt_var.h
@@ -32,17 +32,11 @@ typedef struct {
} nxt_var_field_t;
-typedef enum {
- NXT_VAR_STRZ = 1 << 0,
- NXT_VAR_LOGGING = 1 << 1,
-} nxt_var_flags_t;
-
-
-nxt_inline nxt_bool_t
-nxt_is_var(nxt_str_t *str)
-{
- return (nxt_memchr(str->start, '$', str->length) != NULL);
-}
+typedef struct {
+ nxt_mp_t *pool;
+ nxt_lvlhsh_t hash;
+ nxt_str_t *spare;
+} nxt_var_cache_t;
nxt_int_t nxt_var_register(nxt_var_decl_t *decl, size_t n);
@@ -50,21 +44,13 @@ nxt_int_t nxt_var_index_init(void);
nxt_var_field_t *nxt_var_field_get(nxt_array_t *fields, uint16_t index);
-nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields,
- nxt_var_flags_t flags);
+nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields);
nxt_int_t nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error);
-nxt_bool_t nxt_var_is_const(nxt_var_t *var);
-void nxt_var_raw(nxt_var_t *var, nxt_str_t *str);
-
-nxt_int_t nxt_var_query_init(nxt_var_query_t **query_p, void *ctx,
- nxt_mp_t *mp);
-void nxt_var_query(nxt_task_t *task, nxt_var_query_t *query,
- nxt_var_t *var, nxt_str_t *str);
-void nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
- nxt_work_handler_t ready, nxt_work_handler_t error);
-void nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
- nxt_bool_t failed);
+nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache,
+ nxt_var_t *var, nxt_str_t *str, void *ctx, nxt_bool_t logging);
+nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_var_cache_t *cache,
+ nxt_str_t *name, void *ctx);
#endif /* _NXT_VAR_H_INCLUDED_ */
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index 08a6f29e..5e8d1aee 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -671,7 +671,7 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl,
RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("REMOTE_ADDR"),
&r->remote, r->remote_length));
RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_ADDR"),
- &r->local, r->local_length));
+ &r->local_addr, r->local_addr_length));
RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_NAME"),
&r->server_name, r->server_name_length));
@@ -765,7 +765,7 @@ nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result)
status.start = (u_char *) SvPV(*sv_status, status.length);
- space = nxt_memchr(status.start, ' ', status.length);
+ space = memchr(status.start, ' ', status.length);
if (space != NULL) {
status.length = space - status.start;
}
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
index 188c4920..bdb04579 100644
--- a/src/python/nxt_python.c
+++ b/src/python/nxt_python.c
@@ -22,10 +22,16 @@ typedef struct {
} nxt_py_thread_info_t;
+#if PY_MAJOR_VERSION == 3
+static nxt_int_t nxt_python3_init_config(nxt_int_t pep405);
+#endif
+
static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data);
static nxt_int_t nxt_python_set_target(nxt_task_t *task,
nxt_python_target_t *target, nxt_conf_value_t *conf);
+nxt_inline nxt_int_t nxt_python_set_prefix(nxt_task_t *task,
+ nxt_python_target_t *target, nxt_conf_value_t *value);
static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
static int nxt_python_init_threads(nxt_python_app_conf_t *c);
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
@@ -64,13 +70,70 @@ static nxt_py_thread_info_t *nxt_py_threads;
static nxt_python_proto_t nxt_py_proto;
+#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 8)
+
+static nxt_int_t
+nxt_python3_init_config(nxt_int_t pep405)
+{
+ PyStatus status;
+ PyConfig config;
+
+ PyConfig_InitIsolatedConfig(&config);
+
+ if (pep405) {
+ status = PyConfig_SetString(&config, &config.program_name,
+ nxt_py_home);
+ if (PyStatus_Exception(status)) {
+ goto pyinit_exception;
+ }
+
+ } else {
+ status =PyConfig_SetString(&config, &config.home, nxt_py_home);
+ if (PyStatus_Exception(status)) {
+ goto pyinit_exception;
+ }
+ }
+
+ status = Py_InitializeFromConfig(&config);
+ if (PyStatus_Exception(status)) {
+ goto pyinit_exception;
+ }
+ PyConfig_Clear(&config);
+
+ return NXT_OK;
+
+pyinit_exception:
+
+ PyConfig_Clear(&config);
+
+ return NXT_ERROR;
+}
+
+#elif PY_MAJOR_VERSION == 3
+
+static nxt_int_t
+nxt_python3_init_config(nxt_int_t pep405)
+{
+ if (pep405) {
+ Py_SetProgramName(nxt_py_home);
+
+ } else {
+ Py_SetPythonHome(nxt_py_home);
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
static nxt_int_t
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
{
int rc;
size_t len, size;
uint32_t next;
- PyObject *obj, *module;
+ PyObject *obj;
nxt_str_t proto, probe_proto, name;
nxt_int_t ret, n, i;
nxt_unit_ctx_t *unit_ctx;
@@ -127,11 +190,15 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
if (pep405) {
mbstowcs(nxt_py_home, c->home, len);
mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
- Py_SetProgramName(nxt_py_home);
} else {
mbstowcs(nxt_py_home, c->home, len + 1);
- Py_SetPythonHome(nxt_py_home);
+ }
+
+ ret = nxt_python3_init_config(pep405);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ nxt_alert(task, "Failed to initialise config");
+ return NXT_ERROR;
}
#else
@@ -154,7 +221,6 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
}
#endif
- module = NULL;
obj = NULL;
python_init.ctx_data = NULL;
@@ -307,7 +373,6 @@ fail:
}
Py_XDECREF(obj);
- Py_XDECREF(module);
nxt_python_atexit();
@@ -326,6 +391,7 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
static nxt_str_t module_str = nxt_string("module");
static nxt_str_t callable_str = nxt_string("callable");
+ static nxt_str_t prefix_str = nxt_string("prefix");
module = obj = NULL;
@@ -373,6 +439,11 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
goto fail;
}
+ value = nxt_conf_get_object_member(conf, &prefix_str, NULL);
+ if (nxt_slow_path(nxt_python_set_prefix(task, target, value) != NXT_OK)) {
+ goto fail;
+ }
+
target->application = obj;
obj = NULL;
@@ -390,6 +461,48 @@ fail:
}
+nxt_inline nxt_int_t
+nxt_python_set_prefix(nxt_task_t *task, nxt_python_target_t *target,
+ nxt_conf_value_t *value)
+{
+ u_char *prefix;
+ nxt_str_t str;
+
+ if (value == NULL) {
+ return NXT_OK;
+ }
+
+ nxt_conf_get_string(value, &str);
+
+ if (str.length == 0) {
+ return NXT_OK;
+ }
+
+ if (str.start[str.length - 1] == '/') {
+ str.length--;
+ }
+ target->prefix.length = str.length;
+ prefix = nxt_malloc(str.length);
+ if (nxt_slow_path(prefix == NULL)) {
+ nxt_alert(task, "Failed to allocate target prefix string");
+ return NXT_ERROR;
+ }
+
+ target->py_prefix = PyString_FromStringAndSize((char *)str.start,
+ str.length);
+ if (nxt_slow_path(target->py_prefix == NULL)) {
+ nxt_free(prefix);
+ nxt_alert(task, "Python failed to allocate target prefix "
+ "string");
+ return NXT_ERROR;
+ }
+ nxt_memcpy(prefix, str.start, str.length);
+ target->prefix.start = prefix;
+
+ return NXT_OK;
+}
+
+
static nxt_int_t
nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
{
@@ -667,7 +780,8 @@ nxt_python_done_strings(nxt_python_string_t *pstr)
static void
nxt_python_atexit(void)
{
- nxt_int_t i;
+ nxt_int_t i;
+ nxt_python_target_t *target;
if (nxt_py_proto.done != NULL) {
nxt_py_proto.done();
@@ -677,7 +791,12 @@ nxt_python_atexit(void)
if (nxt_py_targets != NULL) {
for (i = 0; i < nxt_py_targets->count; i++) {
- Py_XDECREF(nxt_py_targets->target[i].application);
+ target = &nxt_py_targets->target[i];
+
+ Py_XDECREF(target->application);
+ Py_XDECREF(target->py_prefix);
+
+ nxt_free(target->prefix.start);
}
nxt_unit_free(NULL, nxt_py_targets);
diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h
index eddb1cfc..37e6265e 100644
--- a/src/python/nxt_python.h
+++ b/src/python/nxt_python.h
@@ -40,6 +40,8 @@
typedef struct {
PyObject *application;
+ PyObject *py_prefix;
+ nxt_str_t prefix;
nxt_bool_t asgi_legacy;
} nxt_python_target_t;
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index 4ad0857d..587a17cf 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -27,7 +27,8 @@ static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
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_http_scope(nxt_unit_request_info_t *req,
+ nxt_python_target_t *app_target);
static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
uint16_t port);
static PyObject *nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr,
@@ -455,16 +456,16 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
goto release_send;
}
- scope = nxt_py_asgi_create_http_scope(req);
+ req->data = asgi;
+ target = &nxt_py_targets->target[req->request->app_target];
+
+ scope = nxt_py_asgi_create_http_scope(req, target);
if (nxt_slow_path(scope == NULL)) {
nxt_unit_request_done(req, NXT_UNIT_ERROR);
goto release_done;
}
- req->data = asgi;
- target = &nxt_py_targets->target[req->request->app_target];
-
if (!target->asgi_legacy) {
nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
@@ -573,12 +574,14 @@ 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)
+nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req,
+ nxt_python_target_t *app_target)
{
char *p, *target, *query;
- uint32_t target_length, i;
+ uint32_t target_length, i, path_length;
PyObject *scope, *v, *type, *scheme;
PyObject *headers, *header;
+ nxt_str_t prefix;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
@@ -612,6 +615,17 @@ nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req)
return NULL;
}
+ prefix = app_target->prefix;
+ path_length = r->path_length;
+ p = nxt_unit_sptr_get(&r->path);
+ if (prefix.length > 0
+ && ((path_length > prefix.length && p[prefix.length] == '/')
+ || path_length == prefix.length)
+ && memcmp(prefix.start, p, prefix.length) == 0)
+ {
+ SET_ITEM(scope, root_path, app_target->py_prefix);
+ }
+
p = nxt_unit_sptr_get(&r->version);
SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str
: nxt_py_1_0_str)
@@ -674,7 +688,7 @@ nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req)
SET_ITEM(scope, client, v)
Py_DECREF(v);
- v = nxt_py_asgi_create_address(&r->local, r->local_length, 80);
+ v = nxt_py_asgi_create_address(&r->local_addr, r->local_addr_length, 80);
if (nxt_slow_path(v == NULL)) {
nxt_unit_req_alert(req, "Python failed to create 'server' pair");
goto fail;
@@ -738,42 +752,40 @@ fail:
static PyObject *
nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port)
{
+#if (NXT_HAVE_UNIX_DOMAIN)
size_t prefix_len;
- nxt_str_t addr;
PyObject *pair, *v;
+ nxt_str_t addr;
addr.length = len;
addr.start = nxt_unit_sptr_get(sptr);
prefix_len = nxt_length("unix:");
- if (!nxt_str_start(&addr, "unix:", prefix_len)) {
- return nxt_py_asgi_create_ip_address(sptr, len, port);
- }
+ if (nxt_str_start(&addr, "unix:", prefix_len)) {
-#if NXT_HAVE_UNIX_DOMAIN
- pair = PyTuple_New(2);
- if (nxt_slow_path(pair == NULL)) {
- return NULL;
- }
+ pair = PyTuple_New(2);
+ if (nxt_slow_path(pair == NULL)) {
+ return NULL;
+ }
- addr.start += prefix_len;
- addr.length -= prefix_len;
+ addr.start += prefix_len;
+ addr.length -= prefix_len;
- v = PyString_FromStringAndSize((const char *) addr.start, addr.length);
- if (nxt_slow_path(v == NULL)) {
- Py_DECREF(pair);
+ v = PyString_FromStringAndSize((const char *) addr.start, addr.length);
+ if (nxt_slow_path(v == NULL)) {
+ Py_DECREF(pair);
- return NULL;
- }
+ return NULL;
+ }
- PyTuple_SET_ITEM(pair, 0, v);
- PyTuple_SET_ITEM(pair, 1, Py_None);
+ PyTuple_SET_ITEM(pair, 0, v);
+ PyTuple_SET_ITEM(pair, 1, Py_None);
- return pair;
+ return pair;
+ }
-#else
- return NULL;
#endif
+ return nxt_py_asgi_create_ip_address(sptr, len, port);
}
diff --git a/src/python/nxt_python_asgi_str.c b/src/python/nxt_python_asgi_str.c
index 34422973..7171d52b 100644
--- a/src/python/nxt_python_asgi_str.c
+++ b/src/python/nxt_python_asgi_str.c
@@ -99,7 +99,7 @@ static nxt_python_string_t nxt_py_asgi_strings[] = {
{ nxt_string("query_string"), &nxt_py_query_string_str },
{ nxt_string("raw_path"), &nxt_py_raw_path_str },
{ nxt_string("result"), &nxt_py_result_str },
- { nxt_string("root_path"), &nxt_py_root_path_str }, // not used
+ { nxt_string("root_path"), &nxt_py_root_path_str },
{ nxt_string("scheme"), &nxt_py_scheme_str },
{ nxt_string("server"), &nxt_py_server_str },
{ nxt_string("set_exception"), &nxt_py_set_exception_str },
diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c
index 87dcfaa2..dfb31509 100644
--- a/src/python/nxt_python_wsgi.c
+++ b/src/python/nxt_python_wsgi.c
@@ -60,9 +60,14 @@ static void nxt_python_request_handler(nxt_unit_request_info_t *req);
static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c);
static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req);
-static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx);
+static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx,
+ nxt_python_target_t *app_target);
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_char(nxt_python_ctx_t *pctx, PyObject *name,
+ char *src, uint32_t size);
+static int nxt_python_add_py_string(nxt_python_ctx_t *pctx, PyObject *name,
+ PyObject *value);
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);
@@ -137,6 +142,7 @@ static PyObject *nxt_py_query_string_str;
static PyObject *nxt_py_remote_addr_str;
static PyObject *nxt_py_request_method_str;
static PyObject *nxt_py_request_uri_str;
+static PyObject *nxt_py_script_name_str;
static PyObject *nxt_py_server_addr_str;
static PyObject *nxt_py_server_name_str;
static PyObject *nxt_py_server_port_str;
@@ -156,6 +162,7 @@ static nxt_python_string_t nxt_python_strings[] = {
{ nxt_string("REMOTE_ADDR"), &nxt_py_remote_addr_str },
{ nxt_string("REQUEST_METHOD"), &nxt_py_request_method_str },
{ nxt_string("REQUEST_URI"), &nxt_py_request_uri_str },
+ { nxt_string("SCRIPT_NAME"), &nxt_py_script_name_str },
{ nxt_string("SERVER_ADDR"), &nxt_py_server_addr_str },
{ nxt_string("SERVER_NAME"), &nxt_py_server_name_str },
{ nxt_string("SERVER_PORT"), &nxt_py_server_port_str },
@@ -300,11 +307,12 @@ 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, *application;
- nxt_bool_t prepare_environ;
- nxt_python_ctx_t *pctx;
+ int rc;
+ PyObject *environ, *args, *response, *iterator, *item;
+ PyObject *close, *result;
+ nxt_bool_t prepare_environ;
+ nxt_python_ctx_t *pctx;
+ nxt_python_target_t *target;
pctx = req->ctx->data;
@@ -327,7 +335,9 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
prepare_environ = 1;
- environ = nxt_python_get_environ(pctx);
+ target = &nxt_py_targets->target[req->request->app_target];
+
+ environ = nxt_python_get_environ(pctx, target);
if (nxt_slow_path(environ == NULL)) {
rc = NXT_UNIT_ERROR;
goto done;
@@ -348,8 +358,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
Py_INCREF(pctx->start_resp);
PyTuple_SET_ITEM(args, 1, pctx->start_resp);
- application = nxt_py_targets->target[req->request->app_target].application;
- response = PyObject_CallObject(application, args);
+ response = PyObject_CallObject(target->application, args);
Py_DECREF(args);
@@ -580,11 +589,14 @@ nxt_python_copy_environ(nxt_unit_request_info_t *req)
static PyObject *
-nxt_python_get_environ(nxt_python_ctx_t *pctx)
+nxt_python_get_environ(nxt_python_ctx_t *pctx,
+ nxt_python_target_t *app_target)
{
int rc;
- uint32_t i, j, vl;
+ char *path;
+ uint32_t i, j, vl, path_length;
PyObject *environ;
+ nxt_str_t prefix;
nxt_unit_field_t *f, *f2;
nxt_unit_request_t *r;
@@ -604,13 +616,28 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
r->target_length));
RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query,
r->query_length));
- RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path,
- r->path_length));
+
+ prefix = app_target->prefix;
+ path_length = r->path_length;
+ path = nxt_unit_sptr_get(&r->path);
+ if (prefix.length > 0
+ && ((path_length > prefix.length && path[prefix.length] == '/')
+ || path_length == prefix.length)
+ && memcmp(prefix.start, path, prefix.length) == 0)
+ {
+ RC(nxt_python_add_py_string(pctx, nxt_py_script_name_str,
+ app_target->py_prefix));
+
+ path += prefix.length;
+ path_length -= prefix.length;
+ }
+
+ RC(nxt_python_add_char(pctx, nxt_py_path_info_str, path, path_length));
RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote,
r->remote_length));
- RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local,
- r->local_length));
+ RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local_addr,
+ r->local_addr_length));
if (r->tls) {
RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
@@ -692,10 +719,16 @@ static int
nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size)
{
- char *src;
- PyObject *value;
+ return nxt_python_add_char(pctx, name, nxt_unit_sptr_get(sptr), size);
+}
+
- src = nxt_unit_sptr_get(sptr);
+static int
+nxt_python_add_char(nxt_python_ctx_t *pctx, PyObject *name,
+ char *src, uint32_t size)
+{
+ int res;
+ PyObject *value;
value = PyString_FromStringAndSize(src, size);
if (nxt_slow_path(value == NULL)) {
@@ -707,17 +740,25 @@ nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
return NXT_UNIT_ERROR;
}
+ res = nxt_python_add_py_string(pctx, name, value);
+
+ Py_DECREF(value);
+
+ return res;
+}
+
+
+static int nxt_python_add_py_string(nxt_python_ctx_t *pctx, PyObject *name,
+ PyObject *value)
+{
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);
return NXT_UNIT_ERROR;
}
- Py_DECREF(value);
-
return NXT_UNIT_OK;
}
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index f316d8a5..bcb48f6b 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -480,14 +480,19 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
rackup = rb_protect(nxt_ruby_rack_parse_script,
(VALUE) (uintptr_t) rack_init, &state);
- if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) {
+
+ if (nxt_slow_path(state != 0)) {
nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to parse rack script");
return Qnil;
}
+ if (TYPE(rackup) != T_ARRAY) {
+ return rackup;
+ }
+
if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) {
- nxt_alert(rack_init->task, "Ruby: Invalid rack config file");
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Invalid rack config file");
return Qnil;
}
@@ -747,8 +752,8 @@ nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env)
r->version_length);
nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote,
r->remote_length);
- 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_addr_str, &r->local_addr,
+ r->local_addr_length);
nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name,
r->server_name_length);
diff --git a/src/test/nxt_http_parse_test.c b/src/test/nxt_http_parse_test.c
index 540309c1..5f1a518c 100644
--- a/src/test/nxt_http_parse_test.c
+++ b/src/test/nxt_http_parse_test.c
@@ -753,7 +753,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp,
return NXT_ERROR;
}
- if (nxt_memcmp(rp->version.str, test->version, 8) != 0) {
+ if (memcmp(rp->version.str, test->version, 8) != 0) {
nxt_log_alert(log, "http parse test case failed:\n"
" - request:\n\"%V\"\n"
" - version: \"%*s\" (expected: \"%*s\")", request,
diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c
index f5a1cbd4..03a2a1df 100644
--- a/src/test/nxt_tests.c
+++ b/src/test/nxt_tests.c
@@ -41,7 +41,7 @@ main(int argc, char **argv)
#if (NXT_TEST_RTDTSC)
if (nxt_process_argv[1] != NULL
- && nxt_memcmp(nxt_process_argv[1], "rbm", 3) == 0)
+ && memcmp(nxt_process_argv[1], "rbm", 3) == 0)
{
if (nxt_rbtree1_mb_start(thr) != NXT_OK) {
return 1;
diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c
index 8fda9740..d83bd83a 100644
--- a/src/test/nxt_unit_app_test.c
+++ b/src/test/nxt_unit_app_test.c
@@ -225,7 +225,7 @@ greeting_app_request_handler(nxt_unit_request_info_t *req)
*p++ = '\n';
p = copy(p, LOCAL_ADDR, nxt_length(LOCAL_ADDR));
- p = copy(p, nxt_unit_sptr_get(&r->local), r->local_length);
+ p = copy(p, nxt_unit_sptr_get(&r->local_addr), r->local_addr_length);
*p++ = '\n';
p = copy(p, TARGET, nxt_length(TARGET));
diff --git a/src/test/nxt_unit_websocket_chat.c b/src/test/nxt_unit_websocket_chat.c
index 39f8a440..ec7c2cc3 100644
--- a/src/test/nxt_unit_websocket_chat.c
+++ b/src/test/nxt_unit_websocket_chat.c
@@ -201,7 +201,7 @@ ws_chat_close_handler(nxt_unit_request_info_t *req)
int
-main()
+main(void)
{
nxt_unit_ctx_t *ctx;
nxt_unit_init_t init;
diff --git a/src/test/nxt_unit_websocket_echo.c b/src/test/nxt_unit_websocket_echo.c
index 2a89cdc0..eab2e45f 100644
--- a/src/test/nxt_unit_websocket_echo.c
+++ b/src/test/nxt_unit_websocket_echo.c
@@ -83,7 +83,7 @@ ws_echo_websocket_handler(nxt_unit_websocket_frame_t *ws)
int
-main()
+main(void)
{
nxt_unit_ctx_t *ctx;
nxt_unit_init_t init;
diff --git a/src/test/nxt_utf8_file_name_test.c b/src/test/nxt_utf8_file_name_test.c
index 30546ffb..5723e19b 100644
--- a/src/test/nxt_utf8_file_name_test.c
+++ b/src/test/nxt_utf8_file_name_test.c
@@ -129,7 +129,7 @@ nxt_utf8_file_name_test(nxt_thread_t *thr)
nxt_file_close(&task, &lc_file);
- if (n != 4 || nxt_memcmp(utf8, test, 4) != 0) {
+ if (n != 4 || memcmp(utf8, test, 4) != 0) {
nxt_log_alert(thr->log, "nxt_file_read() mismatch");
nxt_file_delete(lc_file.name);
diff --git a/src/test/nxt_utf8_test.c b/src/test/nxt_utf8_test.c
index bca9a737..31e5bff9 100644
--- a/src/test/nxt_utf8_test.c
+++ b/src/test/nxt_utf8_test.c
@@ -59,7 +59,7 @@ nxt_utf8_overlong(nxt_thread_t *thr, u_char *overlong, size_t len)
size = (p != NULL) ? p - utf8 : 0;
- if (len != size || nxt_memcmp(overlong, utf8, size) != 0) {
+ if (len != size || memcmp(overlong, utf8, size) != 0) {
u = 0;
for (i = 0; i < len; i++) {
diff --git a/test/conftest.py b/test/conftest.py
index 18851baa..4a1aa7cc 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -17,6 +17,7 @@ import pytest
from unit.check.chroot import check_chroot
from unit.check.go import check_go
from unit.check.isolation import check_isolation
+from unit.check.njs import check_njs
from unit.check.node import check_node
from unit.check.regex import check_regex
from unit.check.tls import check_openssl
@@ -25,8 +26,10 @@ from unit.http import TestHTTP
from unit.log import Log
from unit.option import option
from unit.status import Status
+from unit.utils import check_findmnt
from unit.utils import public_dir
from unit.utils import waitforfiles
+from unit.utils import waitforunmount
def pytest_addoption(parser):
@@ -86,6 +89,7 @@ _fds_info = {
},
}
http = TestHTTP()
+is_findmnt = check_findmnt()
def pytest_configure(config):
@@ -176,6 +180,9 @@ def pytest_sessionstart(session):
option.available = {'modules': {}, 'features': {}}
unit = unit_run()
+ output_version = subprocess.check_output(
+ [unit['unitd'], '--version'], stderr=subprocess.STDOUT
+ ).decode()
# read unit.log
@@ -202,10 +209,11 @@ def pytest_sessionstart(session):
# discover modules from check
- option.available['modules']['openssl'] = check_openssl(unit['unitd'])
option.available['modules']['go'] = check_go()
+ option.available['modules']['njs'] = check_njs(output_version)
option.available['modules']['node'] = check_node(option.current_dir)
- option.available['modules']['regex'] = check_regex(unit['unitd'])
+ option.available['modules']['openssl'] = check_openssl(output_version)
+ option.available['modules']['regex'] = check_regex(output_version)
# remove None values
@@ -310,6 +318,9 @@ def run(request):
if not option.restart:
_clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log)
+ if is_findmnt and not waitforunmount(unit['temp_dir'], timeout=600):
+ exit('Could not unmount some filesystems in tmp dir.')
+
for item in os.listdir(unit['temp_dir']):
if item not in [
'control.unit.sock',
@@ -480,14 +491,15 @@ def _check_alerts(*, log=None):
log = f.read()
found = False
-
alerts = re.findall(r'.+\[alert\].+', log)
if alerts:
- print('\nAll alerts/sanitizer errors found in log:')
- [print(alert) for alert in alerts]
found = True
+ if option.detailed:
+ print('\nAll alerts/sanitizer errors found in log:')
+ [print(alert) for alert in alerts]
+
if option.skip_alerts:
for skip in option.skip_alerts:
alerts = [al for al in alerts if re.search(skip, al) is None]
@@ -499,7 +511,7 @@ def _check_alerts(*, log=None):
assert not sanitizer_errors, 'sanitizer error(s)'
- if found:
+ if found and option.detailed:
print('skipped.')
@@ -571,6 +583,10 @@ def _check_processes():
time.sleep(0.1)
+ if option.restart:
+ assert len(out) == 0, 'all termimated'
+ return
+
assert len(out) == 3, 'main, router, and controller expected'
out = [l for l in out if 'unit: main' not in l]
diff --git a/test/python/prefix/asgi.py b/test/python/prefix/asgi.py
new file mode 100644
index 00000000..234f084f
--- /dev/null
+++ b/test/python/prefix/asgi.py
@@ -0,0 +1,15 @@
+async def application(scope, receive, send):
+ assert scope['type'] == 'http'
+
+ await send(
+ {
+ 'type': 'http.response.start',
+ 'status': 200,
+ 'headers': [
+ (b'content-length', b'0'),
+ (b'prefix', scope.get('root_path', 'NULL').encode()),
+ ],
+ }
+ )
+
+ await send({'type': 'http.response.body', 'body': b''})
diff --git a/test/python/prefix/wsgi.py b/test/python/prefix/wsgi.py
new file mode 100644
index 00000000..83b58c9a
--- /dev/null
+++ b/test/python/prefix/wsgi.py
@@ -0,0 +1,10 @@
+def application(environ, start_response):
+ start_response(
+ '200',
+ [
+ ('Content-Length', '0'),
+ ('Script-Name', environ.get('SCRIPT_NAME', 'NULL')),
+ ('Path-Info', environ['PATH_INFO']),
+ ],
+ )
+ return []
diff --git a/test/python/targets/asgi.py b/test/python/targets/asgi.py
index b51f3964..749ec5b1 100644
--- a/test/python/targets/asgi.py
+++ b/test/python/targets/asgi.py
@@ -22,6 +22,23 @@ async def application_200(scope, receive, send):
)
+async def application_prefix(scope, receive, send):
+ assert scope['type'] == 'http'
+
+ await send(
+ {
+ 'type': 'http.response.start',
+ 'status': 200,
+ 'headers': [
+ (b'content-length', b'0'),
+ (b'prefix', scope.get('root_path', 'NULL').encode()),
+ ],
+ }
+ )
+
+ await send({'type': 'http.response.body', 'body': b''})
+
+
def legacy_application_200(scope):
assert scope['type'] == 'http'
diff --git a/test/python/targets/wsgi.py b/test/python/targets/wsgi.py
index fa17ab87..3f3d4b27 100644
--- a/test/python/targets/wsgi.py
+++ b/test/python/targets/wsgi.py
@@ -6,3 +6,12 @@ def wsgi_target_a(env, start_response):
def wsgi_target_b(env, start_response):
start_response('200', [('Content-Length', '1')])
return [b'2']
+
+
+def wsgi_target_prefix(env, start_response):
+ data = u'%s %s' % (
+ env.get('SCRIPT_NAME', 'No Script Name'),
+ env['PATH_INFO'],
+ )
+ start_response('200', [('Content-Length', '%d' % len(data))])
+ return [data.encode('utf-8')]
diff --git a/test/test_access_log.py b/test/test_access_log.py
index b1d89343..a072858b 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -280,28 +280,6 @@ Connection: close
def test_access_log_variables(self):
self.load('mirror')
- # $time_local
-
- self.set_format('$uri $time_local $uri')
- assert self.get(url='/time_local')['status'] == 200
- assert self.wait_for_record('/time_local') is not None, 'time log'
- date = self.search_in_log(
- r'^\/time_local (.*) \/time_local$', 'access.log'
- )[1]
- assert (
- abs(
- self.date_to_sec_epoch(date, '%d/%b/%Y:%X %z')
- - time.mktime(time.localtime())
- )
- < 5
- ), '$time_local'
-
- # $request_line
-
- self.set_format('$request_line')
- assert self.get(url='/r_line')['status'] == 200
- assert self.wait_for_record(r'^GET \/r_line HTTP\/1\.1$') is not None
-
# $body_bytes_sent
self.set_format('$uri $body_bytes_sent')
diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py
index 34dfe18e..121a2fbc 100644
--- a/test/test_asgi_application.py
+++ b/test/test_asgi_application.py
@@ -79,6 +79,43 @@ custom-header: BLAH
resp['headers']['query-string'] == 'var1=val1&var2=val2'
), 'query-string header'
+ def test_asgi_application_prefix(self):
+ self.load('prefix', prefix='/api/rest')
+
+ def set_prefix(prefix):
+ self.conf('"' + prefix + '"', 'applications/prefix/prefix')
+
+ def check_prefix(url, prefix):
+ resp = self.get(url=url)
+ assert resp['status'] == 200
+ assert resp['headers']['prefix'] == prefix
+
+ check_prefix('/ap', 'NULL')
+ check_prefix('/api', 'NULL')
+ check_prefix('/api/', 'NULL')
+ check_prefix('/api/res', 'NULL')
+ check_prefix('/api/restful', 'NULL')
+ check_prefix('/api/rest', '/api/rest')
+ check_prefix('/api/rest/', '/api/rest')
+ check_prefix('/api/rest/get', '/api/rest')
+ check_prefix('/api/rest/get/blah', '/api/rest')
+
+ set_prefix('/api/rest/')
+ check_prefix('/api/rest', '/api/rest')
+ check_prefix('/api/restful', 'NULL')
+ check_prefix('/api/rest/', '/api/rest')
+ check_prefix('/api/rest/blah', '/api/rest')
+
+ set_prefix('/app')
+ check_prefix('/ap', 'NULL')
+ check_prefix('/app', '/app')
+ check_prefix('/app/', '/app')
+ check_prefix('/application/', 'NULL')
+
+ set_prefix('/')
+ check_prefix('/', 'NULL')
+ check_prefix('/app', 'NULL')
+
def test_asgi_application_query_string_space(self):
self.load('query_string')
@@ -277,10 +314,9 @@ custom-header: BLAH
assert self.get()['status'] == 200, 'init'
- (_, sock) = self.http(
+ sock = self.http(
b"""GET / HTTP/1.1
""",
- start=True,
raw=True,
no_recv=True,
)
@@ -358,14 +394,13 @@ Connection: close
socks = []
for i in range(2):
- (_, sock) = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'X-Delay': '3',
'Connection': 'close',
},
no_recv=True,
- start=True,
)
socks.append(sock)
diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py
index c1e345ef..84d7b3b0 100644
--- a/test/test_asgi_targets.py
+++ b/test/test_asgi_targets.py
@@ -90,3 +90,48 @@ class TestASGITargets(TestApplicationPython):
)
assert self.get(url='/1')['status'] != 200
+
+ def test_asgi_targets_prefix(self):
+ self.conf_targets(
+ {
+ "1": {
+ "module": "asgi",
+ "callable": "application_prefix",
+ "prefix": "/1/",
+ },
+ "2": {
+ "module": "asgi",
+ "callable": "application_prefix",
+ "prefix": "/api",
+ },
+ }
+ )
+ self.conf(
+ [
+ {
+ "match": {"uri": "/1*"},
+ "action": {"pass": "applications/targets/1"},
+ },
+ {
+ "match": {"uri": "*"},
+ "action": {"pass": "applications/targets/2"},
+ },
+ ],
+ "routes",
+ )
+
+ def check_prefix(url, prefix):
+ resp = self.get(url=url)
+ assert resp['status'] == 200
+ assert resp['headers']['prefix'] == prefix
+
+ check_prefix('/1', '/1')
+ check_prefix('/11', 'NULL')
+ check_prefix('/1/', '/1')
+ check_prefix('/', 'NULL')
+ check_prefix('/ap', 'NULL')
+ check_prefix('/api', '/api')
+ check_prefix('/api/', '/api')
+ check_prefix('/api/test/', '/api')
+ check_prefix('/apis', 'NULL')
+ check_prefix('/apis/', 'NULL')
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 7c612db0..9c27222c 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -318,6 +318,92 @@ class TestConfiguration(TestControl):
assert 'success' in self.conf(conf)
+ def test_json_application_python_prefix(self):
+ conf = {
+ "applications": {
+ "sub-app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ "prefix": "/app",
+ }
+ },
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {
+ "match": {"uri": "/app/*"},
+ "action": {"pass": "applications/sub-app"},
+ }
+ ],
+ }
+
+ assert 'success' in self.conf(conf)
+
+ def test_json_application_prefix_target(self):
+ conf = {
+ "applications": {
+ "sub-app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "targets": {
+ "foo": {"module": "foo.wsgi", "prefix": "/app"},
+ "bar": {
+ "module": "bar.wsgi",
+ "callable": "bar",
+ "prefix": "/api",
+ },
+ },
+ }
+ },
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {
+ "match": {"uri": "/app/*"},
+ "action": {"pass": "applications/sub-app/foo"},
+ },
+ {
+ "match": {"uri": "/api/*"},
+ "action": {"pass": "applications/sub-app/bar"},
+ },
+ ],
+ }
+
+ assert 'success' in self.conf(conf)
+
+ def test_json_application_invalid_python_prefix(self):
+ conf = {
+ "applications": {
+ "sub-app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ "prefix": "app",
+ }
+ },
+ "listeners": {"*:7080": {"pass": "applications/sub-app"}},
+ }
+
+ assert 'error' in self.conf(conf)
+
+ def test_json_application_empty_python_prefix(self):
+ conf = {
+ "applications": {
+ "sub-app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ "prefix": "",
+ }
+ },
+ "listeners": {"*:7080": {"pass": "applications/sub-app"}},
+ }
+
+ assert 'error' in self.conf(conf)
+
def test_json_application_many2(self):
conf = {
"applications": {
diff --git a/test/test_java_application.py b/test/test_java_application.py
index adcb4eca..b825d925 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -71,6 +71,11 @@ class TestJavaApplication(TestApplicationJava):
def test_java_application_get_variables(self):
self.load('get_params')
+ def check_header(header, expect):
+ values = header.split(' ')[:-1]
+ assert len(values) == len(expect)
+ assert set(values) == set(expect)
+
headers = self.get(url='/?var1=val1&var2=&var4=val4&var4=foo')[
'headers'
]
@@ -79,13 +84,11 @@ class TestJavaApplication(TestApplicationJava):
assert headers['X-Var-2'] == 'true', 'GET variables 2'
assert headers['X-Var-3'] == 'false', 'GET variables 3'
- assert (
- headers['X-Param-Names'] == 'var4 var2 var1 '
- ), 'getParameterNames'
- assert headers['X-Param-Values'] == 'val4 foo ', 'getParameterValues'
- assert (
- headers['X-Param-Map'] == 'var2= var1=val1 var4=val4,foo '
- ), 'getParameterMap'
+ check_header(headers['X-Param-Names'], ['var4', 'var2', 'var1'])
+ check_header(headers['X-Param-Values'], ['val4', 'foo'])
+ check_header(
+ headers['X-Param-Map'], ['var2=', 'var1=val1', 'var4=val4,foo']
+ )
def test_java_application_post_variables(self):
self.load('post_params')
@@ -1001,14 +1004,13 @@ class TestJavaApplication(TestApplicationJava):
socks = []
for i in range(4):
- (_, sock) = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'X-Delay': '2',
'Connection': 'close',
},
no_recv=True,
- start=True,
)
socks.append(sock)
diff --git a/test/test_njs.py b/test/test_njs.py
new file mode 100644
index 00000000..2cbded5b
--- /dev/null
+++ b/test/test_njs.py
@@ -0,0 +1,90 @@
+import os
+
+from unit.applications.proto import TestApplicationProto
+from unit.option import option
+
+
+class TestNJS(TestApplicationProto):
+ prerequisites = {'modules': {'njs': 'any'}}
+
+ def setup_method(self):
+ os.makedirs(option.temp_dir + '/assets')
+ open(option.temp_dir + '/assets/index.html', 'a')
+ open(option.temp_dir + '/assets/localhost', 'a')
+ open(option.temp_dir + '/assets/`string`', 'a')
+ open(option.temp_dir + '/assets/`backtick', 'a')
+ open(option.temp_dir + '/assets/l1\nl2', 'a')
+ open(option.temp_dir + '/assets/127.0.0.1', 'a')
+
+ assert 'success' in self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {"action": {"share": option.temp_dir + "/assets$uri"}}
+ ],
+ }
+ )
+
+ def set_share(self, share):
+ assert 'success' in self.conf(share, 'routes/0/action/share')
+
+ def test_njs_template_string(self, temp_dir):
+ self.set_share('"`' + temp_dir + '/assets/index.html`"')
+ assert self.get()['status'] == 200, 'string'
+
+ self.set_share('"' + temp_dir + '/assets/`string`"')
+ assert self.get()['status'] == 200, 'string 2'
+
+ self.set_share('"`' + temp_dir + '/assets/\\\\`backtick`"')
+ assert self.get()['status'] == 200, 'escape'
+
+ self.set_share('"`' + temp_dir + '/assets/l1\\nl2`"')
+ assert self.get()['status'] == 200, 'multiline'
+
+ def test_njs_template_expression(self, temp_dir):
+ def check_expression(expression):
+ self.set_share(expression)
+ assert self.get()['status'] == 200
+
+ check_expression('"`' + temp_dir + '/assets${uri}`"')
+ check_expression('"`' + temp_dir + '/assets${uri}${host}`"')
+ check_expression('"`' + temp_dir + '/assets${uri + host}`"')
+ check_expression('"`' + temp_dir + '/assets${uri + `${host}`}`"')
+
+ def test_njs_variables(self, temp_dir):
+ self.set_share('"`' + temp_dir + '/assets/${host}`"')
+ assert self.get()['status'] == 200, 'host'
+
+ self.set_share('"`' + temp_dir + '/assets/${remoteAddr}`"')
+ assert self.get()['status'] == 200, 'remoteAddr'
+
+ self.set_share('"`' + temp_dir + '/assets/${headers.Host}`"')
+ assert self.get()['status'] == 200, 'headers'
+
+ self.set_share('"`' + temp_dir + '/assets/${cookies.foo}`"')
+ assert (
+ self.get(
+ headers={'Cookie': 'foo=localhost', 'Connection': 'close'}
+ )['status']
+ == 200
+ ), 'cookies'
+
+ self.set_share('"`' + temp_dir + '/assets/${args.foo}`"')
+ assert self.get(url='/?foo=localhost')['status'] == 200, 'args'
+
+ def test_njs_invalid(self, temp_dir, skip_alert):
+ skip_alert(r'js exception:')
+
+ def check_invalid(template):
+ assert 'error' in self.conf(template, 'routes/0/action/share')
+
+ check_invalid('"`a"')
+ check_invalid('"`a``"')
+ check_invalid('"`a`/"')
+
+ def check_invalid_resolve(template):
+ assert 'success' in self.conf(template, 'routes/0/action/share')
+ assert self.get()['status'] == 500
+
+ check_invalid_resolve('"`${a}`"')
+ check_invalid_resolve('"`${uri.a.a}`"')
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index 0d1d7906..fe2db72e 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -259,14 +259,13 @@ class TestPerlApplication(TestApplicationPerl):
socks = []
for i in range(4):
- (_, sock) = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'X-Delay': '2',
'Connection': 'close',
},
no_recv=True,
- start=True,
)
socks.append(sock)
diff --git a/test/test_php_application.py b/test/test_php_application.py
index f1dcc995..f442f551 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -4,6 +4,7 @@ import re
import shutil
import signal
import time
+from pathlib import Path
import pytest
from unit.applications.lang.php import TestApplicationPHP
@@ -620,6 +621,49 @@ opcache.preload_user = %(user)s
assert resp['status'] == 200, 'status'
assert resp['body'] != '', 'body not empty'
+ def test_php_application_trailing_slash(self, temp_dir):
+ new_root = temp_dir + "/php-root"
+ os.makedirs(new_root + '/path')
+
+ Path(new_root + '/path/index.php').write_text('<?php echo "OK\n"; ?>')
+
+ addr = temp_dir + '/sock'
+
+ assert 'success' in self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "applications/php-path"},
+ "unix:" + addr: {"pass": "applications/php-path"},
+ },
+ "applications": {
+ "php-path": {
+ "type": self.get_application_type(),
+ "processes": {"spare": 0},
+ "root": new_root,
+ }
+ },
+ }
+ ), 'configure trailing slash'
+
+ assert self.get(url='/path/')['status'] == 200, 'uri with trailing /'
+
+ resp = self.get(url='/path?q=a')
+ assert resp['status'] == 301, 'uri without trailing /'
+ assert (
+ resp['headers']['Location'] == 'http://localhost:7080/path/?q=a'
+ ), 'Location with query string'
+
+ resp = self.get(
+ sock_type='unix',
+ addr=addr,
+ url='/path',
+ headers={'Host': 'foo', 'Connection': 'close'},
+ )
+ assert resp['status'] == 301, 'uri without trailing /'
+ assert (
+ resp['headers']['Location'] == 'http://foo/path/'
+ ), 'Location with custom Host over UDS'
+
def test_php_application_extension_check(self, temp_dir):
self.load('phpinfo')
diff --git a/test/test_proxy.py b/test/test_proxy.py
index b0d471e4..ede91fd6 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -185,9 +185,8 @@ Content-Length: 10
socks = []
for i in range(10):
- _, sock = self.post_http10(
+ sock = self.post_http10(
body=payload + str(i),
- start=True,
no_recv=True,
read_buffer_size=buff_size,
)
@@ -248,9 +247,7 @@ Content-Length: 10
), 'custom header 5'
def test_proxy_fragmented(self):
- _, sock = self.http(
- b"""GET / HTT""", raw=True, start=True, no_recv=True
- )
+ sock = self.http(b"""GET / HTT""", raw=True, no_recv=True)
time.sleep(1)
@@ -266,9 +263,7 @@ Content-Length: 10
sock.close()
def test_proxy_fragmented_close(self):
- _, sock = self.http(
- b"""GET / HTT""", raw=True, start=True, no_recv=True
- )
+ sock = self.http(b"""GET / HTT""", raw=True, no_recv=True)
time.sleep(1)
@@ -277,9 +272,7 @@ Content-Length: 10
sock.close()
def test_proxy_fragmented_body(self):
- _, sock = self.http(
- b"""GET / HTT""", raw=True, start=True, no_recv=True
- )
+ sock = self.http(b"""GET / HTT""", raw=True, no_recv=True)
time.sleep(1)
@@ -306,9 +299,7 @@ Content-Length: 10
assert resp['body'] == "X" * 30000, 'body'
def test_proxy_fragmented_body_close(self):
- _, sock = self.http(
- b"""GET / HTT""", raw=True, start=True, no_recv=True
- )
+ sock = self.http(b"""GET / HTT""", raw=True, no_recv=True)
time.sleep(1)
@@ -398,7 +389,7 @@ Content-Length: 10
{"pass": "applications/delayed"}, 'listeners/*:7081'
), 'delayed configure'
- _, sock = self.post_http10(
+ sock = self.post_http10(
headers={
'Host': 'localhost',
'Content-Length': '10000',
@@ -406,14 +397,13 @@ Content-Length: 10
'X-Delay': '1',
},
body='0123456789' * 1000,
- start=True,
no_recv=True,
)
assert re.search('200 OK', sock.recv(100).decode()), 'first'
sock.close()
- _, sock = self.post_http10(
+ sock = self.post_http10(
headers={
'Host': 'localhost',
'Content-Length': '10000',
@@ -421,7 +411,6 @@ Content-Length: 10
'X-Delay': '1',
},
body='0123456789' * 1000,
- start=True,
no_recv=True,
)
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 2ea9a22e..c9483b6a 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -94,6 +94,44 @@ custom-header: BLAH
resp['headers']['Query-String'] == ' var1= val1 & var2=val2'
), 'Query-String space 4'
+ def test_python_application_prefix(self):
+ self.load('prefix', prefix='/api/rest')
+
+ def set_prefix(prefix):
+ self.conf('"' + prefix + '"', 'applications/prefix/prefix')
+
+ def check_prefix(url, script_name, path_info):
+ resp = self.get(url=url)
+ assert resp['status'] == 200
+ assert resp['headers']['Script-Name'] == script_name
+ assert resp['headers']['Path-Info'] == path_info
+
+ check_prefix('/ap', 'NULL', '/ap')
+ check_prefix('/api', 'NULL', '/api')
+ check_prefix('/api/', 'NULL', '/api/')
+ check_prefix('/api/res', 'NULL', '/api/res')
+ check_prefix('/api/restful', 'NULL', '/api/restful')
+ check_prefix('/api/rest', '/api/rest', '')
+ check_prefix('/api/rest/', '/api/rest', '/')
+ check_prefix('/api/rest/get', '/api/rest', '/get')
+ check_prefix('/api/rest/get/blah', '/api/rest', '/get/blah')
+
+ set_prefix('/api/rest/')
+ check_prefix('/api/rest', '/api/rest', '')
+ check_prefix('/api/restful', 'NULL', '/api/restful')
+ check_prefix('/api/rest/', '/api/rest', '/')
+ check_prefix('/api/rest/blah', '/api/rest', '/blah')
+
+ set_prefix('/app')
+ check_prefix('/ap', 'NULL', '/ap')
+ check_prefix('/app', '/app', '')
+ check_prefix('/app/', '/app', '/')
+ check_prefix('/application/', 'NULL', '/application/')
+
+ set_prefix('/')
+ check_prefix('/', 'NULL', '/')
+ check_prefix('/app', 'NULL', '/app')
+
def test_python_application_query_string_empty(self):
self.load('query_string')
@@ -765,14 +803,13 @@ last line: 987654321
socks = []
for i in range(4):
- (_, sock) = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'X-Delay': '2',
'Connection': 'close',
},
no_recv=True,
- start=True,
)
socks.append(sock)
diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py
index 8cef6812..6d4ffaf3 100644
--- a/test/test_python_isolation.py
+++ b/test/test_python_isolation.py
@@ -1,3 +1,8 @@
+import os
+import re
+import subprocess
+from pathlib import Path
+
import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
@@ -9,6 +14,23 @@ from unit.utils import waitforunmount
class TestPythonIsolation(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']}
+ def get_cgroup(self, app_name):
+ output = subprocess.check_output(
+ ['ps', 'ax', '-o', 'pid', '-o', 'cmd']
+ ).decode()
+
+ pid = re.search(
+ r'(\d+)\s*unit: "' + app_name + '" application', output
+ ).group(1)
+
+ cgroup = '/proc/' + pid + '/cgroup'
+
+ if not os.path.isfile(cgroup):
+ pytest.skip('no cgroup at ' + cgroup)
+
+ with open(cgroup, 'r') as f:
+ return f.read().rstrip()
+
def test_python_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
@@ -63,24 +85,25 @@ class TestPythonIsolation(TestApplicationPython):
pytest.skip('requires root')
isolation = {'rootfs': temp_dir, 'automount': {'language_deps': False}}
-
self.load('empty', isolation=isolation)
- assert findmnt().find(temp_dir) == -1
+ python_path = temp_dir + '/usr'
+
+ assert findmnt().find(python_path) == -1
assert self.get()['status'] != 200, 'disabled language_deps'
- assert findmnt().find(temp_dir) == -1
+ assert findmnt().find(python_path) == -1
isolation['automount']['language_deps'] = True
self.load('empty', isolation=isolation)
- assert findmnt().find(temp_dir) == -1
+ assert findmnt().find(python_path) == -1
assert self.get()['status'] == 200, 'enabled language_deps'
- assert waitformount(temp_dir), 'language_deps mount'
+ assert waitformount(python_path), 'language_deps mount'
self.conf({"listeners": {}, "applications": {}})
- assert waitforunmount(temp_dir), 'language_deps unmount'
+ assert waitforunmount(python_path), 'language_deps unmount'
def test_python_isolation_procfs(self, is_su, temp_dir):
if not is_su:
@@ -101,3 +124,104 @@ class TestPythonIsolation(TestApplicationPython):
assert (
self.getjson(url='/?path=/proc/self')['body']['FileExists'] == True
), '/proc/self'
+
+ def test_python_isolation_cgroup(self, is_su, temp_dir):
+ if not is_su:
+ pytest.skip('requires root')
+
+ if not 'cgroup' in option.available['features']['isolation']:
+ pytest.skip('cgroup is not supported')
+
+ def set_cgroup_path(path):
+ isolation = {'cgroup': {'path': path}}
+ self.load('empty', processes=1, isolation=isolation)
+
+ set_cgroup_path('scope/python')
+
+ cgroup_rel = Path(self.get_cgroup('empty'))
+ assert cgroup_rel.parts[-2:] == ('scope', 'python'), 'cgroup rel'
+
+ set_cgroup_path('/scope2/python')
+
+ cgroup_abs = Path(self.get_cgroup('empty'))
+ assert cgroup_abs.parts[-2:] == ('scope2', 'python'), 'cgroup abs'
+
+ assert len(cgroup_rel.parts) >= len(cgroup_abs.parts)
+
+ def test_python_isolation_cgroup_two(self, is_su, temp_dir):
+ if not is_su:
+ pytest.skip('requires root')
+
+ if not 'cgroup' in option.available['features']['isolation']:
+ pytest.skip('cgroup is not supported')
+
+ def set_two_cgroup_path(path, path2):
+ script_path = option.test_dir + '/python/empty'
+
+ assert 'success' in self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "applications/one"},
+ "*:7081": {"pass": "applications/two"},
+ },
+ "applications": {
+ "one": {
+ "type": "python",
+ "processes": 1,
+ "path": script_path,
+ "working_directory": script_path,
+ "module": "wsgi",
+ "isolation": {
+ 'cgroup': {'path': path},
+ },
+ },
+ "two": {
+ "type": "python",
+ "processes": 1,
+ "path": script_path,
+ "working_directory": script_path,
+ "module": "wsgi",
+ "isolation": {
+ 'cgroup': {'path': path2},
+ },
+ },
+ },
+ }
+ )
+
+ set_two_cgroup_path('/scope/python', '/scope/python')
+ assert self.get_cgroup('one') == self.get_cgroup('two')
+
+ set_two_cgroup_path('/scope/python', '/scope2/python')
+ assert self.get_cgroup('one') != self.get_cgroup('two')
+
+ def test_python_isolation_cgroup_invalid(self, is_su):
+ if not is_su:
+ pytest.skip('requires root')
+
+ if not 'cgroup' in option.available['features']['isolation']:
+ pytest.skip('cgroup is not supported')
+
+ def check_invalid(path):
+ script_path = option.test_dir + '/python/empty'
+ assert 'error' in self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/empty"}},
+ "applications": {
+ "empty": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": script_path,
+ "working_directory": script_path,
+ "module": "wsgi",
+ "isolation": {
+ 'cgroup': {'path': path},
+ },
+ }
+ },
+ }
+ )
+
+ check_invalid('')
+ check_invalid('../scope')
+ check_invalid('scope/../python')
diff --git a/test/test_python_targets.py b/test/test_python_targets.py
index 8e9ecb87..ae271b5f 100644
--- a/test/test_python_targets.py
+++ b/test/test_python_targets.py
@@ -47,3 +47,55 @@ class TestPythonTargets(TestApplicationPython):
resp = self.get(url='/2')
assert resp['status'] == 200
assert resp['body'] == '2'
+
+ def test_python_targets_prefix(self):
+ assert 'success' in self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {
+ "match": {"uri": ["/app*"]},
+ "action": {"pass": "applications/targets/app"},
+ },
+ {
+ "match": {"uri": "*"},
+ "action": {"pass": "applications/targets/catchall"},
+ },
+ ],
+ "applications": {
+ "targets": {
+ "type": "python",
+ "working_directory": option.test_dir
+ + "/python/targets/",
+ "path": option.test_dir + '/python/targets/',
+ "protocol": "wsgi",
+ "targets": {
+ "app": {
+ "module": "wsgi",
+ "callable": "wsgi_target_prefix",
+ "prefix": "/app/",
+ },
+ "catchall": {
+ "module": "wsgi",
+ "callable": "wsgi_target_prefix",
+ "prefix": "/api",
+ },
+ },
+ }
+ },
+ }
+ )
+
+ def check_prefix(url, body):
+ resp = self.get(url=url)
+ assert resp['status'] == 200
+ assert resp['body'] == body
+
+ check_prefix('/app', '/app ')
+ check_prefix('/app/', '/app /')
+ check_prefix('/app/rest/user/', '/app /rest/user/')
+ check_prefix('/catchall', 'No Script Name /catchall')
+ check_prefix('/api', '/api ')
+ check_prefix('/api/', '/api /')
+ check_prefix('/apis', 'No Script Name /apis')
+ check_prefix('/api/users/', '/api /users/')
diff --git a/test/test_reconfigure.py b/test/test_reconfigure.py
index ab05a1c8..feb027aa 100644
--- a/test/test_reconfigure.py
+++ b/test/test_reconfigure.py
@@ -21,10 +21,9 @@ class TestReconfigure(TestApplicationProto):
assert 'success' in self.conf({"listeners": {}, "applications": {}})
def test_reconfigure(self):
- (_, sock) = self.http(
+ sock = self.http(
b"""GET / HTTP/1.1
""",
- start=True,
raw=True,
no_recv=True,
)
@@ -42,7 +41,7 @@ Connection: close
assert resp['status'] == 200, 'finish request'
def test_reconfigure_2(self):
- (_, sock) = self.http(b'', raw=True, start=True, no_recv=True)
+ sock = self.http(b'', raw=True, no_recv=True)
# Waiting for connection completion.
# Delay should be more than TCP_DEFER_ACCEPT.
diff --git a/test/test_routing.py b/test/test_routing.py
index 3649b37c..9e872061 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1401,6 +1401,20 @@ class TestRouting(TestApplicationPython):
self.route_match_invalid({"cookies": ["var"]})
self.route_match_invalid({"cookies": [{"foo": {}}]})
+ def test_routes_match_cookies_complex(self):
+ self.route_match({"cookies": {"foo": "bar=baz"}})
+ self.cookie('foo=bar=baz', 200)
+ self.cookie(' foo=bar=baz ', 200)
+ self.cookie('=foo=bar=baz', 404)
+
+ self.route_match({"cookies": {"foo": ""}})
+ self.cookie('foo=', 200)
+ self.cookie('foo=;', 200)
+ self.cookie(' foo=;', 200)
+ self.cookie('foo', 404)
+ self.cookie('', 404)
+ self.cookie('=', 404)
+
def test_routes_match_cookies_multiple(self):
self.route_match({"cookies": {"foo": "bar", "blah": "blah"}})
@@ -1471,7 +1485,7 @@ class TestRouting(TestApplicationPython):
def test_routes_source_port(self):
def sock_port():
- _, sock = self.http(b'', start=True, raw=True, no_recv=True)
+ sock = self.http(b'', raw=True, no_recv=True)
port = sock.getsockname()[1]
return (sock, port)
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 83af39be..068b587b 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -388,14 +388,13 @@ class TestRubyApplication(TestApplicationRuby):
socks = []
for i in range(4):
- (_, sock) = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'X-Delay': '2',
'Connection': 'close',
},
no_recv=True,
- start=True,
)
socks.append(sock)
diff --git a/test/test_settings.py b/test/test_settings.py
index ea3cfb99..ad8929f8 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -10,6 +10,66 @@ from unit.utils import sysctl
class TestSettings(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
+ def test_settings_large_header_buffer_size(self):
+ self.load('empty')
+
+ def set_buffer_size(size):
+ assert 'success' in self.conf(
+ {'http': {'large_header_buffer_size': size}},
+ 'settings',
+ )
+
+ def header_value(size, expect=200):
+ headers = {'Host': 'a' * (size - 1), 'Connection': 'close'}
+ assert self.get(headers=headers)['status'] == expect
+
+ set_buffer_size(4096)
+ header_value(4096)
+ header_value(4097, 431)
+
+ set_buffer_size(16384)
+ header_value(16384)
+ header_value(16385, 431)
+
+ def test_settings_large_header_buffers(self):
+ self.load('empty')
+
+ def set_buffers(buffers):
+ assert 'success' in self.conf(
+ {'http': {'large_header_buffers': buffers}},
+ 'settings',
+ )
+
+ def big_headers(headers_num, expect=200):
+ headers = {'Host': 'localhost', 'Connection': 'close'}
+
+ for i in range(headers_num):
+ headers['Custom-header-' + str(i)] = 'a' * 8000
+
+ assert self.get(headers=headers)['status'] == expect
+
+ set_buffers(1)
+ big_headers(1)
+ big_headers(2, 431)
+
+ set_buffers(2)
+ big_headers(2)
+ big_headers(3, 431)
+
+ set_buffers(8)
+ big_headers(8)
+ big_headers(9, 431)
+
+ @pytest.mark.skip('not yet')
+ def test_settings_large_header_buffer_invalid(self):
+ def check_error(conf):
+ assert 'error' in self.conf({'http': conf}, 'settings')
+
+ check_error({'large_header_buffer_size': -1})
+ check_error({'large_header_buffer_size': 0})
+ check_error({'large_header_buffers': -1})
+ check_error({'large_header_buffers': 0})
+
def test_settings_header_read_timeout(self):
self.load('empty')
@@ -50,20 +110,18 @@ Connection: close
{'http': {'header_read_timeout': 4}}, 'settings'
)
- (resp, sock) = self.http(
+ sock = self.http(
b"""GET / HTTP/1.1
""",
- start=True,
raw=True,
no_recv=True,
)
time.sleep(2)
- (resp, sock) = self.http(
+ sock = self.http(
b"""Host: localhost
""",
- start=True,
sock=sock,
raw=True,
no_recv=True,
@@ -245,7 +303,7 @@ Connection: close
self.load('empty')
def req():
- _, sock = self.http(b'', start=True, raw=True, no_recv=True)
+ sock = self.http(b'', raw=True, no_recv=True)
time.sleep(3)
diff --git a/test/test_static.py b/test/test_static.py
index b9c78fdd..9013b5c0 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -1,10 +1,7 @@
import os
-import shutil
import socket
import pytest
-from conftest import unit_run
-from conftest import unit_stop
from unit.applications.proto import TestApplicationProto
from unit.option import option
from unit.utils import waitforfiles
@@ -43,49 +40,6 @@ class TestStatic(TestApplicationProto):
}
)
- def test_static_migration(self, skip_fds_check, temp_dir):
- skip_fds_check(True, True, True)
-
- def set_conf_version(path, version):
- with open(path, 'w+') as f:
- f.write(str(version))
-
- with open(temp_dir + '/state/version', 'r') as f:
- assert int(f.read().rstrip()) > 12500, 'current version'
-
- assert 'success' in self.conf(
- {"share": temp_dir + "/assets"}, 'routes/0/action'
- ), 'configure migration 12500'
-
- shutil.copytree(temp_dir + '/state', temp_dir + '/state_copy_12500')
- set_conf_version(temp_dir + '/state_copy_12500/version', 12500)
-
- assert 'success' in self.conf(
- {"share": temp_dir + "/assets$uri"}, 'routes/0/action'
- ), 'configure migration 12600'
- shutil.copytree(temp_dir + '/state', temp_dir + '/state_copy_12600')
- set_conf_version(temp_dir + '/state_copy_12600/version', 12600)
-
- assert 'success' in self.conf(
- {"share": temp_dir + "/assets"}, 'routes/0/action'
- ), 'configure migration no version'
- shutil.copytree(
- temp_dir + '/state', temp_dir + '/state_copy_no_version'
- )
- os.remove(temp_dir + '/state_copy_no_version/version')
-
- unit_stop()
- unit_run(temp_dir + '/state_copy_12500')
- assert self.get(url='/')['body'] == '0123456789', 'before 1.26.0'
-
- unit_stop()
- unit_run(temp_dir + '/state_copy_12600')
- assert self.get(url='/')['body'] == '0123456789', 'after 1.26.0'
-
- unit_stop()
- unit_run(temp_dir + '/state_copy_no_version')
- assert self.get(url='/')['body'] == '0123456789', 'before 1.26.0 2'
-
def test_static_index(self):
def set_index(index):
assert 'success' in self.conf(
@@ -262,8 +216,8 @@ class TestStatic(TestApplicationProto):
assert self.get(url='/../assets/')['status'] == 400, 'path invalid 5'
def test_static_two_clients(self):
- _, sock = self.get(url='/', start=True, no_recv=True)
- _, sock2 = self.get(url='/', start=True, no_recv=True)
+ sock = self.get(no_recv=True)
+ sock2 = self.get(no_recv=True)
assert sock.recv(1) == b'H', 'client 1'
assert sock2.recv(1) == b'H', 'client 2'
diff --git a/test/test_status.py b/test/test_status.py
index 214072d4..6c733474 100644
--- a/test/test_status.py
+++ b/test/test_status.py
@@ -9,6 +9,23 @@ from unit.status import Status
class TestStatus(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
+ def check_connections(self, accepted, active, idle, closed):
+ Status.get('/connections') == {
+ 'accepted': accepted,
+ 'active': active,
+ 'idle': idle,
+ 'closed': closed,
+ }
+
+ def app_default(self, name="empty", module="wsgi"):
+ return {
+ "type": self.get_application_type(),
+ "processes": {"spare": 0},
+ "path": option.test_dir + "/python/" + name,
+ "working_directory": option.test_dir + "/python/" + name,
+ "module": module,
+ }
+
def test_status(self):
assert 'error' in self.conf_delete('/status'), 'DELETE method'
@@ -24,13 +41,7 @@ class TestStatus(TestApplicationPython):
},
"routes": [{"action": {"return": 200}}],
"applications": {
- "empty": {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "path": option.test_dir + '/python/empty',
- "working_directory": option.test_dir + '/python/empty',
- "module": "wsgi",
- },
+ "empty": self.app_default(),
"blah": {
"type": self.get_application_type(),
"processes": {"spare": 0},
@@ -70,7 +81,7 @@ Connection: close
)
assert Status.get('/requests/total') == 6, 'pipeline'
- (_, sock) = self.get(port=7081, no_recv=True, start=True)
+ sock = self.get(port=7081, no_recv=True)
time.sleep(1)
@@ -79,14 +90,6 @@ Connection: close
sock.close()
def test_status_connections(self):
- def check_connections(accepted, active, idle, closed):
- Status.get('/connections') == {
- 'accepted': accepted,
- 'active': active,
- 'idle': idle,
- 'closed': closed,
- }
-
assert 'success' in self.conf(
{
"listeners": {
@@ -95,14 +98,7 @@ Connection: close
},
"routes": [{"action": {"return": 200}}],
"applications": {
- "delayed": {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "path": option.test_dir + "/python/delayed",
- "working_directory": option.test_dir
- + "/python/delayed",
- "module": "wsgi",
- },
+ "delayed": self.app_default("delayed"),
},
},
)
@@ -112,15 +108,15 @@ Connection: close
# accepted, closed
assert self.get()['status'] == 200
- check_connections(1, 0, 0, 1)
+ self.check_connections(1, 0, 0, 1)
# idle
- _, sock = self.http(b'', start=True, raw=True, no_recv=True)
- check_connections(2, 0, 1, 1)
+ sock = self.http(b'', raw=True, no_recv=True)
+ self.check_connections(2, 0, 1, 1)
self.get(sock=sock)
- check_connections(2, 0, 0, 2)
+ self.check_connections(2, 0, 0, 2)
# active
@@ -134,10 +130,10 @@ Connection: close
start=True,
read_timeout=1,
)
- check_connections(3, 1, 0, 2)
+ self.check_connections(3, 1, 0, 2)
self.get(sock=sock)
- check_connections(3, 0, 0, 3)
+ self.check_connections(3, 0, 0, 3)
def test_status_applications(self):
def check_applications(expert):
@@ -192,22 +188,8 @@ Connection: close
},
"routes": [],
"applications": {
- "restart": {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "path": option.test_dir + "/python/restart",
- "working_directory": option.test_dir
- + "/python/restart",
- "module": "longstart",
- },
- "delayed": {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "path": option.test_dir + "/python/delayed",
- "working_directory": option.test_dir
- + "/python/delayed",
- "module": "wsgi",
- },
+ "restart": self.app_default("restart", "longstart"),
+ "delayed": self.app_default("delayed"),
},
},
)
@@ -221,3 +203,28 @@ Connection: close
check_application('restart', 0, 1, 0, 1)
check_application('delayed', 0, 0, 0, 0)
+
+ def test_status_proxy(self):
+ assert 'success' in self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "routes"},
+ "*:7081": {"pass": "applications/empty"},
+ },
+ "routes": [
+ {
+ "match": {"uri": "/"},
+ "action": {"proxy": "http://127.0.0.1:7081"},
+ }
+ ],
+ "applications": {
+ "empty": self.app_default(),
+ },
+ },
+ )
+
+ Status.init()
+
+ assert self.get()['status'] == 200
+ self.check_connections(2, 0, 0, 2)
+ assert Status.get('/requests/total') == 2, 'proxy'
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
index dd64e1d9..71af3f5d 100644
--- a/test/test_upstreams_rr.py
+++ b/test/test_upstreams_rr.py
@@ -290,14 +290,13 @@ Connection: close
socks = []
for i in range(req):
delay = 1 if i % 5 == 0 else 0
- _, sock = self.get(
+ sock = self.get(
headers={
'Host': 'localhost',
'Content-Length': '0',
'X-Delay': str(delay),
'Connection': 'close',
},
- start=True,
no_recv=True,
)
socks.append(sock)
@@ -320,17 +319,16 @@ Connection: close
socks2 = []
for _ in range(conns):
- _, sock = self.get(start=True, no_recv=True)
+ sock = self.get(no_recv=True)
socks.append(sock)
- _, sock2 = self.http(
+ sock2 = self.http(
b"""POST / HTTP/1.1
Host: localhost
Content-Length: 10
Connection: close
""",
- start=True,
no_recv=True,
raw=True,
)
diff --git a/test/test_variables.py b/test/test_variables.py
index 2ddfdc0a..ecce5e6d 100644
--- a/test/test_variables.py
+++ b/test/test_variables.py
@@ -1,4 +1,8 @@
+import re
+import time
+
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestVariables(TestApplicationProto):
@@ -7,79 +11,194 @@ class TestVariables(TestApplicationProto):
def setup_method(self):
assert 'success' in self.conf(
{
- "listeners": {"*:7080": {"pass": "routes/$method"}},
- "routes": {
- "GET": [{"action": {"return": 201}}],
- "POST": [{"action": {"return": 202}}],
- "3": [{"action": {"return": 203}}],
- "4*": [{"action": {"return": 204}}],
- "blahGET}": [{"action": {"return": 205}}],
- "5GET": [{"action": {"return": 206}}],
- "GETGET": [{"action": {"return": 207}}],
- "localhost": [{"action": {"return": 208}}],
- "9?q#a": [{"action": {"return": 209}}],
- "blah": [{"action": {"return": 210}}],
- "127.0.0.1": [{"action": {"return": 211}}],
- "::1": [{"action": {"return": 212}}],
- "referer-value": [{"action": {"return": 213}}],
- "MSIE": [{"action": {"return": 214}}],
- },
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [{"action": {"return": 200}}],
},
), 'configure routes'
- def conf_routes(self, routes):
- assert 'success' in self.conf(routes, 'listeners/*:7080/pass')
+ def set_format(self, format):
+ assert 'success' in self.conf(
+ {
+ 'path': option.temp_dir + '/access.log',
+ 'format': format,
+ },
+ 'access_log',
+ ), 'access_log format'
+
+ def wait_for_record(self, pattern, name='access.log'):
+ return super().wait_for_record(pattern, name)
+
+ def search_in_log(self, pattern, name='access.log'):
+ return super().search_in_log(pattern, name)
+
+ def test_variables_dollar(self):
+ assert 'success' in self.conf("301", 'routes/0/action/return')
+
+ def check_dollar(location, expect):
+ assert 'success' in self.conf(
+ '"' + location + '"',
+ 'routes/0/action/location',
+ )
+ assert self.get()['headers']['Location'] == expect
+
+ check_dollar(
+ 'https://${host}${uri}path${dollar}dollar',
+ 'https://localhost/path$dollar',
+ )
+ check_dollar('path$dollar${dollar}', 'path$$')
+
+ def test_variables_request_time(self):
+ self.set_format('$uri $request_time')
+
+ sock = self.http(b'', raw=True, no_recv=True)
+
+ time.sleep(1)
+
+ assert self.get(url='/r_time_1', sock=sock)['status'] == 200
+ assert self.wait_for_record(r'\/r_time_1 0\.\d{3}') is not None
+
+ sock = self.http(
+ b"""G""",
+ no_recv=True,
+ raw=True,
+ )
+
+ time.sleep(2)
+
+ self.http(
+ b"""ET /r_time_2 HTTP/1.1
+Host: localhost
+Connection: close
+
+""",
+ sock=sock,
+ raw=True,
+ )
+ assert self.wait_for_record(r'\/r_time_2 [1-9]\.\d{3}') is not None
def test_variables_method(self):
- assert self.get()['status'] == 201, 'method GET'
- assert self.post()['status'] == 202, 'method POST'
+ self.set_format('$method')
+
+ reg = r'^GET$'
+ assert self.search_in_log(reg) is None
+ assert self.get()['status'] == 200
+ assert self.wait_for_record(reg) is not None, 'method GET'
+
+ reg = r'^POST$'
+ assert self.search_in_log(reg) is None
+ assert self.post()['status'] == 200
+ assert self.wait_for_record(reg) is not None, 'method POST'
def test_variables_request_uri(self):
- self.conf_routes("\"routes$request_uri\"")
+ self.set_format('$request_uri')
+
+ def check_request_uri(req_uri):
+ reg = r'^' + re.escape(req_uri) + r'$'
+
+ assert self.search_in_log(reg) is None
+ assert self.get(url=req_uri)['status'] == 200
+ assert self.wait_for_record(reg) is not None
- assert self.get(url='/3')['status'] == 203, 'request_uri'
- assert self.get(url='/4*')['status'] == 204, 'request_uri 2'
- assert self.get(url='/4%2A')['status'] == 204, 'request_uri 3'
- assert self.get(url='/9?q#a')['status'] == 209, 'request_uri query'
+ check_request_uri('/3')
+ check_request_uri('/4*')
+ check_request_uri('/4%2A')
+ check_request_uri('/9?q#a')
def test_variables_uri(self):
- self.conf_routes("\"routes$uri\"")
+ self.set_format('$uri')
- assert self.get(url='/3')['status'] == 203, 'uri'
- assert self.get(url='/4*')['status'] == 204, 'uri 2'
- assert self.get(url='/4%2A')['status'] == 204, 'uri 3'
+ def check_uri(uri, expect=None):
+ expect = uri if expect is None else expect
+ reg = r'^' + re.escape(expect) + r'$'
+
+ assert self.search_in_log(reg) is None
+ assert self.get(url=uri)['status'] == 200
+ assert self.wait_for_record(reg) is not None
+
+ check_uri('/3')
+ check_uri('/4*')
+ check_uri('/5%2A', '/5*')
+ check_uri('/9?q#a', '/9')
def test_variables_host(self):
- self.conf_routes("\"routes/$host\"")
+ self.set_format('$host')
+
+ def check_host(host, expect=None):
+ expect = host if expect is None else expect
+ reg = r'^' + re.escape(expect) + r'$'
- def check_host(host, status=208):
+ assert self.search_in_log(reg) is None
assert (
self.get(headers={'Host': host, 'Connection': 'close'})[
'status'
]
- == status
+ == 200
)
+ assert self.wait_for_record(reg) is not None
check_host('localhost')
- check_host('localhost.')
- check_host('localhost:7080')
- check_host('.localhost', 404)
- check_host('www.localhost', 404)
- check_host('localhost1', 404)
+ check_host('localhost1.', 'localhost1')
+ check_host('localhost2:7080', 'localhost2')
+ check_host('.localhost')
+ check_host('www.localhost')
def test_variables_remote_addr(self):
- self.conf_routes("\"routes/$remote_addr\"")
- assert self.get()['status'] == 211
+ self.set_format('$remote_addr')
+
+ assert self.get()['status'] == 200
+ assert self.wait_for_record(r'^127\.0\.0\.1$') is not None
assert 'success' in self.conf(
- {"[::1]:7080": {"pass": "routes/$remote_addr"}}, 'listeners'
+ {"[::1]:7080": {"pass": "routes"}}, 'listeners'
)
- assert self.get(sock_type='ipv6')['status'] == 212
+
+ reg = r'^::1$'
+ assert self.search_in_log(reg) is None
+ assert self.get(sock_type='ipv6')['status'] == 200
+ assert self.wait_for_record(reg) is not None
+
+ def test_variables_time_local(self):
+ self.set_format('$uri $time_local $uri')
+
+ assert self.search_in_log(r'/time_local') is None
+ assert self.get(url='/time_local')['status'] == 200
+ assert self.wait_for_record(r'/time_local') is not None, 'time log'
+ date = self.search_in_log(
+ r'^\/time_local (.*) \/time_local$', 'access.log'
+ )[1]
+ assert (
+ abs(
+ self.date_to_sec_epoch(date, '%d/%b/%Y:%X %z')
+ - time.mktime(time.localtime())
+ )
+ < 5
+ ), '$time_local'
+
+ def test_variables_request_line(self):
+ self.set_format('$request_line')
+
+ reg = r'^GET \/r_line HTTP\/1\.1$'
+ assert self.search_in_log(reg) is None
+ assert self.get(url='/r_line')['status'] == 200
+ assert self.wait_for_record(reg) is not None
+
+ def test_variables_status(self):
+ self.set_format('$status')
+
+ assert 'success' in self.conf("418", 'routes/0/action/return')
+
+ reg = r'^418$'
+ assert self.search_in_log(reg) is None
+ assert self.get()['status'] == 418
+ assert self.wait_for_record(reg) is not None
def test_variables_header_referer(self):
- self.conf_routes("\"routes/$header_referer\"")
+ self.set_format('$method $header_referer')
+
+ def check_referer(referer):
+ reg = r'^GET ' + re.escape(referer) + r'$'
- def check_referer(referer, status=213):
+ assert self.search_in_log(reg) is None
assert (
self.get(
headers={
@@ -88,17 +207,21 @@ class TestVariables(TestApplicationProto):
'Referer': referer,
}
)['status']
- == status
+ == 200
)
+ assert self.wait_for_record(reg) is not None
check_referer('referer-value')
- check_referer('', 404)
- check_referer('no', 404)
+ check_referer('')
+ check_referer('no')
def test_variables_header_user_agent(self):
- self.conf_routes("\"routes/$header_user_agent\"")
+ self.set_format('$method $header_user_agent')
- def check_user_agent(user_agent, status=214):
+ def check_user_agent(user_agent):
+ reg = r'^GET ' + re.escape(user_agent) + r'$'
+
+ assert self.search_in_log(reg) is None
assert (
self.get(
headers={
@@ -107,152 +230,112 @@ class TestVariables(TestApplicationProto):
'User-Agent': user_agent,
}
)['status']
- == status
+ == 200
)
+ assert self.wait_for_record(reg) is not None
check_user_agent('MSIE')
- check_user_agent('', 404)
- check_user_agent('no', 404)
-
- def test_variables_dollar(self):
- assert 'success' in self.conf(
- {
- "listeners": {"*:7080": {"pass": "routes"}},
- "routes": [{"action": {"return": 301}}],
- }
- )
-
- def check_dollar(location, expect):
- assert 'success' in self.conf(
- '"' + location + '"',
- 'routes/0/action/location',
- )
- assert self.get()['headers']['Location'] == expect
-
- check_dollar(
- 'https://${host}${uri}path${dollar}dollar',
- 'https://localhost/path$dollar',
- )
- check_dollar('path$dollar${dollar}', 'path$$')
+ check_user_agent('')
+ check_user_agent('no')
def test_variables_many(self):
- self.conf_routes("\"routes$uri$method\"")
- assert self.get(url='/5')['status'] == 206, 'many'
-
- self.conf_routes("\"routes${uri}${method}\"")
- assert self.get(url='/5')['status'] == 206, 'many 2'
-
- self.conf_routes("\"routes${uri}$method\"")
- assert self.get(url='/5')['status'] == 206, 'many 3'
+ def check_vars(uri, expect):
+ reg = r'^' + re.escape(expect) + r'$'
- self.conf_routes("\"routes/$method$method\"")
- assert self.get()['status'] == 207, 'many 4'
+ assert self.search_in_log(reg) is None
+ assert self.get(url=uri)['status'] == 200
+ assert self.wait_for_record(reg) is not None
- self.conf_routes("\"routes/$method$uri\"")
- assert self.get()['status'] == 404, 'no route'
- assert self.get(url='/blah')['status'] == 404, 'no route 2'
+ self.set_format('$uri$method')
+ check_vars('/1', '/1GET')
- def test_variables_replace(self):
- assert self.get()['status'] == 201
+ self.set_format('${uri}${method}')
+ check_vars('/2', '/2GET')
- self.conf_routes("\"routes$uri\"")
- assert self.get(url='/3')['status'] == 203
+ self.set_format('${uri}$method')
+ check_vars('/3', '/3GET')
- self.conf_routes("\"routes/${method}\"")
- assert self.post()['status'] == 202
-
- self.conf_routes("\"routes${uri}\"")
- assert self.get(url='/4*')['status'] == 204
-
- self.conf_routes("\"routes/blah$method}\"")
- assert self.get()['status'] == 205
-
- def test_variables_upstream(self):
- assert 'success' in self.conf(
- {
- "listeners": {
- "*:7080": {"pass": "upstreams$uri"},
- "*:7081": {"pass": "routes/one"},
- },
- "upstreams": {"1": {"servers": {"127.0.0.1:7081": {}}}},
- "routes": {"one": [{"action": {"return": 200}}]},
- },
- ), 'upstreams initial configuration'
-
- assert self.get(url='/1')['status'] == 200
- assert self.get(url='/2')['status'] == 404
-
- def test_variables_empty(self):
- def update_pass(prefix):
- assert 'success' in self.conf(
- {"listeners": {"*:7080": {"pass": prefix + "/$method"}}},
- ), 'variables empty'
-
- update_pass("routes")
- assert self.get(url='/1')['status'] == 404
-
- update_pass("upstreams")
- assert self.get(url='/2')['status'] == 404
-
- update_pass("applications")
- assert self.get(url='/3')['status'] == 404
+ self.set_format('$method$method')
+ check_vars('/', 'GETGET')
def test_variables_dynamic(self):
- self.conf_routes("\"routes/$header_foo$arg_foo$cookie_foo\"")
+ self.set_format('$header_foo$cookie_foo$arg_foo')
+
+ assert (
+ self.get(
+ url='/?foo=h',
+ headers={'Foo': 'b', 'Cookie': 'foo=la', 'Connection': 'close'},
+ )['status']
+ == 200
+ )
+ assert self.wait_for_record(r'^blah$') is not None
- self.get(
- url='/?foo=h',
- headers={'Foo': 'b', 'Cookie': 'foo=la', 'Connection': 'close'},
- )['status'] = 210
+ def test_variables_dynamic_arguments(self):
+ def check_arg(url, expect=None):
+ expect = url if expect is None else expect
+ reg = r'^' + re.escape(expect) + r'$'
+
+ assert self.search_in_log(reg) is None
+ assert self.get(url=url)['status'] == 200
+ assert self.wait_for_record(reg) is not None
+
+ def check_no_arg(url):
+ assert self.get(url=url)['status'] == 200
+ assert self.search_in_log(r'^0$') is None
+
+ self.set_format('$arg_foo_bar')
+ check_arg('/?foo_bar=1', '1')
+ check_arg('/?foo_b%61r=2', '2')
+ check_arg('/?bar&foo_bar=3&foo', '3')
+ check_arg('/?foo_bar=l&foo_bar=4', '4')
+ check_no_arg('/')
+ check_no_arg('/?foo_bar=')
+ check_no_arg('/?Foo_bar=0')
+ check_no_arg('/?foo-bar=0')
+ check_no_arg('/?foo_bar=0&foo_bar=l')
+
+ self.set_format('$arg_foo_b%61r')
+ check_no_arg('/?foo_b=0')
+ check_no_arg('/?foo_bar=0')
+
+ self.set_format('$arg_f!~')
+ check_no_arg('/?f=0')
+ check_no_arg('/?f!~=0')
def test_variables_dynamic_headers(self):
- def check_header(header, status=210):
+ def check_header(header, value):
+ reg = r'^' + value + r'$'
+
+ assert self.search_in_log(reg) is None
assert (
- self.get(headers={header: "blah", 'Connection': 'close'})[
+ self.get(headers={header: value, 'Connection': 'close'})[
'status'
]
- == status
+ == 200
)
+ assert self.wait_for_record(reg) is not None
- self.conf_routes("\"routes/$header_foo_bar\"")
- check_header('foo-bar')
- check_header('Foo-Bar')
- check_header('foo_bar', 404)
- check_header('Foo', 404)
- check_header('Bar', 404)
- check_header('foobar', 404)
-
- self.conf_routes("\"routes/$header_Foo_Bar\"")
- check_header('Foo-Bar')
- check_header('foo-bar')
- check_header('foo_bar', 404)
- check_header('foobar', 404)
+ def check_no_header(header):
+ assert (
+ self.get(headers={header: '0', 'Connection': 'close'})['status']
+ == 200
+ )
+ assert self.search_in_log(r'^0$') is None
- self.conf_routes("\"routes/$header_foo-bar\"")
- check_header('foo_bar', 404)
+ self.set_format('$header_foo_bar')
+ check_header('foo-bar', '1')
+ check_header('Foo-Bar', '2')
+ check_no_header('foo_bar')
+ check_no_header('foobar')
- def test_variables_dynamic_arguments(self):
- self.conf_routes("\"routes/$arg_foo_bar\"")
- assert self.get(url='/?foo_bar=blah')['status'] == 210
- assert self.get(url='/?foo_b%61r=blah')['status'] == 210
- assert self.get(url='/?bar&foo_bar=blah&foo')['status'] == 210
- assert self.get(url='/?Foo_bar=blah')['status'] == 404
- assert self.get(url='/?foo-bar=blah')['status'] == 404
- assert self.get()['status'] == 404
- assert self.get(url='/?foo_bar=')['status'] == 404
- assert self.get(url='/?foo_bar=l&foo_bar=blah')['status'] == 210
- assert self.get(url='/?foo_bar=blah&foo_bar=l')['status'] == 404
-
- self.conf_routes("\"routes/$arg_foo_b%61r\"")
- assert self.get(url='/?foo_b=blah')['status'] == 404
- assert self.get(url='/?foo_bar=blah')['status'] == 404
-
- self.conf_routes("\"routes/$arg_f!~\"")
- assert self.get(url='/?f=blah')['status'] == 404
- assert self.get(url='/?f!~=blah')['status'] == 404
+ self.set_format('$header_Foo_Bar')
+ check_header('Foo-Bar', '4')
+ check_header('foo-bar', '5')
+ check_no_header('foo_bar')
+ check_no_header('foobar')
def test_variables_dynamic_cookies(self):
- def check_cookie(cookie, status=210):
+ def check_no_cookie(cookie):
assert (
self.get(
headers={
@@ -261,33 +344,48 @@ class TestVariables(TestApplicationProto):
'Connection': 'close',
},
)['status']
- == status
- ), 'match cookie'
+ == 200
+ )
+ assert self.search_in_log(r'^0$') is None
+
+ self.set_format('$cookie_foo_bar')
+
+ reg = r'^1$'
+ assert self.search_in_log(reg) is None
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo_bar=1',
+ 'Connection': 'close',
+ },
+ )['status'] == 200
+ assert self.wait_for_record(reg) is not None
- self.conf_routes("\"routes/$cookie_foo_bar\"")
- check_cookie('foo_bar=blah', 210)
- check_cookie('fOo_bar=blah', 404)
- assert self.get()['status'] == 404
- check_cookie('foo_bar', 404)
- check_cookie('foo_bar=', 404)
+ check_no_cookie('fOo_bar=0')
+ check_no_cookie('foo_bar=')
def test_variables_invalid(self):
- def check_variables(routes):
+ def check_variables(format):
assert 'error' in self.conf(
- routes, 'listeners/*:7080/pass'
- ), 'invalid variables'
-
- check_variables("\"routes$\"")
- check_variables("\"routes${\"")
- check_variables("\"routes${}\"")
- check_variables("\"routes$ur\"")
- check_variables("\"routes$uriblah\"")
- check_variables("\"routes${uri\"")
- check_variables("\"routes${{uri}\"")
- check_variables("\"routes$ar\"")
- check_variables("\"routes$arg\"")
- check_variables("\"routes$arg_\"")
- check_variables("\"routes$cookie\"")
- check_variables("\"routes$cookie_\"")
- check_variables("\"routes$header\"")
- check_variables("\"routes$header_\"")
+ {
+ 'path': option.temp_dir + '/access.log',
+ 'format': format,
+ },
+ 'access_log',
+ ), 'access_log format'
+
+ check_variables("$")
+ check_variables("${")
+ check_variables("${}")
+ check_variables("$ur")
+ check_variables("$uri$$host")
+ check_variables("$uriblah")
+ check_variables("${uri")
+ check_variables("${{uri}")
+ check_variables("$ar")
+ check_variables("$arg")
+ check_variables("$arg_")
+ check_variables("$cookie")
+ check_variables("$cookie_")
+ check_variables("$header")
+ check_variables("$header_")
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index 3db955f3..14e76362 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -67,7 +67,9 @@ replace unit.nginx.org/go => {replace_path}
print("\n$ GOPATH=" + env['GOPATH'] + " " + " ".join(args))
try:
- process = subprocess.run(args, env=env, cwd=temp_dir)
+ output = subprocess.check_output(
+ args, env=env, cwd=temp_dir, stderr=subprocess.STDOUT
+ )
except KeyboardInterrupt:
raise
@@ -75,7 +77,7 @@ replace unit.nginx.org/go => {replace_path}
except subprocess.CalledProcessError:
return None
- return process
+ return output
def load(self, script, name='app', **kwargs):
static_build = False
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index 50998978..c8936274 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -52,7 +52,7 @@ class TestApplicationJava(TestApplicationProto):
os.makedirs(classes_path)
classpath = (
- option.current_dir + '/build/tomcat-servlet-api-9.0.52.jar'
+ option.current_dir + '/build/tomcat-servlet-api-9.0.70.jar'
)
ws_jars = glob.glob(
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index 1e38f3fa..3768cf07 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -50,6 +50,7 @@ class TestApplicationPython(TestApplicationProto):
'protocol',
'targets',
'threads',
+ 'prefix',
):
if attr in kwargs:
app[attr] = kwargs.pop(attr)
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
index d647ce9b..15f212ff 100644
--- a/test/unit/applications/websockets.py
+++ b/test/unit/applications/websockets.py
@@ -43,10 +43,9 @@ class TestApplicationWebsocket(TestApplicationProto):
'Sec-WebSocket-Version': 13,
}
- _, sock = self.get(
+ sock = self.get(
headers=headers,
no_recv=True,
- start=True,
)
resp = ''
diff --git a/test/unit/check/go.py b/test/unit/check/go.py
index 3d9d13e7..09ae641d 100644
--- a/test/unit/check/go.py
+++ b/test/unit/check/go.py
@@ -2,7 +2,5 @@ from unit.applications.lang.go import TestApplicationGo
def check_go():
- process = TestApplicationGo.prepare_env('empty')
-
- if process != None and process.returncode == 0:
+ if TestApplicationGo.prepare_env('empty') is not None:
return True
diff --git a/test/unit/check/njs.py b/test/unit/check/njs.py
new file mode 100644
index 00000000..433473a1
--- /dev/null
+++ b/test/unit/check/njs.py
@@ -0,0 +1,6 @@
+import re
+
+
+def check_njs(output_version):
+ if re.search('--njs', output_version):
+ return True
diff --git a/test/unit/check/regex.py b/test/unit/check/regex.py
index 734c0150..51cf966b 100644
--- a/test/unit/check/regex.py
+++ b/test/unit/check/regex.py
@@ -1,13 +1,8 @@
import re
-import subprocess
-def check_regex(unitd):
- output = subprocess.check_output(
- [unitd, '--version'], stderr=subprocess.STDOUT
- )
-
- if re.search('--no-regex', output.decode()):
+def check_regex(output_version):
+ if re.search('--no-regex', output_version):
return False
return True
diff --git a/test/unit/check/tls.py b/test/unit/check/tls.py
index b878ff7d..53ce5ffc 100644
--- a/test/unit/check/tls.py
+++ b/test/unit/check/tls.py
@@ -2,12 +2,11 @@ import re
import subprocess
-def check_openssl(unitd):
- subprocess.check_output(['which', 'openssl'])
+def check_openssl(output_version):
+ try:
+ subprocess.check_output(['which', 'openssl'])
+ except subprocess.CalledProcessError:
+ return None
- output = subprocess.check_output(
- [unitd, '--version'], stderr=subprocess.STDOUT
- )
-
- if re.search('--openssl', output.decode()):
+ if re.search('--openssl', output_version):
return True
diff --git a/test/unit/http.py b/test/unit/http.py
index b29667c9..c48a720f 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -102,7 +102,12 @@ class TestHTTP:
if 'read_buffer_size' in kwargs:
recvall_kwargs['buff_size'] = kwargs['read_buffer_size']
- resp = self.recvall(sock, **recvall_kwargs).decode(encoding)
+ resp = self.recvall(sock, **recvall_kwargs).decode(
+ encoding, errors='ignore'
+ )
+
+ else:
+ return sock
self.log_in(resp)
diff --git a/test/unit/utils.py b/test/unit/utils.py
index 43aaa81b..d6590b97 100644
--- a/test/unit/utils.py
+++ b/test/unit/utils.py
@@ -12,9 +12,15 @@ def public_dir(path):
for root, dirs, files in os.walk(path):
for d in dirs:
- os.chmod(os.path.join(root, d), 0o777)
+ try:
+ os.chmod(os.path.join(root, d), 0o777)
+ except FileNotFoundError:
+ pass
for f in files:
- os.chmod(os.path.join(root, f), 0o777)
+ try:
+ os.chmod(os.path.join(root, f), 0o777)
+ except FileNotFoundError:
+ pass
def waitforfiles(*files, timeout=50):
@@ -66,12 +72,19 @@ def waitforsocket(port):
pytest.fail('Can\'t connect to the 127.0.0.1:' + str(port))
-def findmnt():
+def check_findmnt():
try:
- out = subprocess.check_output(
+ return subprocess.check_output(
['findmnt', '--raw'], stderr=subprocess.STDOUT
).decode()
except FileNotFoundError:
+ return False
+
+
+def findmnt():
+ out = check_findmnt()
+
+ if not out:
pytest.skip('requires findmnt')
return out
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 00000000..f534aa1f
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,91 @@
+# Unit Tools
+
+This directory contains useful tools for installing, configuring, and
+managing NGINX Unit. They may not be part of official packages and
+should be considered experimental.
+
+* [`setup-unit`](#setup-unit)
+* [`unitc`](#unitc)
+
+---
+
+## setup-unit
+
+### A script that simplifies installing and configuring an NGINX Unit server for first-time users
+
+* `setup-unit repo-config` configures your package manager with the NGINX
+Unit repository for later installation.
+* `setup-unit welcome` creates an initial configuration to serve a welcome
+web page with NGINX Unit.
+
+---
+
+## unitc
+
+### A curl wrapper for managing NGINX Unit configuration
+
+```USAGE: unitc [options] URI```
+
+ * **URI** specifies the target in Unit's control API, e.g. `/config` .
+ * Configuration data is read from stdin.
+ * [jq](https://stedolan.github.io/jq/) is used to prettify JSON output, if
+ available.
+
+| Options | |
+|---------|-|
+| filename … | Read configuration data consequently from the specified files instead of stdin.
+| _HTTP method_ | It is usually not required to specify a HTTP method. `GET` is used to read the configuration. `PUT` is used when making configuration changes unless a specific method is provided.
+| `INSERT` | A _virtual_ HTTP method that prepends data when the URI specifies an existing array. The [jq](https://stedolan.github.io/jq/) tool is required for this option.
+| `-q` \| `--quiet` | No output to stdout.
+
+Options are case insensitive and can appear in any order. For example, a
+redundant part of the configuration can be identified by its URI, and
+followed by `delete` in a subsequent command.
+
+### Local Configuration
+For local instances of Unit, the control socket is automatically detected.
+The error log is monitored; when changes occur, new log entries are shown.
+
+| Options | |
+|---------|-|
+| `-l` \| `--nolog` | Do not monitor the error log after configuration changes.
+
+#### Examples
+```shell
+unitc /config
+unitc /control/applications/my_app/restart
+unitc /config < unitconf.json
+echo '{"*:8080": {"pass": "routes"}}' | unitc /config/listeners
+unitc /config/applications/my_app DELETE
+unitc /certificates/bundle cert.pem key.pem
+```
+
+### Remote Configuration
+For remote instances of NGINX Unit, the control socket on the remote host can
+be set with the `$UNIT_CTRL` environment variable. The remote control socket
+can be accessed over TCP or SSH, depending on the type of control socket:
+
+ * `ssh://[user@]remote_host[:ssh_port]/path/to/control.socket`
+ * `http://remote_host:unit_control_port`
+
+> **Note:** SSH is recommended for remote confguration. Consider the
+> [security implications](https://unit.nginx.org/howto/security/#secure-socket-and-state)
+> of managing remote configuration over plaintext HTTP.
+
+| Options | |
+|---------|-|
+| `ssh://…` | Specify the remote Unix control socket on the command line.
+| `http://…`*URI* | For remote TCP control sockets, the URI may include the protocol, hostname, and port.
+
+#### Examples
+```shell
+unitc http://192.168.0.1:8080/status
+UNIT_CTRL=http://192.168.0.1:8080 unitc /status
+
+export UNIT_CTRL=ssh://root@unithost/var/run/control.unit.sock
+unitc /config/routes
+cat catchall_route.json | unitc POST /config/routes
+echo '{"match":{"uri":"/wp-admin/*"},"action":{"return":403}}' | unitc INSERT /config/routes
+```
+
+---
diff --git a/tools/setup-unit b/tools/setup-unit
new file mode 100755
index 00000000..79dab850
--- /dev/null
+++ b/tools/setup-unit
@@ -0,0 +1,1497 @@
+#!/usr/bin/env bash
+
+#####################################################################
+#
+# Copyright (C) NGINX, Inc.
+# Author: NGINX Unit Team, F5 Inc.
+#
+#####################################################################
+
+
+if test -n ${BASH_VERSION} && test "${BASH_VERSINFO[0]}" -eq 3; then
+ >&2 echo 'Your version of bash(1) isn't supported by this script.';
+ >&2 echo "You're probably running on macOS. We recommend that you either";
+ >&2 echo 'install a newer version of bash(1) or run this script with';
+ >&2 echo 'another shell, such as zsh(1):';
+ >&2 echo " $ zsh ${SUDO_USER:+sudo }$0 ...";
+ exit 1;
+fi;
+
+
+set -Eefuo pipefail;
+
+test -v BASH_VERSION \
+&& shopt -s lastpipe;
+
+export LC_ALL=C
+
+program_name="$0";
+prog_name="$(basename $program_name)";
+
+dry_run='no';
+
+help_unit()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name [-h] COMMAND [ARGS]
+
+ Subcommands
+ +-- repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION]
+ +-- welcome [-hn]
+
+DESCRIPTION
+ This script simplifies installing and configuring an NGINX Unit server
+ for first-time users.
+
+ Run '$program_name COMMAND -h' for more information on a command.
+
+COMMANDS
+ repo-config
+ Configure your package manager with the NGINX Unit repository
+ for later installation.
+
+ welcome
+ Create an initial configuration to serve a welcome web page
+ with NGINX Unit.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ --help-more
+ Print help for more commands. They are experimental. Using
+ these isn't recommended, unless you know what you're doing.
+
+__EOF__
+}
+
+help_more_unit()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name [-h] COMMAND [ARGS]
+
+ Subcommands
+ +-- cmd [-h]
+ +-- ctl [-h] [-s SOCK] SUBCOMMAND [ARGS]
+ | +-- http [-h] [-c CURLOPT] METHOD PATH
+ | +-- insert [-h] PATH INDEX
+ +-- freeport [-h]
+ +-- json-ins [-hn] JSON INDEX
+ +-- os-probe [-h]
+ +-- ps [-h] [-t TYPE]
+ +-- repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION]
+ +-- sock [-h] SUBCOMMAND [ARGS]
+ | +-- filter [-chs]
+ | +-- find [-h]
+ +-- welcome [-hn]
+
+DESCRIPTION
+ This script simplifies installing and configuring
+ an NGINX Unit server for first-time users.
+
+ Run '$program_name COMMAND -h' for more information on a command.
+
+COMMANDS
+ cmd Print the invocation line of unitd(8).
+
+ ctl Control a running unitd(8) instance via its control API socket.
+
+ freeport
+ Print an available TCP port.
+
+ json-ins
+ Insert a JSON element read from standard input into a JSON
+ array read from a file at a given INDEX.
+
+ os-probe
+ Probe the OS and print details about its version.
+
+ ps List unitd(8) processes.
+
+ repo-config
+ Configure your package manager with the NGINX Unit
+ repository for later installation.
+
+ sock Print the control API socket address.
+
+ welcome
+ Create an initial configuration to serve a welcome web page
+ with NGINX Unit.
+
+OPTIONS
+ -h, --help
+ Print basic help (some commands are hidden).
+
+ --help-more
+ Print the hidden help with more commands.
+
+__EOF__
+}
+
+warn()
+{
+ >&2 echo "$prog_name: error: $*";
+}
+
+err()
+{
+ >&2 echo "$prog_name: error: $*";
+ exit 1;
+}
+
+dry_run_echo()
+{
+ if test "$dry_run" = "yes"; then
+ echo "$*";
+ fi;
+}
+
+dry_run_eval()
+{
+ if test "$dry_run" = "yes"; then
+ echo " $*";
+ else
+ eval "$*";
+ fi;
+}
+
+
+help_unit_cmd()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name cmd [-h]
+
+DESCRIPTION
+ Print the invocation line of running unitd(8) instances.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+__EOF__
+}
+
+
+unit_cmd()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_cmd;
+ exit 0;
+ ;;
+ -*)
+ err "cmd: $1: Unknown option.";
+ ;;
+ *)
+ err "cmd: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ unit_ps -t m \
+ | sed 's/.*\[\(.*\)].*/\1/';
+}
+
+
+help_unit_ctl()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name ctl [-h] [-s SOCK] SUBCOMMAND [ARGS]
+
+ Subcommands
+ +-- http [-h] [-c CURLOPT] METHOD PATH
+ +-- insert [-h] PATH INDEX
+
+DESCRIPTION
+ Control a running unitd(8) instance through its control API socket.
+
+ Run '$program_name ctl SUBCOMMAND -h' for more information on a
+ subcommand.
+
+SUBCOMMANDS
+ http Send an HTTP request to the control API socket.
+
+ insert Insert an element at the specified index into an array in the
+ JSON configuration.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ -s, --sock SOCK
+ Use SOCK as the control API socket address. If not specified,
+ the script tries to find it. This value is used by subcommands.
+
+ The socket can be a tcp(7) socket or a unix(7) socket; in
+ the case of a unix(7) socket, it can exist locally or on
+ a remote machine, accessed through ssh(1). Accepted syntax
+ for SOCK:
+
+ unix:/path/to/control.sock
+ ssh://[user@]host[:port]/path/to/control.sock
+ [http[s]://]host[:port]
+
+ The last form is less secure than the first two; have a look:
+ <https://unit.nginx.org/howto/security/#secure-socket-and-stat>
+
+ENVIRONMENT
+ Options take precedence over their equivalent environment variables;
+ if both are specified, the command-line option is used.
+
+ UNIT_CTL_SOCK
+ Equivalent to the option -s (--sock).
+
+__EOF__
+}
+
+
+unit_ctl()
+{
+
+ if test -v UNIT_CTL_SOCK; then
+ local sock="$UNIT_CTL_SOCK";
+ fi;
+
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_ctl;
+ exit 0;
+ ;;
+ -s | --sock)
+ if ! test $# -ge 2; then
+ err "ctl: $1: Missing argument.";
+ fi;
+ local sock="$2";
+ shift;
+ ;;
+ -*)
+ err "ctl: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if test ! $# -ge 1; then
+ err 'ctl: Missing subcommand.';
+ fi;
+
+ if test -v sock && echo $sock | grep '^ssh://' >/dev/null; then
+ local remote="$(echo $sock | sed 's,\(ssh://[^/]*\).*,\1,')";
+ local sock="$(echo $sock | sed 's,ssh://[^/]*\(.*\),unix:\1,')";
+ fi;
+
+ case $1 in
+ http)
+ shift;
+ unit_ctl_http ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@;
+ ;;
+ insert)
+ shift;
+ unit_ctl_insert ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@;
+ ;;
+ *)
+ err "ctl: $1: Unknown argument.";
+ ;;
+ esac;
+}
+
+
+help_unit_ctl_http()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name ctl [CTL-OPTS] http [-h] [-c CURLOPT] METHOD PATH
+
+DESCRIPTION
+ Send an HTTP request to the unitd(8) control API socket.
+
+ The payload is read from standard input.
+
+OPTIONS
+ -c, --curl CURLOPT
+ Pass CURLOPT as an option to curl. This script is implemented
+ in terms of curl(1), so it's useful to be able to tweak its
+ behavior. The option can be cumulatively used multiple times
+ (the result is also appended to UNIT_CTL_HTTP_CURLOPTS).
+
+ -h, --help
+ Print this help.
+
+ENVIRONMENT
+ UNIT_CTL_HTTP_CURLOPTS
+ Equivalent to the option -c (--curl).
+
+EXAMPLES
+ $program_name ctl http -c --no-progress-meter GET /config >tmp;
+
+SEE ALSO
+ <https://unit.nginx.org/controlapi/#api-manipulation>
+
+__EOF__
+}
+
+
+unit_ctl_http()
+{
+ local curl_options="${UNIT_CTL_HTTP_CURLOPTS:-}";
+
+ while test $# -ge 1; do
+ case "$1" in
+ -c | --curl)
+ if ! test $# -ge 2; then
+ err "ctl: http: $1: Missing argument.";
+ fi;
+ curl_options="$curl_options $2";
+ shift;
+ ;;
+ -h | --help)
+ help_unit_ctl_http;
+ exit 0;
+ ;;
+ ---r | ----remote)
+ local remote="$2";
+ shift;
+ ;;
+ ---s | ----sock)
+ local sock="$2";
+ shift;
+ ;;
+ -*)
+ err "ctl: http: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if ! test $# -ge 1; then
+ err 'ctl: http: METHOD: Missing argument.';
+ fi;
+ local method="$1";
+
+ if ! test $# -ge 2; then
+ err 'ctl: http: PATH: Missing argument.';
+ fi;
+ local req_path="$2";
+
+ if test -v remote; then
+ local remote_sock="$(echo "$sock" | unit_sock_filter -s)";
+ local local_sock="$(mktemp -u -p /var/run/unit/)";
+ local ssh_ctrl="$(mktemp -u -p /var/run/unit/)";
+
+ mkdir -p /var/run/unit/;
+
+ ssh -fMNnT -S "$ssh_ctrl" \
+ -o 'ExitOnForwardFailure yes' \
+ -L "$local_sock:$remote_sock" "$remote";
+
+ sock="unix:$local_sock";
+
+ elif ! test -v sock; then
+ local sock="$(unit_sock_find)";
+ fi;
+
+ curl $curl_options -X $method -d@- \
+ $(echo "$sock" | unit_sock_filter -c)${req_path} \
+ ||:;
+
+ if test -v remote; then
+ ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null;
+ unlink "$local_sock";
+ fi;
+}
+
+
+help_unit_ctl_insert()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name ctl [CTL-OPTS] insert [-h] PATH INDEX
+
+DESCRIPTION
+ Insert an element at the specified position (INDEX) into the JSON array
+ located at PATH in unitd(8) control API.
+
+ The new element is read from standard input.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+SEE ALSO
+ $program_name ctl http -h;
+
+__EOF__
+}
+
+
+unit_ctl_insert()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_ctl_insert;
+ exit 0;
+ ;;
+ ---r | ----remote)
+ local remote="$2";
+ shift;
+ ;;
+ ---s | ----sock)
+ local sock="$2";
+ shift;
+ ;;
+ -*)
+ err "ctl: insert: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if ! test $# -ge 1; then
+ err 'ctl: insert: PATH: Missing argument.';
+ fi;
+ local req_path="$1";
+
+ if ! test $# -ge 2; then
+ err 'ctl: insert: INDEX: Missing argument.';
+ fi;
+ local idx="$2";
+
+ if test -v remote; then
+ local remote_sock="$(echo "$sock" | unit_sock_filter -s)";
+ local local_sock="$(mktemp -u -p /var/run/unit/)";
+ local ssh_ctrl="$(mktemp -u -p /var/run/unit/)";
+
+ mkdir -p /var/run/unit/;
+
+ ssh -fMNnT -S "$ssh_ctrl" \
+ -o 'ExitOnForwardFailure yes' \
+ -L "$local_sock:$remote_sock" "$remote";
+
+ sock="unix:$local_sock";
+
+ elif ! test -v sock; then
+ local sock="$(unit_sock_find)";
+ fi;
+
+ local old="$(mktemp ||:)";
+
+ unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \
+ </dev/null >"$old" \
+ ||:;
+
+ unit_json_ins "$old" "$idx" \
+ | unit_ctl_http ---s "$sock" PUT "$req_path" \
+ ||:;
+
+ if test -v remote; then
+ ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null;
+ unlink "$local_sock";
+ fi;
+}
+
+
+help_unit_ctl_welcome()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name welcome [-hn]
+
+DESCRIPTION
+ This script tests an NGINX Unit installation by creating an initial
+ configuration and serving a welcome web page. Recommended for
+ first-time users.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ -n, --dry-run
+ Dry run. Print the commands to be run instead of actually
+ running them. Each command is preceded by a line explaining
+ what it does.
+
+__EOF__
+}
+
+
+unit_ctl_welcome()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -f | --force)
+ local force='yes';
+ ;;
+ -h | --help)
+ help_unit_ctl_welcome;
+ exit 0;
+ ;;
+ -n | --dry-run)
+ dry_run='yes';
+ ;;
+ -*)
+ err "welcome: $1: Unknown option.";
+ ;;
+ *)
+ err "welcome: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ id -u \
+ | xargs test 0 -ne \
+ && err 'welcome: This script requires root privileges to run.';
+
+ command -v curl >/dev/null \
+ || err 'welcome: curl(1) not found in PATH. It must be installed to run this script.';
+
+ www='/srv/www/unit/index.html';
+ if test -e "$www" && ! test -v force || ! test -w /srv; then
+ www="$(mktemp)";
+ mv "$www" "$www.html";
+ www="$www.html"
+ fi;
+
+ unit_ps -t m \
+ | wc -l \
+ | read -r nprocs \
+ ||:
+
+ if test 0 -eq "$nprocs"; then
+ warn "welcome: NGINX Unit isn't running.";
+ warn 'For help with starting NGINX Unit, see:';
+ err " <https://unit.nginx.org/installation/#startup-and-shutdown>";
+ elif test 1 -ne "$nprocs"; then
+ err 'welcome: Only one NGINX Unit instance should be running.';
+ fi;
+
+ local sock="$(unit_sock_find)";
+ local curl_opt="$(unit_sock_find | unit_sock_filter -c)";
+
+ curl $curl_opt/ >/dev/null 2>&1 \
+ || err "welcome: Can't reach the control API socket.";
+
+ if ! test -v force; then
+ unit_cmd \
+ | read -r cmd;
+
+ # Check unitd is not configured already.
+ echo "$cmd" \
+ | if grep '\--state' >/dev/null; then
+ echo "$cmd" \
+ | sed 's/ --/\n--/g' \
+ | grep '\--state' \
+ | cut -d' ' -f2;
+ else
+ $cmd --help \
+ | sed -n '/\--state/,+1p' \
+ | grep 'default:' \
+ | sed 's/ *default: "\(.*\)"/\1/';
+ fi \
+ | sed 's,$,/conf.json,' \
+ | read -r conffile \
+ ||:;
+
+ if test -e $conffile; then
+ if ! unit_ctl_http ---s "$sock" 'GET' '/config' </dev/null 2>/dev/null | grep -q '^{}.\?$'; # The '.\?' is for the possible carriage return.
+ then
+ warn 'welcome: NGINX Unit is already configured. To overwrite';
+ err 'its current configuration, run the script again with --force.';
+ fi;
+ fi;
+ fi;
+
+ (
+ unit_freeport \
+ || err "welcome: Can't find an available port.";
+ ) \
+ | read -r port;
+
+ dry_run_echo 'Create a file to serve:';
+ dry_run_eval "mkdir -p $(dirname $www);";
+ dry_run_eval "cat >'$www'"' <<__EOF__;
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Welcome to NGINX Unit</title>
+ <style type="text/css">
+ body { background: white; color: black; font-family: sans-serif; margin: 2em; line-height: 1.5; }
+ h1,h2 { color: #00974d; }
+ li { margin-bottom: 0.5em; }
+ pre { background-color: beige; padding: 0.4em; }
+ hr { margin-top: 2em; border: 1px solid #00974d; }
+ .indent { margin-left: 1.5em; }
+ </style>
+ </head>
+ <body>
+ <h1>Welcome to NGINX Unit</h1>
+ <p>Congratulations! NGINX Unit is installed and running.</p>
+ <h3>Useful Links</h3>
+ <ul>
+ <li><b><a href="https://unit.nginx.org/configuration/?referer=welcome">https://unit.nginx.org/configuration/</a></b><br>
+ To get started with Unit, see the <em>Configuration</em> docs, starting with
+ the <em>Quick Start</em> guide.</li>
+ <li><b><a href="https://github.com/nginx/unit">https://github.com/nginx/unit</a></b><br>
+ See our GitHub repo to browse the code, contribute, or seek help from the
+ <a href="https://github.com/nginx/unit#community">community</a>.</li>
+ </ul>
+
+ <h2>Next steps</h2>
+
+ <h3>Check Current Configuration</h3>
+ <div class="indent">
+ <p>Unit'"'"'s control API is currently listening for configuration changes
+ on the '"$(unit_sock_find | grep -q '^unix:' && echo '<a href="https://en.wikipedia.org/wiki/Unix_domain_socket">Unix socket</a>' || echo 'socket')"' at
+ <b>'"$(unit_sock_find)"'</b><br>
+ To see the current configuration:</p>
+ <pre>'"${SUDO_USER:+sudo }"'curl '"$curl_opt"'/config</pre>
+ </div>
+
+ <h3>Change Listener Port</h3>
+ <div class="indent">
+ <p>This page is served over a random TCP high port. To choose the default HTTP port (80),
+ replace the <b>"listeners"</b> object:</p>
+ <pre>echo '"'"'{"*:80": {"pass": "routes"}}'"'"' | '"${SUDO_USER:+sudo }"'curl -X PUT -d@- '"$curl_opt"'/config/listeners</pre>
+ Then remove the port number from the address bar and reload the page.
+ </div>
+
+ <hr>
+ <p><a href="https://unit.nginx.org/?referer=welcome">NGINX Unit &mdash; the universal web app server</a><br>
+ NGINX, Inc. &copy; 2022</p>
+ </body>
+ </html>
+__EOF__';
+ dry_run_echo;
+ dry_run_echo 'Give it appropriate permissions:';
+ dry_run_eval "chmod 644 '$www';";
+ dry_run_echo;
+
+ dry_run_echo 'Configure unitd:'
+ dry_run_eval "cat <<__EOF__ \\
+ | sed 's/8080/$port/' \\
+ | curl -X PUT -d@- $curl_opt/config;
+ {
+ \"listeners\": {
+ \"*:8080\": {
+ \"pass\": \"routes\"
+ }
+ },
+ \"routes\": [{
+ \"action\": {
+ \"share\": \"$www\"
+ }
+ }]
+ }
+__EOF__";
+
+ dry_run_echo;
+
+ echo;
+ echo 'You may want to try the following commands now:';
+ echo;
+ echo 'Check out current unitd configuration:';
+ echo " ${SUDO_USER:+sudo} curl $curl_opt/config";
+ echo;
+ echo 'Browse the welcome page:';
+ echo " curl http://localhost:$port/";
+}
+
+
+help_unit_freeport()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name freeport [-h]
+
+DESCRIPTION
+ Print an available TCP port.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+__EOF__
+}
+
+
+unit_freeport()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_freeport;
+ exit 0;
+ ;;
+ -*)
+ err "freeport: $1: Unknown option.";
+ ;;
+ *)
+ err "freeport: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ freeport="$(mktemp -t freeport-XXXXXX)";
+
+ cat <<__EOF__ \
+ | cc -x c -o $freeport -;
+ #include <netinet/in.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <strings.h>
+ #include <sys/socket.h>
+ #include <unistd.h>
+
+
+ int32_t get_free_port(void);
+
+
+ int
+ main(void)
+ {
+ int32_t port;
+
+ port = get_free_port();
+ if (port == -1)
+ exit(EXIT_FAILURE);
+
+ printf("%d\n", port);
+ exit(EXIT_SUCCESS);
+ }
+
+
+ int32_t
+ get_free_port(void)
+ {
+ int sfd;
+ int32_t port;
+ socklen_t len;
+ struct sockaddr_in addr;
+
+ port = -1;
+
+ sfd = socket(PF_INET, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ perror("socket()");
+ return -1;
+ }
+
+ bzero(&addr, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = htons(0); // random port
+
+ len = sizeof(addr);
+ if (bind(sfd, (struct sockaddr *) &addr, len)) {
+ perror("bind()");
+ goto fail;
+ }
+
+ if (getsockname(sfd, (struct sockaddr *) &addr, &len)) {
+ perror("getsockname()");
+ goto fail;
+ }
+
+ port = ntohs(addr.sin_port);
+
+ fail:
+ close(sfd);
+ return port;
+ }
+__EOF__
+
+ $freeport;
+}
+
+
+help_unit_json_ins()
+{
+cat <<__EOF__ ;
+SYNOPSIS
+ $program_name json-ins [-hn] JSON INDEX
+
+ARGUMENTS
+ JSON Path to a JSON file containing a top-level array.
+
+ INDEX Position in the array to insert the element at.
+
+DESCRIPTION
+ Insert a JSON element read from standard input into a JSON array read
+ from a file at a given INDEX.
+
+ The resulting array is printed to standard output.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ -n, --dry-run
+ Dry run. Print the command to be run instead of actually
+ running it.
+
+__EOF__
+}
+
+
+unit_json_ins()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_json_ins;
+ exit 0;
+ ;;
+ -n | --dry-run)
+ dry_run='yes';
+ ;;
+ -*)
+ err "json-ins: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if ! test $# -ge 1; then
+ err 'json-ins: JSON: Missing argument.';
+ fi;
+ local arr=$1;
+
+ if ! test $# -ge 2; then
+ err 'json-ins: INDEX: Missing argument.';
+ fi;
+ local idx=$2;
+
+ dry_run_eval "(
+ jq '.[0:$idx]' <'$arr';
+ echo '[';
+ jq .;
+ echo ']';
+ jq '.[$idx:]' <'$arr';
+ ) \\
+ | sed '/^\[]$/d' \\
+ | sed '/^]$/{N;s/^]\n\[$/,/}' \\
+ | jq .;"
+}
+
+
+help_unit_os_probe()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name os-probe [-h]
+
+DESCRIPTION
+ This script probes the OS and prints three fields, delimited by ':';
+ the first is the package manager, the second is the OS name, the third
+ is the OS version.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+__EOF__
+}
+
+
+unit_os_probe()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_os_probe;
+ exit 0;
+ ;;
+ -*)
+ err "os-probe: $1: Unknown option.";
+ ;;
+ *)
+ err "os-probe: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ local os=$(uname | tr '[:upper:]' '[:lower:]')
+
+ if [ "$os" != 'linux' ] && [ "$os" != 'freebsd' ]; then
+ err "os-probe: The OS isn't Linux or FreeBSD; can't proceed."
+ fi
+
+ if [ "$os" = 'linux' ]; then
+ if command -v apt-get >/dev/null; then
+ local pkgMngr='apt';
+ elif command -v dnf >/dev/null; then
+ local pkgMngr='dnf';
+ elif command -v yum >/dev/null; then
+ local pkgMngr='yum';
+ else
+ local pkgMngr='';
+ fi;
+
+ local osRelease='/etc/os-release';
+
+ if [ -f "$osRelease" ]; then
+ # The value for the ID and VERSION_ID may or may not be in quotes
+ local osName=$(grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' ||:)
+ local osVersion=$(grep '^VERSION_ID=' "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' || lsb_release -cs)
+ else
+ err "os-probe: Unable to determine OS and version, or the OS isn't supported."
+ fi
+ else
+ local pkgMngr='pkg';
+ local osName=$os
+ local osVersion=$(uname -rs | awk -F '[ -]' '{print $2}' ||:)
+ if [ -z "$osVersion" ]; then
+ err 'os-probe: Unable to get the FreeBSD version.'
+ fi
+ fi
+
+ osName=$(echo "$osName" | tr '[:upper:]' '[:lower:]')
+ echo "$pkgMngr:$osName:$osVersion"
+}
+
+
+help_unit_ps()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name ps [-h] [-t TYPE]
+
+DESCRIPTION
+ List unitd(8) processes.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ -t, --type TYPE
+ List only processes of type TYPE. The available types are:
+
+ - controller (c)
+ - main (m)
+ - router (r)
+
+__EOF__
+}
+
+
+unit_ps()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_ps;
+ exit 0;
+ ;;
+ -t | --type)
+ if ! test $# -ge 2; then
+ err "ps: $1: Missing argument.";
+ fi;
+ local type=;
+ case "$2" in
+ c | controller)
+ local type_c='c';
+ ;;
+ m | main)
+ local type_m='m';
+ ;;
+ r | router)
+ local type_r='r';
+ ;;
+ esac;
+ shift;
+ ;;
+ -*)
+ err "ps: $1: Unknown option.";
+ ;;
+ *)
+ err "ps: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ ps ax \
+ | if test -v type; then
+ grep ${type_c:+-e 'unit: controller'} \
+ ${type_m:+-e 'unit: main'} \
+ ${type_r:+-e 'unit: router'};
+ else
+ grep 'unit: ';
+ fi \
+ | grep -v grep \
+ ||:
+}
+
+
+help_unit_repo_config()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION]
+
+DESCRIPTION
+ This script configures the NGINX Unit repository for the system
+ package manager.
+
+ The script automatically detects the OS and proceeds accordingly.
+ However, if this automatic selection fails, you may specify the
+ package manager and the OS name and version.
+
+ARGUMENTS
+ PKG-MANAGER
+ Supported: 'apt', 'dnf', and 'yum'.
+
+ OS-NAME
+ Supported: 'debian', 'ubuntu', 'fedora', 'rhel', and 'amzn2'.
+
+ OS-VERSION
+ For most distributions, this should be a numeric value; for
+ Debian derivatives, use the codename instead.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+ -n, --dry-run
+ Dry run. Print the commands to be run instead of actually
+ running them. Each command is preceded by a line explaining
+ what it does.
+
+EXAMPLES
+ $ $prog_name repo-config apt debian bullseye;
+ $ $prog_name repo-config apt ubuntu jammy;
+ $ $prog_name repo-config dnf fedora 36;
+ $ $prog_name repo-config dnf rhel 9;
+ $ $prog_name repo-config yum amzn2 2;
+
+__EOF__
+}
+
+
+unit_repo_config()
+{
+ installAPT ()
+ {
+ local os_name="$2";
+
+ dry_run_echo "Install on $os_name";
+ dry_run_echo;
+ dry_run_eval 'curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg;';
+ dry_run_echo;
+ dry_run_eval 'apt-get install -y apt-transport-https lsb-release ca-certificates;';
+
+ if test $# -ge 3; then
+ local os_version="$3";
+ else
+ local os_version='$(lsb_release -cs)';
+ fi;
+
+ dry_run_echo;
+ dry_run_eval "printf 'deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee /etc/apt/sources.list.d/unit.list;";
+ dry_run_eval "printf 'deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee -a /etc/apt/sources.list.d/unit.list;";
+ dry_run_echo;
+ dry_run_eval 'apt-get update;';
+ }
+
+ installYumDnf ()
+ {
+ local pkg_mngr="$1";
+ local os_name="$2";
+
+ if test $# -ge 3; then
+ local os_version="$3";
+ else
+ local os_version='\$releasever';
+ fi;
+
+ dry_run_echo "Install on $os_name";
+ dry_run_echo;
+
+ dry_run_eval "cat >/etc/yum.repos.d/unit.repo <<__EOF__
+[unit]
+name=unit repo
+baseurl=https://packages.nginx.org/unit/$os_name/$os_version/\\\$basearch/
+gpgcheck=0
+enabled=1
+__EOF__";
+
+ dry_run_echo;
+ dry_run_eval "$pkg_mngr makecache;";
+ }
+
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_repo_config;
+ exit 0;
+ ;;
+ -n | --dry-run)
+ dry_run='yes';
+ ;;
+ -*)
+ err "repo-config: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if test $# -ge 1; then
+ local pkg_mngr="$1";
+
+ if ! test $# -ge 2; then
+ err "repo-config: OS-NAME: Missing argument.";
+ fi;
+ local os_name="$2";
+
+ if ! test $# -ge 3; then
+ err "repo-config: OS-VERSION: Missing argument.";
+ fi;
+ local os_version="$3";
+ fi;
+
+ command -v curl >/dev/null \
+ || err 'repo-config: curl(1) not found in PATH. It must be installed to run this script.';
+
+ id -u \
+ | xargs test 0 -ne \
+ && err 'repo-config: This script requires root privileges to run.';
+
+ echo 'This script sets up the NGINX Unit repository';
+
+ if ! test $# -ge 3; then
+ local os_pkg_name_version=$(unit_os_probe || warn "On macOS, try 'brew install nginx/unit/unit'.")
+ local pkg_mngr=$(echo "$os_pkg_name_version" | awk -F: '{print $1}')
+ local os_name=$(echo "$os_pkg_name_version" | awk -F: '{print $2}')
+ local os_version=$(echo "$os_pkg_name_version" | awk -F: '{print $3}')
+ fi;
+
+ # Call the appropriate installation function
+ case "$pkg_mngr" in
+ apt)
+ case "$os_name" in
+ debian | ubuntu)
+ installAPT "$pkg_mngr" "$os_name" ${3:+$os_version};
+ ;;
+ *)
+ err "repo-config: $os_name: The OS isn't supported.";
+ ;;
+ esac
+ ;;
+ yum | dnf)
+ case "$os_name" in
+ rhel | amzn | fedora)
+ installYumDnf "$pkg_mngr" "$os_name" "$os_version" ${3:+ovr};
+ ;;
+ *)
+ err "repo-config: $os_name: The OS isn't supported.";
+ ;;
+ esac;
+ ;;
+ *)
+ err "repo-config: $pkg_mngr: The package manager isn't supported.";
+ ;;
+ esac;
+
+ echo
+ echo 'All done; the NGINX Unit repository is set up.';
+ echo "Configured with '$pkg_mngr' on '$os_name' '$os_version'.";
+ echo 'Further steps: <https://unit.nginx.org/installation/#official-packages>'
+}
+
+
+help_unit_sock()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name sock [-h] SUBCOMMAND [ARGS]
+
+ Subcommands
+ +-- filter [-ch]
+ +-- find [-h]
+
+DESCRIPTION
+ Print the control API socket address of running unitd(8)
+ instances.
+
+ Run '$program_name sock SUBCOMMAND -h' for more information on a
+ subcommand.
+
+SUBCOMMANDS
+ filter Filter the output of the 'find' subcommand and transform it
+ to something suitable for running other commands, such as
+ curl(1) or ssh(1).
+
+ find Find and print the control API socket address of running
+ unitd(8) instances.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+__EOF__
+}
+
+
+unit_sock()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_sock;
+ exit 0;
+ ;;
+ -*)
+ err "sock: $1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+ done;
+
+ if ! test $# -ge 1; then
+ err 'sock: Missing subcommand.';
+ fi;
+
+ case $1 in
+ filter)
+ shift;
+ unit_sock_filter $@;
+ ;;
+ find)
+ shift;
+ unit_sock_find $@;
+ ;;
+ *)
+ err "sock: $1: Unknown subcommand.";
+ ;;
+ esac;
+}
+
+
+help_unit_sock_filter()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name sock filter [-chs]
+
+DESCRIPTION
+ Filter the output of the 'sock find' command and transform it to
+ something suitable for running other commands, such as
+ curl(1) or ssh(1).
+
+OPTIONS
+ -c, --curl
+ Print an argument suitable for curl(1).
+
+ -h, --help
+ Print this help.
+
+ -s, --ssh
+ Print a socket address suitable for use in an ssh(1) tunnel.
+
+__EOF__
+}
+
+
+unit_sock_filter()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -c | --curl)
+ if test -v ssh_flag; then
+ err "sock: filter: $1: Missing argument.";
+ fi;
+ local curl_flag='yes';
+ ;;
+ -h | --help)
+ help_unit_sock_filter;
+ exit 0;
+ ;;
+ -s | --ssh)
+ if test -v curl_flag; then
+ err "sock: filter: $1: Missing argument.";
+ fi;
+ local ssh_flag='yes';
+ ;;
+ -*)
+ err "sock: filter: $1: Unknown option.";
+ ;;
+ *)
+ err "sock: filter: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ while read -r control; do
+
+ if test -v curl_flag; then
+ if echo "$control" | grep '^unix:' >/dev/null; then
+ unix_socket="$(echo "$control" | sed 's/unix:/--unix-socket /')";
+ host='http://localhost';
+ else
+ unix_socket='';
+ host="$control";
+ fi;
+
+ echo "$unix_socket $host";
+
+ elif test -v ssh_flag; then
+ echo "$control" \
+ | sed -E 's,^(unix:|http://|https://),,';
+
+ else
+ echo "$control";
+ fi;
+ done;
+}
+
+
+help_unit_sock_find()
+{
+ cat <<__EOF__ ;
+SYNOPSIS
+ $program_name sock find [-h]
+
+DESCRIPTION
+ Find and print the control API socket address of running
+ unitd(8) instances.
+
+OPTIONS
+ -h, --help
+ Print this help.
+
+__EOF__
+}
+
+
+unit_sock_find()
+{
+ while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit_sock_find;
+ exit 0;
+ ;;
+ -*)
+ err "sock: find: $1: Unknown option.";
+ ;;
+ *)
+ err "sock: find: $1: Unknown argument.";
+ ;;
+ esac;
+ shift;
+ done;
+
+ unit_cmd \
+ | while read -r cmd; do
+ if echo "$cmd" | grep '\--control' >/dev/null; then
+ echo "$cmd" \
+ | sed 's/ --/\n--/g' \
+ | grep '\--control' \
+ | cut -d' ' -f2;
+ else
+ if ! command -v $cmd >/dev/null; then
+ local cmd='unitd';
+ fi;
+ $cmd --help \
+ | sed -n '/\--control/,+1p' \
+ | grep 'default:' \
+ | sed 's/ *default: "\(.*\)"/\1/';
+ fi;
+ done;
+}
+
+
+while test $# -ge 1; do
+ case "$1" in
+ -h | --help)
+ help_unit;
+ exit 0;
+ ;;
+ --help-more)
+ help_more_unit;
+ exit 0;
+ ;;
+ -*)
+ err "$1: Unknown option.";
+ ;;
+ *)
+ break;
+ ;;
+ esac;
+ shift;
+done;
+
+if ! test $# -ge 1; then
+ err "Missing command.";
+fi;
+
+case $1 in
+cmd)
+ shift;
+ unit_cmd $@;
+ ;;
+ctl)
+ shift;
+ unit_ctl $@;
+ ;;
+freeport)
+ shift;
+ unit_freeport $@;
+ ;;
+json-ins)
+ shift;
+ unit_json_ins $@;
+ ;;
+os-probe)
+ shift;
+ unit_os_probe $@;
+ ;;
+ps)
+ shift;
+ unit_ps $@;
+ ;;
+repo-config)
+ shift;
+ unit_repo_config $@;
+ ;;
+sock)
+ shift;
+ unit_sock $@;
+ ;;
+welcome)
+ shift;
+ unit_ctl_welcome $@;
+ ;;
+*)
+ err "$1: Unknown command.";
+ ;;
+esac;
diff --git a/tools/unitc b/tools/unitc
new file mode 100755
index 00000000..838f7ebf
--- /dev/null
+++ b/tools/unitc
@@ -0,0 +1,235 @@
+#!/bin/bash
+# unitc - a curl wrapper for configuring NGINX Unit
+# https://github.com/nginx/unit/tree/master/tools
+# NGINX, Inc. (c) 2022
+
+# Defaults
+#
+ERROR_LOG=/dev/null
+REMOTE=0
+SHOW_LOG=1
+NOLOG=0
+QUIET=0
+URI=""
+SSH_CMD=""
+METHOD=PUT
+CONF_FILES=()
+
+while [ $# -gt 0 ]; do
+ OPTION=$(echo $1 | tr '[a-z]' '[A-Z]')
+ case $OPTION in
+ "-H" | "--HELP")
+ shift
+ ;;
+
+ "-L" | "--NOLOG" | "--NO-LOG")
+ NOLOG=1
+ shift
+ ;;
+
+ "-Q" | "--QUIET")
+ QUIET=1
+ shift
+ ;;
+
+ "GET" | "PUT" | "POST" | "DELETE" | "INSERT")
+ METHOD=$OPTION
+ shift
+ ;;
+
+ "HEAD" | "PATCH" | "PURGE" | "OPTIONS")
+ echo "${0##*/}: ERROR: Invalid HTTP method ($OPTION)"
+ exit 1
+ ;;
+
+ *)
+ if [ -r $1 ]; then
+ CONF_FILES+=($1)
+ elif [ "${1:0:1}" = "/" ] || [ "${1:0:4}" = "http" ] && [ "$URI" = "" ]; then
+ URI=$1
+ elif [ "${1:0:6}" = "ssh://" ]; then
+ UNIT_CTRL=$1
+ else
+ echo "${0##*/}: ERROR: Invalid option ($1)"
+ exit 1
+ fi
+ shift
+ ;;
+ esac
+done
+
+if [ "$URI" = "" ]; then
+ cat << __EOF__
+${0##*/} - a curl wrapper for managing NGINX Unit configuration
+
+USAGE: ${0##*/} [options] URI
+
+• URI is for Unit's control API target, e.g. /config
+• A local Unit control socket is detected unless a remote one is specified.
+• Configuration data is read from stdin.
+
+General options
+ filename … # Read configuration data from files instead of stdin
+ HTTP method # Default=GET, or PUT with config data (case-insensitive)
+ INSERT # Virtual HTTP method to prepend data to an existing array
+ -q | --quiet # No output to stdout
+
+Local options
+ -l | --nolog # Do not monitor the error log after applying config changes
+
+Remote options
+ ssh://[user@]remote_host[:port]/path/to/control.socket # Remote Unix socket
+ http://remote_host:port/URI # Remote TCP socket
+
+ A remote Unit control socket may also be defined with the \$UNIT_CTRL
+ environment variable as http://remote_host:port -OR- ssh://… (as above)
+
+__EOF__
+ exit 1
+fi
+
+# Figure out if we're running on the Unit host, or remotely
+#
+if [ "$UNIT_CTRL" = "" ]; then
+ if [ "${URI:0:4}" = "http" ]; then
+ REMOTE=1
+ UNIT_CTRL=$(echo "$URI" | cut -f1-3 -d/)
+ URI=/$(echo "$URI" | cut -f4- -d/)
+ fi
+elif [ "${UNIT_CTRL:0:6}" = "ssh://" ]; then
+ REMOTE=1
+ SSH_CMD="ssh $(echo $UNIT_CTRL | cut -f1-3 -d/)"
+ UNIT_CTRL="--unix-socket /$(echo $UNIT_CTRL | cut -f4- -d/) _"
+elif [ "${URI:0:1}" = "/" ]; then
+ REMOTE=1
+fi
+
+if [ $REMOTE -eq 0 ]; then
+ # Check if Unit is running, find the main process
+ #
+ PID=($(ps ax | grep unit:\ main | grep -v \ grep | awk '{print $1}'))
+ if [ ${#PID[@]} -eq 0 ]; then
+ echo "${0##*/}: ERROR: unitd not running (set \$UNIT_CTRL to configure a remote instance)"
+ exit 1
+ elif [ ${#PID[@]} -gt 1 ]; then
+ echo "${0##*/}: ERROR: multiple unitd processes detected (${PID[@]})"
+ exit 1
+ fi
+
+ # Read the significant unitd conifuration from cache file (or create it)
+ #
+ if [ -r /tmp/${0##*/}.$PID.env ]; then
+ source /tmp/${0##*/}.$PID.env
+ else
+ # Check we have unitd in $PATH (and all the other tools we will need)
+ #
+ MISSING=$(hash unitd curl ps grep tr cut sed tail sleep 2>&1 | cut -f4 -d: | tr -d '\n')
+ if [ "$MISSING" != "" ]; then
+ echo "${0##*/}: ERROR: cannot find$MISSING: please install or add to \$PATH"
+ exit 1
+ fi
+
+ # Get control address
+ #
+ PARAMS=$(ps $PID | grep unitd | cut -f2- -dv | tr '[]' ' ' | cut -f4- -d ' ' | sed -e 's/ --/\n--/g')
+ CTRL_ADDR=$(echo "$PARAMS" | grep '\--control' | cut -f2 -d' ')
+ if [ "$CTRL_ADDR" = "" ]; then
+ CTRL_ADDR=$(unitd --help | grep -A1 '\--control' | tail -1 | cut -f2 -d\")
+ fi
+
+ # Prepare for network or Unix socket addressing
+ #
+ if [ $(echo $CTRL_ADDR | grep -c ^unix:) -eq 1 ]; then
+ SOCK_FILE=$(echo $CTRL_ADDR | cut -f2- -d:)
+ if [ -r $SOCK_FILE ]; then
+ UNIT_CTRL="--unix-socket $SOCK_FILE _"
+ else
+ echo "${0##*/}: ERROR: cannot read unitd control socket: $SOCK_FILE"
+ ls -l $SOCK_FILE
+ exit 2
+ fi
+ else
+ UNIT_CTRL="http://$CTRL_ADDR"
+ fi
+
+ # Get error log filename
+ #
+ ERROR_LOG=$(echo "$PARAMS" | grep '\--log' | cut -f2 -d' ')
+ if [ "$ERROR_LOG" = "" ]; then
+ ERROR_LOG=$(unitd --help | grep -A1 '\--log' | tail -1 | cut -f2 -d\")
+ fi
+
+ # Cache the discovery for this unit PID (and cleanup any old files)
+ #
+ rm /tmp/${0##*/}.* 2> /dev/null
+ echo UNIT_CTRL=\"${UNIT_CTRL}\" > /tmp/${0##*/}.$PID.env
+ echo ERROR_LOG=${ERROR_LOG} >> /tmp/${0##*/}.$PID.env
+ fi
+fi
+
+# Choose presentation style
+#
+if [ $QUIET -eq 1 ]; then
+ OUTPUT="head -c 0" # Equivalent to >/dev/null
+elif hash jq 2> /dev/null; then
+ OUTPUT="jq"
+else
+ OUTPUT="cat"
+fi
+
+# Get current length of error log before we make any changes
+#
+if [ -f $ERROR_LOG ] && [ -r $ERROR_LOG ]; then
+ LOG_LEN=$(wc -l < $ERROR_LOG)
+else
+ NOLOG=1
+fi
+
+# Adjust HTTP method and curl params based on presence of stdin payload
+#
+if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then
+ if [ "$METHOD" = "DELETE" ]; then
+ $SSH_CMD curl -X $METHOD $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
+ else
+ SHOW_LOG=$(echo $URI | grep -c ^/control/)
+ $SSH_CMD curl $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
+ fi
+else
+ if [ "$METHOD" = "INSERT" ]; then
+ if ! hash jq 2> /dev/null; then
+ echo "${0##*/}: ERROR: jq(1) is required to use the INSERT method; install at <https://stedolan.github.io/jq/>"
+ exit 1
+ fi
+ NEW_ELEMENT=$(cat ${CONF_FILES[@]})
+ echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding
+ OLD_ARRAY=$($SSH_CMD curl -s $UNIT_CTRL$URI)
+ if [ "$(echo $OLD_ARRAY | jq -r type)" = "array" ]; then
+ echo $OLD_ARRAY | jq ". |= [$NEW_ELEMENT] + ." | $SSH_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
+ else
+ echo "${0##*/}: ERROR: the INSERT method expects an array"
+ exit 3
+ fi
+ else
+ cat ${CONF_FILES[@]} | $SSH_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
+ fi
+fi
+
+CURL_STATUS=${PIPESTATUS[0]}
+if [ $CURL_STATUS -ne 0 ]; then
+ echo "${0##*/}: ERROR: curl(1) exited with an error ($CURL_STATUS)"
+ if [ $CURL_STATUS -eq 7 ] && [ $REMOTE -eq 0 ]; then
+ echo "${0##*/}: Check that you have permission to access the Unit control socket, or try again with sudo(8)"
+ else
+ echo "${0##*/}: Trying to access $UNIT_CTRL$URI"
+ cat /tmp/${0##*/}.$$ && rm /tmp/${0##*/}.$$
+ fi
+ exit 4
+fi
+rm /tmp/${0##*/}.$$ 2> /dev/null
+
+if [ $SHOW_LOG -gt 0 ] && [ $NOLOG -eq 0 ] && [ $QUIET -eq 0 ]; then
+ echo -n "${0##*/}: Waiting for log..."
+ sleep $SHOW_LOG
+ echo ""
+ sed -n $((LOG_LEN+1)),\$p $ERROR_LOG
+fi
diff --git a/version b/version
index df1f7c8e..d231f5c2 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.28.0
-NXT_VERNUM=12800
+NXT_VERSION=1.29.0
+NXT_VERNUM=12900