diff options
author | Konstantin Pavlov <thresh@nginx.com> | 2022-12-15 08:17:39 -0800 |
---|---|---|
committer | Konstantin Pavlov <thresh@nginx.com> | 2022-12-15 08:17:39 -0800 |
commit | e22669f2728814aba82da14702d18bfa9685311e (patch) | |
tree | c9c9471dab359e8e33fca24c5d4f035ab5b278db | |
parent | a1d28488f9df8e28ee25ea438c275b96b9afe5b6 (diff) | |
parent | 4409a10ff0bd6bb45fb88716bd383cd867958a8a (diff) | |
download | unit-e22669f2728814aba82da14702d18bfa9685311e.tar.gz unit-e22669f2728814aba82da14702d18bfa9685311e.tar.bz2 |
Merged with the default branch.
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 @@ -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> @@ -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. @@ -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 @@ -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); @@ -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)); @@ -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 @@ -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; }" @@ -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 ;; @@ -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; }" @@ -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; }" @@ -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; @@ -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; }" @@ -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; }" @@ -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 <nginx-packaging@f5.com>"> + +<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 <nginx-packaging@f5.com>"> + +<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 <nginx-packaging@f5.com>"> + +<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 — the universal web app server</a><br> + NGINX, Inc. © 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 @@ -1,5 +1,5 @@ # Copyright (C) NGINX, Inc. -NXT_VERSION=1.28.0 -NXT_VERNUM=12800 +NXT_VERSION=1.29.0 +NXT_VERNUM=12900 |