diff options
author | Andrei Belov <defan@nginx.com> | 2021-11-18 17:04:04 +0300 |
---|---|---|
committer | Andrei Belov <defan@nginx.com> | 2021-11-18 17:04:04 +0300 |
commit | b400ccd1aa8eeb6a5de1707e0bb8c3d417fe69b7 (patch) | |
tree | 60ff49ffc16ef7cb3aad1beb2d78f051a8794cdf | |
parent | fafd44166d9e835e91a4c5668048308ce99a62bd (diff) | |
parent | b77895d1c7d6cd4826ac7427c91baa95b998a912 (diff) | |
download | unit-1.26.0-1.tar.gz unit-1.26.0-1.tar.bz2 |
Merged with the default branch.1.26.0-1
149 files changed, 4699 insertions, 1946 deletions
@@ -57,3 +57,4 @@ ad6aad2450c256d4f1a3c32f7091a78dbbc4a6d1 1.23.0-1 5c7ce0da580ef6e83c729dd012e976f22acbac27 1.24.0-1 54ffe5ce4fb3c4304faf6d342d9b17dee2c745ac 1.25.0 aa207ced9712132040e6153ceccdaf04c112d02c 1.25.0-1 +2be7b623fbfafdb470d832a28abb1cd55c76e04f 1.26.0 @@ -1,4 +1,33 @@ +Changes with Unit 1.26.0 18 Nov 2021 + + *) Change: the "share" option now specifies the entire path to the files + it serves, rather than a document root directory to be prepended to + the request URI. + + *) Feature: automatic adjustment of existing configurations to the new + "share" behavior when updating from previous versions. + + *) Feature: variables support in the "share" option. + + *) Feature: multiple paths in the "share" option. + + *) Feature: variables support in the "chroot" option. + + *) Feature: PHP opcache is shared between application processes. + + *) Feature: request routing by the query string. + + *) Bugfix: the router and app processes could crash when the requests + limit was reached by asynchronous or multithreaded apps. + + *) Bugfix: established WebSocket connections could stop reading frames + from the client after the corresponding listener had been + reconfigured. + + *) Bugfix: fixed building with glibc 2.34, notably Fedora 35. + + Changes with Unit 1.25.0 19 Aug 2021 *) Feature: client IP address replacement from a specified HTTP header @@ -20,7 +49,7 @@ Changes with Unit 1.25.0 19 Aug 2021 multiple certificate bundles in a listener if the client did not use SNI. - *) Bugfix: the router process could crash with frequent mutithreaded + *) Bugfix: the router process could crash with frequent multithreaded application reconfiguration. *) Bugfix: compatibility issues with some Python ASGI apps, notably @@ -59,6 +59,7 @@ $echo >> $NXT_MAKEFILE $echo "NXT_LIB_UNIT_OBJS = \\" >> $NXT_MAKEFILE $echo " $NXT_BUILD_DIR/src/nxt_lvlhsh.o \\" >> $NXT_MAKEFILE $echo " $NXT_BUILD_DIR/src/nxt_murmur_hash.o \\" >> $NXT_MAKEFILE +$echo " $NXT_BUILD_DIR/src/nxt_socket_msg.o \\" >> $NXT_MAKEFILE $echo " $NXT_BUILD_DIR/src/nxt_websocket.o \\" >> $NXT_MAKEFILE for nxt_src in $NXT_LIB_UNIT_SRCS diff --git a/auto/sockets b/auto/sockets index c8d1173e..1b6b4368 100644 --- a/auto/sockets +++ b/auto/sockets @@ -158,6 +158,58 @@ nxt_feature_test="#include <stdio.h> }" . auto/feature +if [ $nxt_found = no ]; then + $echo + $echo $0: error: no msghdr.msg_control struct member. + $echo + exit 1; +fi + + +nxt_feature="sockopt SO_PASSCRED" +nxt_feature_name=NXT_HAVE_SOCKOPT_SO_PASSCRED +nxt_feature_run= +nxt_feature_incs= +nxt_feature_libs= +nxt_feature_test="#define _GNU_SOURCE + #include <sys/socket.h> + + int main() { + return SO_PASSCRED == 0; + }" +. auto/feature + + +if [ $nxt_found = yes ]; then + nxt_feature="struct ucred" + nxt_feature_name=NXT_HAVE_UCRED + nxt_feature_run= + nxt_feature_incs= + nxt_feature_libs= + nxt_feature_test="#define _GNU_SOURCE + #include <sys/socket.h> + #include <sys/un.h> + + int main() { + return sizeof(struct ucred); + }" + . auto/feature +fi + + +nxt_feature="struct cmsgcred" +nxt_feature_name=NXT_HAVE_MSGHDR_CMSGCRED +nxt_feature_run= +nxt_feature_incs= +nxt_feature_libs= +nxt_feature_test="#define _GNU_SOURCE + #include <sys/socket.h> + + int main() { + return sizeof(struct cmsgcred); + }" +. auto/feature + nxt_feature="sys/filio.h" nxt_feature_name=NXT_HAVE_SYS_FILIO_H diff --git a/auto/sources b/auto/sources index 01fec6c1..27a45edc 100644 --- a/auto/sources +++ b/auto/sources @@ -13,6 +13,7 @@ NXT_LIB_SRCS=" \ src/nxt_mem_map.c \ src/nxt_socket.c \ src/nxt_socketpair.c \ + src/nxt_socket_msg.c \ src/nxt_credential.c \ src/nxt_isolation.c \ src/nxt_process.c \ @@ -170,6 +171,7 @@ NXT_TEST_SRCS=" \ src/test/nxt_rbtree1_test.c \ src/test/nxt_http_parse_test.c \ src/test/nxt_strverscmp_test.c \ + src/test/nxt_base64_test.c \ " diff --git a/docs/changes.xml b/docs/changes.xml index dca77068..af395fd3 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -5,6 +5,125 @@ <change_log title="unit"> +<changes apply="unit-jsc18" ver="1.26.0" rev="1" + date="2021-11-18" time="18:00:00 +0300" + packager="Andrei Belov <defan@nginx.com>"> + +<change> +<para> +Initial release of Java 18 module for NGINX Unit. +</para> +</change> + +</changes> + + +<changes apply="unit-python3.10" ver="1.26.0" rev="1" + date="2021-11-18" time="18:00:00 +0300" + packager="Andrei Belov <defan@nginx.com>"> + +<change> +<para> +Initial release of Python 3.10 module for NGINX Unit. +</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-go + unit-perl + unit-ruby + unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13 + unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17" + ver="1.26.0" rev="1" + date="2021-11-18" time="18:00:00 +0300" + packager="Andrei Belov <defan@nginx.com>"> + +<change> +<para> +NGINX Unit updated to 1.26.0. +</para> +</change> + +</changes> + + +<changes apply="unit" ver="1.26.0" rev="1" + date="2021-11-18" time="18:00:00 +0300" + packager="Andrei Belov <defan@nginx.com>"> + +<change type="change"> +<para> +the "share" option now specifies the entire path to the files it serves, +rather than a document root directory to be prepended to the request URI. +</para> +</change> + +<change type="feature"> +<para> +automatic adjustment of existing configurations to the new "share" behavior +when updating from previous versions. +</para> +</change> + +<change type="feature"> +<para> +variables support in the "share" option. +</para> +</change> + +<change type="feature"> +<para> +multiple paths in the "share" option. +</para> +</change> + +<change type="feature"> +<para> +variables support in the "chroot" option. +</para> +</change> + +<change type="feature"> +<para> +PHP opcache is shared between application processes. +</para> +</change> + +<change type="feature"> +<para> +request routing by the query string. +</para> +</change> + +<change type="bugfix"> +<para> +the router and app processes could crash when the requests limit was reached +by asynchronous or multithreaded apps. +</para> +</change> + +<change type="bugfix"> +<para> +established WebSocket connections could stop reading frames from the client +after the corresponding listener had been reconfigured. +</para> +</change> + +<change type="bugfix"> +<para> +fixed building with glibc 2.34, notably Fedora 35. +</para> +</change> + +</changes> + + <changes apply="unit-php unit-python unit-python2.7 unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7 @@ -77,7 +196,7 @@ bundles in a listener if the client did not use SNI. <change type="bugfix"> <para> -the router process could crash with frequent mutithreaded application +the router process could crash with frequent multithreaded application reconfiguration. </para> </change> diff --git a/go/nxt_cgo_lib.c b/go/nxt_cgo_lib.c index 330697c1..ca9fc3ab 100644 --- a/go/nxt_cgo_lib.c +++ b/go/nxt_cgo_lib.c @@ -10,10 +10,10 @@ #include <nxt_unit_request.h> -static ssize_t nxt_cgo_port_send(nxt_unit_ctx_t *, nxt_unit_port_t *port, +static ssize_t nxt_cgo_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, const void *buf, size_t buf_size, const void *oob, size_t oob_size); -static ssize_t nxt_cgo_port_recv(nxt_unit_ctx_t *, nxt_unit_port_t *port, - void *buf, size_t buf_size, void *oob, size_t oob_size); +static ssize_t nxt_cgo_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, + void *buf, size_t buf_size, void *oob, size_t *oob_size); int nxt_cgo_run(uintptr_t handler) @@ -30,6 +30,7 @@ nxt_cgo_run(uintptr_t handler) init.callbacks.port_send = nxt_cgo_port_send; init.callbacks.port_recv = nxt_cgo_port_recv; init.callbacks.shm_ack_handler = nxt_go_shm_ack_handler; + init.callbacks.ready_handler = nxt_go_ready; init.data = (void *) handler; @@ -57,7 +58,7 @@ nxt_cgo_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, static ssize_t nxt_cgo_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, - void *buf, size_t buf_size, void *oob, size_t oob_size) + void *buf, size_t buf_size, void *oob, size_t *oob_size) { return nxt_go_port_recv(port->id.pid, port->id.id, buf, buf_size, oob, oob_size); @@ -110,17 +110,21 @@ func nxt_go_add_port(ctx *C.nxt_unit_ctx_t, p *C.nxt_unit_port_t) C.int { p.in_fd = -1 p.out_fd = -1 - if new_port.key.id == 65535 { - go func(ctx *C.nxt_unit_ctx_t) { - C.nxt_unit_run_shared(ctx); - }(ctx) - } + return C.NXT_UNIT_OK +} + +//export nxt_go_ready +func nxt_go_ready(ctx *C.nxt_unit_ctx_t) C.int { + go func(ctx *C.nxt_unit_ctx_t) { + C.nxt_unit_run_shared(ctx) + }(ctx) return C.NXT_UNIT_OK } //export nxt_go_remove_port -func nxt_go_remove_port(unit *C.nxt_unit_t, p *C.nxt_unit_port_t) { +func nxt_go_remove_port(unit *C.nxt_unit_t, ctx *C.nxt_unit_ctx_t, + p *C.nxt_unit_port_t) { key := port_key{ pid: int(p.id.pid), @@ -165,7 +169,7 @@ func nxt_go_port_send(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, //export nxt_go_port_recv func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, - oob unsafe.Pointer, oob_size C.int) C.ssize_t { + oob unsafe.Pointer, oob_size *C.size_t) C.ssize_t { key := port_key{ pid: int(pid), @@ -180,7 +184,7 @@ func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, } n, oobn, _, _, err := p.rcv.ReadMsgUnix(GoBytes(buf, buf_size), - GoBytes(oob, oob_size)) + GoBytes(oob, C.int(*oob_size))) if err != nil { if nerr, ok := err.(*net.OpError); ok { @@ -192,6 +196,9 @@ func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, nxt_go_warn("read result %d (%d), %s", n, oobn, err) n = -1 + + } else { + *oob_size = C.size_t(oobn) } return C.ssize_t(n) diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index 85d5545e..66a5f33a 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -19,34 +19,35 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit) MODULES= -# Ubuntu 21.04 -ifeq ($(CODENAME),hirsute) +# Ubuntu 21.10 +ifeq ($(CODENAME),impish) include Makefile.php include Makefile.python27 include Makefile.python39 +include Makefile.python310 include Makefile.go include Makefile.perl include Makefile.ruby include Makefile.jsc-common include Makefile.jsc11 -include Makefile.jsc15 include Makefile.jsc16 include Makefile.jsc17 +include Makefile.jsc18 endif -# Ubuntu 20.10 -ifeq ($(CODENAME),groovy) +# Ubuntu 21.04 +ifeq ($(CODENAME),hirsute) include Makefile.php include Makefile.python27 -include Makefile.python38 +include Makefile.python39 include Makefile.go include Makefile.perl include Makefile.ruby include Makefile.jsc-common include Makefile.jsc11 -include Makefile.jsc13 -include Makefile.jsc14 include Makefile.jsc15 +include Makefile.jsc16 +include Makefile.jsc17 endif # Ubuntu 20.04 @@ -61,46 +62,6 @@ include Makefile.jsc-common include Makefile.jsc11 endif -# Ubuntu 19.10 -ifeq ($(CODENAME),eoan) -include Makefile.php -include Makefile.python27 -include Makefile.python37 -include Makefile.python38 -include Makefile.go -include Makefile.perl -include Makefile.ruby -include Makefile.jsc-common -include Makefile.jsc11 -endif - -# Ubuntu 19.04 -ifeq ($(CODENAME),disco) -include Makefile.php -include Makefile.python27 -include Makefile.python37 -include Makefile.python38 -include Makefile.go -include Makefile.perl -include Makefile.ruby -include Makefile.jsc-common -include Makefile.jsc11 -endif - -# Ubuntu 18.10 -ifeq ($(CODENAME),cosmic) -include Makefile.php -include Makefile.python27 -include Makefile.python36 -include Makefile.python37 -include Makefile.go -include Makefile.perl -include Makefile.ruby -include Makefile.jsc-common -include Makefile.jsc8 -include Makefile.jsc11 -endif - # Ubuntu 18.04 ifeq ($(CODENAME),bionic) include Makefile.php @@ -116,25 +77,6 @@ include Makefile.jsc8 include Makefile.jsc11 endif -# Ubuntu 16.04 -ifeq ($(CODENAME),xenial) -include Makefile.php -include Makefile.python27 -include Makefile.python35 -include Makefile.go -include Makefile.perl -include Makefile.ruby -include Makefile.jsc-common -include Makefile.jsc8 -endif - -# Ubuntu 14.04 -ifeq ($(CODENAME),trusty) -include Makefile.php -include Makefile.python -include Makefile.perl -endif - # Debian 11 ifeq ($(CODENAME),bullseye) include Makefile.php @@ -159,27 +101,6 @@ include Makefile.jsc-common include Makefile.jsc11 endif -# Debian 9 -ifeq ($(CODENAME),stretch) -include Makefile.php -include Makefile.python27 -include Makefile.python35 -include Makefile.go -include Makefile.perl -include Makefile.ruby -include Makefile.jsc-common -include Makefile.jsc8 -endif - -# Debian 8 -ifeq ($(CODENAME),jessie) -include Makefile.php -include Makefile.python27 -include Makefile.python34 -include Makefile.perl -include Makefile.ruby -endif - CONFIGURE_ARGS=\ --prefix=/usr \ --state=/var/lib/unit \ @@ -210,7 +131,7 @@ check-build-depends-%: esac ; \ not_installed= ; \ for pkg in $${pkgs}; do \ - dpkg -s $${pkg} >/dev/null 2>&1 ; \ + dpkg-query -W $${pkg} >/dev/null 2>&1 ; \ if [ $$? -ne 0 ]; then \ not_installed="$${not_installed} $${pkg}" ; \ fi ; \ @@ -231,7 +152,7 @@ debuild/$(SRCDIR)/debian: set -e ; \ mkdir -p debuild/$(SRCDIR) ; \ cp -pr debian debuild/$(SRCDIR) ; \ - echo '9' > debuild/$(SRCDIR)/debian/compat ; \ + echo '11' > debuild/$(SRCDIR)/debian/compat ; \ mkdir -p debuild/$(SRCDIR)/debian/source ; \ echo '3.0 (quilt)' > debuild/$(SRCDIR)/debian/source/format ; \ cat debian/control.in | sed \ @@ -275,7 +196,7 @@ debuild-%: debuild/unit_$(VERSION).orig.tar.gz ../../docs/changes.xml cp debuild/unit_$(VERSION).orig.tar.gz debuild-$*/unit-$(MODULE_SUFFIX_$*)_$(VERSION).orig.tar.gz cd $@ && tar zxf unit-$(MODULE_SUFFIX_$*)_$(VERSION).orig.tar.gz mkdir $@/$(SRCDIR)/debian - echo '9' > $@/$(SRCDIR)/debian/compat + echo '11' > $@/$(SRCDIR)/debian/compat mkdir $@/$(SRCDIR)/debian/source echo '3.0 (quilt)' > $@/$(SRCDIR)/debian/source/format cd ../../docs && make ../build/unit-$(MODULE_SUFFIX_$*).deb-changelog diff --git a/pkg/deb/Makefile.jsc-common b/pkg/deb/Makefile.jsc-common index 5f727124..2b098025 100644 --- a/pkg/deb/Makefile.jsc-common +++ b/pkg/deb/Makefile.jsc-common @@ -6,10 +6,10 @@ MODULE_SUMMARY_jsc_common= Java shared packages for NGINX Unit MODULE_VERSION_jsc_common= $(VERSION) MODULE_RELEASE_jsc_common= 1 -ifneq (,$(findstring $(CODENAME),hirsute groovy focal eoan disco buster bullseye)) -JAVA_MINVERSION= 11 -else +ifneq (,$(findstring $(CODENAME),bionic)) JAVA_MINVERSION= 8 +else +JAVA_MINVERSION= 11 endif MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-$(JAVA_MINVERSION)-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/ diff --git a/pkg/deb/Makefile.jsc18 b/pkg/deb/Makefile.jsc18 new file mode 100644 index 00000000..806aa320 --- /dev/null +++ b/pkg/deb/Makefile.jsc18 @@ -0,0 +1,71 @@ +MODULES+= jsc18 +MODULE_SUFFIX_jsc18= jsc18 + +MODULE_SUMMARY_jsc18= Java 18 module for NGINX Unit + +MODULE_VERSION_jsc18= $(VERSION) +MODULE_RELEASE_jsc18= 1 + +MODULE_CONFARGS_jsc18= java --module=java18 --home=/usr/lib/jvm/java-18-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/ +MODULE_MAKEARGS_jsc18= java18 +MODULE_INSTARGS_jsc18= java18-install + +MODULE_SOURCES_jsc18= unit.example-jsc-app \ + unit.example-jsc18-config + +BUILD_DEPENDS_jsc18= openjdk-18-jdk-headless openjdk-18-jre-headless +BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc18) + +MODULE_BUILD_DEPENDS_jsc18=,openjdk-18-jdk-headless +MODULE_DEPENDS_jsc18=,openjdk-18-jre-headless,unit-jsc-common (= $(MODULE_VERSION_jsc_common)-$(MODULE_RELEASE_jsc_common)~$(CODENAME)) + +define MODULE_PREINSTALL_jsc18 + mkdir -p debian/unit-jsc18/usr/share/doc/unit-jsc18/examples/jsc-app + install -m 644 -p debian/unit.example-jsc-app debian/unit-jsc18/usr/share/doc/unit-jsc18/examples/jsc-app/index.jsp + install -m 644 -p debian/unit.example-jsc18-config debian/unit-jsc18/usr/share/doc/unit-jsc18/examples/unit.config + install -m 644 -p src/java/README.JSR-340 debian/unit-jsc18/usr/share/doc/unit-jsc18/ +endef +export MODULE_PREINSTALL_jsc18 + +define MODULE_POSTINSTALL_jsc18 + cd $$\(BUILDDIR_unit\) \&\& \ + DESTDIR=$$\(INSTALLDIR\) make java-shared-uninstall +endef +export MODULE_POSTINSTALL_jsc18 + +define MODULE_POST_jsc18 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_jsc18) has been installed. + +To check out the sample app, run these commands: + + sudo service unit restart + cd /usr/share/doc/unit-$(MODULE_SUFFIX_jsc18)/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_jsc18 diff --git a/pkg/deb/Makefile.python310 b/pkg/deb/Makefile.python310 new file mode 100644 index 00000000..960510a3 --- /dev/null +++ b/pkg/deb/Makefile.python310 @@ -0,0 +1,46 @@ +MODULES+= python310 +MODULE_SUFFIX_python310= python3.10 + +MODULE_SUMMARY_python310= Python 3.10 module for NGINX Unit + +MODULE_VERSION_python310= $(VERSION) +MODULE_RELEASE_python310= 1 + +MODULE_CONFARGS_python310= python --config=python3.10-config +MODULE_MAKEARGS_python310= python3.10 +MODULE_INSTARGS_python310= python3.10-install + +MODULE_SOURCES_python310= unit.example-python-app \ + unit.example-python3.10-config + +BUILD_DEPENDS_python310= python3.10-dev +BUILD_DEPENDS+= $(BUILD_DEPENDS_python310) + +MODULE_BUILD_DEPENDS_python310=,python3.10-dev + +define MODULE_PREINSTALL_python310 + mkdir -p debian/unit-python3.10/usr/share/doc/unit-python3.10/examples/python-app + install -m 644 -p debian/unit.example-python-app debian/unit-python3.10/usr/share/doc/unit-python3.10/examples/python-app/wsgi.py + install -m 644 -p debian/unit.example-python3.10-config debian/unit-python3.10/usr/share/doc/unit-python3.10/examples/unit.config +endef +export MODULE_PREINSTALL_python310 + +define MODULE_POST_python310 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_python310) has been installed. + +To check out the sample app, run these commands: + + sudo service unit restart + cd /usr/share/doc/unit-$(MODULE_SUFFIX_python310)/examples + sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config + curl http://localhost:8400/ + +Online documentation is available at https://unit.nginx.org + +---------------------------------------------------------------------- +BANNER +endef +export MODULE_POST_python310 diff --git a/pkg/deb/debian.module/unit.example-jsc18-config b/pkg/deb/debian.module/unit.example-jsc18-config new file mode 100644 index 00000000..08f9f596 --- /dev/null +++ b/pkg/deb/debian.module/unit.example-jsc18-config @@ -0,0 +1,15 @@ +{ + "applications": { + "example_java18": { + "processes": 1, + "type": "java 18", + "webapp": "/usr/share/doc/unit-jsc18/examples/jsc-app" + } + }, + + "listeners": { + "*:8800": { + "pass": "applications/example_java18" + } + } +} diff --git a/pkg/deb/debian.module/unit.example-python3.10-config b/pkg/deb/debian.module/unit.example-python3.10-config new file mode 100644 index 00000000..eaeb6103 --- /dev/null +++ b/pkg/deb/debian.module/unit.example-python3.10-config @@ -0,0 +1,16 @@ +{ + "applications": { + "example_python": { + "type": "python 3.10", + "processes": 2, + "path": "/usr/share/doc/unit-python3.10/examples/python-app", + "module": "wsgi" + } + }, + + "listeners": { + "*:8400": { + "pass": "applications/example_python" + } + } +} diff --git a/pkg/deb/debian/rules.in b/pkg/deb/debian/rules.in index c7a56b6b..3f1f9db8 100644 --- a/pkg/deb/debian/rules.in +++ b/pkg/deb/debian/rules.in @@ -99,11 +99,7 @@ install: build do.tests dh_testroot dh_prep dh_installdirs -ifeq ($(CODENAME), xenial) - dh_installinit -else dh_installsystemd -endif dh_installlogrotate cd $(BUILDDIR_unit) && DESTDIR=$(INSTALLDIR) make install cd $(BUILDDIR_unit) && DESTDIR=$(INSTALLDIR_dev) make libunit-install diff --git a/pkg/docker/Dockerfile.go1.15 b/pkg/docker/Dockerfile.go1.17 index 0c88ff64..2555cb23 100644 --- a/pkg/docker/Dockerfile.go1.15 +++ b/pkg/docker/Dockerfile.go1.17 @@ -1,4 +1,4 @@ -FROM golang:1.15 as BUILDER +FROM golang:1.17 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.25.0 \ + && hg up 1.26.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 go-install-src 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 golang:1.15 +FROM golang:1.17 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.jsc11 b/pkg/docker/Dockerfile.jsc11 index 8f62ad3e..091ddf01 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.25.0 \ + && hg up 1.26.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 00875732..18c02898 100644 --- a/pkg/docker/Dockerfile.minimal +++ b/pkg/docker/Dockerfile.minimal @@ -1,4 +1,4 @@ -FROM debian:buster-slim as BUILDER +FROM debian:bullseye-slim 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.25.0 \ + && hg up 1.26.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 version \ && 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 debian:buster-slim +FROM debian:bullseye-slim 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.node15 b/pkg/docker/Dockerfile.node16 index f98d0ef3..386d0c24 100644 --- a/pkg/docker/Dockerfile.node15 +++ b/pkg/docker/Dockerfile.node16 @@ -1,4 +1,4 @@ -FROM node:15 as BUILDER +FROM node:16 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.25.0 \ + && hg up 1.26.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:15 +FROM node:16 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.32 b/pkg/docker/Dockerfile.perl5.34 index 244eb076..624a059c 100644 --- a/pkg/docker/Dockerfile.perl5.32 +++ b/pkg/docker/Dockerfile.perl5.34 @@ -1,4 +1,4 @@ -FROM perl:5.32 as BUILDER +FROM perl:5.34 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.25.0 \ + && hg up 1.26.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 perl-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 perl:5.32 +FROM perl:5.34 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.php8.0 b/pkg/docker/Dockerfile.php8.0 index ba85ce0e..f3a1e10b 100644 --- a/pkg/docker/Dockerfile.php8.0 +++ b/pkg/docker/Dockerfile.php8.0 @@ -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.25.0 \ + && hg up 1.26.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.9 b/pkg/docker/Dockerfile.python3.9 index a3ca8d4a..87e807cc 100644 --- a/pkg/docker/Dockerfile.python3.9 +++ b/pkg/docker/Dockerfile.python3.9 @@ -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.25.0 \ + && hg up 1.26.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.ruby2.7 b/pkg/docker/Dockerfile.ruby3.0 index d5140288..da4dd559 100644 --- a/pkg/docker/Dockerfile.ruby2.7 +++ b/pkg/docker/Dockerfile.ruby3.0 @@ -1,4 +1,4 @@ -FROM ruby:2.7 as BUILDER +FROM ruby:3.0 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.25.0 \ + && hg up 1.26.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 ruby-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 ruby:2.7 +FROM ruby:3.0 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/Makefile b/pkg/docker/Makefile index 00625526..a2c1a52b 100644 --- a/pkg/docker/Makefile +++ b/pkg/docker/Makefile @@ -12,13 +12,13 @@ EXPORT_DIR := $(VERSION) MODULES ?= go jsc node perl php python ruby minimal VERSION_minimal ?= -CONTAINER_minimal ?= debian:buster-slim +CONTAINER_minimal ?= debian:bullseye-slim CONFIGURE_minimal ?= INSTALL_minimal ?= version define COPY_minimal endef -VERSION_go ?= 1.15 +VERSION_go ?= 1.17 CONTAINER_go ?= golang:$(VERSION_go) CONFIGURE_go ?= go --go-path=$$GOPATH INSTALL_go ?= go-install-src libunit-install @@ -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 ?= 15 +VERSION_node ?= 16 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 @@ -44,7 +44,7 @@ COPY --from=BUILDER /usr/include/nxt_* /usr/include/\n\$ COPY --from=BUILDER /usr/local/lib/node_modules/unit-http/ /usr/local/lib/node_modules/unit-http/ endef -VERSION_perl ?= 5.32 +VERSION_perl ?= 5.34 CONTAINER_perl ?= perl:$(VERSION_perl) CONFIGURE_perl ?= perl INSTALL_perl ?= perl-install @@ -62,7 +62,7 @@ CONFIGURE_python ?= python --config=/usr/local/bin/python3-config INSTALL_python ?= python3-install COPY_python = -VERSION_ruby ?= 2.7 +VERSION_ruby ?= 3.0 CONTAINER_ruby ?= ruby:$(VERSION_ruby) CONFIGURE_ruby ?= ruby INSTALL_ruby ?= ruby-install diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile index e67846cf..011eba5b 100644 --- a/pkg/rpm/Makefile +++ b/pkg/rpm/Makefile @@ -134,7 +134,9 @@ include Makefile.php ifeq ($(shell test `rpm --eval '0%{?fedora} -lt 32'`; echo $$?),0) include Makefile.python27 endif -ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 33'`; echo $$?),0) +ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35'`; echo $$?),0) +include Makefile.python310 +else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 33'`; echo $$?),0) include Makefile.python39 else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 32'`; echo $$?),0) include Makefile.python38 diff --git a/pkg/rpm/Makefile.python310 b/pkg/rpm/Makefile.python310 new file mode 100644 index 00000000..82bc311a --- /dev/null +++ b/pkg/rpm/Makefile.python310 @@ -0,0 +1,57 @@ +MODULES+= python310 +MODULE_SUFFIX_python310= python3.10 + +MODULE_SUMMARY_python310= Python 3.10 module for NGINX Unit + +MODULE_VERSION_python310= $(VERSION) +MODULE_RELEASE_python310= 1 + +MODULE_CONFARGS_python310= python --config=python3.10-config +MODULE_MAKEARGS_python310= python3.10 +MODULE_INSTARGS_python310= python3.10-install + +MODULE_SOURCES_python310= unit.example-python-app \ + unit.example-python310-config + +ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora amazonlinux2)) +BUILD_DEPENDS_python310= python3-devel +else +BUILD_DEPENDS_python310= python310-devel +endif + +BUILD_DEPENDS+= $(BUILD_DEPENDS_python310) + +define MODULE_PREINSTALL_python310 +%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python310/examples/python-app +%{__install} -m 644 -p %{SOURCE100} \ + %{buildroot}%{_datadir}/doc/unit-python310/examples/python-app/wsgi.py +%{__install} -m 644 -p %{SOURCE101} \ + %{buildroot}%{_datadir}/doc/unit-python310/examples/unit.config +endef +export MODULE_PREINSTALL_python310 + +define MODULE_FILES_python310 +%{_libdir}/unit/modules/* +%{_libdir}/unit/debug-modules/* +endef +export MODULE_FILES_python310 + +define MODULE_POST_python310 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_python310) 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_python310 diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python310-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python310-config new file mode 100644 index 00000000..8a73ca53 --- /dev/null +++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python310-config @@ -0,0 +1,16 @@ +{ + "applications": { + "example_python": { + "type": "python 3.10", + "processes": 2, + "path": "/usr/share/doc/unit-python310/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 4f096c73..9b0f6221 100644 --- a/pkg/rpm/unit.module.spec.in +++ b/pkg/rpm/unit.module.spec.in @@ -1,12 +1,12 @@ # distribution specific definitions %define bdir %{_builddir}/%{name}-%{version} -%%MODULE_DEFINITIONS%% - %if (0%{?rhel} == 7 && 0%{?amzn} == 0) %define dist .el7 %endif +%%MODULE_DEFINITIONS%% + %if 0%{?rhel}%{?fedora} BuildRequires: gcc %if 0%{?amzn2} diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 589eca3f..ee5dc46f 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -519,11 +519,11 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) void -Unit::remove_port(nxt_unit_t *unit, nxt_unit_port_t *port) +Unit::remove_port(nxt_unit_t *unit, nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { port_data_t *data; - if (port->data != NULL) { + if (port->data != NULL && ctx != NULL) { data = (port_data_t *) port->data; data->stop(); diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h index 4ef40d45..1aa93073 100644 --- a/src/nodejs/unit-http/unit.h +++ b/src/nodejs/unit-http/unit.h @@ -41,7 +41,8 @@ private: void shm_ack_handler(nxt_unit_ctx_t *ctx); static int add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); - static void remove_port(nxt_unit_t *unit, nxt_unit_port_t *port); + static void remove_port(nxt_unit_t *unit, nxt_unit_ctx_t *ctx, + nxt_unit_port_t *port); static void quit_cb(nxt_unit_ctx_t *ctx); void quit(nxt_unit_ctx_t *ctx); diff --git a/src/nxt_application.c b/src/nxt_application.c index 5d58e60c..589821fb 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -42,9 +42,23 @@ static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, const char *name); +static nxt_int_t nxt_proto_setup(nxt_task_t *task, nxt_process_t *process); +static nxt_int_t nxt_proto_start(nxt_task_t *task, nxt_process_data_t *data); static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); +static void nxt_proto_start_process_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); +static void nxt_proto_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +static void nxt_proto_process_created_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); +static void nxt_proto_quit_children(nxt_task_t *task); +static nxt_process_t *nxt_proto_process_find(nxt_task_t *task, nxt_pid_t pid); +static void nxt_proto_process_add(nxt_task_t *task, nxt_process_t *process); +static nxt_process_t *nxt_proto_process_remove(nxt_task_t *task, nxt_pid_t pid); static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src); +static void nxt_proto_signal_handler(nxt_task_t *task, void *obj, void *data); +static void nxt_proto_sigterm_handler(nxt_task_t *task, void *obj, void *data); +static void nxt_proto_sigchld_handler(nxt_task_t *task, void *obj, void *data); nxt_str_t nxt_server = nxt_string(NXT_SERVER); @@ -55,7 +69,12 @@ static uint32_t compat[] = { }; -static nxt_app_module_t *nxt_app; +static nxt_lvlhsh_t nxt_proto_processes; +static nxt_queue_t nxt_proto_children; +static nxt_bool_t nxt_proto_exiting; + +static nxt_app_module_t *nxt_app; +static nxt_common_app_conf_t *nxt_app_conf; static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { @@ -70,6 +89,29 @@ static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { }; +const nxt_sig_event_t nxt_prototype_signals[] = { + nxt_event_signal(SIGHUP, nxt_proto_signal_handler), + nxt_event_signal(SIGINT, nxt_proto_sigterm_handler), + nxt_event_signal(SIGQUIT, nxt_proto_sigterm_handler), + nxt_event_signal(SIGTERM, nxt_proto_sigterm_handler), + nxt_event_signal(SIGCHLD, nxt_proto_sigchld_handler), + nxt_event_signal_end, +}; + + +static const nxt_port_handlers_t nxt_proto_process_port_handlers = { + .quit = nxt_proto_quit_handler, + .change_file = nxt_port_change_log_file_handler, + .new_port = nxt_port_new_port_handler, + .process_created = nxt_proto_process_created_handler, + .process_ready = nxt_port_process_ready_handler, + .remove_pid = nxt_port_remove_pid_handler, + .start_process = nxt_proto_start_process_handler, + .rpc_ready = nxt_port_rpc_handler, + .rpc_error = nxt_port_rpc_handler, +}; + + static const nxt_port_handlers_t nxt_app_process_port_handlers = { .quit = nxt_signal_quit_handler, .rpc_ready = nxt_port_rpc_handler, @@ -89,12 +131,23 @@ const nxt_process_init_t nxt_discovery_process = { }; +const nxt_process_init_t nxt_proto_process = { + .type = NXT_PROCESS_PROTOTYPE, + .prefork = nxt_isolation_main_prefork, + .restart = 0, + .setup = nxt_proto_setup, + .start = nxt_proto_start, + .port_handlers = &nxt_proto_process_port_handlers, + .signals = nxt_prototype_signals, +}; + + const nxt_process_init_t nxt_app_process = { .type = NXT_PROCESS_APP, .setup = nxt_app_setup, - .prefork = nxt_isolation_main_prefork, + .start = NULL, + .prefork = NULL, .restart = 0, - .start = NULL, /* set to module->start */ .port_handlers = &nxt_app_process_port_handlers, .signals = nxt_process_signals, }; @@ -443,15 +496,18 @@ nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) static nxt_int_t -nxt_app_setup(nxt_task_t *task, nxt_process_t *process) +nxt_proto_setup(nxt_task_t *task, nxt_process_t *process) { nxt_int_t ret; - nxt_process_init_t *init; nxt_app_lang_module_t *lang; nxt_common_app_conf_t *app_conf; app_conf = process->data.app; + nxt_queue_init(&nxt_proto_children); + + nxt_app_conf = app_conf; + lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type); if (nxt_slow_path(lang == NULL)) { nxt_alert(task, "unknown application type: \"%V\"", &app_conf->type); @@ -479,7 +535,6 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) if (nxt_app->setup != NULL) { ret = nxt_app->setup(task, process, app_conf); - if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -514,30 +569,285 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } + process->state = NXT_PROCESS_STATE_CREATED; + + return NXT_OK; +} + + +static nxt_int_t +nxt_proto_start(nxt_task_t *task, nxt_process_data_t *data) +{ + nxt_debug(task, "prototype waiting for clone messages"); + + return NXT_OK; +} + + +static void +nxt_proto_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + u_char *p; + nxt_int_t ret; + nxt_port_t *port; + nxt_runtime_t *rt; + nxt_process_t *process; + nxt_process_init_t *init; + + rt = task->thread->runtime; + + process = nxt_process_new(rt); + if (nxt_slow_path(process == NULL)) { + goto failed; + } + + process->mem_pool = nxt_mp_create(1024, 128, 256, 32); + if (nxt_slow_path(process->mem_pool == NULL)) { + nxt_process_use(task, process, -1); + goto failed; + } + + process->parent_port = rt->port_by_type[NXT_PROCESS_PROTOTYPE]; + init = nxt_process_init(process); + *init = nxt_app_process; + + process->name = nxt_mp_alloc(process->mem_pool, nxt_app_conf->name.length + + sizeof("\"\" application") + 1); + + if (nxt_slow_path(process->name == NULL)) { + nxt_process_use(task, process, -1); + + goto failed; + } init->start = nxt_app->start; + init->name = (const char *) nxt_app_conf->name.start; + + p = (u_char *) process->name; + *p++ = '"'; + p = nxt_cpymem(p, nxt_app_conf->name.start, nxt_app_conf->name.length); + p = nxt_cpymem(p, "\" application", 13); + *p = '\0'; + + process->user_cred = &rt->user_cred; + + process->data.app = nxt_app_conf; + process->stream = msg->port_msg.stream; + + ret = nxt_process_start(task, process); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_process_use(task, process, -1); + + goto failed; + } + + nxt_proto_process_add(task, process); + + return; + +failed: + + port = nxt_runtime_port_find(rt, msg->port_msg.pid, + msg->port_msg.reply_port); + + if (nxt_fast_path(port != NULL)) { + nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, + -1, msg->port_msg.stream, 0, NULL); + } +} + + +static void +nxt_proto_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_debug(task, "prototype quit handler"); + + nxt_proto_quit_children(task); + + nxt_proto_exiting = 1; + + if (nxt_queue_is_empty(&nxt_proto_children)) { + nxt_process_quit(task, 0); + } +} + + +static void +nxt_proto_quit_children(nxt_task_t *task) +{ + nxt_port_t *port; + nxt_process_t *process; + + nxt_queue_each(process, &nxt_proto_children, nxt_process_t, link) { + port = nxt_process_port_first(process); + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, + -1, 0, 0, NULL); + } + nxt_queue_loop; +} + + +static void +nxt_proto_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_pid_t isolated_pid, pid; + nxt_process_t *process; + + isolated_pid = nxt_recv_msg_cmsg_pid(msg); + + process = nxt_proto_process_find(task, isolated_pid); + if (nxt_slow_path(process == NULL)) { + return; + } + process->state = NXT_PROCESS_STATE_CREATED; - return NXT_OK; + pid = msg->port_msg.pid; + + if (process->pid != pid) { + nxt_debug(task, "app process %PI (aka %PI) is created", isolated_pid, + pid); + + nxt_runtime_process_remove(task->thread->runtime, process); + + process->pid = pid; + + nxt_runtime_process_add(task, process); + + } else { + nxt_debug(task, "app process %PI is created", isolated_pid); + } +} + + +static void +nxt_proto_signal_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_trace(task, "signal signo:%d (%s) received, ignored", + (int) (uintptr_t) obj, data); +} + + +static void +nxt_proto_sigterm_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_trace(task, "signal signo:%d (%s) received", + (int) (uintptr_t) obj, data); + + nxt_proto_quit_children(task); + + nxt_proto_exiting = 1; + + if (nxt_queue_is_empty(&nxt_proto_children)) { + nxt_process_quit(task, 0); + } +} + + +static void +nxt_proto_sigchld_handler(nxt_task_t *task, void *obj, void *data) +{ + int status; + nxt_err_t err; + nxt_pid_t pid; + nxt_process_t *process; + + nxt_debug(task, "proto sigchld handler signo:%d (%s)", + (int) (uintptr_t) obj, data); + + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); + + if (pid == -1) { + + switch (err = nxt_errno) { + + case NXT_ECHILD: + return; + + case NXT_EINTR: + continue; + + default: + nxt_alert(task, "waitpid() failed: %E", err); + return; + } + } + + nxt_debug(task, "waitpid(): %PI", pid); + + if (pid == 0) { + return; + } + + if (WTERMSIG(status)) { +#ifdef WCOREDUMP + nxt_alert(task, "app process (isolated %PI) exited on signal %d%s", + pid, WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); +#else + nxt_alert(task, "app process (isolated %PI) exited on signal %d", + pid, WTERMSIG(status)); +#endif + + } else { + nxt_trace(task, "app process (isolated %PI) exited with code %d", + pid, WEXITSTATUS(status)); + } + + process = nxt_proto_process_remove(task, pid); + if (process == NULL) { + continue; + } + + if (process->state != NXT_PROCESS_STATE_CREATING) { + nxt_port_remove_notify_others(task, process); + } + + nxt_process_close_ports(task, process); + + if (nxt_proto_exiting && nxt_queue_is_empty(&nxt_proto_children)) { + nxt_process_quit(task, 0); + return; + } + } } static nxt_app_module_t * nxt_app_module_load(nxt_task_t *task, const char *name) { - void *dl; + char *err; + void *dl; + nxt_app_module_t *app; dl = dlopen(name, RTLD_GLOBAL | RTLD_LAZY); - if (dl != NULL) { - return dlsym(dl, "nxt_app_module"); + if (nxt_slow_path(dl == NULL)) { + err = dlerror(); + nxt_alert(task, "dlopen(\"%s\") failed: \"%s\"", + name, err != NULL ? err : "(null)"); + return NULL; } - nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); + app = dlsym(dl, "nxt_app_module"); - return NULL; + if (nxt_slow_path(app == NULL)) { + err = dlerror(); + nxt_alert(task, "dlsym(\"%s\", \"nxt_app_module\") failed: \"%s\"", + name, err != NULL ? err : "(null)"); + + if (dlclose(dl) != 0) { + err = dlerror(); + nxt_alert(task, "dlclose(\"%s\") failed: \"%s\"", + name, err != NULL ? err : "(null)"); + } + } + + return app; } @@ -602,6 +912,19 @@ nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src) } +static nxt_int_t +nxt_app_setup(nxt_task_t *task, nxt_process_t *process) +{ + nxt_process_init_t *init; + + process->state = NXT_PROCESS_STATE_CREATED; + + init = nxt_process_init(process); + + return init->start(task, &process->data); +} + + nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) { @@ -687,17 +1010,18 @@ nxt_app_parse_type(u_char *p, size_t length) nxt_int_t -nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) +nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init, + nxt_common_app_conf_t *conf) { - nxt_port_t *my_port, *main_port, *router_port; + nxt_port_t *my_port, *proto_port, *router_port; nxt_runtime_t *rt; nxt_memzero(init, sizeof(nxt_unit_init_t)); rt = task->thread->runtime; - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; - if (nxt_slow_path(main_port == NULL)) { + proto_port = rt->port_by_type[NXT_PROCESS_PROTOTYPE]; + if (nxt_slow_path(proto_port == NULL)) { return NXT_ERROR; } @@ -711,10 +1035,10 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) return NXT_ERROR; } - init->ready_port.id.pid = main_port->pid; - init->ready_port.id.id = main_port->id; + init->ready_port.id.pid = proto_port->pid; + init->ready_port.id.id = proto_port->id; init->ready_port.in_fd = -1; - init->ready_port.out_fd = main_port->pair[1]; + init->ready_port.out_fd = proto_port->pair[1]; init->ready_stream = my_port->process->stream; @@ -730,5 +1054,127 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) init->log_fd = 2; + init->shm_limit = conf->shm_limit; + init->request_limit = conf->request_limit; + return NXT_OK; } + + +static nxt_int_t +nxt_proto_lvlhsh_isolated_pid_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_pid_t *qpid; + nxt_process_t *process; + + process = data; + qpid = (nxt_pid_t *) lhq->key.start; + + if (*qpid == process->isolated_pid) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static const nxt_lvlhsh_proto_t lvlhsh_processes_proto nxt_aligned(64) = { + NXT_LVLHSH_DEFAULT, + nxt_proto_lvlhsh_isolated_pid_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + + +nxt_inline void +nxt_proto_process_lhq_pid(nxt_lvlhsh_query_t *lhq, nxt_pid_t *pid) +{ + lhq->key_hash = nxt_murmur_hash2(pid, sizeof(nxt_pid_t)); + lhq->key.length = sizeof(nxt_pid_t); + lhq->key.start = (u_char *) pid; + lhq->proto = &lvlhsh_processes_proto; +} + + +static void +nxt_proto_process_add(nxt_task_t *task, nxt_process_t *process) +{ + nxt_runtime_t *rt; + nxt_lvlhsh_query_t lhq; + + rt = task->thread->runtime; + + nxt_proto_process_lhq_pid(&lhq, &process->isolated_pid); + + lhq.replace = 0; + lhq.value = process; + lhq.pool = rt->mem_pool; + + switch (nxt_lvlhsh_insert(&nxt_proto_processes, &lhq)) { + + case NXT_OK: + nxt_debug(task, "process (isolated %PI) added", process->isolated_pid); + + nxt_queue_insert_tail(&nxt_proto_children, &process->link); + break; + + default: + nxt_debug(task, "process (isolated %PI) failed to add", + process->isolated_pid); + break; + } +} + + +static nxt_process_t * +nxt_proto_process_remove(nxt_task_t *task, nxt_pid_t pid) +{ + nxt_runtime_t *rt; + nxt_process_t *process; + nxt_lvlhsh_query_t lhq; + + nxt_proto_process_lhq_pid(&lhq, &pid); + + rt = task->thread->runtime; + + lhq.pool = rt->mem_pool; + + switch (nxt_lvlhsh_delete(&nxt_proto_processes, &lhq)) { + + case NXT_OK: + nxt_debug(task, "process (isolated %PI) removed", pid); + + process = lhq.value; + + nxt_queue_remove(&process->link); + break; + + default: + nxt_debug(task, "process (isolated %PI) remove failed", pid); + process = NULL; + break; + } + + return process; +} + + +static nxt_process_t * +nxt_proto_process_find(nxt_task_t *task, nxt_pid_t pid) +{ + nxt_process_t *process; + nxt_lvlhsh_query_t lhq; + + nxt_proto_process_lhq_pid(&lhq, &pid); + + if (nxt_lvlhsh_find(&nxt_proto_processes, &lhq) == NXT_OK) { + process = lhq.value; + + } else { + nxt_debug(task, "process (isolated %PI) not found", pid); + + process = NULL; + } + + return process; +} diff --git a/src/nxt_application.h b/src/nxt_application.h index 6fbdc4be..4612f072 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -101,6 +101,7 @@ struct nxt_common_app_conf_s { nxt_conf_value_t *limits; size_t shm_limit; + uint32_t request_limit; union { nxt_external_app_conf_t external; @@ -137,7 +138,7 @@ NXT_EXPORT extern nxt_str_t nxt_server; extern nxt_app_module_t nxt_external_module; NXT_EXPORT nxt_int_t nxt_unit_default_init(nxt_task_t *task, - nxt_unit_init_t *init); + nxt_unit_init_t *init, nxt_common_app_conf_t *conf); #endif /* _NXT_APPLICATION_H_INCLIDED_ */ diff --git a/src/nxt_cert.c b/src/nxt_cert.c index 1806bc19..01d413e0 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -1135,7 +1135,17 @@ nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, msg->port_msg.reply_port); - if (port == NULL) { + if (nxt_slow_path(port == NULL)) { + nxt_alert(task, "process port not found (pid %PI, reply_port %d)", + msg->port_msg.pid, msg->port_msg.reply_port); + return; + } + + if (nxt_slow_path(port->type != NXT_PROCESS_CONTROLLER + && port->type != NXT_PROCESS_ROUTER)) + { + nxt_alert(task, "process %PI cannot store certificates", + msg->port_msg.pid); return; } @@ -1206,10 +1216,23 @@ nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { u_char *p; nxt_str_t name; + nxt_port_t *ctl_port; nxt_runtime_t *rt; nxt_file_name_t *path; rt = task->thread->runtime; + ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; + + if (nxt_slow_path(ctl_port == NULL)) { + nxt_alert(task, "controller port not found"); + return; + } + + if (nxt_slow_path(nxt_recv_msg_cmsg_pid(msg) != ctl_port->pid)) { + nxt_alert(task, "process %PI cannot delete certificates", + nxt_recv_msg_cmsg_pid(msg)); + return; + } if (nxt_slow_path(rt->certs.start == NULL)) { nxt_alert(task, "no certificates storage directory"); diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 149af39a..8b3565fd 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -72,6 +72,8 @@ typedef struct { nxt_mp_t *pool; nxt_str_t error; void *ctx; + 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 a53fff74..3f068bbb 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -33,7 +33,8 @@ typedef enum { typedef enum { - NXT_CONF_VLDT_REQUIRED = 1, + NXT_CONF_VLDT_REQUIRED = 1 << 0, + NXT_CONF_VLDT_VAR = 1 << 1, } nxt_conf_vldt_flags_t; @@ -73,8 +74,8 @@ static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type); static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...); -static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, - const char *option, nxt_str_t *value); +static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, + nxt_str_t *value); nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -112,6 +113,10 @@ static nxt_int_t nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_share(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt, @@ -354,6 +359,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, }, { .name = nxt_string("application"), .type = NXT_CONF_VLDT_STRING, @@ -511,8 +517,8 @@ static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { + ssize_t ret; nxt_str_t key; - nxt_int_t ret; if (nxt_conf_type(value) != NXT_CONF_STRING) { return nxt_conf_vldt_error(vldt, "The \"key\" array must " @@ -521,12 +527,8 @@ nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, nxt_conf_get_string(value, &key); - ret = nxt_openssl_base64_decode(NULL, 0, key.start, key.length); - if (nxt_slow_path(ret == NXT_ERROR)) { - return NXT_ERROR; - } - - if (ret == NXT_DECLINED) { + ret = nxt_base64_decode(NULL, key.start, key.length); + if (ret == NXT_ERROR) { return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket " "key \"%V\".", &key); } @@ -564,6 +566,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { .name = nxt_string("method"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_patterns, + .u.string = "method", }, { .name = nxt_string("scheme"), .type = NXT_CONF_VLDT_STRING, @@ -572,6 +575,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { .name = nxt_string("host"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_patterns, + .u.string = "host", }, { .name = nxt_string("source"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, @@ -584,6 +588,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { .name = nxt_string("uri"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_encoded_patterns, + .u.string = "uri" + }, { + .name = nxt_string("query"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_match_encoded_patterns, + .u.string = "query" }, { .name = nxt_string("arguments"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, @@ -592,10 +602,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { .name = nxt_string("headers"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_patterns_sets, + .u.string = "headers" }, { .name = nxt_string("cookies"), .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_patterns_sets, + .u.string = "cookies" }, NXT_CONF_VLDT_END @@ -607,6 +619,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, }, NXT_CONF_VLDT_END @@ -630,7 +643,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = { static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { { .name = nxt_string("share"), - .type = NXT_CONF_VLDT_STRING, + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_share, }, { .name = nxt_string("types"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, @@ -646,6 +660,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, }, { .name = nxt_string("follow_symlinks"), .type = NXT_CONF_VLDT_BOOLEAN, @@ -1163,7 +1178,6 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) nxt_int_t ret; ret = nxt_conf_vldt_type(vldt, NULL, vldt->conf, NXT_CONF_VLDT_OBJECT); - if (ret != NXT_OK) { return ret; } @@ -1290,14 +1304,14 @@ nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, static nxt_int_t -nxt_conf_vldt_var(nxt_conf_validation_t *vldt, const char *option, +nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t *value) { u_char error[NXT_MAX_ERROR_STR]; if (nxt_var_test(value, error) != NXT_OK) { - return nxt_conf_vldt_error(vldt, "%s in the \"%s\" value.", - error, option); + return nxt_conf_vldt_error(vldt, "%s in the \"%V\" value.", + error, name); } return NXT_OK; @@ -1488,10 +1502,6 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, nxt_conf_get_string(value, &pass); - if (nxt_is_var(&pass)) { - return nxt_conf_vldt_var(vldt, "pass", &pass); - } - ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 3); if (ret != NXT_OK) { @@ -1617,6 +1627,70 @@ nxt_conf_vldt_return(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, 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 " + "must contain at least one element."); + } + + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_share_element); + } + + /* 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); +} + + +static nxt_int_t +nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + nxt_str_t str; + + static nxt_str_t share = nxt_string("share"); + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"share\" array must " + "contain only string values."); + } + + nxt_conf_get_string(value, &str); + + if (nxt_is_var(&str)) { + return nxt_conf_vldt_var(vldt, &share, &str); + } + + return NXT_OK; +} + + +static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { @@ -1733,14 +1807,15 @@ static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - int64_t size; + int64_t size, min_size; size = nxt_conf_get_number(value); + min_size = sysconf(_SC_THREAD_STACK_MIN); - if (size < NXT_THREAD_STACK_MIN) { + if (size < min_size) { return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number " "must be equal to or greater than %d.", - NXT_THREAD_STACK_MIN); + min_size); } if ((size % nxt_pagesize) != 0) { @@ -1801,14 +1876,22 @@ static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { + nxt_int_t ret; + + vldt->ctx = data; + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { - return nxt_conf_vldt_array_iterator(vldt, value, - &nxt_conf_vldt_match_pattern); + ret = nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_pattern); + + } else { + /* NXT_CONF_STRING */ + ret = nxt_conf_vldt_match_pattern(vldt, value); } - /* NXT_CONF_STRING */ + vldt->ctx = NULL; - return nxt_conf_vldt_match_pattern(vldt, value); + return ret; } @@ -1824,8 +1907,8 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, #endif if (nxt_conf_type(value) != NXT_CONF_STRING) { - return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", " - "\"uri\", and \"method\" must be strings."); + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" " + "must be strings.", vldt->ctx); } nxt_conf_get_string(value, &pattern); @@ -1882,7 +1965,7 @@ static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets( &nxt_conf_vldt_match_encoded_patterns_set); } - /* NXT_CONF_STRING */ + /* NXT_CONF_OBJECT */ return nxt_conf_vldt_match_encoded_patterns_set(vldt, value); } @@ -1923,7 +2006,8 @@ nxt_conf_vldt_match_encoded_patterns_set_member(nxt_conf_validation_t *vldt, "\"arguments\" is encoded but is invalid."); } - return nxt_conf_vldt_match_encoded_patterns(vldt, value, NULL); + return nxt_conf_vldt_match_encoded_patterns(vldt, value, + (void *) "arguments"); } @@ -1931,14 +2015,22 @@ static nxt_int_t nxt_conf_vldt_match_encoded_patterns(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { + nxt_int_t ret; + + vldt->ctx = data; + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { - return nxt_conf_vldt_array_iterator(vldt, value, + ret = nxt_conf_vldt_array_iterator(vldt, value, &nxt_conf_vldt_match_encoded_pattern); + + } else { + /* NXT_CONF_STRING */ + ret = nxt_conf_vldt_match_encoded_pattern(vldt, value); } - /* NXT_CONF_STRING */ + vldt->ctx = NULL; - return nxt_conf_vldt_match_encoded_pattern(vldt, value); + return ret; } @@ -1951,8 +2043,8 @@ nxt_conf_vldt_match_encoded_pattern(nxt_conf_validation_t *vldt, nxt_str_t pattern; if (nxt_conf_type(value) != NXT_CONF_STRING) { - return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" " - "must be a string."); + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" " + "must be a string.", vldt->ctx); } ret = nxt_conf_vldt_match_pattern(vldt, value); @@ -1969,8 +2061,8 @@ nxt_conf_vldt_match_encoded_pattern(nxt_conf_validation_t *vldt, end = nxt_decode_uri(p, pattern.start, pattern.length); if (nxt_slow_path(end == NULL)) { - return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" " - "is encoded but is invalid."); + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" " + "is encoded but is invalid.", vldt->ctx); } return NXT_OK; @@ -2060,14 +2152,22 @@ static nxt_int_t nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { + nxt_int_t ret; + + vldt->ctx = data; + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { - return nxt_conf_vldt_array_iterator(vldt, value, - &nxt_conf_vldt_match_patterns_set); + ret = nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_patterns_set); + + } else { + /* NXT_CONF_OBJECT */ + ret = nxt_conf_vldt_match_patterns_set(vldt, value); } - /* NXT_CONF_OBJECT */ + vldt->ctx = NULL; - return nxt_conf_vldt_match_patterns_set(vldt, value); + return ret; } @@ -2077,8 +2177,7 @@ nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt, { if (nxt_conf_type(value) != NXT_CONF_OBJECT) { return nxt_conf_vldt_error(vldt, "The \"match\" patterns for " - "\"arguments\", \"cookies\", and " - "\"headers\" must be objects."); + "\"%s\" must be objects.", vldt->ctx); } return nxt_conf_vldt_object_iterator(vldt, value, @@ -2095,7 +2194,7 @@ nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt, "not contain empty member names."); } - return nxt_conf_vldt_match_patterns(vldt, value, NULL); + return nxt_conf_vldt_match_patterns(vldt, value, vldt->ctx); } @@ -2279,7 +2378,7 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, { uint32_t index; nxt_int_t ret; - nxt_str_t name; + nxt_str_t name, var; nxt_conf_value_t *member; nxt_conf_vldt_object_t *vals; @@ -2336,8 +2435,22 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, continue; } - ret = nxt_conf_vldt_type(vldt, &name, member, vals->type); + if (vals->flags & NXT_CONF_VLDT_VAR + && nxt_conf_type(member) == NXT_CONF_STRING) + { + nxt_conf_get_string(member, &var); + + if (nxt_is_var(&var)) { + ret = nxt_conf_vldt_var(vldt, &name, &var); + if (ret != NXT_OK) { + return ret; + } + break; + } + } + + ret = nxt_conf_vldt_type(vldt, &name, member, vals->type); if (ret != NXT_OK) { return ret; } diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 779a625d..7510d6f0 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -41,6 +41,8 @@ typedef struct { static nxt_int_t nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp); +static nxt_int_t nxt_controller_file_read(nxt_task_t *task, const char *name, + nxt_str_t *str, nxt_mp_t *mp); static nxt_int_t nxt_controller_start(nxt_task_t *task, nxt_process_data_t *data); static void nxt_controller_process_new_port_handler(nxt_task_t *task, @@ -154,12 +156,9 @@ const nxt_process_init_t nxt_controller_process = { static nxt_int_t nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) { - ssize_t n; - nxt_int_t ret; - nxt_str_t *conf; - nxt_file_t file; + nxt_str_t ver; + nxt_int_t ret, num; nxt_runtime_t *rt; - nxt_file_info_t fi; nxt_controller_init_t ctrl_init; nxt_log(task, NXT_LOG_INFO, "controller started"); @@ -168,48 +167,98 @@ nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t)); - conf = &ctrl_init.conf; + /* + * Since configuration version has only been introduced in 1.26, + * set the default version to 1.25. + */ + nxt_conf_ver = 12500; + + ret = nxt_controller_file_read(task, rt->conf, &ctrl_init.conf, mp); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == NXT_OK) { + ret = nxt_controller_file_read(task, rt->ver, &ver, mp); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == NXT_OK) { + num = nxt_int_parse(ver.start, ver.length); + + if (nxt_slow_path(num < 0)) { + nxt_alert(task, "failed to restore previous configuration: " + "invalid version string \"%V\"", &ver); + + nxt_str_null(&ctrl_init.conf); + + } else { + nxt_conf_ver = num; + } + } + } + +#if (NXT_TLS) + ctrl_init.certs = nxt_cert_store_load(task, mp); + + nxt_mp_cleanup(mp, nxt_controller_cert_cleanup, task, ctrl_init.certs, rt); +#endif + + process->data.controller = ctrl_init; + + return NXT_OK; +} + + +static nxt_int_t +nxt_controller_file_read(nxt_task_t *task, const char *name, nxt_str_t *str, + nxt_mp_t *mp) +{ + ssize_t n; + nxt_int_t ret; + nxt_file_t file; + nxt_file_info_t fi; nxt_memzero(&file, sizeof(nxt_file_t)); - file.name = (nxt_file_name_t *) rt->conf; + file.name = (nxt_file_name_t *) name; ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); if (ret == NXT_OK) { ret = nxt_file_info(&file, &fi); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } - if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) { - conf->length = nxt_file_size(&fi); - conf->start = nxt_mp_alloc(mp, conf->length); - if (nxt_slow_path(conf->start == NULL)) { - nxt_file_close(task, &file); - return NXT_ERROR; + if (nxt_fast_path(nxt_is_file(&fi))) { + str->length = nxt_file_size(&fi); + str->start = nxt_mp_nget(mp, str->length); + if (nxt_slow_path(str->start == NULL)) { + goto fail; } - n = nxt_file_read(&file, conf->start, conf->length, 0); + n = nxt_file_read(&file, str->start, str->length, 0); + if (nxt_slow_path(n != (ssize_t) str->length)) { + goto fail; + } - if (nxt_slow_path(n != (ssize_t) conf->length)) { - conf->start = NULL; - conf->length = 0; + nxt_file_close(task, &file); - nxt_alert(task, "failed to restore previous configuration: " - "cannot read the file"); - } + return NXT_OK; } nxt_file_close(task, &file); } -#if (NXT_TLS) - ctrl_init.certs = nxt_cert_store_load(task, mp); + return NXT_DECLINED; - nxt_mp_cleanup(mp, nxt_controller_cert_cleanup, task, ctrl_init.certs, rt); -#endif +fail: - process->data.controller = ctrl_init; + nxt_file_close(task, &file); - return NXT_OK; + return NXT_ERROR; } @@ -293,6 +342,8 @@ nxt_controller_start(nxt_task_t *task, nxt_process_data_t *data) } vldt.conf = conf; + vldt.conf_pool = mp; + vldt.ver = nxt_conf_ver; ret = nxt_conf_validate(&vldt); @@ -1224,6 +1275,8 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, vldt.conf = value; vldt.pool = c->mem_pool; + vldt.conf_pool = mp; + vldt.ver = NXT_VERNUM; rc = nxt_conf_validate(&vldt); @@ -1305,6 +1358,8 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, vldt.conf = value; vldt.pool = c->mem_pool; + vldt.conf_pool = mp; + vldt.ver = NXT_VERNUM; rc = nxt_conf_validate(&vldt); diff --git a/src/nxt_external.c b/src/nxt_external.c index 5703e294..b41ca51b 100644 --- a/src/nxt_external.c +++ b/src/nxt_external.c @@ -67,7 +67,7 @@ nxt_external_start(nxt_task_t *task, nxt_process_data_t *data) nxt_str_t str; nxt_int_t rc; nxt_uint_t i, argc; - nxt_port_t *my_port, *main_port, *router_port; + nxt_port_t *my_port, *proto_port, *router_port; nxt_runtime_t *rt; nxt_conf_value_t *value; nxt_common_app_conf_t *conf; @@ -76,17 +76,17 @@ nxt_external_start(nxt_task_t *task, nxt_process_data_t *data) rt = task->thread->runtime; conf = data->app; - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + proto_port = rt->port_by_type[NXT_PROCESS_PROTOTYPE]; router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; my_port = nxt_runtime_port_find(rt, nxt_pid, 0); - if (nxt_slow_path(main_port == NULL || my_port == NULL + if (nxt_slow_path(proto_port == NULL || my_port == NULL || router_port == NULL)) { return NXT_ERROR; } - rc = nxt_external_fd_no_cloexec(task, main_port->pair[1]); + rc = nxt_external_fd_no_cloexec(task, proto_port->pair[1]); if (nxt_slow_path(rc != NXT_OK)) { return NXT_ERROR; } @@ -113,13 +113,13 @@ nxt_external_start(nxt_task_t *task, nxt_process_data_t *data) "%PI,%ud,%d;" "%PI,%ud,%d;" "%PI,%ud,%d,%d;" - "%d,%z,%Z", + "%d,%z,%uD,%Z", NXT_VERSION, my_port->process->stream, - main_port->pid, main_port->id, main_port->pair[1], + proto_port->pid, proto_port->id, proto_port->pair[1], router_port->pid, router_port->id, router_port->pair[1], my_port->pid, my_port->id, my_port->pair[0], my_port->pair[1], - 2, conf->shm_limit); + 2, conf->shm_limit, conf->request_limit); if (nxt_slow_path(p == end)) { nxt_alert(task, "internal error: buffer too small for NXT_UNIT_INIT"); diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index b683cb22..b683cb22 100755..100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c diff --git a/src/nxt_h1proto_websocket.c b/src/nxt_h1proto_websocket.c index 42a50a34..7be190f6 100644 --- a/src/nxt_h1proto_websocket.c +++ b/src/nxt_h1proto_websocket.c @@ -73,10 +73,10 @@ void nxt_h1p_websocket_first_frame_start(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *ws_frame) { - nxt_conn_t *c; - nxt_timer_t *timer; - nxt_h1proto_t *h1p; - nxt_socket_conf_joint_t *joint; + nxt_conn_t *c; + nxt_timer_t *timer; + nxt_h1proto_t *h1p; + nxt_websocket_conf_t *websocket_conf; nxt_debug(task, "h1p ws first frame start"); @@ -87,11 +87,9 @@ nxt_h1p_websocket_first_frame_start(nxt_task_t *task, nxt_http_request_t *r, nxt_conn_tcp_nodelay_on(task, c); } - joint = c->listen->socket.data; + websocket_conf = &r->conf->socket_conf->websocket_conf; - if (nxt_slow_path(joint != NULL - && joint->socket_conf->websocket_conf.keepalive_interval != 0)) - { + if (nxt_slow_path(websocket_conf->keepalive_interval != 0)) { h1p->websocket_timer = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1p_websocket_timer_t)); if (nxt_slow_path(h1p->websocket_timer == NULL)) { @@ -100,7 +98,7 @@ nxt_h1p_websocket_first_frame_start(nxt_task_t *task, nxt_http_request_t *r, } h1p->websocket_timer->keepalive_interval = - joint->socket_conf->websocket_conf.keepalive_interval; + websocket_conf->keepalive_interval; h1p->websocket_timer->h1p = h1p; timer = &h1p->websocket_timer->timer; @@ -218,14 +216,13 @@ static const nxt_conn_state_t nxt_h1p_read_ws_frame_header_state static void nxt_h1p_conn_ws_frame_header_read(nxt_task_t *task, void *obj, void *data) { - size_t size, hsize, frame_size, max_frame_size; - uint64_t payload_len; - nxt_conn_t *c; - nxt_h1proto_t *h1p; - nxt_http_request_t *r; - nxt_event_engine_t *engine; - nxt_websocket_header_t *wsh; - nxt_socket_conf_joint_t *joint; + size_t size, hsize, frame_size, max_frame_size; + uint64_t payload_len; + nxt_conn_t *c; + nxt_h1proto_t *h1p; + nxt_http_request_t *r; + nxt_event_engine_t *engine; + nxt_websocket_header_t *wsh; c = obj; h1p = data; @@ -265,17 +262,6 @@ nxt_h1p_conn_ws_frame_header_read(nxt_task_t *task, void *obj, void *data) r->ws_frame = c->read; - joint = c->listen->socket.data; - - if (nxt_slow_path(joint == NULL)) { - /* - * Listening socket had been closed while - * connection was in keep-alive state. - */ - c->read_state = &nxt_h1p_idle_close_state; - return; - } - if (nxt_slow_path(wsh->mask == 0)) { hxt_h1p_send_ws_error(task, r, &nxt_ws_err_not_masked); return; @@ -330,7 +316,7 @@ nxt_h1p_conn_ws_frame_header_read(nxt_task_t *task, void *obj, void *data) h1p->websocket_cont_expected = !wsh->fin; } - max_frame_size = joint->socket_conf->websocket_conf.max_frame_size; + max_frame_size = r->conf->socket_conf->websocket_conf.max_frame_size; payload_len = nxt_websocket_frame_payload_len(wsh); diff --git a/src/nxt_http.h b/src/nxt_http.h index 3bc2fd61..02d66f58 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -148,6 +148,7 @@ struct nxt_http_request_s { nxt_str_t *path; nxt_str_t *args; + nxt_str_t args_decoded; nxt_array_t *arguments; /* of nxt_http_name_value_t */ nxt_array_t *cookies; /* of nxt_http_name_value_t */ nxt_list_t *fields; @@ -226,9 +227,9 @@ struct nxt_http_action_s { nxt_upstream_t *upstream; uint32_t upstream_number; nxt_var_t *var; + nxt_str_t *pass; } u; - nxt_str_t name; nxt_http_action_t *fallback; }; @@ -313,7 +314,7 @@ nxt_int_t nxt_http_request_content_length(void *ctx, nxt_http_field_t *field, nxt_http_routes_t *nxt_http_routes_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *routes_conf); nxt_http_action_t *nxt_http_action_create(nxt_task_t *task, - nxt_router_temp_conf_t *tmcf, nxt_str_t *name); + nxt_router_temp_conf_t *tmcf, nxt_str_t *pass); nxt_int_t nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf); nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index b71b25d9..ac614df6 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -460,8 +460,6 @@ nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, if (nxt_fast_path(action != NULL)) { do { - nxt_debug(task, "http request route: %V", &action->name); - action = action->handler(task, r, action); if (action == NULL) { diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index cff69f96..606bf266 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -19,6 +19,7 @@ typedef enum { NXT_HTTP_ROUTE_ARGUMENT, NXT_HTTP_ROUTE_COOKIE, NXT_HTTP_ROUTE_SCHEME, + NXT_HTTP_ROUTE_QUERY, NXT_HTTP_ROUTE_SOURCE, NXT_HTTP_ROUTE_DESTINATION, } nxt_http_route_object_t; @@ -54,6 +55,7 @@ typedef struct { nxt_conf_value_t *arguments; nxt_conf_value_t *cookies; nxt_conf_value_t *scheme; + nxt_conf_value_t *query; nxt_conf_value_t *source; nxt_conf_value_t *destination; } nxt_http_route_match_conf_t; @@ -216,14 +218,12 @@ static nxt_int_t nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route); 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_action_pass_var(nxt_task_t *task, +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_action_pass_var_ready(nxt_task_t *task, void *obj, - void *data); -static void nxt_http_action_pass_var_error(nxt_task_t *task, void *obj, - void *data); -static nxt_int_t nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, - nxt_router_conf_t *rtcf, 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 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, nxt_http_action_t *action); @@ -249,6 +249,8 @@ static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array); static nxt_int_t nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule); +static nxt_int_t nxt_http_route_query(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule); static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r); @@ -368,6 +370,12 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = { }, { + nxt_string("query"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, query), + }, + + { nxt_string("source"), NXT_CONF_MAP_PTR, offsetof(nxt_http_route_match_conf_t, source), @@ -566,6 +574,19 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, test++; } + if (mtcf.query != NULL) { + rule = nxt_http_route_rule_create(task, mp, mtcf.query, 1, + NXT_HTTP_ROUTE_PATTERN_NOCASE, + NXT_HTTP_ROUTE_ENCODING_URI_PLUS); + if (rule == NULL) { + return NULL; + } + + rule->object = NXT_HTTP_ROUTE_QUERY; + test->rule = rule; + test++; + } + if (mtcf.source != NULL) { addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.source); if (addr_rule == NULL) { @@ -652,7 +673,7 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, { nxt_mp_t *mp; nxt_int_t ret; - nxt_str_t name, *string; + nxt_str_t pass; nxt_http_action_conf_t acf; nxt_memzero(&acf, sizeof(acf)); @@ -679,10 +700,10 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return nxt_http_proxy_init(mp, action, &acf); } - nxt_conf_get_string(acf.pass, &name); + nxt_conf_get_string(acf.pass, &pass); - string = nxt_str_dup(mp, &action->name, &name); - if (nxt_slow_path(string == NULL)) { + action->u.var = nxt_var_compile(&pass, mp, 0); + if (nxt_slow_path(action->u.var == NULL)) { return NXT_ERROR; } @@ -1372,7 +1393,7 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { nxt_int_t ret; - nxt_var_t *var; + nxt_str_t pass; if (action->handler != NULL) { if (action->fallback != NULL) { @@ -1382,20 +1403,17 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_OK; } - if (nxt_is_var(&action->name)) { - var = nxt_var_compile(&action->name, tmcf->router_conf->mem_pool); - if (nxt_slow_path(var == NULL)) { + if (nxt_var_is_const(action->u.var)) { + nxt_var_raw(action->u.var, &pass); + + ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass, + action); + if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } - action->u.var = var; - action->handler = nxt_http_action_pass_var; - return NXT_OK; - } - - ret = nxt_http_pass_find(task, tmcf->mem_pool, tmcf->router_conf, action); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; + } else { + action->handler = nxt_http_pass_var; } return NXT_OK; @@ -1403,28 +1421,36 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_http_action_t * -nxt_http_action_pass_var(nxt_task_t *task, nxt_http_request_t *r, +nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - nxt_var_t *var; nxt_int_t ret; + nxt_str_t str; + nxt_var_t *var; + + var = action->u.var; + + nxt_var_raw(var, &str); + + nxt_debug(task, "http pass: \"%V\"", &str); ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } - var = action->u.var; - - action = nxt_mp_get(r->mem_pool, sizeof(nxt_http_action_t)); + action = nxt_mp_get(r->mem_pool, + sizeof(nxt_http_action_t) + sizeof(nxt_str_t)); if (nxt_slow_path(action == NULL)) { goto fail; } - nxt_var_query(task, r->var_query, var, &action->name); + 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_action_pass_var_ready, - nxt_http_action_pass_var_error); + nxt_http_pass_var_ready, + nxt_http_pass_var_error); return NULL; fail: @@ -1435,7 +1461,7 @@ fail: static void -nxt_http_action_pass_var_ready(nxt_task_t *task, void *obj, void *data) +nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data) { nxt_int_t ret; nxt_router_conf_t *rtcf; @@ -1447,9 +1473,9 @@ nxt_http_action_pass_var_ready(nxt_task_t *task, void *obj, void *data) action = data; rtcf = r->conf->socket_conf->router_conf; - nxt_debug(task, "http pass lookup: %V", &action->name); + nxt_debug(task, "http pass lookup: %V", action->u.pass); - ret = nxt_http_pass_find(task, r->mem_pool, rtcf, action); + ret = nxt_http_pass_find(r->mem_pool, rtcf, action->u.pass, action); if (ret != NXT_OK) { status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND @@ -1464,7 +1490,7 @@ nxt_http_action_pass_var_ready(nxt_task_t *task, void *obj, void *data) static void -nxt_http_action_pass_var_error(nxt_task_t *task, void *obj, void *data) +nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data) { nxt_http_request_t *r; @@ -1475,13 +1501,13 @@ nxt_http_action_pass_var_error(nxt_task_t *task, void *obj, void *data) static nxt_int_t -nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, +nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass, nxt_http_action_t *action) { - nxt_int_t ret; - nxt_str_t segments[3]; + nxt_int_t ret; + nxt_str_t segments[3]; - ret = nxt_http_pass_segments(mp, &action->name, segments, 3); + ret = nxt_http_pass_segments(mp, pass, segments, 3); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -1587,18 +1613,24 @@ nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name, nxt_http_action_t * nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, - nxt_str_t *name) + nxt_str_t *pass) { + nxt_mp_t *mp; nxt_int_t ret; nxt_http_action_t *action; - action = nxt_mp_alloc(tmcf->router_conf->mem_pool, - sizeof(nxt_http_action_t)); + mp = tmcf->router_conf->mem_pool; + + action = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); if (nxt_slow_path(action == NULL)) { return NULL; } - action->name = *name; + action->u.var = nxt_var_compile(pass, mp, 0); + if (nxt_slow_path(action->u.var == NULL)) { + return NULL; + } + action->handler = NULL; ret = nxt_http_action_resolve(task, tmcf, action); @@ -1623,8 +1655,6 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, return NULL; } - action->name = *name; - (void) nxt_router_application_init(rtcf, name, NULL, action); return action; @@ -1769,6 +1799,9 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule) case NXT_HTTP_ROUTE_SCHEME: return nxt_http_route_scheme(r, rule); + case NXT_HTTP_ROUTE_QUERY: + return nxt_http_route_query(r, rule); + default: break; } @@ -2001,10 +2034,6 @@ nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule) { nxt_array_t *arguments; - if (r->args == NULL) { - return 0; - } - arguments = nxt_http_route_arguments_parse(r); if (nxt_slow_path(arguments == NULL)) { return -1; @@ -2018,10 +2047,9 @@ static nxt_array_t * nxt_http_route_arguments_parse(nxt_http_request_t *r) { size_t name_length; - u_char c, *p, *dst, *dst_start, *start, *end, *name; + u_char *p, *dst, *dst_start, *start, *end, *name; uint8_t d0, d1; uint32_t hash; - nxt_bool_t valid; nxt_array_t *args; nxt_http_name_value_t *nv; @@ -2035,7 +2063,6 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r) } hash = NXT_HTTP_FIELD_HASH_INIT; - valid = 1; name = NULL; name_length = 0; @@ -2044,28 +2071,26 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r) return NULL; } + r->args_decoded.start = dst_start; + start = r->args->start; end = start + r->args->length; for (p = start, dst = dst_start; p < end; p++, dst++) { - c = *p; - *dst = c; + *dst = *p; - switch (c) { + switch (*p) { case '=': - if (name != NULL) { - break; + if (name == NULL) { + name_length = dst - dst_start; + name = dst_start; + dst_start = dst + 1; } - name_length = dst - dst_start; - valid = (name_length != 0); - name = dst_start; - dst_start = dst + 1; - continue; case '&': - if (valid) { + if (name_length != 0 || dst != dst_start) { nv = nxt_http_route_argument(args, name, name_length, hash, dst_start, dst); if (nxt_slow_path(nv == NULL)) { @@ -2075,14 +2100,12 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r) hash = NXT_HTTP_FIELD_HASH_INIT; name_length = 0; - valid = 1; name = NULL; dst_start = dst + 1; continue; case '+': - c = ' '; *dst = ' '; break; @@ -2100,18 +2123,19 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r) } p += 2; - c = (d0 << 4) + d1; - *dst = c; + *dst = (d0 << 4) + d1; break; } if (name == NULL) { - hash = nxt_http_field_hash_char(hash, c); + hash = nxt_http_field_hash_char(hash, *dst); } } - if (valid) { + r->args_decoded.length = dst - r->args_decoded.start; + + if (name_length != 0 || dst != dst_start) { nv = nxt_http_route_argument(args, name, name_length, hash, dst_start, dst); if (nxt_slow_path(nv == NULL)) { @@ -2207,6 +2231,21 @@ nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule) static nxt_int_t +nxt_http_route_query(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_array_t *arguments; + + arguments = nxt_http_route_arguments_parse(r); + if (nxt_slow_path(arguments == NULL)) { + return -1; + } + + return nxt_http_route_test_rule(r, rule, r->args_decoded.start, + r->args_decoded.length); +} + + +static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule) { nxt_array_t *cookies; diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 9b79a666..36c1ebc9 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -8,19 +8,51 @@ typedef struct { - nxt_str_t share; - nxt_str_t chroot; - nxt_uint_t resolve; - nxt_http_route_rule_t *types; + nxt_var_t *var; +#if (NXT_HAVE_OPENAT2) + u_char *fname; +#endif + uint8_t is_const; /* 1 bit */ +} nxt_http_static_share_t; + + +typedef struct { + nxt_uint_t nshares; + nxt_http_static_share_t *shares; +#if (NXT_HAVE_OPENAT2) + nxt_var_t *chroot; + nxt_uint_t resolve; +#endif + nxt_http_route_rule_t *types; } nxt_http_static_conf_t; +typedef struct { + nxt_http_action_t *action; + nxt_str_t share; +#if (NXT_HAVE_OPENAT2) + nxt_str_t chroot; +#endif + uint32_t index; + uint8_t need_body; /* 1 bit */ +} nxt_http_static_ctx_t; + + #define NXT_HTTP_STATIC_BUF_COUNT 2 #define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024) static nxt_http_action_t *nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); +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_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) +static u_char *nxt_http_static_chroot_match(u_char *chr, u_char *shr); +#endif static void nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten); static void nxt_http_static_body_handler(nxt_task_t *task, void *obj, @@ -41,8 +73,12 @@ nxt_int_t nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action, nxt_http_action_conf_t *acf) { + uint32_t i; nxt_mp_t *mp; - nxt_str_t *str, value; + nxt_str_t str; + nxt_var_t *var; + nxt_bool_t array; + nxt_conf_value_t *cv; nxt_http_static_conf_t *conf; mp = tmcf->router_conf->mem_pool; @@ -55,39 +91,64 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, action->handler = nxt_http_static; action->u.conf = conf; - nxt_conf_get_string(acf->share, &value); + array = (nxt_conf_type(acf->share) == NXT_CONF_ARRAY); + conf->nshares = array ? nxt_conf_array_elements_count(acf->share) : 1; - str = nxt_str_dup(mp, &conf->share, &value); - if (nxt_slow_path(str == NULL)) { + conf->shares = nxt_mp_zget(mp, sizeof(nxt_http_static_share_t) + * conf->nshares); + if (nxt_slow_path(conf->shares == NULL)) { return NXT_ERROR; } -#if (NXT_HAVE_OPENAT2) - if (acf->chroot.length > 0) { - u_char *p; - nxt_str_t slash; + if (array) { + for (i = 0; i < conf->nshares; i++) { + cv = nxt_conf_get_array_element(acf->share, i); + nxt_conf_get_string(cv, &str); - if (acf->chroot.start[acf->chroot.length - 1] != '/') { - nxt_str_set(&slash, "/"); + var = nxt_var_compile(&str, mp, 1); + if (nxt_slow_path(var == NULL)) { + return NXT_ERROR; + } - } else { - nxt_str_set(&slash, ""); + conf->shares[i].var = var; + conf->shares[i].is_const = nxt_var_is_const(var); } - value.length = acf->chroot.length + slash.length; + } else { + nxt_conf_get_string(acf->share, &str); - value.start = nxt_mp_alloc(mp, value.length + 1); - if (nxt_slow_path(value.start == NULL)) { + var = nxt_var_compile(&str, mp, 1); + if (nxt_slow_path(var == NULL)) { return NXT_ERROR; } - p = value.start; - p = nxt_cpymem(p, acf->chroot.start, acf->chroot.length); - p = nxt_cpymem(p, slash.start, slash.length); - *p = '\0'; + conf->shares[0].var = var; + conf->shares[0].is_const = nxt_var_is_const(var); + } + +#if (NXT_HAVE_OPENAT2) + if (acf->chroot.length > 0) { + nxt_str_t chr, shr; + nxt_bool_t is_const; + + conf->chroot = nxt_var_compile(&acf->chroot, mp, 1); + if (nxt_slow_path(conf->chroot == NULL)) { + return NXT_ERROR; + } + + is_const = nxt_var_is_const(conf->chroot); - conf->chroot = value; - conf->resolve |= RESOLVE_IN_ROOT; + 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); + + conf->shares[i].fname = nxt_http_static_chroot_match(chr.start, + shr.start); + } + } } if (acf->follow_symlinks != NULL @@ -128,25 +189,8 @@ static nxt_http_action_t * nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - size_t length, encode; - u_char *p, *fname; - struct tm tm; - nxt_buf_t *fb; - nxt_int_t ret; - nxt_str_t index, exten, *mtype, *chroot; - nxt_uint_t level; - nxt_bool_t need_body; - nxt_file_t *f, file; - nxt_file_info_t fi; - nxt_http_field_t *field; - nxt_http_status_t status; - nxt_router_conf_t *rtcf; - nxt_work_handler_t body_handler; - nxt_http_static_conf_t *conf; - - conf = action->u.conf; - - nxt_debug(task, "http static: \"%V\"", &conf->share); + nxt_bool_t need_body; + nxt_http_static_ctx_t *ctx; if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) { @@ -165,71 +209,184 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, need_body = 1; } - if (r->path->start[r->path->length - 1] == '/') { - /* TODO: dynamic index setting. */ - nxt_str_set(&index, "index.html"); - nxt_str_set(&exten, ".html"); + ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_static_ctx_t)); + if (nxt_slow_path(ctx == NULL)) { + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + ctx->action = action; + ctx->need_body = need_body; + + nxt_http_static_iterate(task, r, ctx); + + return NULL; +} + + +static void +nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, + nxt_http_static_ctx_t *ctx) +{ + nxt_int_t ret; + nxt_http_static_conf_t *conf; + nxt_http_static_share_t *share; + + conf = ctx->action->u.conf; + + share = &conf->shares[ctx->index]; + +#if (NXT_DEBUG) + nxt_str_t shr; + + nxt_var_raw(share->var, &shr); + +#if (NXT_HAVE_OPENAT2) + nxt_str_t chr; + + if (conf->chroot != NULL) { + nxt_var_raw(conf->chroot, &chr); } else { - nxt_str_set(&index, ""); - nxt_str_null(&exten); + nxt_str_set(&chr, ""); } - f = NULL; + nxt_debug(task, "http static: \"%V\" (chroot: \"%V\")", &shr, &chr); +#else + nxt_debug(task, "http static: \"%V\"", &shr); +#endif +#endif /* NXT_DEBUG */ + + if (share->is_const) { + nxt_var_raw(share->var, &ctx->share); + +#if (NXT_HAVE_OPENAT2) + if (conf->chroot != NULL && ctx->index == 0) { + nxt_var_raw(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); + 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); + +#if (NXT_HAVE_OPENAT2) + if (conf->chroot != NULL && ctx->index == 0) { + nxt_var_query(task, r->var_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); + } +} + + +static void +nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data) +{ + size_t length, encode; + u_char *p, *fname; + struct tm tm; + nxt_buf_t *fb; + nxt_int_t ret; + nxt_str_t *shr, exten, *mtype; + nxt_uint_t level; + nxt_file_t *f, file; + nxt_file_info_t fi; + nxt_http_field_t *field; + nxt_http_status_t status; + nxt_router_conf_t *rtcf; + nxt_http_action_t *action; + nxt_http_request_t *r; + nxt_work_handler_t body_handler; + nxt_http_static_ctx_t *ctx; + nxt_http_static_conf_t *conf; + + static const nxt_str_t index = nxt_string("index.html"); + + r = obj; + ctx = data; + action = ctx->action; + conf = action->u.conf; rtcf = r->conf->socket_conf->router_conf; + f = NULL; mtype = NULL; - if (conf->types != NULL && exten.start == NULL) { - nxt_http_static_extract_extension(r->path, &exten); - mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); + shr = &ctx->share; + + if (shr->start[shr->length - 1] == '/') { + /* TODO: dynamic index setting. */ + nxt_str_set(&exten, ".html"); - ret = nxt_http_route_test_rule(r, conf->types, mtype->start, - mtype->length); - if (nxt_slow_path(ret == NXT_ERROR)) { + length = shr->length + index.length; + + fname = nxt_mp_nget(r->mem_pool, length + 1); + if (nxt_slow_path(fname == NULL)) { goto fail; } - if (ret == 0) { - if (action->fallback != NULL) { - return action->fallback; + p = fname; + p = nxt_cpymem(p, shr->start, shr->length); + p = nxt_cpymem(p, index.start, index.length); + *p = '\0'; + + } else { + if (conf->types == NULL) { + nxt_str_null(&exten); + + } else { + nxt_http_static_extract_extension(shr, &exten); + mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); + + ret = nxt_http_route_test_rule(r, conf->types, mtype->start, + mtype->length); + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; } - nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); - return NULL; + if (ret == 0) { + nxt_http_static_next(task, r, ctx, NXT_HTTP_FORBIDDEN); + return; + } } - } - length = conf->share.length + r->path->length + index.length; - - fname = nxt_mp_nget(r->mem_pool, length + 1); - if (nxt_slow_path(fname == NULL)) { - goto fail; + fname = ctx->share.start; } - p = fname; - p = nxt_cpymem(p, conf->share.start, conf->share.length); - p = nxt_cpymem(p, r->path->start, r->path->length); - p = nxt_cpymem(p, index.start, index.length); - *p = '\0'; - nxt_memzero(&file, sizeof(nxt_file_t)); file.name = fname; - chroot = &conf->chroot; - #if (NXT_HAVE_OPENAT2) - if (conf->resolve != 0) { + if (conf->resolve != 0 || ctx->chroot.length > 0) { + nxt_str_t *chr; + nxt_uint_t resolve; + nxt_http_static_share_t *share; + + share = &conf->shares[ctx->index]; + + resolve = conf->resolve; + chr = &ctx->chroot; - if (chroot->length > 0) { - file.name = chroot->start; + if (chr->length > 0) { + resolve |= RESOLVE_IN_ROOT; - if (length > chroot->length - && nxt_memcmp(fname, chroot->start, chroot->length) == 0) - { - fname += chroot->length; + fname = share->is_const + ? share->fname + : nxt_http_static_chroot_match(chr->start, file.name); + + if (fname != NULL) { + file.name = chr->start; ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, 0); @@ -256,7 +413,7 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, file.name = fname; ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY, - NXT_FILE_OPEN, 0, af.fd, conf->resolve); + NXT_FILE_OPEN, 0, af.fd, resolve); if (af.fd != AT_FDCWD) { nxt_file_close(task, &af); @@ -308,27 +465,35 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, break; } - if (level == NXT_LOG_ERR && action->fallback != NULL) { - return action->fallback; - } - if (status != NXT_HTTP_NOT_FOUND) { - if (chroot->length > 0) { +#if (NXT_HAVE_OPENAT2) + nxt_str_t *chr = &ctx->chroot; + + if (chr->length > 0) { nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E", - fname, chroot, file.error); + fname, chr, file.error); } else { nxt_log(task, level, "opening \"%s\" failed %E", fname, file.error); } + +#else + nxt_log(task, level, "opening \"%s\" failed %E", fname, file.error); +#endif } - nxt_http_request_error(task, r, status); - return NULL; + if (level == NXT_LOG_ERR) { + nxt_http_static_next(task, r, ctx, status); + return; + } + + goto fail; } f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t)); if (nxt_slow_path(f == NULL)) { + nxt_file_close(task, &file); goto fail; } @@ -381,7 +546,7 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, - p; if (exten.start == NULL) { - nxt_http_static_extract_extension(r->path, &exten); + nxt_http_static_extract_extension(shr, &exten); } if (mtype == NULL) { @@ -400,7 +565,7 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, field->value_length = mtype->length; } - if (need_body && nxt_file_size(&fi) > 0) { + if (ctx->need_body && nxt_file_size(&fi) > 0) { fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE); if (nxt_slow_path(fb == NULL)) { goto fail; @@ -420,19 +585,14 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, } else { /* Not a file. */ - nxt_file_close(task, f); if (nxt_slow_path(!nxt_is_dir(&fi))) { - if (action->fallback != NULL) { - return action->fallback; - } - nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file", f->name); - nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND); - return NULL; + nxt_http_static_next(task, r, ctx, NXT_HTTP_NOT_FOUND); + return; } f = NULL; @@ -482,19 +642,111 @@ nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, nxt_http_request_header_send(task, r, body_handler, NULL); r->state = &nxt_http_static_send_state; - return NULL; + return; fail: - nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - if (f != NULL) { nxt_file_close(task, f); } - return NULL; + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); +} + + +static void +nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data) +{ + nxt_http_request_t *r; + + r = obj; + + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); +} + + +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) +{ + nxt_http_action_t *action; + nxt_http_static_conf_t *conf; + + action = ctx->action; + conf = action->u.conf; + + ctx->index++; + + if (ctx->index < conf->nshares) { + nxt_http_static_iterate(task, r, ctx); + return; + } + + if (action->fallback != NULL) { + nxt_http_request_action(task, r, action->fallback); + return; + } + + nxt_http_request_error(task, r, status); +} + + +#if (NXT_HAVE_OPENAT2) + +static u_char * +nxt_http_static_chroot_match(u_char *chr, u_char *shr) +{ + if (*chr != *shr) { + return NULL; + } + + chr++; + shr++; + + for ( ;; ) { + if (*shr == '\0') { + return NULL; + } + + if (*chr == *shr) { + chr++; + shr++; + continue; + } + + if (*chr == '\0') { + break; + } + + if (*chr == '/') { + if (chr[-1] == '/') { + chr++; + continue; + } + + } else if (*shr == '/') { + if (shr[-1] == '/') { + shr++; + continue; + } + } + + return NULL; + } + + if (shr[-1] != '/' && *shr != '/') { + return NULL; + } + + while (*shr == '/') { + shr++; + } + + return (*shr != '\0') ? shr : NULL; } +#endif + static void nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten) diff --git a/src/nxt_java.c b/src/nxt_java.c index ac715c0b..75c8ee19 100644 --- a/src/nxt_java.c +++ b/src/nxt_java.c @@ -428,7 +428,7 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) return NXT_ERROR; } - nxt_unit_default_init(task, &java_init); + nxt_unit_default_init(task, &java_init, app_conf); java_init.callbacks.request_handler = nxt_java_request_handler; java_init.callbacks.websocket_handler = nxt_java_websocket_handler; @@ -437,7 +437,6 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) java_init.request_data_size = sizeof(nxt_java_request_data_t); java_init.data = &java_data; java_init.ctx_data = env; - java_init.shm_limit = app_conf->shm_limit; ctx = nxt_unit_init(&java_init); if (nxt_slow_path(ctx == NULL)) { @@ -616,11 +615,6 @@ nxt_java_ready_handler(nxt_unit_ctx_t *ctx) nxt_java_data_t *java_data; nxt_java_app_conf_t *c; - /* Worker thread context. */ - if (!nxt_unit_is_main_ctx(ctx)) { - return NXT_UNIT_OK; - } - java_data = ctx->unit->data; c = java_data->conf; diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 16c6a297..a5a20d3d 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -10,6 +10,7 @@ #include <nxt_main_process.h> #include <nxt_conf.h> #include <nxt_router.h> +#include <nxt_port_queue.h> #if (NXT_TLS) #include <nxt_cert.h> #endif @@ -31,18 +32,9 @@ typedef struct { } nxt_conf_app_map_t; -extern nxt_port_handlers_t nxt_controller_process_port_handlers; -extern nxt_port_handlers_t nxt_router_process_port_handlers; - - static nxt_int_t nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt); static void nxt_main_process_title(nxt_task_t *task); -static nxt_int_t nxt_main_process_create(nxt_task_t *task, - const nxt_process_init_t init); -static nxt_int_t nxt_main_start_process(nxt_task_t *task, - nxt_process_t *process); -static nxt_process_t *nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt); static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data); static void nxt_main_process_sigquit_handler(nxt_task_t *task, void *obj, @@ -53,7 +45,7 @@ static void nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data); static void nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data); -static void nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid); +static void nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process); static void nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static nxt_int_t nxt_main_listening_socket(nxt_sockaddr_t *sa, @@ -61,8 +53,12 @@ static nxt_int_t nxt_main_listening_socket(nxt_sockaddr_t *sa, static void nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static int nxt_cdecl nxt_app_lang_compare(const void *v1, const void *v2); +static void nxt_main_process_whoami_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); static void nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +static nxt_int_t nxt_main_file_store(nxt_task_t *task, const char *tmp_name, + const char *name, u_char *buf, size_t size); static void nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); @@ -77,6 +73,8 @@ const nxt_sig_event_t nxt_main_process_signals[] = { }; +nxt_uint_t nxt_conf_ver; + static nxt_bool_t nxt_exiting; @@ -97,7 +95,7 @@ nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task, * nxt_main_port_modules_handler() which starts the controller * and router processes. */ - return nxt_main_process_create(task, nxt_discovery_process); + return nxt_process_init_start(task, nxt_discovery_process); } @@ -154,6 +152,12 @@ static nxt_conf_map_t nxt_common_app_limits_conf[] = { offsetof(nxt_common_app_conf_t, shm_limit), }, + { + nxt_string("requests"), + NXT_CONF_MAP_INT32, + offsetof(nxt_common_app_conf_t, request_limit), + }, + }; @@ -325,7 +329,7 @@ static nxt_conf_app_map_t nxt_app_maps[] = { static void -nxt_port_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +nxt_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { nxt_debug(task, "main data: %*s", nxt_buf_mem_used_size(&msg->buf->mem), msg->buf->mem.pos); @@ -333,7 +337,33 @@ nxt_port_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) static void -nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +nxt_main_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + void *mem; + nxt_port_t *port; + + nxt_port_new_port_handler(task, msg); + + port = msg->u.new_port; + + if (port != NULL + && port->type == NXT_PROCESS_APP + && msg->fd[1] != -1) + { + mem = nxt_mem_mmap(NULL, sizeof(nxt_port_queue_t), + PROT_READ | PROT_WRITE, MAP_SHARED, msg->fd[1], 0); + if (nxt_fast_path(mem != MAP_FAILED)) { + port->queue = mem; + } + + nxt_fd_close(msg->fd[1]); + msg->fd[1] = -1; + } +} + + +static void +nxt_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { u_char *start, *p, ch; size_t type_len; @@ -349,21 +379,42 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) rt = task->thread->runtime; - process = nxt_main_process_new(task, rt); + port = rt->port_by_type[NXT_PROCESS_ROUTER]; + if (nxt_slow_path(port == NULL)) { + nxt_alert(task, "router port not found"); + return; + } + + if (nxt_slow_path(port->pid != nxt_recv_msg_cmsg_pid(msg))) { + nxt_alert(task, "process %PI cannot start processes", + nxt_recv_msg_cmsg_pid(msg)); + + return; + } + + process = nxt_process_new(rt); if (nxt_slow_path(process == NULL)) { return; } + process->mem_pool = nxt_mp_create(1024, 128, 256, 32); + if (process->mem_pool == NULL) { + nxt_process_use(task, process, -1); + return; + } + + process->parent_port = rt->port_by_type[NXT_PROCESS_MAIN]; + init = nxt_process_init(process); - *init = nxt_app_process; + *init = nxt_proto_process; b = nxt_buf_chk_make_plain(process->mem_pool, msg->buf, msg->size); if (b == NULL) { goto failed; } - nxt_debug(task, "main start process: %*s", b->mem.free - b->mem.pos, + nxt_debug(task, "main start prototype: %*s", b->mem.free - b->mem.pos, b->mem.pos); app_conf = nxt_mp_zalloc(process->mem_pool, sizeof(nxt_common_app_conf_t)); @@ -379,7 +430,7 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) init->name = (const char *) start; process->name = nxt_mp_alloc(process->mem_pool, app_conf->name.length - + sizeof("\"\" application") + 1); + + sizeof("\"\" prototype") + 1); if (nxt_slow_path(process->name == NULL)) { goto failed; @@ -388,10 +439,11 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) p = (u_char *) process->name; *p++ = '"'; p = nxt_cpymem(p, init->name, app_conf->name.length); - p = nxt_cpymem(p, "\" application", 13); + p = nxt_cpymem(p, "\" prototype", 11); *p = '\0'; app_conf->shm_limit = 100 * 1024 * 1024; + app_conf->request_limit = 0; start += app_conf->name.length + 1; @@ -455,7 +507,7 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) process->stream = msg->port_msg.stream; process->data.app = app_conf; - ret = nxt_main_start_process(task, process); + ret = nxt_process_start(task, process); if (nxt_fast_path(ret == NXT_OK || ret == NXT_AGAIN)) { return; } @@ -483,21 +535,17 @@ nxt_main_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) rt = task->thread->runtime; - process = nxt_runtime_process_find(rt, msg->port_msg.pid); - if (nxt_slow_path(process == NULL)) { - return; - } - - nxt_assert(process->state == NXT_PROCESS_STATE_CREATING); - port = nxt_runtime_port_find(rt, msg->port_msg.pid, msg->port_msg.reply_port); - - if (nxt_slow_path(port == NULL)) { return; } + process = port->process; + + nxt_assert(process != NULL); + nxt_assert(process->state == NXT_PROCESS_STATE_CREATING); + #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { if (nxt_slow_path(nxt_clone_credential_map(task, process->pid, @@ -521,10 +569,13 @@ nxt_main_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) static nxt_port_handlers_t nxt_main_process_port_handlers = { - .data = nxt_port_main_data_handler, + .data = nxt_main_data_handler, + .new_port = nxt_main_new_port_handler, .process_created = nxt_main_process_created_handler, .process_ready = nxt_port_process_ready_handler, - .start_process = nxt_port_main_start_process_handler, + .whoami = nxt_main_process_whoami_handler, + .remove_pid = nxt_port_remove_pid_handler, + .start_process = nxt_main_start_process_handler, .socket = nxt_main_port_socket_handler, .modules = nxt_main_port_modules_handler, .conf_store = nxt_main_port_conf_store_handler, @@ -538,6 +589,88 @@ static nxt_port_handlers_t nxt_main_process_port_handlers = { }; +static void +nxt_main_process_whoami_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_buf_t *buf; + nxt_pid_t pid, ppid; + nxt_port_t *port; + nxt_runtime_t *rt; + nxt_process_t *pprocess; + + nxt_assert(msg->port_msg.reply_port == 0); + + if (nxt_slow_path(msg->buf == NULL + || nxt_buf_used_size(msg->buf) != sizeof(nxt_pid_t))) + { + nxt_alert(task, "whoami: buffer is NULL or unexpected size"); + goto fail; + } + + nxt_memcpy(&ppid, msg->buf->mem.pos, sizeof(nxt_pid_t)); + + rt = task->thread->runtime; + + pprocess = nxt_runtime_process_find(rt, ppid); + if (nxt_slow_path(pprocess == NULL)) { + nxt_alert(task, "whoami: parent process %PI not found", ppid); + goto fail; + } + + pid = nxt_recv_msg_cmsg_pid(msg); + + nxt_debug(task, "whoami: from %PI, parent %PI, fd %d", pid, ppid, + msg->fd[0]); + + if (msg->fd[0] != -1) { + port = nxt_runtime_process_port_create(task, rt, pid, 0, + NXT_PROCESS_APP); + if (nxt_slow_path(port == NULL)) { + goto fail; + } + + nxt_fd_nonblocking(task, msg->fd[0]); + + port->pair[0] = -1; + port->pair[1] = msg->fd[0]; + msg->fd[0] = -1; + + port->max_size = 16 * 1024; + port->max_share = 64 * 1024; + port->socket.task = task; + + nxt_port_write_enable(task, port); + + } else { + port = nxt_runtime_port_find(rt, pid, 0); + if (nxt_slow_path(port == NULL)) { + goto fail; + } + } + + if (ppid != nxt_pid) { + nxt_queue_insert_tail(&pprocess->children, &port->process->link); + } + + buf = nxt_buf_mem_alloc(task->thread->engine->mem_pool, + sizeof(nxt_pid_t), 0); + if (nxt_slow_path(buf == NULL)) { + goto fail; + } + + buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(nxt_pid_t)); + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1, + msg->port_msg.stream, 0, buf); + +fail: + + if (msg->fd[0] != -1) { + nxt_fd_close(msg->fd[0]); + } +} + + static nxt_int_t nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt) { @@ -597,139 +730,6 @@ nxt_main_process_title(nxt_task_t *task) } -static nxt_int_t -nxt_main_process_create(nxt_task_t *task, const nxt_process_init_t init) -{ - nxt_int_t ret; - nxt_runtime_t *rt; - nxt_process_t *process; - nxt_process_init_t *pinit; - - rt = task->thread->runtime; - - process = nxt_main_process_new(task, rt); - if (nxt_slow_path(process == NULL)) { - return NXT_ERROR; - } - - process->name = init.name; - process->user_cred = &rt->user_cred; - - pinit = nxt_process_init(process); - *pinit = init; - - ret = nxt_main_start_process(task, process); - if (nxt_slow_path(ret == NXT_ERROR)) { - nxt_process_use(task, process, -1); - } - - return ret; -} - - -static nxt_process_t * -nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt) -{ - nxt_process_t *process; - - process = nxt_runtime_process_new(rt); - if (nxt_slow_path(process == NULL)) { - return NULL; - } - - process->mem_pool = nxt_mp_create(1024, 128, 256, 32); - if (process->mem_pool == NULL) { - nxt_process_use(task, process, -1); - return NULL; - } - - return process; -} - - -static nxt_int_t -nxt_main_start_process(nxt_task_t *task, nxt_process_t *process) -{ - nxt_mp_t *tmp_mp; - nxt_int_t ret; - nxt_pid_t pid; - nxt_port_t *port; - nxt_process_init_t *init; - - init = nxt_process_init(process); - - port = nxt_port_new(task, 0, 0, init->type); - if (nxt_slow_path(port == NULL)) { - return NXT_ERROR; - } - - nxt_process_port_add(task, process, port); - - ret = nxt_port_socket_init(task, port, 0); - if (nxt_slow_path(ret != NXT_OK)) { - goto free_port; - } - - tmp_mp = nxt_mp_create(1024, 128, 256, 32); - if (nxt_slow_path(tmp_mp == NULL)) { - ret = NXT_ERROR; - - goto close_port; - } - - if (init->prefork) { - ret = init->prefork(task, process, tmp_mp); - if (nxt_slow_path(ret != NXT_OK)) { - goto free_mempool; - } - } - - pid = nxt_process_create(task, process); - - switch (pid) { - - case -1: - ret = NXT_ERROR; - break; - - case 0: - /* The child process: return to the event engine work queue loop. */ - - nxt_process_use(task, process, -1); - - ret = NXT_AGAIN; - break; - - default: - /* The main process created a new process. */ - - nxt_process_use(task, process, -1); - - nxt_port_read_close(port); - nxt_port_write_enable(task, port); - - ret = NXT_OK; - break; - } - -free_mempool: - - nxt_mp_destroy(tmp_mp); - -close_port: - - if (nxt_slow_path(ret == NXT_ERROR)) { - nxt_port_close(task, port); - } - -free_port: - - nxt_port_use(task, port, -1); - - return ret; -} - - static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data) { @@ -859,13 +859,21 @@ fail: static void nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data) { - int status; - nxt_err_t err; - nxt_pid_t pid; + int status; + nxt_int_t ret; + nxt_err_t err; + nxt_pid_t pid; + nxt_port_t *port; + nxt_queue_t children; + nxt_runtime_t *rt; + nxt_process_t *process, *child; + nxt_process_init_t init; nxt_debug(task, "sigchld handler signo:%d (%s)", (int) (uintptr_t) obj, data); + rt = task->thread->runtime; + for ( ;; ) { pid = waitpid(-1, &status, WNOHANG); @@ -906,94 +914,86 @@ nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data) pid, WEXITSTATUS(status)); } - nxt_main_cleanup_process(task, pid); - } -} + process = nxt_runtime_process_find(rt, pid); + if (process != NULL) { + nxt_main_process_cleanup(task, process); -static void -nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data) -{ - nxt_trace(task, "signal signo:%d (%s) recevied, ignored", - (int) (uintptr_t) obj, data); -} + if (process->state == NXT_PROCESS_STATE_READY) { + process->stream = 0; + } + nxt_queue_init(&children); -static void -nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid) -{ - int stream; - nxt_int_t ret; - nxt_buf_t *buf; - nxt_port_t *port; - const char *name; - nxt_runtime_t *rt; - nxt_process_t *process; - nxt_process_init_t init; + if (!nxt_queue_is_empty(&process->children)) { + nxt_queue_add(&children, &process->children); - rt = task->thread->runtime; + nxt_queue_init(&process->children); - process = nxt_runtime_process_find(rt, pid); - if (!process) { - return; - } + nxt_queue_each(child, &children, nxt_process_t, link) { + port = nxt_process_port_first(child); - if (process->isolation.cleanup != NULL) { - process->isolation.cleanup(task, process); - } + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, + -1, 0, 0, NULL); + } nxt_queue_loop; + } - name = process->name; - stream = process->stream; - init = *((nxt_process_init_t *) nxt_process_init(process)); + if (nxt_exiting) { + nxt_process_close_ports(task, process); - if (process->state == NXT_PROCESS_STATE_READY) { - process->stream = 0; - } + nxt_queue_each(child, &children, nxt_process_t, link) { + nxt_queue_remove(&child->link); + child->link.next = NULL; - nxt_process_close_ports(task, process); + nxt_process_close_ports(task, child); + } nxt_queue_loop; - if (nxt_exiting) { - if (rt->nprocesses <= 1) { - nxt_runtime_quit(task, 0); - } + if (rt->nprocesses <= 1) { + nxt_runtime_quit(task, 0); + } - return; - } + return; + } - nxt_runtime_process_each(rt, process) { + nxt_port_remove_notify_others(task, process); - if (process->pid == nxt_pid - || process->pid == pid - || nxt_queue_is_empty(&process->ports)) - { - continue; - } + nxt_queue_each(child, &children, nxt_process_t, link) { + nxt_port_remove_notify_others(task, child); - port = nxt_process_port_first(process); + nxt_queue_remove(&child->link); + child->link.next = NULL; - if (nxt_proc_remove_notify_matrix[init.type][port->type] == 0) { - continue; - } + nxt_process_close_ports(task, child); + } nxt_queue_loop; + + init = *(nxt_process_init_t *) nxt_process_init(process); - buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, - sizeof(pid)); + nxt_process_close_ports(task, process); - if (nxt_slow_path(buf == NULL)) { - continue; + if (init.restart) { + ret = nxt_process_init_start(task, init); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_alert(task, "failed to restart %s", init.name); + } + } } + } +} - buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid)); - nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID, -1, - stream, 0, buf); +static void +nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_trace(task, "signal signo:%d (%s) recevied, ignored", + (int) (uintptr_t) obj, data); +} - } nxt_runtime_process_loop; - if (init.restart) { - ret = nxt_main_process_create(task, init); - if (nxt_slow_path(ret == NXT_ERROR)) { - nxt_alert(task, "failed to restart %s", name); - } +static void +nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process) +{ + if (process->isolation.cleanup != NULL) { + process->isolation.cleanup(task, process); } } @@ -1016,6 +1016,13 @@ nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) return; } + if (nxt_slow_path(port->type != NXT_PROCESS_ROUTER)) { + nxt_alert(task, "process %PI cannot create listener sockets", + msg->port_msg.pid); + + return; + } + b = msg->buf; sa = (nxt_sockaddr_t *) b->mem.pos; @@ -1259,6 +1266,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) rt = task->thread->runtime; if (msg->port_msg.pid != rt->port_by_type[NXT_PROCESS_DISCOVERY]->pid) { + nxt_alert(task, "process %PI cannot send modules", msg->port_msg.pid); return; } @@ -1379,9 +1387,9 @@ fail: nxt_mp_destroy(mp); - ret = nxt_main_process_create(task, nxt_controller_process); + ret = nxt_process_init_start(task, nxt_controller_process); if (ret == NXT_OK) { - ret = nxt_main_process_create(task, nxt_router_process); + ret = nxt_process_init_start(task, nxt_router_process); } if (nxt_slow_path(ret == NXT_ERROR)) { @@ -1419,11 +1427,20 @@ static void nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { void *p; - size_t size; - ssize_t n; + size_t n, size; nxt_int_t ret; - nxt_file_t file; + nxt_port_t *ctl_port; nxt_runtime_t *rt; + u_char ver[NXT_INT_T_LEN]; + + rt = task->thread->runtime; + + ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; + + if (nxt_slow_path(msg->port_msg.pid != ctl_port->pid)) { + nxt_alert(task, "process %PI cannot store conf", msg->port_msg.pid); + return; + } p = MAP_FAILED; @@ -1457,29 +1474,18 @@ nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_debug(task, "conf_store_handler(%uz): %*s", size, size, p); - nxt_memzero(&file, sizeof(nxt_file_t)); - - rt = task->thread->runtime; - - file.name = (nxt_file_name_t *) rt->conf_tmp; + if (nxt_conf_ver != NXT_VERNUM) { + n = nxt_sprintf(ver, ver + NXT_INT_T_LEN, "%d", NXT_VERNUM) - ver; - if (nxt_slow_path(nxt_file_open(task, &file, NXT_FILE_WRONLY, - NXT_FILE_TRUNCATE, NXT_FILE_OWNER_ACCESS) - != NXT_OK)) - { - goto error; - } - - n = nxt_file_write(&file, p, size, 0); - - nxt_file_close(task, &file); + ret = nxt_main_file_store(task, rt->ver_tmp, rt->ver, ver, n); + if (nxt_slow_path(ret != NXT_OK)) { + goto error; + } - if (nxt_slow_path(n != (ssize_t) size)) { - (void) nxt_file_delete(file.name); - goto error; + nxt_conf_ver = NXT_VERNUM; } - ret = nxt_file_rename(file.name, (nxt_file_name_t *) rt->conf); + ret = nxt_main_file_store(task, rt->conf_tmp, rt->conf, p, size); if (nxt_fast_path(ret == NXT_OK)) { goto cleanup; @@ -1502,6 +1508,37 @@ cleanup: } +static nxt_int_t +nxt_main_file_store(nxt_task_t *task, const char *tmp_name, const char *name, + u_char *buf, size_t size) +{ + ssize_t n; + nxt_int_t ret; + nxt_file_t file; + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.name = (nxt_file_name_t *) name; + + ret = nxt_file_open(task, &file, NXT_FILE_WRONLY, NXT_FILE_TRUNCATE, + NXT_FILE_OWNER_ACCESS); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + n = nxt_file_write(&file, buf, size, 0); + + nxt_file_close(task, &file); + + if (nxt_slow_path(n != (ssize_t) size)) { + (void) nxt_file_delete(file.name); + return NXT_ERROR; + } + + return nxt_file_rename(file.name, (nxt_file_name_t *) name); +} + + static void nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { diff --git a/src/nxt_main_process.h b/src/nxt_main_process.h index f9c974d8..ef083d63 100644 --- a/src/nxt_main_process.h +++ b/src/nxt_main_process.h @@ -23,9 +23,11 @@ nxt_int_t nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task, nxt_runtime_t *runtime); +NXT_EXPORT extern nxt_uint_t nxt_conf_ver; NXT_EXPORT extern const nxt_process_init_t nxt_discovery_process; NXT_EXPORT extern const nxt_process_init_t nxt_controller_process; NXT_EXPORT extern const nxt_process_init_t nxt_router_process; +NXT_EXPORT extern const nxt_process_init_t nxt_proto_process; NXT_EXPORT extern const nxt_process_init_t nxt_app_process; extern const nxt_sig_event_t nxt_main_process_signals[]; diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 273ca7f4..1e08015e 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -16,18 +16,32 @@ typedef struct { - SSL *session; - nxt_conn_t *conn; + SSL *session; + nxt_conn_t *conn; - int ssl_error; - uint8_t times; /* 2 bits */ - uint8_t handshake; /* 1 bit */ + int ssl_error; + uint8_t times; /* 2 bits */ + uint8_t handshake; /* 1 bit */ - nxt_tls_conf_t *conf; - nxt_buf_mem_t buffer; + nxt_tls_conf_t *conf; + nxt_buf_mem_t buffer; } nxt_openssl_conn_t; +struct nxt_tls_ticket_s { + u_char name[16]; + u_char hmac_key[32]; + u_char aes_key[32]; + uint8_t size; +}; + + +struct nxt_tls_tickets_s { + nxt_uint_t count; + nxt_tls_ticket_t tickets[]; +}; + + typedef enum { NXT_OPENSSL_HANDSHAKE = 0, NXT_OPENSSL_READ, @@ -607,8 +621,8 @@ static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_mp_t *mp) { + size_t len; uint32_t i; - nxt_int_t ret; nxt_str_t value; nxt_uint_t count; nxt_conf_value_t *member, *tickets_conf; @@ -672,23 +686,21 @@ nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_conf_get_string(member, &value); - ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length); - if (nxt_slow_path(ret == NXT_ERROR)) { - return NXT_ERROR; - } + len = nxt_base64_decode(buf, value.start, value.length); - if (ret == 48) { - ticket->aes128 = 1; + nxt_memcpy(ticket->name, buf, 16); + + if (len == 48) { nxt_memcpy(ticket->aes_key, buf + 16, 16); nxt_memcpy(ticket->hmac_key, buf + 32, 16); + ticket->size = 16; } else { - ticket->aes128 = 0; nxt_memcpy(ticket->hmac_key, buf + 16, 32); nxt_memcpy(ticket->aes_key, buf + 48, 32); + ticket->size = 32; } - nxt_memcpy(ticket->name, buf, 16); } while (i < count); if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback) @@ -727,7 +739,6 @@ static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc) { - size_t size; nxt_uint_t i; nxt_conn_t *c; const EVP_MD *digest; @@ -745,25 +756,14 @@ nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, tls = c->u.tls; ticket = tls->conf->tickets->tickets; -#ifdef OPENSSL_NO_SHA256 - digest = EVP_sha1(); -#else - digest = EVP_sha256(); -#endif + i = 0; if (enc == 1) { /* encrypt session ticket */ nxt_debug(c->socket.task, "TLS session ticket encrypt"); - if (ticket[0].aes128 == 1) { - cipher = EVP_aes_128_cbc(); - size = 16; - - } else { - cipher = EVP_aes_256_cbc(); - size = 32; - } + cipher = (ticket[0].size == 16) ? EVP_aes_128_cbc() : EVP_aes_256_cbc(); if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, @@ -771,32 +771,24 @@ nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, return -1; } - if (EVP_EncryptInit_ex(ectx, cipher, NULL, ticket[0].aes_key, iv) - != 1) + nxt_memcpy(name, ticket[0].name, 16); + + if (EVP_EncryptInit_ex(ectx, cipher, NULL, ticket[0].aes_key, iv) != 1) { nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, "EVP_EncryptInit_ex() failed"); return -1; } - if (HMAC_Init_ex(hctx, ticket[0].hmac_key, size, digest, NULL) != 1) { - nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, - "HMAC_Init_ex() failed"); - return -1; - } - - nxt_memcpy(name, ticket[0].name, 16); - - return 1; - } else { /* decrypt session ticket */ - for (i = 0; i < tls->conf->tickets->count; i++) { + do { if (nxt_memcmp(name, ticket[i].name, 16) == 0) { goto found; } - } + + } while (++i < tls->conf->tickets->count); nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found"); @@ -807,29 +799,33 @@ nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, nxt_debug(c->socket.task, "TLS session ticket decrypt, key number: \"%d\"", i); - if (ticket[i].aes128 == 1) { - cipher = EVP_aes_128_cbc(); - size = 16; + enc = (i == 0) ? 1 : 2 /* renew */; - } else { - cipher = EVP_aes_256_cbc(); - size = 32; - } + cipher = (ticket[i].size == 16) ? EVP_aes_128_cbc() : EVP_aes_256_cbc(); - if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) { + if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) + { nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, "EVP_DecryptInit_ex() failed"); return -1; } + } - if (HMAC_Init_ex(hctx, ticket[i].hmac_key, size, digest, NULL) != 1) { - nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, - "HMAC_Init_ex() failed"); - return -1; - } +#ifdef OPENSSL_NO_SHA256 + digest = EVP_sha1(); +#else + digest = EVP_sha256(); +#endif - return (i == 0) ? 1 : 2 /* renew */; + if (HMAC_Init_ex(hctx, ticket[i].hmac_key, ticket[i].size, digest, NULL) + != 1) + { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "HMAC_Init_ex() failed"); + return -1; } + + return enc; } #endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */ @@ -1819,70 +1815,3 @@ nxt_openssl_copy_error(u_char *p, u_char *end) return p; } - - -nxt_int_t -nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen) -{ - BIO *bio, *b64; - nxt_int_t count, ret; - u_char buf[128]; - - b64 = BIO_new(BIO_f_base64()); - if (nxt_slow_path(b64 == NULL)) { - goto error; - } - - bio = BIO_new_mem_buf(s, slen); - if (nxt_slow_path(bio == NULL)) { - goto error; - } - - bio = BIO_push(b64, bio); - - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); - - count = 0; - - if (d == NULL) { - - for ( ;; ) { - ret = BIO_read(bio, buf, 128); - - if (ret < 0) { - goto invalid; - } - - count += ret; - - if (ret != 128) { - break; - } - } - - } else { - count = BIO_read(bio, d, dlen); - - if (count < 0) { - goto invalid; - } - } - - BIO_free_all(bio); - - return count; - -error: - - BIO_vfree(b64); - ERR_clear_error(); - - return NXT_ERROR; - -invalid: - - BIO_free_all(bio); - ERR_clear_error(); - - return NXT_DECLINED; -} diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 3fb3b0db..ea5f5581 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -485,14 +485,13 @@ nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) } } - ret = nxt_unit_default_init(task, &php_init); + ret = nxt_unit_default_init(task, &php_init, conf); if (nxt_slow_path(ret != NXT_OK)) { nxt_alert(task, "nxt_unit_default_init() failed"); return ret; } php_init.callbacks.request_handler = nxt_php_request_handler; - php_init.shm_limit = conf->shm_limit; unit_ctx = nxt_unit_init(&php_init); if (nxt_slow_path(unit_ctx == NULL)) { diff --git a/src/nxt_port.c b/src/nxt_port.c index d4e46564..1e8fa28a 100644 --- a/src/nxt_port.c +++ b/src/nxt_port.c @@ -12,6 +12,8 @@ #include <nxt_port_queue.h> +static void nxt_port_remove_pid(nxt_task_t *task, nxt_port_recv_msg_t *msg, + nxt_pid_t pid); static void nxt_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static nxt_atomic_uint_t nxt_port_last_id = 1; @@ -274,6 +276,8 @@ nxt_port_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_debug(task, "port %PI:%d already exists", new_port_msg->pid, new_port_msg->id); + msg->u.new_port = port; + nxt_fd_close(msg->fd[0]); msg->fd[0] = -1; return; @@ -384,14 +388,13 @@ nxt_port_change_log_file(nxt_task_t *task, nxt_runtime_t *rt, nxt_uint_t slot, port = nxt_process_port_first(process); - b = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, - sizeof(nxt_port_data_t)); + b = nxt_buf_mem_alloc(task->thread->engine->mem_pool, + sizeof(nxt_uint_t), 0); if (nxt_slow_path(b == NULL)) { continue; } - *(nxt_uint_t *) b->mem.pos = slot; - b->mem.free += sizeof(nxt_uint_t); + b->mem.free = nxt_cpymem(b->mem.free, &slot, sizeof(nxt_uint_t)); (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_CHANGE_FILE, fd, 0, 0, b); @@ -448,18 +451,74 @@ nxt_port_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) void -nxt_port_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +nxt_port_remove_notify_others(nxt_task_t *task, nxt_process_t *process) { - nxt_buf_t *buf; nxt_pid_t pid; + nxt_buf_t *buf; + nxt_port_t *port; nxt_runtime_t *rt; - nxt_process_t *process; + nxt_process_t *p; + nxt_process_type_t ptype; + + pid = process->pid; + + ptype = nxt_process_type(process); + + rt = task->thread->runtime; + + nxt_runtime_process_each(rt, p) { + + if (p->pid == nxt_pid + || p->pid == pid + || nxt_queue_is_empty(&p->ports)) + { + continue; + } + + port = nxt_process_port_first(p); + + if (nxt_proc_remove_notify_matrix[ptype][port->type] == 0) { + continue; + } + + buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, + sizeof(pid)); + + if (nxt_slow_path(buf == NULL)) { + continue; + } + + buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid)); + + nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID, -1, + process->stream, 0, buf); + + } nxt_runtime_process_loop; +} + + +void +nxt_port_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_pid_t pid; + nxt_buf_t *buf; buf = msg->buf; nxt_assert(nxt_buf_used_size(buf) == sizeof(pid)); - nxt_memcpy(&pid, buf->mem.pos, sizeof(pid)); + nxt_memcpy(&pid, buf->mem.pos, sizeof(nxt_pid_t)); + + nxt_port_remove_pid(task, msg, pid); +} + + +static void +nxt_port_remove_pid(nxt_task_t *task, nxt_port_recv_msg_t *msg, + nxt_pid_t pid) +{ + nxt_runtime_t *rt; + nxt_process_t *process; msg->u.removed_pid = pid; diff --git a/src/nxt_port.h b/src/nxt_port.h index a0bc2512..3b66edfd 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -33,6 +33,7 @@ struct nxt_port_handlers_s { /* New process */ nxt_port_handler_t process_created; nxt_port_handler_t process_ready; + nxt_port_handler_t whoami; /* Process exit/crash notification. */ nxt_port_handler_t remove_pid; @@ -92,6 +93,7 @@ typedef enum { _NXT_PORT_MSG_PROCESS_CREATED = nxt_port_handler_idx(process_created), _NXT_PORT_MSG_PROCESS_READY = nxt_port_handler_idx(process_ready), + _NXT_PORT_MSG_WHOAMI = nxt_port_handler_idx(whoami), _NXT_PORT_MSG_REMOVE_PID = nxt_port_handler_idx(remove_pid), _NXT_PORT_MSG_QUIT = nxt_port_handler_idx(quit), @@ -131,6 +133,7 @@ typedef enum { NXT_PORT_MSG_PROCESS_CREATED = nxt_msg_last(_NXT_PORT_MSG_PROCESS_CREATED), NXT_PORT_MSG_PROCESS_READY = nxt_msg_last(_NXT_PORT_MSG_PROCESS_READY), + NXT_PORT_MSG_WHOAMI = nxt_msg_last(_NXT_PORT_MSG_WHOAMI), NXT_PORT_MSG_QUIT = nxt_msg_last(_NXT_PORT_MSG_QUIT), NXT_PORT_MSG_REMOVE_PID = nxt_msg_last(_NXT_PORT_MSG_REMOVE_PID), @@ -153,7 +156,9 @@ typedef enum { /* Passed as a first iov chunk. */ typedef struct { uint32_t stream; - nxt_pid_t pid; + + nxt_pid_t pid; /* not used on Linux and FreeBSD */ + nxt_port_id_t reply_port; uint8_t type; @@ -186,6 +191,9 @@ typedef struct { uint8_t allocated; /* 1 bit */ } nxt_port_send_msg_t; +#if (NXT_HAVE_UCRED) || (NXT_HAVE_MSGHDR_CMSGCRED) +#define NXT_USE_CMSG_PID 1 +#endif struct nxt_port_recv_msg_s { nxt_fd_t fd[2]; @@ -193,6 +201,9 @@ struct nxt_port_recv_msg_s { nxt_port_t *port; nxt_port_msg_t port_msg; size_t size; +#if (NXT_USE_CMSG_PID) + nxt_pid_t cmsg_pid; +#endif nxt_bool_t cancelled; union { nxt_port_t *new_port; @@ -201,6 +212,15 @@ struct nxt_port_recv_msg_s { } u; }; + +#if (NXT_USE_CMSG_PID) +#define nxt_recv_msg_cmsg_pid(msg) ((msg)->cmsg_pid) +#define nxt_recv_msg_cmsg_pid_ref(msg) (&(msg)->cmsg_pid) +#else +#define nxt_recv_msg_cmsg_pid(msg) ((msg)->port_msg.pid) +#define nxt_recv_msg_cmsg_pid_ref(msg) (NULL) +#endif + typedef struct nxt_app_s nxt_app_t; struct nxt_port_s { @@ -224,8 +244,6 @@ struct nxt_port_s { /* Maximum interleave of message parts. */ uint32_t max_share; - uint32_t app_responses; - uint32_t active_websockets; uint32_t active_requests; @@ -324,6 +342,7 @@ nxt_int_t nxt_port_send_port(nxt_task_t *task, nxt_port_t *port, nxt_port_t *new_port, uint32_t stream); void nxt_port_change_log_file(nxt_task_t *task, nxt_runtime_t *rt, nxt_uint_t slot, nxt_fd_t fd); +void nxt_port_remove_notify_others(nxt_task_t *task, nxt_process_t *process); void nxt_port_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); void nxt_port_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index bffae8a1..e799f860 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -232,6 +232,18 @@ nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process, hdr = mem; + if (nxt_slow_path(hdr->src_pid != process->pid + || hdr->dst_pid != nxt_pid)) + { + nxt_log(task, NXT_LOG_WARN, "unexpected pid in mmap header detected: " + "%PI != %PI or %PI != %PI", hdr->src_pid, process->pid, + hdr->dst_pid, nxt_pid); + + nxt_mem_munmap(mem, PORT_MMAP_SIZE); + + return NULL; + } + mmap_handler = nxt_zalloc(sizeof(nxt_port_mmap_handler_t)); if (nxt_slow_path(mmap_handler == NULL)) { nxt_log(task, NXT_LOG_WARN, "failed to allocate mmap_handler"); @@ -244,16 +256,6 @@ nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process, mmap_handler->hdr = hdr; mmap_handler->fd = -1; - if (nxt_slow_path(hdr->src_pid != process->pid - || hdr->dst_pid != nxt_pid)) - { - nxt_log(task, NXT_LOG_WARN, "unexpected pid in mmap header detected: " - "%PI != %PI or %PI != %PI", hdr->src_pid, process->pid, - hdr->dst_pid, nxt_pid); - - return NULL; - } - nxt_thread_mutex_lock(&process->incoming.mutex); port_mmap = nxt_port_mmap_at(&process->incoming, hdr->id); @@ -261,7 +263,6 @@ nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process, nxt_log(task, NXT_LOG_WARN, "failed to add mmap to incoming array"); nxt_mem_munmap(mem, PORT_MMAP_SIZE); - hdr = NULL; nxt_free(mmap_handler); mmap_handler = NULL; @@ -318,6 +319,7 @@ nxt_port_new_port_mmap(nxt_task_t *task, nxt_port_mmaps_t *mmaps, MAP_SHARED, fd, 0); if (nxt_slow_path(mem == MAP_FAILED)) { + nxt_fd_close(fd); goto remove_fail; } diff --git a/src/nxt_port_rpc.c b/src/nxt_port_rpc.c index f4008a18..0cac5cbb 100644 --- a/src/nxt_port_rpc.c +++ b/src/nxt_port_rpc.c @@ -393,8 +393,8 @@ nxt_port_rpc_remove_peer(nxt_task_t *task, nxt_port_t *port, nxt_pid_t peer) msg.fd[1] = -1; msg.buf = &buf; msg.port = port; - - msg.port_msg.pid = peer; + msg.u.removed_pid = peer; + msg.port_msg.pid = nxt_pid; msg.port_msg.type = _NXT_PORT_MSG_REMOVE_PID; peer_link = lhq.value; diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index ba1b7081..2a51dfb6 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -5,6 +5,7 @@ */ #include <nxt_main.h> +#include <nxt_socket_msg.h> #include <nxt_port_queue.h> #include <nxt_port_memory_int.h> @@ -22,6 +23,7 @@ static nxt_port_send_msg_t *nxt_port_msg_alloc(nxt_port_send_msg_t *m); static void nxt_port_write_handler(nxt_task_t *task, void *obj, void *data); static nxt_port_send_msg_t *nxt_port_msg_first(nxt_port_t *port); nxt_inline void nxt_port_msg_close_fd(nxt_port_send_msg_t *msg); +nxt_inline void nxt_port_close_fds(nxt_fd_t *fd); static nxt_buf_t *nxt_port_buf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, size_t sent, nxt_bool_t mmap_mode); static nxt_port_send_msg_t *nxt_port_msg_insert_tail(nxt_port_t *port, @@ -593,16 +595,21 @@ nxt_port_msg_close_fd(nxt_port_send_msg_t *msg) return; } - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); + nxt_port_close_fds(msg->fd); +} - msg->fd[0] = -1; - } - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); +nxt_inline void +nxt_port_close_fds(nxt_fd_t *fd) +{ + if (fd[0] != -1) { + nxt_fd_close(fd[0]); + fd[0] = -1; + } - msg->fd[1] = -1; + if (fd[1] != -1) { + nxt_fd_close(fd[1]); + fd[1] = -1; } } @@ -725,16 +732,17 @@ nxt_port_read_handler(nxt_task_t *task, void *obj, void *data) { ssize_t n; nxt_buf_t *b; + nxt_int_t ret; nxt_port_t *port; - struct iovec iov[2]; + nxt_recv_oob_t oob; nxt_port_recv_msg_t msg; + struct iovec iov[2]; port = msg.port = nxt_container_of(obj, nxt_port_t, socket); nxt_assert(port->engine == task->thread->engine); for ( ;; ) { - b = nxt_port_buf_alloc(port); if (nxt_slow_path(b == NULL)) { @@ -747,9 +755,22 @@ nxt_port_read_handler(nxt_task_t *task, void *obj, void *data) iov[1].iov_base = b->mem.pos; iov[1].iov_len = port->max_size; - n = nxt_socketpair_recv(&port->socket, msg.fd, iov, 2); + n = nxt_socketpair_recv(&port->socket, iov, 2, &oob); if (n > 0) { + msg.fd[0] = -1; + msg.fd[1] = -1; + + ret = nxt_socket_msg_oob_get(&oob, msg.fd, + nxt_recv_msg_cmsg_pid_ref(&msg)); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "failed to get oob data from %d", + port->socket.fd); + + nxt_port_close_fds(msg.fd); + + goto fail; + } msg.buf = b; msg.size = n; @@ -778,8 +799,8 @@ nxt_port_read_handler(nxt_task_t *task, void *obj, void *data) return; } - /* n == 0 || n == NXT_ERROR */ - +fail: + /* n == 0 || error */ nxt_work_queue_add(&task->thread->engine->fast_work_queue, nxt_port_error_handler, task, &port->socket, NULL); return; @@ -792,8 +813,10 @@ nxt_port_queue_read_handler(nxt_task_t *task, void *obj, void *data) { ssize_t n; nxt_buf_t *b; + nxt_int_t ret; nxt_port_t *port; struct iovec iov[2]; + nxt_recv_oob_t oob; nxt_port_queue_t *queue; nxt_port_recv_msg_t msg, *smsg; uint8_t qmsg[NXT_PORT_QUEUE_MSG_SIZE]; @@ -884,7 +907,23 @@ nxt_port_queue_read_handler(nxt_task_t *task, void *obj, void *data) iov[1].iov_base = b->mem.pos; iov[1].iov_len = port->max_size; - n = nxt_socketpair_recv(&port->socket, msg.fd, iov, 2); + n = nxt_socketpair_recv(&port->socket, iov, 2, &oob); + + if (n > 0) { + msg.fd[0] = -1; + msg.fd[1] = -1; + + ret = nxt_socket_msg_oob_get(&oob, msg.fd, + nxt_recv_msg_cmsg_pid_ref(&msg)); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "failed to get oob data from %d", + port->socket.fd); + + nxt_port_close_fds(msg.fd); + + return; + } + } if (n == (ssize_t) sizeof(nxt_port_msg_t) && msg.port_msg.type == _NXT_PORT_MSG_READ_QUEUE) @@ -1139,13 +1178,7 @@ nxt_port_read_msg_process(nxt_task_t *task, nxt_port_t *port, nxt_alert(task, "port %d: too small message:%uz", port->socket.fd, msg->size); - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - } + nxt_port_close_fds(msg->fd); return; } @@ -1225,13 +1258,7 @@ nxt_port_read_msg_process(nxt_task_t *task, nxt_port_t *port, b = NULL; } else { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - } + nxt_port_close_fds(msg->fd); } } else { if (nxt_fast_path(msg->cancelled == 0)) { diff --git a/src/nxt_process.c b/src/nxt_process.c index 87419313..fca197eb 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -5,7 +5,6 @@ */ #include <nxt_main.h> -#include <nxt_main_process.h> #if (NXT_HAVE_CLONE) #include <nxt_clone.h> @@ -17,9 +16,26 @@ #include <sys/prctl.h> #endif + +#if (NXT_HAVE_CLONE) && (NXT_HAVE_CLONE_NEWPID) +#define nxt_is_pid_isolated(process) \ + nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID) +#else +#define nxt_is_pid_isolated(process) \ + (0) +#endif + + +static nxt_pid_t nxt_process_create(nxt_task_t *task, nxt_process_t *process); +static nxt_int_t nxt_process_do_start(nxt_task_t *task, nxt_process_t *process); +static nxt_int_t nxt_process_whoami(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process); +static void nxt_process_whoami_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data); +static void nxt_process_whoami_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data); static nxt_int_t nxt_process_send_created(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_process_send_ready(nxt_task_t *task, @@ -43,66 +59,200 @@ nxt_uid_t nxt_euid; nxt_gid_t nxt_egid; nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { - { 1, 1, 1, 1, 1 }, - { 1, 0, 0, 0, 0 }, - { 1, 0, 0, 1, 0 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 1, 1, 1 }, + { 1, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 1, 0, 0 }, + { 1, 0, 1, 1, 1, 1 }, + { 1, 0, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0, 0 }, }; nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { - { 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0 }, - { 0, 0, 0, 1, 0 }, - { 0, 0, 1, 0, 1 }, - { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 1, 1 }, + { 0, 0, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0, 0 }, }; -static nxt_int_t -nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) +static const nxt_port_handlers_t nxt_process_whoami_port_handlers = { + .quit = nxt_signal_quit_handler, + .rpc_ready = nxt_port_rpc_handler, + .rpc_error = nxt_port_rpc_handler, +}; + + +nxt_process_t * +nxt_process_new(nxt_runtime_t *rt) { - nxt_process_t *p; + nxt_process_t *process; + + process = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_t) + + sizeof(nxt_process_init_t)); + + if (nxt_slow_path(process == NULL)) { + return NULL; + } + + nxt_queue_init(&process->ports); + + nxt_thread_mutex_create(&process->incoming.mutex); + + process->use_count = 1; + + nxt_queue_init(&process->children); + + return process; +} + + +void +nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i) +{ + process->use_count += i; + + if (process->use_count == 0) { + nxt_runtime_process_release(task->thread->runtime, process); + } +} + + +nxt_int_t +nxt_process_init_start(nxt_task_t *task, nxt_process_init_t init) +{ + nxt_int_t ret; nxt_runtime_t *rt; + nxt_process_t *process; + nxt_process_init_t *pinit; + + rt = task->thread->runtime; + + process = nxt_process_new(rt); + if (nxt_slow_path(process == NULL)) { + return NXT_ERROR; + } + + process->parent_port = rt->port_by_type[rt->type]; + + process->name = init.name; + process->user_cred = &rt->user_cred; + + pinit = nxt_process_init(process); + *pinit = init; + + ret = nxt_process_start(task, process); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_process_use(task, process, -1); + } + + return ret; +} + + +nxt_int_t +nxt_process_start(nxt_task_t *task, nxt_process_t *process) +{ + nxt_mp_t *tmp_mp; + nxt_int_t ret; + nxt_pid_t pid; + nxt_port_t *port; nxt_process_init_t *init; - nxt_process_type_t ptype; init = nxt_process_init(process); - nxt_pid = nxt_getpid(); - - process->pid = nxt_pid; + port = nxt_port_new(task, 0, 0, init->type); + if (nxt_slow_path(port == NULL)) { + return NXT_ERROR; + } - /* Clean inherited cached thread tid. */ - task->thread->tid = 0; + nxt_process_port_add(task, process, port); -#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWPID) - if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)) { - ssize_t pidsz; - char procpid[10]; + ret = nxt_port_socket_init(task, port, 0); + if (nxt_slow_path(ret != NXT_OK)) { + goto free_port; + } - nxt_debug(task, "%s isolated pid is %d", process->name, nxt_pid); + tmp_mp = nxt_mp_create(1024, 128, 256, 32); + if (nxt_slow_path(tmp_mp == NULL)) { + ret = NXT_ERROR; - pidsz = readlink("/proc/self", procpid, sizeof(procpid)); + goto close_port; + } - if (nxt_slow_path(pidsz < 0 || pidsz >= (ssize_t) sizeof(procpid))) { - nxt_alert(task, "failed to read real pid from /proc/self"); - return NXT_ERROR; + if (init->prefork) { + ret = init->prefork(task, process, tmp_mp); + if (nxt_slow_path(ret != NXT_OK)) { + goto free_mempool; } + } + + pid = nxt_process_create(task, process); + + switch (pid) { - procpid[pidsz] = '\0'; + case -1: + ret = NXT_ERROR; + break; + + case 0: + /* The child process: return to the event engine work queue loop. */ - nxt_pid = (nxt_pid_t) strtol(procpid, NULL, 10); + nxt_process_use(task, process, -1); + + ret = NXT_AGAIN; + break; + + default: + /* The parent process created a new process. */ - nxt_assert(nxt_pid > 0 && nxt_errno != ERANGE); + nxt_process_use(task, process, -1); - process->pid = nxt_pid; - task->thread->tid = nxt_pid; + nxt_port_read_close(port); + nxt_port_write_enable(task, port); - nxt_debug(task, "%s real pid is %d", process->name, nxt_pid); + ret = NXT_OK; + break; } -#endif +free_mempool: + + nxt_mp_destroy(tmp_mp); + +close_port: + + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_port_close(task, port); + } + +free_port: + + nxt_port_use(task, port, -1); + + return ret; +} + + +static nxt_int_t +nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) +{ + nxt_process_t *p; + nxt_runtime_t *rt; + nxt_process_init_t *init; + nxt_process_type_t ptype; + + init = nxt_process_init(process); + + nxt_ppid = nxt_pid; + + nxt_pid = nxt_getpid(); + + process->pid = nxt_pid; + process->isolated_pid = nxt_pid; + + /* Clean inherited cached thread tid. */ + task->thread->tid = 0; ptype = init->type; @@ -115,7 +265,9 @@ nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) /* Remove not ready processes. */ nxt_runtime_process_each(rt, p) { - if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) { + if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0 + && p->pid != nxt_ppid) /* Always keep parent's port. */ + { nxt_debug(task, "remove not required process %PI", p->pid); nxt_process_close_ports(task, p); @@ -139,7 +291,7 @@ nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) } -nxt_pid_t +static nxt_pid_t nxt_process_create(nxt_task_t *task, nxt_process_t *process) { nxt_int_t ret; @@ -168,9 +320,8 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process) return -1; } - nxt_runtime_process_add(task, process); - - if (nxt_slow_path(nxt_process_setup(task, process) != NXT_OK)) { + ret = nxt_process_setup(task, process); + if (nxt_slow_path(ret != NXT_OK)) { nxt_process_quit(task, 1); } @@ -190,6 +341,7 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process) #endif process->pid = pid; + process->isolated_pid = pid; nxt_runtime_process_add(task, process); @@ -201,7 +353,6 @@ static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process) { nxt_int_t ret; - nxt_port_t *port, *main_port; nxt_thread_t *thread; nxt_runtime_t *rt; nxt_process_init_t *init; @@ -241,17 +392,45 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process) return NXT_ERROR; } - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + nxt_port_read_close(process->parent_port); + nxt_port_write_enable(task, process->parent_port); - nxt_port_read_close(main_port); - nxt_port_write_enable(task, main_port); + /* + * If the parent process is already isolated, rt->pid_isolation is already + * set to 1 at this point. + */ + if (nxt_is_pid_isolated(process)) { + rt->is_pid_isolated = 1; + } + + if (rt->is_pid_isolated + || process->parent_port != rt->port_by_type[NXT_PROCESS_MAIN]) + { + ret = nxt_process_whoami(task, process); + + } else { + ret = nxt_process_do_start(task, process); + } + + return ret; +} + + +static nxt_int_t +nxt_process_do_start(nxt_task_t *task, nxt_process_t *process) +{ + nxt_int_t ret; + nxt_port_t *port; + nxt_process_init_t *init; + + nxt_runtime_process_add(task, process); + init = nxt_process_init(process); port = nxt_process_port_first(process); nxt_port_enable(task, port, init->port_handlers); ret = init->setup(task, process); - if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -288,6 +467,113 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process) static nxt_int_t +nxt_process_whoami(nxt_task_t *task, nxt_process_t *process) +{ + uint32_t stream; + nxt_fd_t fd; + nxt_buf_t *buf; + nxt_int_t ret; + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + + rt = task->thread->runtime; + + my_port = nxt_process_port_first(process); + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + + nxt_assert(my_port != NULL && main_port != NULL); + + nxt_port_enable(task, my_port, &nxt_process_whoami_port_handlers); + + buf = nxt_buf_mem_alloc(main_port->mem_pool, sizeof(nxt_pid_t), 0); + if (nxt_slow_path(buf == NULL)) { + return NXT_ERROR; + } + + buf->mem.free = nxt_cpymem(buf->mem.free, &nxt_ppid, sizeof(nxt_pid_t)); + + stream = nxt_port_rpc_register_handler(task, my_port, + nxt_process_whoami_ok, + nxt_process_whoami_error, + main_port->pid, process); + if (nxt_slow_path(stream == 0)) { + nxt_mp_free(main_port->mem_pool, buf); + + return NXT_ERROR; + } + + fd = (process->parent_port != main_port) ? my_port->pair[1] : -1; + + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_WHOAMI, + fd, stream, my_port->id, buf); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "%s failed to send WHOAMI message", process->name); + nxt_port_rpc_cancel(task, my_port, stream); + nxt_mp_free(main_port->mem_pool, buf); + + return NXT_ERROR; + } + + return NXT_OK; +} + + +static void +nxt_process_whoami_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) +{ + nxt_pid_t pid, isolated_pid; + nxt_buf_t *buf; + nxt_port_t *port; + nxt_process_t *process; + nxt_runtime_t *rt; + + process = data; + + buf = msg->buf; + + nxt_assert(nxt_buf_used_size(buf) == sizeof(nxt_pid_t)); + + nxt_memcpy(&pid, buf->mem.pos, sizeof(nxt_pid_t)); + + isolated_pid = nxt_pid; + + if (isolated_pid != pid) { + nxt_pid = pid; + process->pid = pid; + + nxt_process_port_each(process, port) { + port->pid = pid; + } nxt_process_port_loop; + } + + rt = task->thread->runtime; + + if (process->parent_port != rt->port_by_type[NXT_PROCESS_MAIN]) { + port = process->parent_port; + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_PROCESS_CREATED, + -1, 0, 0, NULL); + + nxt_log(task, NXT_LOG_INFO, "%s started", process->name); + } + + if (nxt_slow_path(nxt_process_do_start(task, process) != NXT_OK)) { + nxt_process_quit(task, 1); + } +} + + +static void +nxt_process_whoami_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) +{ + nxt_alert(task, "WHOAMI error"); + + nxt_process_quit(task, 1); +} + + +static nxt_int_t nxt_process_send_created(nxt_task_t *task, nxt_process_t *process) { uint32_t stream; @@ -336,6 +622,9 @@ nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) nxt_process_init_t *init; process = data; + + process->state = NXT_PROCESS_STATE_READY; + init = nxt_process_init(process); ret = nxt_process_apply_creds(task, process); @@ -345,11 +634,23 @@ nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) nxt_log(task, NXT_LOG_INFO, "%s started", process->name); + ret = nxt_process_send_ready(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + ret = init->start(task, &process->data); -fail: + if (nxt_process_type(process) != NXT_PROCESS_PROTOTYPE) { + nxt_port_write_close(nxt_process_port_first(process)); + } + + if (nxt_fast_path(ret == NXT_OK)) { + return; + } - nxt_process_quit(task, ret == NXT_OK ? 0 : 1); +fail: + nxt_process_quit(task, 1); } @@ -437,7 +738,8 @@ nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process) #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) if (!cap_setid - && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { + && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) + { cap_setid = 1; } #endif @@ -470,17 +772,10 @@ nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process) static nxt_int_t nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process) { - nxt_int_t ret; - nxt_port_t *main_port; - nxt_runtime_t *rt; - - rt = task->thread->runtime; - - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; - - nxt_assert(main_port != NULL); + nxt_int_t ret; - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY, + ret = nxt_port_socket_write(task, process->parent_port, + NXT_PORT_MSG_PROCESS_READY, -1, process->stream, 0, NULL); if (nxt_slow_path(ret != NXT_OK)) { @@ -680,17 +975,6 @@ nxt_nanosleep(nxt_nsec_t ns) void -nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i) -{ - process->use_count += i; - - if (process->use_count == 0) { - nxt_runtime_process_release(task->thread->runtime, process); - } -} - - -void nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port) { nxt_assert(port->process == NULL); diff --git a/src/nxt_process.h b/src/nxt_process.h index 4f24b179..c92eebd8 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -99,19 +99,26 @@ typedef struct { struct nxt_process_s { nxt_pid_t pid; - const char *name; - nxt_queue_t ports; /* of nxt_port_t */ + nxt_queue_t ports; /* of nxt_port_t.link */ nxt_process_state_t state; nxt_bool_t registered; nxt_int_t use_count; nxt_port_mmaps_t incoming; + + nxt_pid_t isolated_pid; + const char *name; + nxt_port_t *parent_port; + uint32_t stream; nxt_mp_t *mem_pool; nxt_credential_t *user_cred; + nxt_queue_t children; /* of nxt_process_t.link */ + nxt_queue_link_t link; /* for nxt_process_t.children */ + nxt_process_data_t data; nxt_process_isolation_t isolation; @@ -148,8 +155,6 @@ extern nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX]; extern nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX]; -NXT_EXPORT nxt_pid_t nxt_process_create(nxt_task_t *task, - nxt_process_t *process); NXT_EXPORT nxt_pid_t nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp); NXT_EXPORT nxt_int_t nxt_process_daemon(nxt_task_t *task); @@ -176,6 +181,10 @@ NXT_EXPORT void nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, #define nxt_process_port_loop \ nxt_queue_loop +nxt_process_t *nxt_process_new(nxt_runtime_t *rt); +void nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i); +nxt_int_t nxt_process_init_start(nxt_task_t *task, nxt_process_init_t init); +nxt_int_t nxt_process_start(nxt_task_t *task, nxt_process_t *process); nxt_process_type_t nxt_process_type(nxt_process_t *process); void nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i); diff --git a/src/nxt_process_type.h b/src/nxt_process_type.h index 14deda19..d0093431 100644 --- a/src/nxt_process_type.h +++ b/src/nxt_process_type.h @@ -13,6 +13,7 @@ typedef enum { NXT_PROCESS_DISCOVERY, NXT_PROCESS_CONTROLLER, NXT_PROCESS_ROUTER, + NXT_PROCESS_PROTOTYPE, NXT_PROCESS_APP, NXT_PROCESS_MAX, diff --git a/src/nxt_router.c b/src/nxt_router.c index 39d375f8..7623ccbb 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -27,7 +27,6 @@ typedef struct { uint32_t spare_processes; nxt_msec_t timeout; nxt_msec_t idle_timeout; - uint32_t requests; nxt_conf_value_t *limits_value; nxt_conf_value_t *processes_value; nxt_conf_value_t *targets_value; @@ -66,12 +65,14 @@ typedef struct { typedef struct { nxt_app_t *app; nxt_router_temp_conf_t *temp_conf; + uint8_t proto; /* 1 bit */ } nxt_app_rpc_t; typedef struct { nxt_app_joint_t *app_joint; uint32_t generation; + uint8_t proto; /* 1 bit */ } nxt_app_joint_rpc_t; @@ -228,8 +229,8 @@ static void nxt_router_app_port_error(nxt_task_t *task, static void nxt_router_app_use(nxt_task_t *task, nxt_app_t *app, int i); static void nxt_router_app_unlink(nxt_task_t *task, nxt_app_t *app); -static void nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, - nxt_apr_action_t action); +static void nxt_router_app_port_release(nxt_task_t *task, nxt_app_t *app, + nxt_port_t *port, nxt_apr_action_t action); static void nxt_router_app_port_get(nxt_task_t *task, nxt_app_t *app, nxt_request_rpc_data_t *req_rpc_data); static void nxt_router_http_request_error(nxt_task_t *task, void *obj, @@ -393,32 +394,52 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, { size_t size; uint32_t stream; - nxt_mp_t *mp; nxt_int_t ret; nxt_app_t *app; nxt_buf_t *b; - nxt_port_t *main_port; + nxt_port_t *dport; nxt_runtime_t *rt; nxt_app_joint_rpc_t *app_joint_rpc; app = data; - rt = task->thread->runtime; - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + nxt_thread_mutex_lock(&app->mutex); - nxt_debug(task, "app '%V' %p start process", &app->name, app); + dport = app->proto_port; - size = app->name.length + 1 + app->conf.length; + nxt_thread_mutex_unlock(&app->mutex); - b = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, size); + if (dport != NULL) { + nxt_debug(task, "app '%V' %p start process", &app->name, app); - if (nxt_slow_path(b == NULL)) { - goto failed; - } + b = NULL; - nxt_buf_cpystr(b, &app->name); - *b->mem.free++ = '\0'; - nxt_buf_cpystr(b, &app->conf); + } else { + if (app->proto_port_requests > 0) { + nxt_debug(task, "app '%V' %p wait for prototype process", + &app->name, app); + + app->proto_port_requests++; + + goto skip; + } + + nxt_debug(task, "app '%V' %p start prototype process", &app->name, app); + + rt = task->thread->runtime; + dport = rt->port_by_type[NXT_PROCESS_MAIN]; + + size = app->name.length + 1 + app->conf.length; + + b = nxt_buf_mem_alloc(task->thread->engine->mem_pool, size, 0); + if (nxt_slow_path(b == NULL)) { + goto failed; + } + + nxt_buf_cpystr(b, &app->name); + *b->mem.free++ = '\0'; + nxt_buf_cpystr(b, &app->conf); + } app_joint_rpc = nxt_port_rpc_register_handler_ex(task, port, nxt_router_app_port_ready, @@ -430,7 +451,7 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, stream = nxt_port_rpc_ex_stream(app_joint_rpc); - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, + ret = nxt_port_socket_write(task, dport, NXT_PORT_MSG_START_PROCESS, -1, stream, port->id, b); if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, port, stream); @@ -440,26 +461,23 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, app_joint_rpc->app_joint = app->joint; app_joint_rpc->generation = app->generation; + app_joint_rpc->proto = (b != NULL); - nxt_router_app_joint_use(task, app->joint, 1); + if (b != NULL) { + app->proto_port_requests++; - nxt_router_app_use(task, app, -1); + b = NULL; + } - return; + nxt_router_app_joint_use(task, app->joint, 1); failed: if (b != NULL) { - mp = b->data; - nxt_mp_free(mp, b); - nxt_mp_release(mp); + nxt_mp_free(b->data, b); } - nxt_thread_mutex_lock(&app->mutex); - - app->pending_processes--; - - nxt_thread_mutex_unlock(&app->mutex); +skip: nxt_router_app_use(task, app, -1); } @@ -583,14 +601,15 @@ nxt_request_rpc_data_unlink(nxt_task_t *task, nxt_router_msg_cancel(task, req_rpc_data); + app = req_rpc_data->app; + if (req_rpc_data->app_port != NULL) { - nxt_router_app_port_release(task, req_rpc_data->app_port, + nxt_router_app_port_release(task, app, req_rpc_data->app_port, req_rpc_data->apr_action); req_rpc_data->app_port = NULL; } - app = req_rpc_data->app; r = req_rpc_data->request; if (r != NULL) { @@ -658,6 +677,12 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_router_greet_controller(task, msg->u.new_port); } + if (port != NULL && port->type == NXT_PROCESS_PROTOTYPE) { + nxt_port_rpc_handler(task, msg); + + return; + } + if (port == NULL || port->type != NXT_PROCESS_APP) { if (msg->port_msg.stream == 0) { @@ -683,6 +708,8 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) return; } + nxt_debug(task, "new port id %d (%d)", port->id, port->type); + /* * Port with "id == 0" is application 'main' port and it always * should come with non-zero stream. @@ -819,7 +846,8 @@ nxt_router_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_app_t *app; nxt_int_t ret; nxt_str_t app_name; - nxt_port_t *port, *reply_port, *shared_port, *old_shared_port; + nxt_port_t *reply_port, *shared_port, *old_shared_port; + nxt_port_t *proto_port; nxt_port_msg_type_t reply; reply_port = nxt_runtime_port_find(task->thread->runtime, @@ -862,12 +890,15 @@ nxt_router_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_thread_mutex_lock(&app->mutex); - nxt_queue_each(port, &app->ports, nxt_port_t, app_link) { + proto_port = app->proto_port; - (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, - 0, 0, NULL); + if (proto_port != NULL) { + nxt_debug(task, "send QUIT to prototype '%V' pid %PI", &app->name, + proto_port->pid); - } nxt_queue_loop; + app->proto_port = NULL; + proto_port->app = NULL; + } app->generation++; @@ -883,6 +914,15 @@ nxt_router_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_port_close(task, old_shared_port); nxt_port_use(task, old_shared_port, -1); + if (proto_port != NULL) { + (void) nxt_port_socket_write(task, proto_port, NXT_PORT_MSG_QUIT, + -1, 0, 0, NULL); + + nxt_port_close(task, proto_port); + + nxt_port_use(task, proto_port, -1); + } + reply = NXT_PORT_MSG_RPC_READY_LAST; } else { @@ -1292,12 +1332,6 @@ static nxt_conf_map_t nxt_router_app_limits_conf[] = { NXT_CONF_MAP_MSEC, offsetof(nxt_router_app_conf_t, timeout), }, - - { - nxt_string("requests"), - NXT_CONF_MAP_INT32, - offsetof(nxt_router_app_conf_t, requests), - }, }; @@ -1566,7 +1600,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, apcf.spare_processes = 0; apcf.timeout = 0; apcf.idle_timeout = 15000; - apcf.requests = 0; apcf.limits_value = NULL; apcf.processes_value = NULL; apcf.targets_value = NULL; @@ -1646,7 +1679,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_debug(task, "application type: %V", &apcf.type); nxt_debug(task, "application processes: %D", apcf.processes); nxt_debug(task, "application request timeout: %M", apcf.timeout); - nxt_debug(task, "application requests: %D", apcf.requests); lang = nxt_app_lang_module(task->thread->runtime, &apcf.type); @@ -1677,7 +1709,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, ? apcf.spare_processes : 1; app->timeout = apcf.timeout; app->idle_timeout = apcf.idle_timeout; - app->max_requests = apcf.requests; app->targets = targets; @@ -2744,54 +2775,67 @@ nxt_router_app_rpc_create(nxt_task_t *task, uint32_t stream; nxt_int_t ret; nxt_buf_t *b; - nxt_port_t *main_port, *router_port; + nxt_port_t *router_port, *dport; nxt_runtime_t *rt; nxt_app_rpc_t *rpc; - rpc = nxt_mp_alloc(tmcf->mem_pool, sizeof(nxt_app_rpc_t)); - if (rpc == NULL) { - goto fail; - } + rt = task->thread->runtime; - rpc->app = app; - rpc->temp_conf = tmcf; + dport = app->proto_port; - nxt_debug(task, "app '%V' prefork", &app->name); + if (dport == NULL) { + nxt_debug(task, "app '%V' prototype prefork", &app->name); - size = app->name.length + 1 + app->conf.length; + size = app->name.length + 1 + app->conf.length; - b = nxt_buf_mem_alloc(tmcf->mem_pool, size, 0); - if (nxt_slow_path(b == NULL)) { - goto fail; - } + b = nxt_buf_mem_alloc(tmcf->mem_pool, size, 0); + if (nxt_slow_path(b == NULL)) { + goto fail; + } - b->completion_handler = nxt_buf_dummy_completion; + b->completion_handler = nxt_buf_dummy_completion; - nxt_buf_cpystr(b, &app->name); - *b->mem.free++ = '\0'; - nxt_buf_cpystr(b, &app->conf); + nxt_buf_cpystr(b, &app->name); + *b->mem.free++ = '\0'; + nxt_buf_cpystr(b, &app->conf); + + dport = rt->port_by_type[NXT_PROCESS_MAIN]; + + } else { + nxt_debug(task, "app '%V' prefork", &app->name); + + b = NULL; + } - rt = task->thread->runtime; - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; - stream = nxt_port_rpc_register_handler(task, router_port, + rpc = nxt_port_rpc_register_handler_ex(task, router_port, nxt_router_app_prefork_ready, nxt_router_app_prefork_error, - -1, rpc); - if (nxt_slow_path(stream == 0)) { + sizeof(nxt_app_rpc_t)); + if (nxt_slow_path(rpc == NULL)) { goto fail; } - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, - -1, stream, router_port->id, b); + rpc->app = app; + rpc->temp_conf = tmcf; + rpc->proto = (b != NULL); + + stream = nxt_port_rpc_ex_stream(rpc); + ret = nxt_port_socket_write(task, dport, + NXT_PORT_MSG_START_PROCESS, + -1, stream, router_port->id, b); if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, router_port, stream); goto fail; } - app->pending_processes++; + if (b == NULL) { + nxt_port_rpc_ex_set_peer(task, router_port, rpc, dport->pid); + + app->pending_processes++; + } return; @@ -2816,9 +2860,24 @@ nxt_router_app_prefork_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, port = msg->u.new_port; nxt_assert(port != NULL); - nxt_assert(port->type == NXT_PROCESS_APP); nxt_assert(port->id == 0); + if (rpc->proto) { + nxt_assert(app->proto_port == NULL); + nxt_assert(port->type == NXT_PROCESS_PROTOTYPE); + + nxt_port_inc_use(port); + + app->proto_port = port; + port->app = app; + + nxt_router_app_rpc_create(task, rpc->temp_conf, app); + + return; + } + + nxt_assert(port->type == NXT_PROCESS_APP); + port->app = app; port->main_app_port = port; @@ -2860,10 +2919,16 @@ nxt_router_app_prefork_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, app = rpc->app; tmcf = rpc->temp_conf; - nxt_log(task, NXT_LOG_WARN, "failed to start application \"%V\"", - &app->name); + if (rpc->proto) { + nxt_log(task, NXT_LOG_WARN, "failed to start prototype \"%V\"", + &app->name); - app->pending_processes--; + } else { + nxt_log(task, NXT_LOG_WARN, "failed to start application \"%V\"", + &app->name); + + app->pending_processes--; + } nxt_router_conf_error(task, tmcf); } @@ -4211,7 +4276,7 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_thread_mutex_unlock(&app->mutex); - nxt_router_app_port_release(task, app_port, NXT_APR_UPGRADE); + nxt_router_app_port_release(task, app, app_port, NXT_APR_UPGRADE); req_rpc_data->apr_action = NXT_APR_CLOSE; nxt_debug(task, "stream #%uD upgrade", req_rpc_data->stream); @@ -4422,8 +4487,9 @@ static void nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { + uint32_t n; nxt_app_t *app; - nxt_bool_t start_process; + nxt_bool_t start_process, restarted; nxt_port_t *port; nxt_app_joint_t *app_joint; nxt_app_joint_rpc_t *app_joint_rpc; @@ -4436,7 +4502,6 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_assert(app_joint != NULL); nxt_assert(port != NULL); - nxt_assert(port->type == NXT_PROCESS_APP); nxt_assert(port->id == 0); app = app_joint->app; @@ -4453,11 +4518,51 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_thread_mutex_lock(&app->mutex); + restarted = (app->generation != app_joint_rpc->generation); + + if (app_joint_rpc->proto) { + nxt_assert(app->proto_port == NULL); + nxt_assert(port->type == NXT_PROCESS_PROTOTYPE); + + n = app->proto_port_requests; + app->proto_port_requests = 0; + + if (nxt_slow_path(restarted)) { + nxt_thread_mutex_unlock(&app->mutex); + + nxt_debug(task, "proto port ready for restarted app, send QUIT"); + + nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, + NULL); + + } else { + port->app = app; + app->proto_port = port; + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_port_use(task, port, 1); + } + + port = task->thread->runtime->port_by_type[NXT_PROCESS_ROUTER]; + + while (n > 0) { + nxt_router_app_use(task, app, 1); + + nxt_router_start_app_process_handler(task, port, app); + + n--; + } + + return; + } + + nxt_assert(port->type == NXT_PROCESS_APP); nxt_assert(app->pending_processes != 0); app->pending_processes--; - if (nxt_slow_path(app->generation != app_joint_rpc->generation)) { + if (nxt_slow_path(restarted)) { nxt_debug(task, "new port ready for restarted app, send QUIT"); start_process = !task->thread->engine->shutdown @@ -4493,7 +4598,7 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_router_app_shared_port_send(task, port); - nxt_router_app_port_release(task, port, NXT_APR_NEW_PORT); + nxt_router_app_port_release(task, app, port, NXT_APR_NEW_PORT); } @@ -4600,7 +4705,6 @@ nxt_router_app_port_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, } - nxt_inline nxt_port_t * nxt_router_app_get_port_for_quit(nxt_task_t *task, nxt_app_t *app) { @@ -4670,19 +4774,15 @@ nxt_router_app_unlink(nxt_task_t *task, nxt_app_t *app) static void -nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, +nxt_router_app_port_release(nxt_task_t *task, nxt_app_t *app, nxt_port_t *port, nxt_apr_action_t action) { int inc_use; uint32_t got_response, dec_requests; - nxt_app_t *app; - nxt_bool_t port_unchained, send_quit, adjust_idle_timer; + nxt_bool_t adjust_idle_timer; nxt_port_t *main_app_port; nxt_assert(port != NULL); - nxt_assert(port->app != NULL); - - app = port->app; inc_use = 0; got_response = 0; @@ -4725,40 +4825,18 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, nxt_thread_mutex_lock(&app->mutex); - main_app_port->app_responses += got_response; main_app_port->active_requests -= got_response + dec_requests; app->active_requests -= got_response + dec_requests; - if (main_app_port->pair[1] != -1 - && (app->max_requests == 0 - || main_app_port->app_responses < app->max_requests)) - { - if (main_app_port->app_link.next == NULL) { - nxt_queue_insert_tail(&app->ports, &main_app_port->app_link); + if (main_app_port->pair[1] != -1 && main_app_port->app_link.next == NULL) { + nxt_queue_insert_tail(&app->ports, &main_app_port->app_link); - nxt_port_inc_use(main_app_port); - } - } - - send_quit = (app->max_requests > 0 - && main_app_port->app_responses >= app->max_requests); - - if (send_quit) { - port_unchained = nxt_queue_chk_remove(&main_app_port->app_link); - - nxt_port_hash_remove(&app->port_hash, main_app_port); - app->port_hash_count--; - - main_app_port->app = NULL; - app->processes--; - - } else { - port_unchained = 0; + nxt_port_inc_use(main_app_port); } adjust_idle_timer = 0; - if (main_app_port->pair[1] != -1 && !send_quit + if (main_app_port->pair[1] != -1 && main_app_port->active_requests == 0 && main_app_port->active_websockets == 0 && main_app_port->idle_link.next == NULL) @@ -4803,19 +4881,6 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, goto adjust_use; } - if (send_quit) { - nxt_debug(task, "app '%V' %p send QUIT to port", &app->name, app); - - nxt_port_socket_write(task, main_app_port, NXT_PORT_MSG_QUIT, -1, 0, 0, - NULL); - - if (port_unchained) { - nxt_port_use(task, main_app_port, -1); - } - - goto adjust_use; - } - nxt_debug(task, "app '%V' %p requests queue is empty, keep the port", &app->name, app); @@ -4839,6 +4904,20 @@ nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port) nxt_thread_mutex_lock(&app->mutex); + if (port == app->proto_port) { + app->proto_port = NULL; + port->app = NULL; + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_debug(task, "app '%V' prototype pid %PI closed", &app->name, + port->pid); + + nxt_port_use(task, port, -1); + + return; + } + nxt_port_hash_remove(&app->port_hash, port); app->port_hash_count--; @@ -5027,7 +5106,7 @@ static void nxt_router_free_app(nxt_task_t *task, void *obj, void *data) { nxt_app_t *app; - nxt_port_t *port; + nxt_port_t *port, *proto_port; nxt_app_joint_t *app_joint; app_joint = obj; @@ -5039,10 +5118,6 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) break; } - nxt_debug(task, "send QUIT to app '%V' pid %PI", &app->name, port->pid); - - nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); - nxt_port_use(task, port, -1); } @@ -5063,8 +5138,28 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) nxt_port_use(task, port, -1); } + proto_port = app->proto_port; + + if (proto_port != NULL) { + nxt_debug(task, "send QUIT to prototype '%V' pid %PI", &app->name, + proto_port->pid); + + app->proto_port = NULL; + proto_port->app = NULL; + } + nxt_thread_mutex_unlock(&app->mutex); + if (proto_port != NULL) { + nxt_port_socket_write(task, proto_port, NXT_PORT_MSG_QUIT, + -1, 0, 0, NULL); + + nxt_port_close(task, proto_port); + + nxt_port_use(task, proto_port, -1); + } + + nxt_assert(app->proto_port == NULL); nxt_assert(app->processes == 0); nxt_assert(app->active_requests == 0); nxt_assert(app->port_hash_count == 0); @@ -5501,8 +5596,8 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, *p++ = '\0'; } - req->query_length = r->args != NULL ? (uint32_t) r->args->length : 0; - if (r->args != NULL && r->args->start != NULL) { + req->query_length = (uint32_t) r->args->length; + if (r->args->start != NULL) { query_pos = nxt_pointer_to(target_pos, r->args->start - r->target.start); diff --git a/src/nxt_router.h b/src/nxt_router.h index fc068b53..7e337d27 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -124,9 +124,9 @@ struct nxt_app_s { uint32_t max_processes; uint32_t spare_processes; uint32_t max_pending_processes; - uint32_t max_requests; uint32_t generation; + uint32_t proto_port_requests; nxt_msec_t timeout; nxt_msec_t idle_timeout; @@ -145,6 +145,7 @@ struct nxt_app_s { nxt_app_joint_t *joint; nxt_port_t *shared_port; + nxt_port_t *proto_port; nxt_port_mmaps_t outgoing; }; diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 8a86d38a..46955f1c 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -40,8 +40,6 @@ static void nxt_runtime_thread_pool_init(void); static void nxt_runtime_thread_pool_exit(nxt_task_t *task, void *obj, void *data); static nxt_process_t *nxt_runtime_process_get(nxt_runtime_t *rt, nxt_pid_t pid); -static void nxt_runtime_process_remove(nxt_runtime_t *rt, - nxt_process_t *process); static void nxt_runtime_port_add(nxt_task_t *task, nxt_port_t *port); @@ -504,7 +502,9 @@ nxt_runtime_stop_app_processes(nxt_task_t *task, nxt_runtime_t *rt) init = nxt_process_init(process); - if (init->type == NXT_PROCESS_APP) { + if (init->type == NXT_PROCESS_APP + || init->type == NXT_PROCESS_PROTOTYPE) + { nxt_process_port_each(process, port) { @@ -528,6 +528,8 @@ nxt_runtime_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt) nxt_process_port_each(process, port) { + nxt_debug(task, "%d sending quit to %PI", rt->type, port->pid); + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); @@ -580,6 +582,7 @@ nxt_runtime_exit(nxt_task_t *task, void *obj, void *data) nxt_runtime_process_each(rt, process) { + nxt_runtime_process_remove(rt, process); nxt_process_close_ports(task, process); } nxt_runtime_process_loop; @@ -839,6 +842,21 @@ nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt) slash = "/"; } + ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%sversion%Z", + rt->state, slash); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + rt->ver = (char *) file_name.start; + + ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s.tmp%Z", rt->ver); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + rt->ver_tmp = (char *) file_name.start; + ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%sconf.json%Z", rt->state, slash); if (nxt_slow_path(ret != NXT_OK)) { @@ -1370,37 +1388,24 @@ nxt_runtime_pid_file_create(nxt_task_t *task, nxt_file_name_t *pid_file) } -nxt_process_t * -nxt_runtime_process_new(nxt_runtime_t *rt) -{ - nxt_process_t *process; - - /* TODO: memory failures. */ - - process = nxt_mp_zalloc(rt->mem_pool, - sizeof(nxt_process_t) + sizeof(nxt_process_init_t)); - - if (nxt_slow_path(process == NULL)) { - return NULL; - } - - nxt_queue_init(&process->ports); - - nxt_thread_mutex_create(&process->incoming.mutex); - - process->use_count = 1; - - return process; -} - - void nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process) { + nxt_process_t *child; + if (process->registered == 1) { nxt_runtime_process_remove(rt, process); } + if (process->link.next != NULL) { + nxt_queue_remove(&process->link); + } + + nxt_queue_each(child, &process->children, nxt_process_t, link) { + nxt_queue_remove(&child->link); + child->link.next = NULL; + } nxt_queue_loop; + nxt_assert(process->use_count == 0); nxt_assert(process->registered == 0); @@ -1497,7 +1502,7 @@ nxt_runtime_process_get(nxt_runtime_t *rt, nxt_pid_t pid) return process; } - process = nxt_runtime_process_new(rt); + process = nxt_process_new(rt); if (nxt_slow_path(process == NULL)) { nxt_thread_mutex_unlock(&rt->processes_mutex); @@ -1586,7 +1591,7 @@ nxt_runtime_process_add(nxt_task_t *task, nxt_process_t *process) } -static void +void nxt_runtime_process_remove(nxt_runtime_t *rt, nxt_process_t *process) { nxt_pid_t pid; diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index 0fb8c9a1..d7fe2f38 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -54,6 +54,7 @@ struct nxt_runtime_s { uint8_t daemon; uint8_t batch; uint8_t status; + uint8_t is_pid_isolated; const char *engine; uint32_t engine_connections; @@ -65,6 +66,8 @@ struct nxt_runtime_s { const char *log; const char *modules; const char *state; + const char *ver; + const char *ver_tmp; const char *conf; const char *conf_tmp; const char *control; @@ -92,9 +95,8 @@ nxt_int_t nxt_runtime_thread_pool_create(nxt_thread_t *thr, nxt_runtime_t *rt, nxt_uint_t max_threads, nxt_nsec_t timeout); -nxt_process_t *nxt_runtime_process_new(nxt_runtime_t *rt); - void nxt_runtime_process_add(nxt_task_t *task, nxt_process_t *process); +void nxt_runtime_process_remove(nxt_runtime_t *rt, nxt_process_t *process); nxt_process_t *nxt_runtime_process_find(nxt_runtime_t *rt, nxt_pid_t pid); diff --git a/src/nxt_socket.h b/src/nxt_socket.h index 7403de3d..ec21d779 100644 --- a/src/nxt_socket.h +++ b/src/nxt_socket.h @@ -114,8 +114,8 @@ NXT_EXPORT nxt_int_t nxt_socketpair_create(nxt_task_t *task, NXT_EXPORT void nxt_socketpair_close(nxt_task_t *task, nxt_socket_t *pair); NXT_EXPORT ssize_t nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob); -NXT_EXPORT ssize_t nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_fd_t *fd, - nxt_iobuf_t *iob, nxt_uint_t niob); +NXT_EXPORT ssize_t nxt_socketpair_recv(nxt_fd_event_t *ev, + nxt_iobuf_t *iob, nxt_uint_t niob, void *oob); #define \ diff --git a/src/nxt_socket_msg.c b/src/nxt_socket_msg.c new file mode 100644 index 00000000..3b35ab29 --- /dev/null +++ b/src/nxt_socket_msg.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <nxt_socket_msg.h> + + +ssize_t +nxt_sendmsg(nxt_socket_t s, nxt_iobuf_t *iob, nxt_uint_t niob, + const nxt_send_oob_t *oob) +{ + struct msghdr msg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iob; + msg.msg_iovlen = niob; + /* Flags are cleared just to suppress valgrind warning. */ + msg.msg_flags = 0; + + if (oob != NULL && oob->size != 0) { + msg.msg_control = (void *) oob->buf; + msg.msg_controllen = oob->size; + + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + return sendmsg(s, &msg, 0); +} + + +ssize_t +nxt_recvmsg(nxt_socket_t s, nxt_iobuf_t *iob, nxt_uint_t niob, + nxt_recv_oob_t *oob) +{ + ssize_t n; + struct msghdr msg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iob; + msg.msg_iovlen = niob; + msg.msg_control = oob->buf; + msg.msg_controllen = sizeof(oob->buf); + + n = recvmsg(s, &msg, 0); + + if (nxt_fast_path(n != -1)) { + oob->size = msg.msg_controllen; + } + + return n; +} diff --git a/src/nxt_socket_msg.h b/src/nxt_socket_msg.h new file mode 100644 index 00000000..04de1761 --- /dev/null +++ b/src/nxt_socket_msg.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_SOCKET_MSG_H_INCLUDED_ +#define _NXT_SOCKET_MSG_H_INCLUDED_ + +#if (NXT_HAVE_UCRED) +#include <sys/un.h> +#endif + + +#if (NXT_HAVE_UCRED) +#define NXT_CRED_USECMSG 1 +#define NXT_CRED_CMSGTYPE SCM_CREDENTIALS +#define NXT_CRED_GETPID(u) (u->pid) + +typedef struct ucred nxt_socket_cred_t; + +#elif (NXT_HAVE_MSGHDR_CMSGCRED) +#define NXT_CRED_USECMSG 1 +#define NXT_CRED_CMSGTYPE SCM_CREDS +#define NXT_CRED_GETPID(u) (u->cmcred_pid) + +typedef struct cmsgcred nxt_socket_cred_t; +#endif + +#if (NXT_CRED_USECMSG) +#define NXT_OOB_RECV_SIZE \ + (CMSG_SPACE(2 * sizeof(int)) + CMSG_SPACE(sizeof(nxt_socket_cred_t))) +#else +#define NXT_OOB_RECV_SIZE \ + CMSG_SPACE(2 * sizeof(int)) +#endif + +#if (NXT_HAVE_MSGHDR_CMSGCRED) +#define NXT_OOB_SEND_SIZE \ + (CMSG_SPACE(2 * sizeof(int)) + CMSG_SPACE(sizeof(nxt_socket_cred_t))) +#else +#define NXT_OOB_SEND_SIZE \ + CMSG_SPACE(2 * sizeof(int)) +#endif + + +typedef struct { + size_t size; + u_char buf[NXT_OOB_RECV_SIZE]; +} nxt_recv_oob_t; + + +typedef struct { + size_t size; + u_char buf[NXT_OOB_SEND_SIZE]; +} nxt_send_oob_t; + + +/** + * The nxt_sendmsg is a wrapper for sendmsg. + * The oob struct must be initialized using nxt_socket_msg_oob_init(). + */ +NXT_EXPORT ssize_t nxt_sendmsg(nxt_socket_t s, nxt_iobuf_t *iob, + nxt_uint_t niob, const nxt_send_oob_t *oob); + +/** + * The nxt_recvmsg is a wrapper for recvmsg. + * The oob buffer must be consumed by using nxt_socket_msg_oob_get(). + */ +NXT_EXPORT ssize_t nxt_recvmsg(nxt_socket_t s, + nxt_iobuf_t *iob, nxt_uint_t niob, nxt_recv_oob_t *oob); + + +nxt_inline void +nxt_socket_msg_oob_init(nxt_send_oob_t *oob, int *fds) +{ + int nfds; + struct cmsghdr *cmsg; + +#if (NXT_HAVE_MSGHDR_CMSGCRED) + cmsg = (struct cmsghdr *) (oob->buf); + /* + * Fill all padding fields with 0. + * Code in Go 1.11 validate cmsghdr using padding field as part of len. + * See Cmsghdr definition and socketControlMessageHeaderAndData function. + */ + nxt_memzero(cmsg, sizeof(struct cmsghdr)); + + cmsg->cmsg_len = CMSG_LEN(sizeof(nxt_socket_cred_t)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = NXT_CRED_CMSGTYPE; + + oob->size = CMSG_SPACE(sizeof(nxt_socket_cred_t)); + +#else + oob->size = 0; +#endif + + nfds = (fds[0] != -1 ? 1 : 0) + (fds[1] != -1 ? 1 : 0); + + if (nfds == 0) { + return; + } + + cmsg = (struct cmsghdr *) (oob->buf + oob->size); + + nxt_memzero(cmsg, sizeof(struct cmsghdr)); + + cmsg->cmsg_len = CMSG_LEN(nfds * sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + /* + * nxt_memcpy() is used instead of simple + * *(int *) CMSG_DATA(&cmsg.cm) = fd; + * because GCC 4.4 with -O2/3/s optimization may issue a warning: + * dereferencing type-punned pointer will break strict-aliasing rules + * + * Fortunately, GCC with -O1 compiles this nxt_memcpy() + * in the same simple assignment as in the code above. + */ + nxt_memcpy(CMSG_DATA(cmsg), fds, nfds * sizeof(int)); + + oob->size += CMSG_SPACE(nfds * sizeof(int)); +} + + +nxt_inline nxt_int_t +nxt_socket_msg_oob_get_fds(nxt_recv_oob_t *oob, nxt_fd_t *fd) +{ + size_t size; + struct msghdr msg; + struct cmsghdr *cmsg; + + msg.msg_control = oob->buf; + msg.msg_controllen = oob->size; + + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + size = cmsg->cmsg_len - CMSG_LEN(0); + + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + if (nxt_slow_path(size != sizeof(int) && size != 2 * sizeof(int))) { + return NXT_ERROR; + } + + nxt_memcpy(fd, CMSG_DATA(cmsg), size); + + return NXT_OK; + } + } + + return NXT_OK; +} + + +nxt_inline nxt_int_t +nxt_socket_msg_oob_get(nxt_recv_oob_t *oob, nxt_fd_t *fd, nxt_pid_t *pid) +{ + size_t size; + struct msghdr msg; + struct cmsghdr *cmsg; + + if (oob->size == 0) { + return NXT_OK; + } + +#if (NXT_CRED_USECMSG) + *pid = -1; +#endif + + msg.msg_control = oob->buf; + msg.msg_controllen = oob->size; + + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + size = cmsg->cmsg_len - CMSG_LEN(0); + + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + if (nxt_slow_path(size != sizeof(int) && size != 2 * sizeof(int))) { + return NXT_ERROR; + } + + nxt_memcpy(fd, CMSG_DATA(cmsg), size); + +#if (!NXT_CRED_USECMSG) + break; +#endif + } + +#if (NXT_CRED_USECMSG) + else if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == NXT_CRED_CMSGTYPE) + { + nxt_socket_cred_t *creds; + + if (nxt_slow_path(size != sizeof(nxt_socket_cred_t))) { + return NXT_ERROR; + } + + creds = (nxt_socket_cred_t *) CMSG_DATA(cmsg); + *pid = NXT_CRED_GETPID(creds); + } +#endif + } + +#if (NXT_CRED_USECMSG) + /* For platforms supporting credential passing, it's enforced */ + if (nxt_slow_path(*pid == -1)) { + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#endif /* _NXT_SOCKET_MSG_H_INCLUDED_ */ diff --git a/src/nxt_socketpair.c b/src/nxt_socketpair.c index 8b9d12bf..45274b78 100644 --- a/src/nxt_socketpair.c +++ b/src/nxt_socketpair.c @@ -5,7 +5,7 @@ */ #include <nxt_main.h> - +#include <nxt_socket_msg.h> /* * SOCK_SEQPACKET protocol is supported for AF_UNIX in Solaris 8 X/Open @@ -20,12 +20,6 @@ #endif -static ssize_t nxt_sendmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, - nxt_uint_t niob); -static ssize_t nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, - nxt_uint_t niob); - - nxt_int_t nxt_socketpair_create(nxt_task_t *task, nxt_socket_t *pair) { @@ -52,6 +46,24 @@ nxt_socketpair_create(nxt_task_t *task, nxt_socket_t *pair) goto fail; } +#if NXT_HAVE_SOCKOPT_SO_PASSCRED + int enable_creds = 1; + + if (nxt_slow_path(setsockopt(pair[0], SOL_SOCKET, SO_PASSCRED, + &enable_creds, sizeof(enable_creds)) == -1)) + { + nxt_alert(task, "failed to set SO_PASSCRED %E", nxt_errno); + goto fail; + } + + if (nxt_slow_path(setsockopt(pair[1], SOL_SOCKET, SO_PASSCRED, + &enable_creds, sizeof(enable_creds)) == -1)) + { + nxt_alert(task, "failed to set SO_PASSCRED %E", nxt_errno); + goto fail; + } +#endif + return NXT_OK; fail: @@ -74,11 +86,14 @@ ssize_t nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob) { - ssize_t n; - nxt_err_t err; + ssize_t n; + nxt_err_t err; + nxt_send_oob_t oob; + + nxt_socket_msg_oob_init(&oob, fd); for ( ;; ) { - n = nxt_sendmsg(ev->fd, fd, iob, niob); + n = nxt_sendmsg(ev->fd, iob, niob, &oob); err = (n == -1) ? nxt_socket_errno : 0; @@ -123,19 +138,19 @@ nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob, ssize_t -nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob, - nxt_uint_t niob) +nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_iobuf_t *iob, nxt_uint_t niob, + void *oob) { ssize_t n; nxt_err_t err; for ( ;; ) { - n = nxt_recvmsg(ev->fd, fd, iob, niob); + n = nxt_recvmsg(ev->fd, iob, niob, oob); err = (n == -1) ? nxt_socket_errno : 0; - nxt_debug(ev->task, "recvmsg(%d, %FD, %FD, %ui): %z", ev->fd, fd[0], - fd[1], niob, n); + nxt_debug(ev->task, "recvmsg(%d, %ui, %uz): %z", + ev->fd, niob, ((nxt_recv_oob_t *) oob)->size, n); if (n > 0) { return n; @@ -163,162 +178,10 @@ nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob, continue; default: - nxt_alert(ev->task, "recvmsg(%d, %p, %ui) failed %E", - ev->fd, fd, niob, err); + nxt_alert(ev->task, "recvmsg(%d, %ui) failed %E", + ev->fd, niob, err); return NXT_ERROR; } } } - - -#if (NXT_HAVE_MSGHDR_MSG_CONTROL) - -/* - * Linux, FreeBSD, Solaris X/Open sockets, - * MacOSX, NetBSD, AIX, HP-UX X/Open sockets. - */ - -static ssize_t -nxt_sendmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob) -{ - size_t csize; - struct msghdr msg; - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int) * 2)]; - } cmsg; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iob; - msg.msg_iovlen = niob; - /* Flags are cleared just to suppress valgrind warning. */ - msg.msg_flags = 0; - - if (fd[0] != -1) { - csize = (fd[1] == -1) ? sizeof(int) : sizeof(int) * 2; - - msg.msg_control = (caddr_t) &cmsg; - msg.msg_controllen = CMSG_SPACE(csize); - -#if (NXT_VALGRIND) - nxt_memzero(&cmsg, sizeof(cmsg)); -#endif - - cmsg.cm.cmsg_len = CMSG_LEN(csize); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - /* - * nxt_memcpy() is used instead of simple - * *(int *) CMSG_DATA(&cmsg.cm) = fd; - * because GCC 4.4 with -O2/3/s optimization may issue a warning: - * dereferencing type-punned pointer will break strict-aliasing rules - * - * Fortunately, GCC with -O1 compiles this nxt_memcpy() - * in the same simple assignment as in the code above. - */ - nxt_memcpy(CMSG_DATA(&cmsg.cm), fd, csize); - - } else { - msg.msg_control = NULL; - msg.msg_controllen = 0; - } - - return sendmsg(s, &msg, 0); -} - - -static ssize_t -nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob) -{ - ssize_t n; - struct msghdr msg; - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int) * 2)]; - } cmsg; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iob; - msg.msg_iovlen = niob; - msg.msg_control = (caddr_t) &cmsg; - msg.msg_controllen = sizeof(cmsg); - - fd[0] = -1; - fd[1] = -1; - -#if (NXT_VALGRIND) - nxt_memzero(&cmsg, sizeof(cmsg)); -#endif - - n = recvmsg(s, &msg, 0); - - if (n > 0 - && cmsg.cm.cmsg_level == SOL_SOCKET - && cmsg.cm.cmsg_type == SCM_RIGHTS) - { - if (cmsg.cm.cmsg_len == CMSG_LEN(sizeof(int))) { - nxt_memcpy(fd, CMSG_DATA(&cmsg.cm), sizeof(int)); - } - - if (cmsg.cm.cmsg_len == CMSG_LEN(sizeof(int) * 2)) { - nxt_memcpy(fd, CMSG_DATA(&cmsg.cm), sizeof(int) * 2); - } - } - - return n; -} - -#else - -/* Solaris 4.3BSD sockets. */ - -static ssize_t -nxt_sendmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob) -{ - struct msghdr msg; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iob; - msg.msg_iovlen = niob; - - if (fd[0] != -1) { - msg.msg_accrights = (caddr_t) fd; - msg.msg_accrightslen = sizeof(int); - - if (fd[1] != -1) { - msg.msg_accrightslen += sizeof(int); - } - - } else { - msg.msg_accrights = NULL; - msg.msg_accrightslen = 0; - } - - return sendmsg(s, &msg, 0); -} - - -static ssize_t -nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob) -{ - struct msghdr msg; - - fd[0] = -1; - fd[1] = -1; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iob; - msg.msg_iovlen = niob; - msg.msg_accrights = (caddr_t) fd; - msg.msg_accrightslen = sizeof(int) * 2; - - return recvmsg(s, &msg, 0); -} - -#endif diff --git a/src/nxt_string.c b/src/nxt_string.c index ab568990..b7aef79e 100644 --- a/src/nxt_string.c +++ b/src/nxt_string.c @@ -745,3 +745,100 @@ nxt_is_complex_uri_encoded(u_char *src, size_t length) return 1; } + + +ssize_t +nxt_base64_decode(u_char *dst, u_char *src, size_t length) +{ + u_char *end, *p; + size_t pad; + uint8_t v1, v2, v3, v4; + + static const uint8_t decode[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + end = src + length; + pad = (4 - (length % 4)) % 4; + + if (dst == NULL) { + if (pad > 2) { + return NXT_ERROR; + } + + while (src < end) { + if (decode[*src] != 77) { + src++; + continue; + } + + if (pad == 0) { + pad = end - src; + + if ((pad == 1 || (pad == 2 && src[1] == '=')) && src[0] == '=') + { + break; + } + } + + return NXT_ERROR; + } + + return (length + 3) / 4 * 3 - pad; + } + + nxt_assert(length != 0); + + if (pad == 0) { + pad = (end[-1] == '=') + (end[-2] == '='); + end -= (pad + 3) & 4; + + } else { + end -= 4 - pad; + } + + p = dst; + + while (src < end) { + v1 = decode[src[0]]; + v2 = decode[src[1]]; + v3 = decode[src[2]]; + v4 = decode[src[3]]; + + *p++ = (v1 << 2 | v2 >> 4); + *p++ = (v2 << 4 | v3 >> 2); + *p++ = (v3 << 6 | v4); + + src += 4; + } + + if (pad > 0) { + v1 = decode[src[0]]; + v2 = decode[src[1]]; + + *p++ = (v1 << 2 | v2 >> 4); + + if (pad == 1) { + v3 = decode[src[2]]; + *p++ = (v2 << 4 | v3 >> 2); + } + } + + return (p - dst); +} diff --git a/src/nxt_string.h b/src/nxt_string.h index 7e02f59a..4d565e87 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -190,6 +190,8 @@ NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length); NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length); +NXT_EXPORT ssize_t nxt_base64_decode(u_char *dst, u_char *src, size_t length); + extern const uint8_t nxt_hex2int[256]; diff --git a/src/nxt_thread.h b/src/nxt_thread.h index d7800cc6..2ebc331d 100644 --- a/src/nxt_thread.h +++ b/src/nxt_thread.h @@ -142,14 +142,6 @@ nxt_thread_yield() \ #endif -#if (PTHREAD_STACK_MIN) -#define NXT_THREAD_STACK_MIN PTHREAD_STACK_MIN - -#else -#define NXT_THREAD_STACK_MIN sysconf(_SC_THREAD_STACK_MIN) -#endif - - struct nxt_thread_s { nxt_log_t *log; nxt_log_t main_log; diff --git a/src/nxt_tls.h b/src/nxt_tls.h index eeb4e7ba..0667ade3 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -92,28 +92,12 @@ struct nxt_tls_init_s { }; -struct nxt_tls_ticket_s { - uint8_t aes128; - u_char name[16]; - u_char hmac_key[32]; - u_char aes_key[32]; -}; - - -struct nxt_tls_tickets_s { - nxt_uint_t count; - nxt_tls_ticket_t tickets[]; -}; - - #if (NXT_HAVE_OPENSSL) extern const nxt_tls_lib_t nxt_openssl_lib; void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...); u_char *nxt_openssl_copy_error(u_char *p, u_char *end); -nxt_int_t nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, - size_t slen); #endif #if (NXT_HAVE_GNUTLS) diff --git a/src/nxt_unit.c b/src/nxt_unit.c index ae4499d8..06ad1636 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -3,10 +3,9 @@ * Copyright (C) NGINX, Inc. */ -#include <stdlib.h> - #include "nxt_main.h" #include "nxt_port_memory_int.h" +#include "nxt_socket_msg.h" #include "nxt_port_queue.h" #include "nxt_app_queue.h" @@ -25,6 +24,11 @@ #define NXT_UNIT_LOCAL_BUF_SIZE \ (NXT_UNIT_MAX_PLAIN_SIZE + sizeof(nxt_port_msg_t)) +enum { + NXT_QUIT_NORMAL = 0, + NXT_QUIT_GRACEFUL = 1, +}; + typedef struct nxt_unit_impl_s nxt_unit_impl_t; typedef struct nxt_unit_mmap_s nxt_unit_mmap_t; typedef struct nxt_unit_mmaps_s nxt_unit_mmaps_t; @@ -51,7 +55,8 @@ nxt_inline void nxt_unit_mmap_buf_insert_tail(nxt_unit_mmap_buf_t **prev, nxt_inline void nxt_unit_mmap_buf_unlink(nxt_unit_mmap_buf_t *mmap_buf); static int nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, nxt_unit_port_t *read_port, - int *log_fd, uint32_t *stream, uint32_t *shm_limit); + int *log_fd, uint32_t *stream, uint32_t *shm_limit, + uint32_t *request_limit); static int nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd); static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, @@ -130,6 +135,7 @@ static nxt_unit_process_t *nxt_unit_process_find(nxt_unit_impl_t *lib, static nxt_unit_process_t *nxt_unit_process_pop_first(nxt_unit_impl_t *lib); static int nxt_unit_run_once_impl(nxt_unit_ctx_t *ctx); static int nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf); +static int nxt_unit_chk_ready(nxt_unit_ctx_t *ctx); static int nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx); static void nxt_unit_process_ready_req(nxt_unit_ctx_t *ctx); nxt_inline int nxt_unit_is_read_queue(nxt_unit_read_buf_t *rbuf); @@ -150,20 +156,20 @@ static nxt_unit_port_t *nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue); static void nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx, nxt_queue_t *awaiting_req); -static void nxt_unit_remove_port(nxt_unit_impl_t *lib, +static void nxt_unit_remove_port(nxt_unit_impl_t *lib, nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id); static nxt_unit_port_t *nxt_unit_remove_port_unsafe(nxt_unit_impl_t *lib, nxt_unit_port_id_t *port_id); static void nxt_unit_remove_pid(nxt_unit_impl_t *lib, pid_t pid); static void nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process); -static void nxt_unit_quit(nxt_unit_ctx_t *ctx); +static void nxt_unit_quit(nxt_unit_ctx_t *ctx, uint8_t quit_param); static int nxt_unit_get_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id); static ssize_t nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, const void *buf, size_t buf_size, - const void *oob, size_t oob_size); + const nxt_send_oob_t *oob); static ssize_t nxt_unit_sendmsg(nxt_unit_ctx_t *ctx, int fd, - const void *buf, size_t buf_size, const void *oob, size_t oob_size); + const void *buf, size_t buf_size, const nxt_send_oob_t *oob); static int nxt_unit_ctx_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf); nxt_inline void nxt_unit_rbuf_cpy(nxt_unit_read_buf_t *dst, @@ -174,7 +180,7 @@ static int nxt_unit_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf); static int nxt_unit_port_queue_recv(nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf); -static int nxt_unit_app_queue_recv(nxt_unit_port_t *port, +static int nxt_unit_app_queue_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf); nxt_inline int nxt_unit_close(int fd); static int nxt_unit_fd_blocking(int fd); @@ -271,8 +277,8 @@ struct nxt_unit_read_buf_s { nxt_queue_link_t link; nxt_unit_ctx_impl_t *ctx_impl; ssize_t size; + nxt_recv_oob_t oob; char buf[16384]; - char oob[256]; }; @@ -311,8 +317,9 @@ struct nxt_unit_ctx_impl_s { /* of nxt_unit_read_buf_t */ nxt_queue_t free_rbuf; - int online; - int ready; + uint8_t online; /* 1 bit */ + uint8_t ready; /* 1 bit */ + uint8_t quit_param; nxt_unit_mmap_buf_t ctx_buf[2]; nxt_unit_read_buf_t ctx_read_buf; @@ -344,9 +351,11 @@ struct nxt_unit_impl_s { nxt_unit_callbacks_t callbacks; nxt_atomic_t use_count; + nxt_atomic_t request_count; uint32_t request_data_size; uint32_t shm_mmap_limit; + uint32_t request_limit; pthread_mutex_t mutex; @@ -409,16 +418,21 @@ typedef struct { } nxt_unit_port_hash_id_t; +static pid_t nxt_unit_pid; + + nxt_unit_ctx_t * nxt_unit_init(nxt_unit_init_t *init) { int rc, queue_fd; void *mem; - uint32_t ready_stream, shm_limit; + uint32_t ready_stream, shm_limit, request_limit; nxt_unit_ctx_t *ctx; nxt_unit_impl_t *lib; nxt_unit_port_t ready_port, router_port, read_port; + nxt_unit_pid = getpid(); + lib = nxt_unit_create(init); if (nxt_slow_path(lib == NULL)) { return NULL; @@ -446,13 +460,15 @@ nxt_unit_init(nxt_unit_init_t *init) } else { rc = nxt_unit_read_env(&ready_port, &router_port, &read_port, - &lib->log_fd, &ready_stream, &shm_limit); + &lib->log_fd, &ready_stream, &shm_limit, + &request_limit); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } lib->shm_mmap_limit = (shm_limit + PORT_MMAP_DATA_SIZE - 1) / PORT_MMAP_DATA_SIZE; + lib->request_limit = request_limit; } if (nxt_slow_path(lib->shm_mmap_limit < 1)) { @@ -460,6 +476,7 @@ nxt_unit_init(nxt_unit_init_t *init) } lib->pid = read_port.id.pid; + nxt_unit_pid = lib->pid; ctx = &lib->main_ctx.ctx; @@ -564,6 +581,7 @@ nxt_unit_create(nxt_unit_init_t *init) lib->request_data_size = init->request_data_size; lib->shm_mmap_limit = (init->shm_limit + PORT_MMAP_DATA_SIZE - 1) / PORT_MMAP_DATA_SIZE; + lib->request_limit = init->request_limit; lib->processes.slot = NULL; lib->ports.slot = NULL; @@ -573,6 +591,7 @@ nxt_unit_create(nxt_unit_init_t *init) nxt_queue_init(&lib->contexts); lib->use_count = 0; + lib->request_count = 0; lib->router_port = NULL; lib->shared_port = NULL; @@ -632,6 +651,7 @@ nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl, ctx_impl->wait_items = 0; ctx_impl->online = 1; ctx_impl->ready = 0; + ctx_impl->quit_param = NXT_QUIT_GRACEFUL; nxt_queue_init(&ctx_impl->free_req); nxt_queue_init(&ctx_impl->free_ws); @@ -780,7 +800,7 @@ nxt_unit_mmap_buf_unlink(nxt_unit_mmap_buf_t *mmap_buf) static int nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, nxt_unit_port_t *read_port, int *log_fd, uint32_t *stream, - uint32_t *shm_limit) + uint32_t *shm_limit, uint32_t *request_limit) { int rc; int ready_fd, router_fd, read_in_fd, read_out_fd; @@ -825,12 +845,12 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, "%"PRId64",%"PRIu32",%d;" "%"PRId64",%"PRIu32",%d;" "%"PRId64",%"PRIu32",%d,%d;" - "%d,%"PRIu32, + "%d,%"PRIu32",%"PRIu32, &ready_stream, &ready_pid, &ready_id, &ready_fd, &router_pid, &router_id, &router_fd, &read_pid, &read_id, &read_in_fd, &read_out_fd, - log_fd, shm_limit); + log_fd, shm_limit, request_limit); if (nxt_slow_path(rc == EOF)) { nxt_unit_alert(NULL, "sscanf(%s) failed: %s (%d) for %s env", @@ -839,9 +859,9 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port, return NXT_UNIT_ERROR; } - if (nxt_slow_path(rc != 13)) { + if (nxt_slow_path(rc != 14)) { nxt_unit_alert(NULL, "invalid number of variables in %s env: " - "found %d of %d in %s", NXT_UNIT_INIT_ENV, rc, 13, vars); + "found %d of %d in %s", NXT_UNIT_INIT_ENV, rc, 14, vars); return NXT_UNIT_ERROR; } @@ -876,13 +896,10 @@ static int nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd) { ssize_t res; + nxt_send_oob_t oob; nxt_port_msg_t msg; nxt_unit_impl_t *lib; - - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int))]; - } cmsg; + int fds[2] = {queue_fd, -1}; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); @@ -896,25 +913,9 @@ nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd) msg.mf = 0; msg.tracking = 0; - memset(&cmsg, 0, sizeof(cmsg)); - - cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - /* - * memcpy() is used instead of simple - * *(int *) CMSG_DATA(&cmsg.cm) = fd; - * because GCC 4.4 with -O2/3/s optimization may issue a warning: - * dereferencing type-punned pointer will break strict-aliasing rules - * - * Fortunately, GCC with -O1 compiles this nxt_memcpy() - * in the same simple assignment as in the code above. - */ - memcpy(CMSG_DATA(&cmsg.cm), &queue_fd, sizeof(int)); + nxt_socket_msg_oob_init(&oob, fds); - res = nxt_unit_sendmsg(ctx, ready_fd, &msg, sizeof(msg), - &cmsg, sizeof(cmsg)); + res = nxt_unit_sendmsg(ctx, ready_fd, &msg, sizeof(msg), &oob); if (res != sizeof(msg)) { return NXT_UNIT_ERROR; } @@ -929,7 +930,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, { int rc; pid_t pid; - struct cmsghdr *cm; + uint8_t quit_param; nxt_port_msg_t *port_msg; nxt_unit_impl_t *lib; nxt_unit_recv_msg_t recv_msg; @@ -939,18 +940,12 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, recv_msg.fd[0] = -1; recv_msg.fd[1] = -1; port_msg = (nxt_port_msg_t *) rbuf->buf; - cm = (struct cmsghdr *) rbuf->oob; - if (cm->cmsg_level == SOL_SOCKET - && cm->cmsg_type == SCM_RIGHTS) - { - if (cm->cmsg_len == CMSG_LEN(sizeof(int))) { - memcpy(recv_msg.fd, CMSG_DATA(cm), sizeof(int)); - } - - if (cm->cmsg_len == CMSG_LEN(sizeof(int) * 2)) { - memcpy(recv_msg.fd, CMSG_DATA(cm), sizeof(int) * 2); - } + rc = nxt_socket_msg_oob_get_fds(&rbuf->oob, recv_msg.fd); + if (nxt_slow_path(rc != NXT_OK)) { + nxt_unit_alert(ctx, "failed to receive file descriptor over cmsg"); + rc = NXT_UNIT_ERROR; + goto done; } recv_msg.incoming_buf = NULL; @@ -959,7 +954,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, if (nxt_slow_path(rbuf->size == 0)) { nxt_unit_debug(ctx, "read port closed"); - nxt_unit_quit(ctx); + nxt_unit_quit(ctx, NXT_QUIT_GRACEFUL); rc = NXT_UNIT_OK; goto done; } @@ -1018,9 +1013,18 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf, break; case _NXT_PORT_MSG_QUIT: - nxt_unit_debug(ctx, "#%"PRIu32": quit", port_msg->stream); + if (recv_msg.size == sizeof(quit_param)) { + memcpy(&quit_param, recv_msg.start, sizeof(quit_param)); + + } else { + quit_param = NXT_QUIT_NORMAL; + } + + nxt_unit_debug(ctx, "#%"PRIu32": %squit", port_msg->stream, + (quit_param == NXT_QUIT_GRACEFUL ? "graceful " : "")); + + nxt_unit_quit(ctx, quit_param); - nxt_unit_quit(ctx); rc = NXT_UNIT_OK; break; @@ -1220,15 +1224,36 @@ nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx) nxt_unit_impl_t *lib; nxt_unit_ctx_impl_t *ctx_impl; - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + if (nxt_slow_path(ctx_impl->ready)) { + return NXT_UNIT_OK; + } + ctx_impl->ready = 1; - if (lib->callbacks.ready_handler) { + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + /* Call ready_handler() only for main context. */ + if (&lib->main_ctx == ctx_impl && lib->callbacks.ready_handler != NULL) { return lib->callbacks.ready_handler(ctx); } + if (&lib->main_ctx != ctx_impl) { + /* Check if the main context is already stopped or quit. */ + if (nxt_slow_path(!lib->main_ctx.ready)) { + ctx_impl->ready = 0; + + nxt_unit_quit(ctx, lib->main_ctx.quit_param); + + return NXT_UNIT_OK; + } + + if (lib->callbacks.add_port != NULL) { + lib->callbacks.add_port(ctx, lib->shared_port); + } + } + return NXT_UNIT_OK; } @@ -1561,7 +1586,7 @@ nxt_unit_send_req_headers_ack(nxt_unit_request_info_t *req) msg.type = _NXT_PORT_MSG_REQ_HEADERS_ACK; res = nxt_unit_port_send(req->ctx, req->response_port, - &msg, sizeof(msg), NULL, 0); + &msg, sizeof(msg), NULL); if (nxt_slow_path(res != sizeof(msg))) { return NXT_UNIT_ERROR; } @@ -1741,10 +1766,12 @@ nxt_unit_request_info_get(nxt_unit_ctx_t *ctx) static void nxt_unit_request_info_release(nxt_unit_request_info_t *req) { + nxt_unit_ctx_t *ctx; nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_request_info_impl_t *req_impl; - ctx_impl = nxt_container_of(req->ctx, nxt_unit_ctx_impl_t, ctx); + ctx = req->ctx; + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); req->response = NULL; @@ -1783,6 +1810,10 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req) nxt_queue_insert_tail(&ctx_impl->free_req, &req_impl->link); pthread_mutex_unlock(&ctx_impl->mutex); + + if (nxt_slow_path(!nxt_unit_chk_ready(ctx))) { + nxt_unit_quit(ctx, NXT_QUIT_GRACEFUL); + } } @@ -2621,7 +2652,7 @@ nxt_unit_mmap_buf_send(nxt_unit_request_info_t *req, (int) m.mmap_msg.size); res = nxt_unit_port_send(req->ctx, req->response_port, &m, sizeof(m), - NULL, 0); + NULL); if (nxt_slow_path(res != sizeof(m))) { goto free_buf; } @@ -2673,8 +2704,8 @@ nxt_unit_mmap_buf_send(nxt_unit_request_info_t *req, res = nxt_unit_port_send(req->ctx, req->response_port, buf->start - sizeof(m.msg), - m.mmap_msg.size + sizeof(m.msg), - NULL, 0); + m.mmap_msg.size + sizeof(m.msg), NULL); + if (nxt_slow_path(res != (ssize_t) (m.mmap_msg.size + sizeof(m.msg)))) { goto free_buf; } @@ -2741,7 +2772,7 @@ nxt_unit_read_buf_get(nxt_unit_ctx_t *ctx) pthread_mutex_unlock(&ctx_impl->mutex); - memset(rbuf->oob, 0, sizeof(struct cmsghdr)); + rbuf->oob.size = 0; return rbuf; } @@ -3260,7 +3291,7 @@ skip_response_send: msg.tracking = 0; (void) nxt_unit_port_send(req->ctx, req->response_port, - &msg, sizeof(msg), NULL, 0); + &msg, sizeof(msg), NULL); nxt_unit_request_info_release(req); } @@ -3582,7 +3613,7 @@ nxt_unit_send_oosm(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) msg.mf = 0; msg.tracking = 0; - res = nxt_unit_port_send(ctx, lib->router_port, &msg, sizeof(msg), NULL, 0); + res = nxt_unit_port_send(ctx, lib->router_port, &msg, sizeof(msg), NULL); if (nxt_slow_path(res != sizeof(msg))) { return NXT_UNIT_ERROR; } @@ -3851,12 +3882,10 @@ static int nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, int fd) { ssize_t res; + nxt_send_oob_t oob; nxt_port_msg_t msg; nxt_unit_impl_t *lib; - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int))]; - } cmsg; + int fds[2] = {fd, -1}; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); @@ -3870,30 +3899,9 @@ nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, int fd) msg.mf = 0; msg.tracking = 0; - /* - * Fill all padding fields with 0. - * Code in Go 1.11 validate cmsghdr using padding field as part of len. - * See Cmsghdr definition and socketControlMessageHeaderAndData function. - */ - memset(&cmsg, 0, sizeof(cmsg)); - - cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - /* - * memcpy() is used instead of simple - * *(int *) CMSG_DATA(&cmsg.cm) = fd; - * because GCC 4.4 with -O2/3/s optimization may issue a warning: - * dereferencing type-punned pointer will break strict-aliasing rules - * - * Fortunately, GCC with -O1 compiles this nxt_memcpy() - * in the same simple assignment as in the code above. - */ - memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int)); + nxt_socket_msg_oob_init(&oob, fds); - res = nxt_unit_port_send(ctx, port, &msg, sizeof(msg), - &cmsg, sizeof(cmsg)); + res = nxt_unit_port_send(ctx, port, &msg, sizeof(msg), &oob); if (nxt_slow_path(res != sizeof(msg))) { return NXT_UNIT_ERROR; } @@ -4083,7 +4091,7 @@ nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl) msg.type = _NXT_PORT_MSG_RPC_READY; (void) nxt_unit_port_send(ctx, ctx_impl->read_port, - &msg, sizeof(msg), NULL, 0); + &msg, sizeof(msg), NULL); } @@ -4306,7 +4314,7 @@ nxt_unit_get_mmap(nxt_unit_ctx_t *ctx, pid_t pid, uint32_t id) nxt_unit_debug(ctx, "get_mmap: %d %d", (int) pid, (int) id); - res = nxt_unit_port_send(ctx, lib->router_port, &m, sizeof(m), NULL, 0); + res = nxt_unit_port_send(ctx, lib->router_port, &m, sizeof(m), NULL); if (nxt_slow_path(res != sizeof(m))) { return NXT_UNIT_ERROR; } @@ -4376,7 +4384,7 @@ nxt_unit_send_shm_ack(nxt_unit_ctx_t *ctx, pid_t pid) msg.mf = 0; msg.tracking = 0; - res = nxt_unit_port_send(ctx, lib->router_port, &msg, sizeof(msg), NULL, 0); + res = nxt_unit_port_send(ctx, lib->router_port, &msg, sizeof(msg), NULL); if (nxt_slow_path(res != sizeof(msg))) { return NXT_UNIT_ERROR; } @@ -4522,7 +4530,7 @@ nxt_unit_run(nxt_unit_ctx_t *ctx) rc = nxt_unit_run_once_impl(ctx); if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { - nxt_unit_quit(ctx); + nxt_unit_quit(ctx, NXT_QUIT_NORMAL); break; } } @@ -4586,6 +4594,7 @@ static int nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) { int nevents, res, err; + nxt_uint_t nfds; nxt_unit_impl_t *lib; nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_port_impl_t *port_impl; @@ -4593,7 +4602,7 @@ nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf) ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); - if (ctx_impl->wait_items > 0 || ctx_impl->ready == 0) { + if (ctx_impl->wait_items > 0 || !nxt_unit_chk_ready(ctx)) { return nxt_unit_ctx_port_recv(ctx, ctx_impl->read_port, rbuf); } @@ -4626,20 +4635,28 @@ retry: } } - res = nxt_unit_app_queue_recv(lib->shared_port, rbuf); - if (res == NXT_UNIT_OK) { - return NXT_UNIT_OK; + if (nxt_fast_path(nxt_unit_chk_ready(ctx))) { + res = nxt_unit_app_queue_recv(ctx, lib->shared_port, rbuf); + if (res == NXT_UNIT_OK) { + return NXT_UNIT_OK; + } + + fds[1].fd = lib->shared_port->in_fd; + fds[1].events = POLLIN; + + nfds = 2; + + } else { + nfds = 1; } fds[0].fd = ctx_impl->read_port->in_fd; fds[0].events = POLLIN; fds[0].revents = 0; - fds[1].fd = lib->shared_port->in_fd; - fds[1].events = POLLIN; fds[1].revents = 0; - nevents = poll(fds, 2, -1); + nevents = poll(fds, nfds, -1); if (nxt_slow_path(nevents == -1)) { err = errno; @@ -4655,7 +4672,7 @@ retry: return (err == EAGAIN) ? NXT_UNIT_AGAIN : NXT_UNIT_ERROR; } - nxt_unit_debug(ctx, "poll(%d,%d): %d, revents [%04uXi, %04uXi]", + nxt_unit_debug(ctx, "poll(%d,%d): %d, revents [%04X, %04X]", fds[0].fd, fds[1].fd, nevents, fds[0].revents, fds[1].revents); @@ -4686,6 +4703,21 @@ retry: static int +nxt_unit_chk_ready(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + return (ctx_impl->ready + && (lib->request_limit == 0 + || lib->request_count < lib->request_limit)); +} + + +static int nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx) { int rc; @@ -4723,6 +4755,10 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx) } nxt_queue_loop; + if (!ctx_impl->ready) { + nxt_unit_quit(ctx, NXT_QUIT_GRACEFUL); + } + return rc; } @@ -4903,16 +4939,14 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx) int rc; nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; - nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_ctx_use(ctx); lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); - ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); rc = NXT_UNIT_OK; - while (nxt_fast_path(ctx_impl->online)) { + while (nxt_fast_path(nxt_unit_chk_ready(ctx))) { rbuf = nxt_unit_read_buf_get(ctx); if (nxt_slow_path(rbuf == NULL)) { rc = NXT_UNIT_ERROR; @@ -4949,17 +4983,15 @@ nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx) int rc; nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; - nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_request_info_t *req; nxt_unit_ctx_use(ctx); lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); - ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); req = NULL; - if (nxt_slow_path(!ctx_impl->online)) { + if (nxt_slow_path(!nxt_unit_chk_ready(ctx))) { goto done; } @@ -4968,7 +5000,7 @@ nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx) goto done; } - rc = nxt_unit_app_queue_recv(lib->shared_port, rbuf); + rc = nxt_unit_app_queue_recv(ctx, lib->shared_port, rbuf); if (rc != NXT_UNIT_OK) { nxt_unit_read_buf_release(ctx, rbuf); goto done; @@ -4985,17 +5017,6 @@ done: int -nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx) -{ - nxt_unit_impl_t *lib; - - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); - - return (ctx == &lib->main_ctx.ctx); -} - - -int nxt_unit_process_port_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { int rc; @@ -5017,13 +5038,17 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) nxt_unit_impl_t *lib; nxt_unit_read_buf_t *rbuf; + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + if (port == lib->shared_port && !nxt_unit_chk_ready(ctx)) { + return NXT_UNIT_AGAIN; + } + rbuf = nxt_unit_read_buf_get(ctx); if (nxt_slow_path(rbuf == NULL)) { return NXT_UNIT_ERROR; } - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); - if (port == lib->shared_port) { rc = nxt_unit_shared_port_recv(ctx, port, rbuf); @@ -5194,7 +5219,7 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl) pthread_mutex_unlock(&lib->mutex); if (nxt_fast_path(ctx_impl->read_port != NULL)) { - nxt_unit_remove_port(lib, &ctx_impl->read_port->id); + nxt_unit_remove_port(lib, NULL, &ctx_impl->read_port->id); nxt_unit_port_release(ctx_impl->read_port); } @@ -5246,6 +5271,24 @@ nxt_unit_create_port(nxt_unit_ctx_t *ctx) return NULL; } +#if (NXT_HAVE_SOCKOPT_SO_PASSCRED) + int enable_creds = 1; + + if (nxt_slow_path(setsockopt(port_sockets[0], SOL_SOCKET, SO_PASSCRED, + &enable_creds, sizeof(enable_creds)) == -1)) + { + nxt_unit_warn(ctx, "failed to set SO_PASSCRED %s", strerror(errno)); + return NULL; + } + + if (nxt_slow_path(setsockopt(port_sockets[1], SOL_SOCKET, SO_PASSCRED, + &enable_creds, sizeof(enable_creds)) == -1)) + { + nxt_unit_warn(ctx, "failed to set SO_PASSCRED %s", strerror(errno)); + return NULL; + } +#endif + nxt_unit_debug(ctx, "create_port: new socketpair: %d->%d", port_sockets[0], port_sockets[1]); @@ -5286,6 +5329,7 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *dst, nxt_unit_port_t *port, int queue_fd) { ssize_t res; + nxt_send_oob_t oob; nxt_unit_impl_t *lib; int fds[2] = { port->out_fd, queue_fd }; @@ -5294,11 +5338,6 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *dst, nxt_port_msg_new_port_t new_port; } m; - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int) * 2)]; - } cmsg; - lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); m.msg.stream = 0; @@ -5317,24 +5356,9 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *dst, m.new_port.max_size = 16 * 1024; m.new_port.max_share = 64 * 1024; - memset(&cmsg, 0, sizeof(cmsg)); - - cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int) * 2); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - /* - * memcpy() is used instead of simple - * *(int *) CMSG_DATA(&cmsg.cm) = fd; - * because GCC 4.4 with -O2/3/s optimization may issue a warning: - * dereferencing type-punned pointer will break strict-aliasing rules - * - * Fortunately, GCC with -O1 compiles this nxt_memcpy() - * in the same simple assignment as in the code above. - */ - memcpy(CMSG_DATA(&cmsg.cm), fds, sizeof(int) * 2); + nxt_socket_msg_oob_init(&oob, fds); - res = nxt_unit_port_send(ctx, dst, &m, sizeof(m), &cmsg, sizeof(cmsg)); + res = nxt_unit_port_send(ctx, dst, &m, sizeof(m), &oob); return (res == sizeof(m)) ? NXT_UNIT_OK : NXT_UNIT_ERROR; } @@ -5605,7 +5629,8 @@ nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx, nxt_queue_t *awaiting_req) static void -nxt_unit_remove_port(nxt_unit_impl_t *lib, nxt_unit_port_id_t *port_id) +nxt_unit_remove_port(nxt_unit_impl_t *lib, nxt_unit_ctx_t *ctx, + nxt_unit_port_id_t *port_id) { nxt_unit_port_t *port; nxt_unit_port_impl_t *port_impl; @@ -5623,7 +5648,7 @@ nxt_unit_remove_port(nxt_unit_impl_t *lib, nxt_unit_port_id_t *port_id) pthread_mutex_unlock(&lib->mutex); if (lib->callbacks.remove_port != NULL && port != NULL) { - lib->callbacks.remove_port(&lib->unit, port); + lib->callbacks.remove_port(&lib->unit, ctx, port); } if (nxt_fast_path(port != NULL)) { @@ -5700,7 +5725,7 @@ nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process) nxt_queue_remove(&port->link); if (lib->callbacks.remove_port != NULL) { - lib->callbacks.remove_port(&lib->unit, &port->port); + lib->callbacks.remove_port(&lib->unit, NULL, &port->port); } nxt_unit_port_release(&port->port); @@ -5712,56 +5737,96 @@ nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process) static void -nxt_unit_quit(nxt_unit_ctx_t *ctx) +nxt_unit_quit(nxt_unit_ctx_t *ctx, uint8_t quit_param) { - nxt_port_msg_t msg; + nxt_bool_t skip_graceful_broadcast, quit; nxt_unit_impl_t *lib; nxt_unit_ctx_impl_t *ctx_impl; nxt_unit_callbacks_t *cb; nxt_unit_request_info_t *req; nxt_unit_request_info_impl_t *req_impl; + struct { + nxt_port_msg_t msg; + uint8_t quit_param; + } nxt_packed m; + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); - if (!ctx_impl->online) { + nxt_unit_debug(ctx, "quit: %d/%d/%d", (int) quit_param, ctx_impl->ready, + ctx_impl->online); + + if (nxt_slow_path(!ctx_impl->online)) { return; } - ctx_impl->online = 0; + skip_graceful_broadcast = quit_param == NXT_QUIT_GRACEFUL + && !ctx_impl->ready; cb = &lib->callbacks; - if (cb->quit != NULL) { - cb->quit(ctx); + if (nxt_fast_path(ctx_impl->ready)) { + ctx_impl->ready = 0; + + if (cb->remove_port != NULL) { + cb->remove_port(&lib->unit, ctx, lib->shared_port); + } } - nxt_queue_each(req_impl, &ctx_impl->active_req, - nxt_unit_request_info_impl_t, link) - { - req = &req_impl->req; + if (quit_param == NXT_QUIT_GRACEFUL) { + pthread_mutex_lock(&ctx_impl->mutex); - nxt_unit_req_warn(req, "active request on ctx quit"); + quit = nxt_queue_is_empty(&ctx_impl->active_req) + && nxt_queue_is_empty(&ctx_impl->pending_rbuf) + && ctx_impl->wait_items == 0; - if (cb->close_handler) { - nxt_unit_req_debug(req, "close_handler"); + pthread_mutex_unlock(&ctx_impl->mutex); - cb->close_handler(req); + } else { + quit = 1; + ctx_impl->quit_param = NXT_QUIT_GRACEFUL; + } - } else { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + if (quit) { + ctx_impl->online = 0; + + if (cb->quit != NULL) { + cb->quit(ctx); } - } nxt_queue_loop; + nxt_queue_each(req_impl, &ctx_impl->active_req, + nxt_unit_request_info_impl_t, link) + { + req = &req_impl->req; - if (ctx != &lib->main_ctx.ctx) { + nxt_unit_req_warn(req, "active request on ctx quit"); + + if (cb->close_handler) { + nxt_unit_req_debug(req, "close_handler"); + + cb->close_handler(req); + + } else { + nxt_unit_request_done(req, NXT_UNIT_ERROR); + } + + } nxt_queue_loop; + + if (nxt_fast_path(ctx_impl->read_port != NULL)) { + nxt_unit_remove_port(lib, ctx, &ctx_impl->read_port->id); + } + } + + if (ctx != &lib->main_ctx.ctx || skip_graceful_broadcast) { return; } - memset(&msg, 0, sizeof(nxt_port_msg_t)); + memset(&m.msg, 0, sizeof(nxt_port_msg_t)); - msg.pid = lib->pid; - msg.type = _NXT_PORT_MSG_QUIT; + m.msg.pid = lib->pid; + m.msg.type = _NXT_PORT_MSG_QUIT; + m.quit_param = quit_param; pthread_mutex_lock(&lib->mutex); @@ -5775,7 +5840,7 @@ nxt_unit_quit(nxt_unit_ctx_t *ctx) } (void) nxt_unit_port_send(ctx, ctx_impl->read_port, - &msg, sizeof(msg), NULL, 0); + &m, sizeof(m), NULL); } nxt_queue_loop; @@ -5810,7 +5875,7 @@ nxt_unit_get_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) nxt_unit_debug(ctx, "get_port: %d %d", (int) port_id->pid, (int) port_id->id); - res = nxt_unit_port_send(ctx, lib->router_port, &m, sizeof(m), NULL, 0); + res = nxt_unit_port_send(ctx, lib->router_port, &m, sizeof(m), NULL); if (nxt_slow_path(res != sizeof(m))) { return NXT_UNIT_ERROR; } @@ -5821,7 +5886,7 @@ nxt_unit_get_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) static ssize_t nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, - const void *buf, size_t buf_size, const void *oob, size_t oob_size) + const void *buf, size_t buf_size, const nxt_send_oob_t *oob) { int notify; ssize_t ret; @@ -5833,7 +5898,7 @@ nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); port_impl = nxt_container_of(port, nxt_unit_port_impl_t, port); - if (port_impl->queue != NULL && oob_size == 0 + if (port_impl->queue != NULL && (oob == NULL || oob->size == 0) && buf_size <= NXT_PORT_QUEUE_MSG_SIZE) { rc = nxt_port_queue_send(port_impl->queue, buf, buf_size, ¬ify); @@ -5855,7 +5920,7 @@ nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, if (lib->callbacks.port_send == NULL) { ret = nxt_unit_sendmsg(ctx, port->out_fd, &msg, - sizeof(nxt_port_msg_t), NULL, 0); + sizeof(nxt_port_msg_t), NULL); nxt_unit_debug(ctx, "port{%d,%d} send %d read_queue", (int) port->id.pid, (int) port->id.id, @@ -5892,15 +5957,15 @@ nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, if (lib->callbacks.port_send != NULL) { ret = lib->callbacks.port_send(ctx, port, buf, buf_size, - oob, oob_size); + oob != NULL ? oob->buf : NULL, + oob != NULL ? oob->size : 0); nxt_unit_debug(ctx, "port{%d,%d} sendcb %d", (int) port->id.pid, (int) port->id.id, (int) ret); } else { - ret = nxt_unit_sendmsg(ctx, port->out_fd, buf, buf_size, - oob, oob_size); + ret = nxt_unit_sendmsg(ctx, port->out_fd, buf, buf_size, oob); nxt_unit_debug(ctx, "port{%d,%d} sendmsg %d", (int) port->id.pid, (int) port->id.id, @@ -5913,29 +5978,20 @@ nxt_unit_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, static ssize_t nxt_unit_sendmsg(nxt_unit_ctx_t *ctx, int fd, - const void *buf, size_t buf_size, const void *oob, size_t oob_size) + const void *buf, size_t buf_size, const nxt_send_oob_t *oob) { int err; - ssize_t res; + ssize_t n; struct iovec iov[1]; - struct msghdr msg; iov[0].iov_base = (void *) buf; iov[0].iov_len = buf_size; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = (void *) oob; - msg.msg_controllen = oob_size; - retry: - res = sendmsg(fd, &msg, 0); + n = nxt_sendmsg(fd, iov, 1, oob); - if (nxt_slow_path(res == -1)) { + if (nxt_slow_path(n == -1)) { err = errno; if (err == EINTR) { @@ -5950,11 +6006,11 @@ retry: fd, (int) buf_size, strerror(err), err); } else { - nxt_unit_debug(ctx, "sendmsg(%d, %d): %d", fd, (int) buf_size, - (int) res); + nxt_unit_debug(ctx, "sendmsg(%d, %d, %d): %d", fd, (int) buf_size, + (oob != NULL ? (int) oob->size : 0), (int) n); } - return res; + return n; } @@ -6063,7 +6119,7 @@ retry: nxt_unit_rbuf_cpy(port_impl->socket_rbuf, rbuf); - memset(rbuf->oob, 0, sizeof(struct cmsghdr)); + rbuf->oob.size = 0; goto retry; } @@ -6074,7 +6130,8 @@ nxt_unit_rbuf_cpy(nxt_unit_read_buf_t *dst, nxt_unit_read_buf_t *src) { memcpy(dst->buf, src->buf, src->size); dst->size = src->size; - memcpy(dst->oob, src->oob, sizeof(src->oob)); + dst->oob.size = src->oob.size; + memcpy(dst->oob.buf, src->oob.buf, src->oob.size); } @@ -6089,7 +6146,11 @@ nxt_unit_shared_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, retry: - res = nxt_unit_app_queue_recv(port, rbuf); + res = nxt_unit_app_queue_recv(ctx, port, rbuf); + + if (res == NXT_UNIT_OK) { + return NXT_UNIT_OK; + } if (res == NXT_UNIT_AGAIN) { res = nxt_unit_port_recv(ctx, port, rbuf); @@ -6116,16 +6177,18 @@ nxt_unit_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf) { int fd, err; + size_t oob_size; struct iovec iov[1]; - struct msghdr msg; nxt_unit_impl_t *lib; lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); if (lib->callbacks.port_recv != NULL) { + oob_size = sizeof(rbuf->oob.buf); + rbuf->size = lib->callbacks.port_recv(ctx, port, rbuf->buf, sizeof(rbuf->buf), - rbuf->oob, sizeof(rbuf->oob)); + rbuf->oob.buf, &oob_size); nxt_unit_debug(ctx, "port{%d,%d} recvcb %d", (int) port->id.pid, (int) port->id.id, (int) rbuf->size); @@ -6134,25 +6197,18 @@ nxt_unit_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, return NXT_UNIT_ERROR; } + rbuf->oob.size = oob_size; return NXT_UNIT_OK; } iov[0].iov_base = rbuf->buf; iov[0].iov_len = sizeof(rbuf->buf); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = rbuf->oob; - msg.msg_controllen = sizeof(rbuf->oob); - fd = port->in_fd; retry: - rbuf->size = recvmsg(fd, &msg, 0); + rbuf->size = nxt_recvmsg(fd, iov, 1, &rbuf->oob); if (nxt_slow_path(rbuf->size == -1)) { err = errno; @@ -6194,13 +6250,20 @@ nxt_unit_port_queue_recv(nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf) static int -nxt_unit_app_queue_recv(nxt_unit_port_t *port, nxt_unit_read_buf_t *rbuf) +nxt_unit_app_queue_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, + nxt_unit_read_buf_t *rbuf) { uint32_t cookie; nxt_port_msg_t *port_msg; nxt_app_queue_t *queue; + nxt_unit_impl_t *lib; nxt_unit_port_impl_t *port_impl; + struct { + nxt_port_msg_t msg; + uint8_t quit_param; + } nxt_packed m; + port_impl = nxt_container_of(port, nxt_unit_port_impl_t, port); queue = port_impl->queue; @@ -6214,6 +6277,25 @@ retry: port_msg = (nxt_port_msg_t *) rbuf->buf; if (nxt_app_queue_cancel(queue, cookie, port_msg->stream)) { + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + if (lib->request_limit != 0) { + nxt_atomic_fetch_add(&lib->request_count, 1); + + if (nxt_slow_path(lib->request_count >= lib->request_limit)) { + nxt_unit_debug(ctx, "request limit reached"); + + memset(&m.msg, 0, sizeof(nxt_port_msg_t)); + + m.msg.pid = lib->pid; + m.msg.type = _NXT_PORT_MSG_QUIT; + m.quit_param = NXT_QUIT_GRACEFUL; + + (void) nxt_unit_port_send(ctx, lib->main_ctx.read_port, + &m, sizeof(m), NULL); + } + } + return NXT_UNIT_OK; } @@ -6495,7 +6577,7 @@ nxt_unit_log(nxt_unit_ctx_t *ctx, int level, const char *fmt, ...) log_fd = lib->log_fd; } else { - pid = getpid(); + pid = nxt_unit_pid; log_fd = STDERR_FILENO; } @@ -6539,7 +6621,7 @@ nxt_unit_req_log(nxt_unit_request_info_t *req, int level, const char *fmt, ...) log_fd = lib->log_fd; } else { - pid = getpid(); + pid = nxt_unit_pid; log_fd = STDERR_FILENO; } diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 1e1a8dbe..1b5280af 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -136,7 +136,8 @@ struct nxt_unit_callbacks_s { int (*add_port)(nxt_unit_ctx_t *, nxt_unit_port_t *port); /* Remove previously added port. Optional. */ - void (*remove_port)(nxt_unit_t *, nxt_unit_port_t *port); + void (*remove_port)(nxt_unit_t *, nxt_unit_ctx_t *, + nxt_unit_port_t *port); /* Remove all data associated with process pid including ports. Optional. */ void (*remove_pid)(nxt_unit_t *, pid_t pid); @@ -154,7 +155,7 @@ struct nxt_unit_callbacks_s { /* Receive data on port id. Optional. */ ssize_t (*port_recv)(nxt_unit_ctx_t *, nxt_unit_port_t *port, - void *buf, size_t buf_size, void *oob, size_t oob_size); + void *buf, size_t buf_size, void *oob, size_t *oob_size); int (*ready_handler)(nxt_unit_ctx_t *); }; @@ -167,6 +168,7 @@ struct nxt_unit_init_s { uint32_t request_data_size; uint32_t shm_limit; + uint32_t request_limit; nxt_unit_callbacks_t callbacks; @@ -215,8 +217,6 @@ int nxt_unit_run_shared(nxt_unit_ctx_t *ctx); nxt_unit_request_info_t *nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx); -int nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx); - /* * Receive and process one message, invoke configured callbacks. * diff --git a/src/nxt_upstream.c b/src/nxt_upstream.c index de9b1d49..17593173 100644 --- a/src/nxt_upstream.c +++ b/src/nxt_upstream.c @@ -141,6 +141,11 @@ static nxt_http_action_t * nxt_upstream_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - return nxt_upstream_proxy_handler(task, r, - r->conf->upstreams[action->u.upstream_number]); + nxt_upstream_t *u; + + u = r->conf->upstreams[action->u.upstream_number]; + + nxt_debug(task, "upstream handler: \"%V\"", &u->name); + + return nxt_upstream_proxy_handler(task, r, u); } diff --git a/src/nxt_var.c b/src/nxt_var.c index 2731fd09..60650ef4 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -7,21 +7,28 @@ struct nxt_var_s { - size_t plain; - nxt_uint_t vars; - u_char data[]; + size_t length; + nxt_uint_t vars; + uint8_t strz; /* 1 bit */ + u_char data[]; /* - uint32_t indexes[vars]; - size_t positions[vars]; - u_char chars[plain]; + nxt_var_sub_t subs[vars]; + u_char raw[length]; */ }; typedef struct { - nxt_var_t *var; - nxt_str_t *value; + uint32_t index; + uint32_t length; + uint32_t position; +} nxt_var_sub_t; + + +typedef struct { + nxt_var_t *var; + nxt_str_t *value; } nxt_var_value_t; @@ -43,13 +50,10 @@ struct nxt_var_query_s { }; -#define nxt_var_indexes(var) ((uint32_t *) (var)->data) +#define nxt_var_subs(var) ((nxt_var_sub_t *) (var)->data) -#define nxt_var_positions(var) \ - ((size_t *) ((var)->data + (var)->vars * sizeof(uint32_t))) - -#define nxt_var_plain_start(var) \ - ((var)->data + (var)->vars * (sizeof(uint32_t) + sizeof(size_t))) +#define nxt_var_raw_start(var) \ + ((var)->data + (var)->vars * sizeof(nxt_var_sub_t)) static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data); @@ -87,6 +91,21 @@ static uint32_t nxt_var_count; static nxt_var_handler_t *nxt_var_index; +void +nxt_var_raw(nxt_var_t *var, nxt_str_t *str) +{ + str->length = var->length; + str->start = nxt_var_raw_start(var); +} + + +nxt_bool_t +nxt_var_is_const(nxt_var_t *var) +{ + return (var->vars == 0); +} + + static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data) { @@ -211,18 +230,17 @@ nxt_var_index_init(void) nxt_var_t * -nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp) +nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz) { - u_char *p, *end, *plain_pos; - size_t plain, size, *positions; - uint32_t *indexes; - nxt_var_t *var; - nxt_str_t part; - nxt_uint_t n; - nxt_bool_t is_var; - nxt_var_decl_t *decl; + u_char *p, *end, *next, *src; + size_t size; + 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; - plain = 0; n = 0; p = str->start; @@ -230,59 +248,55 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp) while (p < end) { p = nxt_var_next_part(p, end - p, &part, &is_var); - if (nxt_slow_path(p == NULL)) { return NULL; } if (is_var) { n++; - - } else { - plain += part.length; } } - size = sizeof(nxt_var_t) - + n * (sizeof(nxt_var_handler_t) + sizeof (size_t)) - + plain; + size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length; - var = nxt_mp_get(mp, size); + var = nxt_mp_get(mp, size + strz); if (nxt_slow_path(var == NULL)) { return NULL; } - var->plain = plain; + var->length = str->length; var->vars = n; + var->strz = strz; - indexes = nxt_var_indexes(var); - positions = nxt_var_positions(var); - plain_pos = nxt_var_plain_start(var); + subs = nxt_var_subs(var); + src = nxt_var_raw_start(var); - plain = 0; - n = 0; + nxt_memcpy(src, str->start, str->length); + if (strz) { + src[str->length] = '\0'; + } + + n = 0; p = str->start; while (p < end) { - p = nxt_var_next_part(p, end - p, &part, &is_var); + next = nxt_var_next_part(p, end - p, &part, &is_var); if (is_var) { decl = nxt_var_hash_find(&part); - if (nxt_slow_path(decl == NULL)) { return NULL; } - indexes[n] = decl->index; - positions[n] = plain; + subs[n].index = decl->index; + subs[n].length = next - p; + subs[n].position = p - str->start; n++; - - } else { - plain_pos = nxt_cpymem(plain_pos, part.start, part.length); - plain += part.length; } + + p = next; } return var; @@ -438,16 +452,16 @@ void nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, nxt_str_t *str) { - uint32_t *indexes; - nxt_mp_t *mp; - nxt_str_t *value; - nxt_int_t ret; - nxt_uint_t i; - nxt_var_value_t *val; - - if (var->vars == 0) { - str->length = var->plain; - str->start = nxt_var_plain_start(var); + uint32_t index; + nxt_mp_t *mp; + nxt_str_t *value; + nxt_int_t ret; + nxt_uint_t i; + nxt_var_sub_t *subs; + nxt_var_value_t *val; + + if (nxt_var_is_const(var)) { + nxt_var_raw(var, str); return; } @@ -456,7 +470,7 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, } mp = query->values.mem_pool; - indexes = nxt_var_indexes(var); + subs = nxt_var_subs(var); value = query->spare; for (i = 0; i < var->vars; i++) { @@ -468,7 +482,9 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, } } - ret = nxt_var_cache_add(&query->cache, indexes[i], value, mp); + index = subs[i].index; + + ret = nxt_var_cache_add(&query->cache, index, value, mp); if (ret != NXT_OK) { if (nxt_slow_path(ret == NXT_ERROR)) { @@ -478,7 +494,7 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, continue; /* NXT_DECLINED */ } - ret = nxt_var_index[indexes[i]](task, query, value, query->ctx); + ret = nxt_var_index[index](task, query, value, query->ctx); value = NULL; @@ -538,13 +554,13 @@ nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query, static void nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query) { - u_char *p, *src; - size_t length, plain, next, *positions; - uint32_t *indexes; - nxt_str_t *str, **part; - nxt_var_t *var; - nxt_uint_t i, j; - nxt_var_value_t *val; + u_char *p, *src; + size_t length, last, next; + nxt_str_t *str, **part; + nxt_var_t *var; + nxt_uint_t i, j; + nxt_var_sub_t *subs; + nxt_var_value_t *val; if (query->failed) { goto done; @@ -555,11 +571,11 @@ nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query) for (i = 0; i < query->values.nelts; i++) { var = val[i].var; - length = var->plain; - indexes = nxt_var_indexes(var); + subs = nxt_var_subs(var); + length = var->length; for (j = 0; j < var->vars; j++) { - str = nxt_var_cache_find(&query->cache, indexes[j]); + str = nxt_var_cache_find(&query->cache, subs[j].index); nxt_assert(str != NULL); @@ -572,10 +588,10 @@ nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query) *part = str; - length += str->length; + length += str->length - subs[j].length; } - p = nxt_mp_nget(query->values.mem_pool, length); + p = nxt_mp_nget(query->values.mem_pool, length + var->strz); if (nxt_slow_path(p == NULL)) { query->failed = 1; goto done; @@ -585,27 +601,33 @@ nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query) val[i].value->start = p; part = query->parts.elts; - positions = nxt_var_positions(var); - src = nxt_var_plain_start(var); + src = nxt_var_raw_start(var); - plain = 0; + last = 0; for (j = 0; j < var->vars; j++) { - next = positions[j]; + next = subs[j].position; - if (next != plain) { - p = nxt_cpymem(p, &src[plain], next - plain); - plain = next; + if (next != last) { + p = nxt_cpymem(p, &src[last], next - last); } p = nxt_cpymem(p, part[j]->start, part[j]->length); + + last = next + subs[j].length; + } + + if (last != var->length) { + p = nxt_cpymem(p, &src[last], var->length - last); } - if (plain != var->plain) { - nxt_memcpy(p, &src[plain], var->plain - plain); + if (var->strz) { + *p = '\0'; } nxt_array_reset(&query->parts); + + nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, val[i].value); } done: diff --git a/src/nxt_var.h b/src/nxt_var.h index 7e0a2a21..3b7d0c28 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -30,9 +30,12 @@ nxt_is_var(nxt_str_t *str) } +void nxt_var_raw(nxt_var_t *var, nxt_str_t *str); +nxt_bool_t nxt_var_is_const(nxt_var_t *var); + nxt_int_t nxt_var_register(nxt_var_decl_t *decl, size_t n); nxt_int_t nxt_var_index_init(void); -nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp); +nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz); nxt_int_t nxt_var_test(nxt_str_t *str, u_char *error); nxt_int_t nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 5df1465d..02555c96 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -1184,13 +1184,12 @@ nxt_perl_psgi_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } - nxt_unit_default_init(task, &perl_init); + nxt_unit_default_init(task, &perl_init, common_conf); perl_init.callbacks.request_handler = nxt_perl_psgi_request_handler; perl_init.callbacks.ready_handler = nxt_perl_psgi_ready_handler; perl_init.data = c; perl_init.ctx_data = &pctx; - perl_init.shm_limit = common_conf->shm_limit; unit_ctx = nxt_unit_init(&perl_init); if (nxt_slow_path(unit_ctx == NULL)) { @@ -1292,11 +1291,6 @@ nxt_perl_psgi_ready_handler(nxt_unit_ctx_t *ctx) nxt_perl_app_conf_t *c; nxt_perl_psgi_ctx_t *pctx; - /* Worker thread context. */ - if (!nxt_unit_is_main_ctx(ctx)) { - return NXT_UNIT_OK; - } - c = ctx->unit->data; if (c->threads <= 1) { diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index abb04194..8687c869 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -230,10 +230,9 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) } } - nxt_unit_default_init(task, &python_init); + nxt_unit_default_init(task, &python_init, data->app); python_init.data = c; - python_init.shm_limit = data->app->shm_limit; python_init.callbacks.ready_handler = nxt_python_ready_handler; proto = c->protocol; @@ -522,18 +521,6 @@ nxt_python_ready_handler(nxt_unit_ctx_t *ctx) nxt_py_thread_info_t *ti; nxt_python_app_conf_t *c; - if (nxt_py_proto.ready != NULL) { - res = nxt_py_proto.ready(ctx); - if (nxt_slow_path(res != NXT_UNIT_OK)) { - return NXT_UNIT_ERROR; - } - } - - /* Worker thread context. */ - if (!nxt_unit_is_main_ctx(ctx)) { - return NXT_UNIT_OK; - } - c = ctx->unit->data; if (c->threads <= 1) { diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index e4eac9dc..eddb1cfc 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -64,7 +64,6 @@ typedef struct { void (*ctx_data_free)(void *data); int (*startup)(void *data); int (*run)(nxt_unit_ctx_t *ctx); - int (*ready)(nxt_unit_ctx_t *ctx); void (*done)(void); } nxt_python_proto_t; diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 26003805..354e3a81 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -33,10 +33,10 @@ static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f); static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f); -static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx); - static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); -static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port); +static int nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); +static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx, + nxt_unit_port_t *port); static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); @@ -44,7 +44,6 @@ static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); static void nxt_python_asgi_done(void); static PyObject *nxt_py_port_read; -static nxt_unit_port_t *nxt_py_shared_port; static PyMethodDef nxt_py_port_read_method = {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""}; @@ -54,7 +53,6 @@ static nxt_python_proto_t nxt_py_asgi_proto = { .ctx_data_free = nxt_python_asgi_ctx_data_free, .startup = nxt_python_asgi_startup, .run = nxt_python_asgi_run, - .ready = nxt_python_asgi_ready, .done = nxt_python_asgi_done, }; @@ -361,14 +359,6 @@ nxt_python_asgi_run(nxt_unit_ctx_t *ctx) Py_DECREF(res); - nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port); - nxt_py_asgi_remove_reader(ctx, ctx_data->port); - - if (ctx_data->port != NULL) { - ctx_data->port->data = NULL; - ctx_data->port = NULL; - } - nxt_py_asgi_lifespan_shutdown(ctx); return NXT_UNIT_OK; @@ -892,82 +882,9 @@ fail: static int -nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) -{ - int rc; - PyObject *res, *fd, *py_ctx, *py_port; - nxt_unit_port_t *port; - nxt_py_asgi_ctx_data_t *ctx_data; - - if (nxt_slow_path(nxt_py_shared_port == NULL)) { - return NXT_UNIT_ERROR; - } - - port = nxt_py_shared_port; - - nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port); - - ctx_data = ctx->data; - - rc = NXT_UNIT_ERROR; - - fd = PyLong_FromLong(port->in_fd); - if (nxt_slow_path(fd == NULL)) { - nxt_unit_alert(ctx, "Python failed to create fd"); - nxt_python_print_exception(); - - return rc; - } - - py_ctx = PyLong_FromVoidPtr(ctx); - if (nxt_slow_path(py_ctx == NULL)) { - nxt_unit_alert(ctx, "Python failed to create py_ctx"); - nxt_python_print_exception(); - - goto clean_fd; - } - - py_port = PyLong_FromVoidPtr(port); - if (nxt_slow_path(py_port == NULL)) { - nxt_unit_alert(ctx, "Python failed to create py_port"); - nxt_python_print_exception(); - - goto clean_py_ctx; - } - - res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, - fd, nxt_py_port_read, - py_ctx, py_port, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_alert(ctx, "Python failed to add_reader"); - nxt_python_print_exception(); - - } else { - Py_DECREF(res); - - rc = NXT_UNIT_OK; - } - - Py_DECREF(py_port); - -clean_py_ctx: - - Py_DECREF(py_ctx); - -clean_fd: - - Py_DECREF(fd); - - return rc; -} - - -static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) { - int nb, rc; - PyObject *res, *fd, *py_ctx, *py_port; - nxt_py_asgi_ctx_data_t *ctx_data; + int nb; if (port->in_fd == -1) { return NXT_UNIT_OK; @@ -984,27 +901,31 @@ nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); - if (port->id.id == NXT_UNIT_SHARED_PORT_ID) { - nxt_py_shared_port = port; + return nxt_py_asgi_add_reader(ctx, port); +} - return NXT_UNIT_OK; - } - ctx_data = ctx->data; +static int +nxt_py_asgi_add_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) +{ + int rc; + PyObject *res, *fd, *py_ctx, *py_port; + nxt_py_asgi_ctx_data_t *ctx_data; - ctx_data->port = port; - port->data = ctx_data; + nxt_unit_debug(ctx, "asgi_add_reader %d %p %p", port->in_fd, ctx, port); - rc = NXT_UNIT_ERROR; + ctx_data = ctx->data; fd = PyLong_FromLong(port->in_fd); if (nxt_slow_path(fd == NULL)) { nxt_unit_alert(ctx, "Python failed to create fd"); nxt_python_print_exception(); - return rc; + return NXT_UNIT_ERROR; } + rc = NXT_UNIT_ERROR; + py_ctx = PyLong_FromVoidPtr(ctx); if (nxt_slow_path(py_ctx == NULL)) { nxt_unit_alert(ctx, "Python failed to create py_ctx"); @@ -1049,17 +970,16 @@ clean_fd: static void -nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) +nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_ctx_t *ctx, + nxt_unit_port_t *port) { - if (port->in_fd == -1) { + if (port->in_fd == -1 || ctx == NULL) { return; } nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); - if (nxt_py_shared_port == port) { - nxt_py_shared_port = NULL; - } + nxt_py_asgi_remove_reader(ctx, port); } @@ -1073,27 +993,6 @@ nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) ctx_data = ctx->data; - if (nxt_py_shared_port != NULL) { - p = PyLong_FromLong(nxt_py_shared_port->in_fd); - if (nxt_slow_path(p == NULL)) { - nxt_unit_alert(NULL, "Python failed to create Long"); - nxt_python_print_exception(); - - } else { - res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, - p, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_alert(NULL, "Python failed to remove_reader"); - nxt_python_print_exception(); - - } else { - Py_DECREF(res); - } - - Py_DECREF(p); - } - } - p = PyLong_FromLong(0); if (nxt_slow_path(p == NULL)) { nxt_unit_alert(NULL, "Python failed to create Long"); diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h index 20702065..94478a36 100644 --- a/src/python/nxt_python_asgi.h +++ b/src/python/nxt_python_asgi.h @@ -34,7 +34,6 @@ typedef struct { PyObject *quit_future; PyObject *quit_future_set_result; PyObject **target_lifespans; - nxt_unit_port_t *port; } nxt_py_asgi_ctx_data_t; PyObject *nxt_py_asgi_enum_headers(PyObject *headers, diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index c4a77d53..05c0da4f 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -636,9 +636,11 @@ nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) nxt_unit_req_debug(req, "asgi_http_close_handler"); - http->closed = 1; + if (nxt_fast_path(http != NULL)) { + http->closed = 1; - nxt_py_asgi_http_emit_disconnect(http); + nxt_py_asgi_http_emit_disconnect(http); + } } diff --git a/src/python/nxt_python_asgi_websocket.c b/src/python/nxt_python_asgi_websocket.c index fc7d9fa4..ab1d0324 100644 --- a/src/python/nxt_python_asgi_websocket.c +++ b/src/python/nxt_python_asgi_websocket.c @@ -980,6 +980,10 @@ nxt_py_asgi_websocket_close_handler(nxt_unit_request_info_t *req) nxt_unit_req_debug(req, "asgi_websocket_close_handler"); + if (nxt_slow_path(ws == NULL)) { + return; + } + if (ws->receive_future == NULL) { ws->state = NXT_WS_DISCONNECTED; diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 522869b5..62498127 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -357,11 +357,10 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } - nxt_unit_default_init(task, &ruby_unit_init); + nxt_unit_default_init(task, &ruby_unit_init, conf); ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler; - ruby_unit_init.shm_limit = conf->shm_limit; ruby_unit_init.data = c; ruby_unit_init.ctx_data = &ruby_ctx; @@ -1258,11 +1257,6 @@ nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) nxt_ruby_ctx_t *rctx; nxt_ruby_app_conf_t *c; - /* Worker thread context. */ - if (!nxt_unit_is_main_ctx(ctx)) { - return NXT_UNIT_OK; - } - c = ctx->unit->data; if (c->threads <= 1) { diff --git a/src/test/nxt_base64_test.c b/src/test/nxt_base64_test.c new file mode 100644 index 00000000..13a772b6 --- /dev/null +++ b/src/test/nxt_base64_test.c @@ -0,0 +1,98 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include "nxt_tests.h" + + +nxt_int_t +nxt_base64_test(nxt_thread_t *thr) +{ + ssize_t ret; + nxt_uint_t i; + + static struct { + nxt_str_t enc; + nxt_str_t dec; + + } tests[] = { + { nxt_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+//+9876543210" + "zyxwvutsrqponmlkjihgfedcba" + "ZYXWVUTSRQPONMLKJIHGFEDCBA"), + nxt_string("\x00\x10\x83\x10\x51\x87\x20\x92\x8b\x30\xd3\x8f" + "\x41\x14\x93\x51\x55\x97\x61\x96\x9b\x71\xd7\x9f" + "\x82\x18\xa3\x92\x59\xa7\xa2\x9a\xab\xb2\xdb\xaf" + "\xc3\x1c\xb3\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xbf" + "\xff\xef\x7c\xef\xae\x78\xdf\x6d\x74\xcf\x2c\x70" + "\xbe\xeb\x6c\xae\xaa\x68\x9e\x69\x64\x8e\x28\x60" + "\x7d\xe7\x5c\x6d\xa6\x58\x5d\x65\x54\x4d\x24\x50" + "\x3c\xe3\x4c\x2c\xa2\x48\x1c\x61\x44\x0c\x20\x40") }, + + { nxt_string("Aa=="), + nxt_string("\x01") }, + { nxt_string("0Z"), + nxt_string("\xd1") }, + { nxt_string("0aA="), + nxt_string("\xd1\xa0") }, + { nxt_string("z/+"), + nxt_string("\xcf\xff") }, + { nxt_string("z9+Npe=="), + nxt_string("\xcf\xdf\x8d\xa5") }, + { nxt_string("/+98765"), + nxt_string("\xff\xef\x7c\xef\xae") }, + + { nxt_string("aBc_"), + nxt_null_string }, + { nxt_string("5"), + nxt_null_string }, + { nxt_string("M==="), + nxt_null_string }, + { nxt_string("===="), + nxt_null_string }, + { nxt_string("Ab="), + nxt_null_string }, + { nxt_string("00=0"), + nxt_null_string }, + { nxt_string("\0"), + nxt_null_string }, + { nxt_string("\r\naaaa"), + nxt_null_string }, + { nxt_string("=0000"), + nxt_null_string }, + }; + + u_char buf[96]; + + nxt_thread_time_update(thr); + + for (i = 0; i < nxt_nitems(tests); i++) { + ret = nxt_base64_decode(NULL, tests[i].enc.start, tests[i].enc.length); + + if (ret == NXT_ERROR && tests[i].dec.start == NULL) { + continue; + } + + if ((size_t) ret != tests[i].dec.length) { + nxt_log_alert(thr->log, + "nxt_base64_decode() test \"%V\" failed: incorrect " + "length of decoded string %z, expected %uz", + &tests[i].enc, ret, tests[i].dec.length); + return NXT_ERROR; + } + + ret = nxt_base64_decode(buf, tests[i].enc.start, tests[i].enc.length); + + if (!nxt_str_eq(&tests[i].dec, buf, (size_t) ret)) { + nxt_log_alert(thr->log, "nxt_base64_decode() test \"%V\" failed"); + return NXT_ERROR; + } + } + + nxt_log_error(NXT_LOG_NOTICE, thr->log, "nxt_base64_decode() test passed"); + + return NXT_OK; +} diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c index 901d76c3..f5a1cbd4 100644 --- a/src/test/nxt_tests.c +++ b/src/test/nxt_tests.c @@ -162,6 +162,10 @@ main(int argc, char **argv) return 1; } + if (nxt_base64_test(thr) != NXT_OK) { + return 1; + } + #if (NXT_HAVE_CLONE_NEWUSER) if (nxt_clone_creds_test(thr) != NXT_OK) { return 1; diff --git a/src/test/nxt_tests.h b/src/test/nxt_tests.h index d531cc7d..463dc851 100644 --- a/src/test/nxt_tests.h +++ b/src/test/nxt_tests.h @@ -64,6 +64,7 @@ nxt_int_t nxt_malloc_test(nxt_thread_t *thr); nxt_int_t nxt_utf8_test(nxt_thread_t *thr); nxt_int_t nxt_http_parse_test(nxt_thread_t *thr); nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr); +nxt_int_t nxt_base64_test(nxt_thread_t *thr); nxt_int_t nxt_clone_creds_test(nxt_thread_t *thr); diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c index a5f3728c..8fda9740 100644 --- a/src/test/nxt_unit_app_test.c +++ b/src/test/nxt_unit_app_test.c @@ -97,7 +97,7 @@ ready_handler(nxt_unit_ctx_t *ctx) nxt_unit_debug(ctx, "ready"); - if (!nxt_unit_is_main_ctx(ctx) || thread_count <= 1) { + if (thread_count <= 1) { return NXT_UNIT_OK; } diff --git a/test/conftest.py b/test/conftest.py index 4d46e2fc..689c857a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -14,7 +14,6 @@ import time from multiprocessing import Process import pytest - from unit.check.chroot import check_chroot from unit.check.go import check_go from unit.check.isolation import check_isolation @@ -357,7 +356,7 @@ def run(request): _check_alerts(log=log) -def unit_run(): +def unit_run(state_dir=None): global unit_instance if not option.restart and 'unitd' in unit_instance: @@ -375,7 +374,9 @@ def unit_run(): if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': public_dir(build_dir) - os.mkdir(temp_dir + '/state') + state = temp_dir + '/state' if state_dir is None else state_dir + if not os.path.isdir(state): + os.mkdir(state) unitd_args = [ unitd, @@ -383,7 +384,7 @@ def unit_run(): '--modules', build_dir, '--state', - temp_dir + '/state', + state, '--pid', temp_dir + '/unit.pid', '--log', @@ -415,7 +416,8 @@ def unit_run(): with open(temp_dir + '/unit.pid', 'r') as f: unit_instance['pid'] = f.read().rstrip() - _clear_conf(unit_instance['temp_dir'] + '/control.unit.sock') + if state_dir is None: + _clear_conf(unit_instance['temp_dir'] + '/control.unit.sock') _fds_info['main']['fds'] = _count_fds(unit_instance['pid']) diff --git a/test/php/opcache/index.php b/test/php/opcache/index.php new file mode 100644 index 00000000..de4002bb --- /dev/null +++ b/test/php/opcache/index.php @@ -0,0 +1,18 @@ +<?php + +$pid = getmypid(); + +header('X-Pid: ' . $pid); + +if (function_exists('opcache_is_script_cached')) { + if (opcache_is_script_cached(__DIR__ . '/test.php')) { + header('X-Cached: 1'); + } else { + header('X-Cached: 0'); + opcache_compile_file(__DIR__ . '/test.php'); + } +} else { + header('X-Cached: -1'); +} + +?> diff --git a/test/php/opcache/test.php b/test/php/opcache/test.php new file mode 100644 index 00000000..147cebcd --- /dev/null +++ b/test/php/opcache/test.php @@ -0,0 +1 @@ +<?php phpinfo(); ?> diff --git a/test/python/restart/v1.py b/test/python/restart/v1.py index 2e45b269..08f7dd64 100644 --- a/test/python/restart/v1.py +++ b/test/python/restart/v1.py @@ -1,5 +1,3 @@ -import os - def application(environ, start_response): body = "v1".encode() diff --git a/test/python/restart/v2.py b/test/python/restart/v2.py index 59e3d30f..163d0d17 100644 --- a/test/python/restart/v2.py +++ b/test/python/restart/v2.py @@ -1,5 +1,3 @@ -import os - def application(environ, start_response): body = "v2".encode() diff --git a/test/python/threading/asgi.py b/test/python/threading/asgi.py index c4169a24..fed6fcce 100644 --- a/test/python/threading/asgi.py +++ b/test/python/threading/asgi.py @@ -1,7 +1,6 @@ -import asyncio import sys -import time import threading +import time class Foo(threading.Thread): diff --git a/test/python/threading/wsgi.py b/test/python/threading/wsgi.py index adaa2a37..48a73afd 100644 --- a/test/python/threading/wsgi.py +++ b/test/python/threading/wsgi.py @@ -1,6 +1,6 @@ import sys -import time import threading +import time class Foo(threading.Thread): diff --git a/test/python/threads/asgi.py b/test/python/threads/asgi.py index ff4e52ad..0537f59b 100644 --- a/test/python/threads/asgi.py +++ b/test/python/threads/asgi.py @@ -1,6 +1,5 @@ -import asyncio -import time import threading +import time async def application(scope, receive, send): diff --git a/test/python/threads/wsgi.py b/test/python/threads/wsgi.py index cc283cfe..35db2d07 100644 --- a/test/python/threads/wsgi.py +++ b/test/python/threads/wsgi.py @@ -1,5 +1,5 @@ -import time import threading +import time def application(environ, start_response): diff --git a/test/python/upload/wsgi.py b/test/python/upload/wsgi.py index 953c5ecc..2c820d06 100644 --- a/test/python/upload/wsgi.py +++ b/test/python/upload/wsgi.py @@ -1,5 +1,5 @@ +import cgi from tempfile import TemporaryFile -import os, cgi def read(environ): diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 00000000..5f94fad2 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,2 @@ +pyOpenSSL>=20.0.1 +pytest>=6.0.1 diff --git a/test/test_access_log.py b/test/test_access_log.py index ba254c5e..5d242a1a 100644 --- a/test/test_access_log.py +++ b/test/test_access_log.py @@ -1,7 +1,6 @@ import time import pytest - from unit.applications.lang.python import TestApplicationPython from unit.option import option diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py index f503fa82..021aa2b2 100644 --- a/test/test_asgi_application.py +++ b/test/test_asgi_application.py @@ -3,9 +3,7 @@ import time from distutils.version import LooseVersion import pytest - from unit.applications.lang.python import TestApplicationPython -from unit.option import option class TestASGIApplication(TestApplicationPython): diff --git a/test/test_asgi_lifespan.py b/test/test_asgi_lifespan.py index 90866ec3..912d0d85 100644 --- a/test/test_asgi_lifespan.py +++ b/test/test_asgi_lifespan.py @@ -1,8 +1,6 @@ import os from distutils.version import LooseVersion -import pytest - from conftest import unit_stop from unit.applications.lang.python import TestApplicationPython from unit.option import option diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py index a0eb1f84..b9489cd3 100644 --- a/test/test_asgi_targets.py +++ b/test/test_asgi_targets.py @@ -1,7 +1,6 @@ from distutils.version import LooseVersion import pytest - from unit.applications.lang.python import TestApplicationPython from unit.option import option diff --git a/test/test_asgi_websockets.py b/test/test_asgi_websockets.py index 140bcb9a..bad54e22 100644 --- a/test/test_asgi_websockets.py +++ b/test/test_asgi_websockets.py @@ -3,7 +3,6 @@ import time from distutils.version import LooseVersion import pytest - from unit.applications.lang.python import TestApplicationPython from unit.applications.websockets import TestApplicationWebsocket from unit.option import option @@ -1481,3 +1480,20 @@ class TestASGIWebsockets(TestApplicationPython): self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame sock.close() + + def test_asgi_websockets_client_locks_app(self): + self.load('websockets/mirror') + + message = 'blah' + + _, sock, _ = self.ws.upgrade() + + assert 'success' in self.conf({}), 'remove app' + + self.ws.frame_write(sock, self.ws.OP_TEXT, message) + + frame = self.ws.frame_read(sock) + + assert message == frame['data'].decode('utf-8'), 'client' + + sock.close() diff --git a/test/test_client_ip.py b/test/test_client_ip.py index 0084574e..4b2b2fa1 100644 --- a/test/test_client_ip.py +++ b/test/test_client_ip.py @@ -1,5 +1,3 @@ -import pytest - from unit.applications.lang.python import TestApplicationPython diff --git a/test/test_configuration.py b/test/test_configuration.py index 8655968f..4a9d9840 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -1,7 +1,6 @@ import socket import pytest - from unit.control import TestControl diff --git a/test/test_go_application.py b/test/test_go_application.py index 438ce2e0..94da1aee 100644 --- a/test/test_go_application.py +++ b/test/test_go_application.py @@ -1,11 +1,17 @@ import re +import pytest + from unit.applications.lang.go import TestApplicationGo class TestGoApplication(TestApplicationGo): prerequisites = {'modules': {'go': 'all'}} + @pytest.fixture(autouse=True) + def setup_method_fixture(self, request, skip_alert): + skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor') + def test_go_application_variables(self): self.load('variables') diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py index e02ef1cf..72988a34 100644 --- a/test/test_go_isolation.py +++ b/test/test_go_isolation.py @@ -3,7 +3,6 @@ import os import pwd import pytest - from unit.applications.lang.go import TestApplicationGo from unit.option import option from unit.utils import getns @@ -12,6 +11,10 @@ from unit.utils import getns class TestGoIsolation(TestApplicationGo): prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']} + @pytest.fixture(autouse=True) + def setup_method_fixture(self, request, skip_alert): + skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor') + def unpriv_creds(self): nobody_uid = pwd.getpwnam('nobody').pw_uid @@ -228,7 +231,7 @@ class TestGoIsolation(TestApplicationGo): obj = self.getjson()['body'] - assert obj['PID'] == 1, 'pid of container is 1' + assert obj['PID'] == 2, 'pid of container is 2' def test_isolation_namespace_false(self): self.load('ns_inspect') diff --git a/test/test_go_isolation_rootfs.py b/test/test_go_isolation_rootfs.py index 1cc59c67..d246a48d 100644 --- a/test/test_go_isolation_rootfs.py +++ b/test/test_go_isolation_rootfs.py @@ -1,13 +1,16 @@ import os import pytest - from unit.applications.lang.go import TestApplicationGo class TestGoIsolationRootfs(TestApplicationGo): prerequisites = {'modules': {'go': 'all'}} + @pytest.fixture(autouse=True) + def setup_method_fixture(self, request, skip_alert): + skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor') + def test_go_isolation_rootfs_chroot(self, is_su, temp_dir): if not is_su: pytest.skip('requires root') diff --git a/test/test_http_header.py b/test/test_http_header.py index fdb557cf..ca355eb7 100644 --- a/test/test_http_header.py +++ b/test/test_http_header.py @@ -1,5 +1,4 @@ import pytest - from unit.applications.lang.python import TestApplicationPython diff --git a/test/test_java_isolation_rootfs.py b/test/test_java_isolation_rootfs.py index a401e23b..eac86a0c 100644 --- a/test/test_java_isolation_rootfs.py +++ b/test/test_java_isolation_rootfs.py @@ -2,7 +2,6 @@ import os import subprocess import pytest - from unit.applications.lang.java import TestApplicationJava from unit.option import option @@ -19,7 +18,7 @@ class TestJavaIsolationRootfs(TestApplicationJava): os.chmod(option.temp_dir + '/tmp', 0o777) try: - process = subprocess.Popen( + subprocess.run( [ "mount", "--bind", @@ -29,12 +28,10 @@ class TestJavaIsolationRootfs(TestApplicationJava): stderr=subprocess.STDOUT, ) - process.communicate() - except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: pytest.fail('Can\'t run mount process.') def teardown_method(self, is_su): @@ -42,18 +39,16 @@ class TestJavaIsolationRootfs(TestApplicationJava): return try: - process = subprocess.Popen( + subprocess.run( ["umount", "--lazy", option.temp_dir + "/jars"], stderr=subprocess.STDOUT, ) - process.communicate() - except KeyboardInterrupt: raise - except: - pytest.fail('Can\'t run mount process.') + except subprocess.CalledProcessError: + pytest.fail('Can\'t run umount process.') def test_java_isolation_rootfs_chroot_war(self, is_su, temp_dir): if not is_su: diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py index df0f76e8..a80d3bf3 100644 --- a/test/test_java_websockets.py +++ b/test/test_java_websockets.py @@ -2,7 +2,6 @@ import struct import time import pytest - from unit.applications.lang.java import TestApplicationJava from unit.applications.websockets import TestApplicationWebsocket from unit.option import option diff --git a/test/test_node_application.py b/test/test_node_application.py index 48ed8d3d..62a09c43 100644 --- a/test/test_node_application.py +++ b/test/test_node_application.py @@ -1,7 +1,6 @@ import re import pytest - from unit.applications.lang.node import TestApplicationNode from unit.utils import waitforfiles diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py index 5464d4a6..12788fa4 100644 --- a/test/test_node_es_modules.py +++ b/test/test_node_es_modules.py @@ -1,7 +1,5 @@ from distutils.version import LooseVersion -import pytest - from unit.applications.lang.node import TestApplicationNode from unit.applications.websockets import TestApplicationWebsocket diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py index 51515f4e..e4c8a05e 100644 --- a/test/test_node_websockets.py +++ b/test/test_node_websockets.py @@ -2,7 +2,6 @@ import struct import time import pytest - from unit.applications.lang.node import TestApplicationNode from unit.applications.websockets import TestApplicationWebsocket from unit.option import option diff --git a/test/test_perl_application.py b/test/test_perl_application.py index e906aaca..dfd8be6c 100644 --- a/test/test_perl_application.py +++ b/test/test_perl_application.py @@ -1,7 +1,6 @@ import re import pytest - from unit.applications.lang.perl import TestApplicationPerl diff --git a/test/test_php_application.py b/test/test_php_application.py index 66e2ef7d..d9c16a6d 100644 --- a/test/test_php_application.py +++ b/test/test_php_application.py @@ -5,7 +5,6 @@ import signal import time import pytest - from unit.applications.lang.php import TestApplicationPHP from unit.option import option @@ -714,3 +713,20 @@ class TestPHPApplication(TestApplicationPHP): ), 'relative path w/ chdir' assert self.get()['body'] == 'test', 'relative path 2' + + def test_php_application_shared_opcache(self): + self.load('opcache', limits={'requests': 1}) + + r = self.get() + cached = r['headers']['X-Cached'] + if cached == '-1': + pytest.skip('opcache is not supported') + + pid = r['headers']['X-Pid'] + + assert cached == '0', 'not cached' + + r = self.get() + + assert r['headers']['X-Pid'] != pid, 'new instance' + assert r['headers']['X-Cached'] == '1', 'cached' diff --git a/test/test_php_isolation.py b/test/test_php_isolation.py index 8db6b590..aebeefa6 100644 --- a/test/test_php_isolation.py +++ b/test/test_php_isolation.py @@ -1,5 +1,4 @@ import pytest - from unit.applications.lang.php import TestApplicationPHP from unit.option import option diff --git a/test/test_proxy.py b/test/test_proxy.py index 3d59cf24..553cb07c 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -3,7 +3,6 @@ import socket import time import pytest - from conftest import run_process from unit.applications.lang.python import TestApplicationPython from unit.option import option diff --git a/test/test_python_application.py b/test/test_python_application.py index 48c3d603..7bd43664 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -5,7 +5,6 @@ import re import time import pytest - from unit.applications.lang.python import TestApplicationPython diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py index 93f85264..53d28285 100644 --- a/test/test_python_isolation.py +++ b/test/test_python_isolation.py @@ -1,5 +1,4 @@ import pytest - from unit.applications.lang.python import TestApplicationPython from unit.option import option from unit.utils import findmnt diff --git a/test/test_python_isolation_chroot.py b/test/test_python_isolation_chroot.py index 54fbad12..1554fb72 100644 --- a/test/test_python_isolation_chroot.py +++ b/test/test_python_isolation_chroot.py @@ -1,5 +1,4 @@ import pytest - from unit.applications.lang.python import TestApplicationPython diff --git a/test/test_python_procman.py b/test/test_python_procman.py index a95c5680..a25b84ec 100644 --- a/test/test_python_procman.py +++ b/test/test_python_procman.py @@ -4,7 +4,6 @@ import subprocess import time import pytest - from unit.applications.lang.python import TestApplicationPython from unit.option import option @@ -23,7 +22,9 @@ class TestPythonProcman(TestApplicationPython): output = subprocess.check_output(['ps', 'ax']) pids = set() - for m in re.findall('.*' + self.app_name, output.decode()): + for m in re.findall( + '.*unit: "' + self.app_name + '" application', output.decode() + ): pids.add(re.search(r'^\s*(\d+)', m).group(1)) return pids @@ -265,7 +266,8 @@ class TestPythonProcman(TestApplicationPython): assert len(self.pids_for_process()) == 1, 'longstarts == 1' - pid = self.get()['body'] + self.get() + pids = self.pids_for_process() assert len(pids) == 2, 'longstarts == 2' diff --git a/test/test_python_targets.py b/test/test_python_targets.py index ca736c0d..e5dca870 100644 --- a/test/test_python_targets.py +++ b/test/test_python_targets.py @@ -1,5 +1,3 @@ -import pytest - from unit.applications.lang.python import TestApplicationPython from unit.option import option diff --git a/test/test_routing.py b/test/test_routing.py index ef5622c2..167d2640 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import pytest - from unit.applications.proto import TestApplicationProto from unit.option import option @@ -1321,6 +1320,58 @@ class TestRouting(TestApplicationProto): self.route_match_invalid({"arguments": {"%%1F": ""}}) self.route_match_invalid({"arguments": {"%7%F": ""}}) + def test_routes_match_query(self): + self.route_match({"query": "!"}) + assert self.get(url='/')['status'] == 404 + assert self.get(url='/?')['status'] == 404 + assert self.get(url='/?foo')['status'] == 200 + assert self.get(url='/?foo=')['status'] == 200 + assert self.get(url='/?foo=baz')['status'] == 200 + + self.route_match({"query": "foo=%26"}) + assert self.get(url='/?foo=&')['status'] == 200 + + self.route_match({"query": "a=b&c=d"}) + assert self.get(url='/?a=b&c=d')['status'] == 200 + + self.route_match({"query": "a=b%26c%3Dd"}) + assert self.get(url='/?a=b%26c%3Dd')['status'] == 200 + assert self.get(url='/?a=b&c=d')['status'] == 200 + + self.route_match({"query": "a=b%26c%3Dd+e"}) + assert self.get(url='/?a=b&c=d e')['status'] == 200 + + def test_routes_match_query_array(self): + self.route_match({"query": ["foo", "bar"]}) + + assert self.get()['status'] == 404, 'no args' + assert self.get(url='/?foo')['status'] == 200, 'arg first' + assert self.get(url='/?bar')['status'] == 200, 'arg second' + + assert 'success' in self.conf_delete( + 'routes/0/match/query/1' + ), 'query array remove second' + + assert self.get(url='/?foo')['status'] == 200, 'still arg first' + assert self.get(url='/?bar')['status'] == 404, 'no arg second' + + self.route_match({"query": ["!f", "foo"]}) + + assert self.get(url='/?f')['status'] == 404, 'negative arg' + assert self.get(url='/?fo')['status'] == 404, 'negative arg 2' + assert self.get(url='/?foo')['status'] == 200, 'negative arg 3' + + self.route_match({"query": []}) + assert self.get()['status'] == 200, 'empty array' + + def test_routes_match_query_invalid(self): + self.route_match_invalid({"query": [1]}) + self.route_match_invalid({"query": "%"}) + self.route_match_invalid({"query": "%1G"}) + self.route_match_invalid({"query": "%0"}) + self.route_match_invalid({"query": "%%1F"}) + self.route_match_invalid({"query": ["foo", "%3D", "%%1F"]}) + def test_routes_match_cookies(self): self.route_match({"cookies": {"foO": "bar"}}) diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py index ddd31f59..ed0200d9 100644 --- a/test/test_ruby_application.py +++ b/test/test_ruby_application.py @@ -2,7 +2,6 @@ import re import subprocess import pytest - from unit.applications.lang.ruby import TestApplicationRuby diff --git a/test/test_ruby_hooks.py b/test/test_ruby_hooks.py index af8ce337..20980ad7 100644 --- a/test/test_ruby_hooks.py +++ b/test/test_ruby_hooks.py @@ -1,10 +1,3 @@ -import os -import time -from pathlib import Path - -import pytest - -from conftest import unit_stop from unit.applications.lang.ruby import TestApplicationRuby from unit.option import option from unit.utils import waitforglob diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py index f414d610..940427f1 100644 --- a/test/test_ruby_isolation.py +++ b/test/test_ruby_isolation.py @@ -1,8 +1,4 @@ -import os -import shutil - import pytest - from unit.applications.lang.ruby import TestApplicationRuby from unit.option import option diff --git a/test/test_settings.py b/test/test_settings.py index 49041b62..a16e35e8 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -3,7 +3,6 @@ import socket import time import pytest - from unit.applications.lang.python import TestApplicationPython from unit.utils import sysctl diff --git a/test/test_static.py b/test/test_static.py index 669e265d..80f4c610 100644 --- a/test/test_static.py +++ b/test/test_static.py @@ -1,8 +1,9 @@ import os +import shutil import socket import pytest - +from conftest import unit_run, unit_stop from unit.applications.proto import TestApplicationProto from unit.option import option from unit.utils import waitforfiles @@ -28,7 +29,9 @@ class TestStatic(TestApplicationProto): self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"share": option.temp_dir + "/assets"}}], + "routes": [ + {"action": {"share": option.temp_dir + "/assets$uri"}} + ], "settings": { "http": { "static": { @@ -39,6 +42,49 @@ 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): assert self.get(url='/index.html')['body'] == '0123456789', 'index' assert self.get(url='/')['body'] == '0123456789', 'index 2' diff --git a/test/test_static_chroot.py b/test/test_static_chroot.py index f9bc93a8..62288807 100644 --- a/test/test_static_chroot.py +++ b/test/test_static_chroot.py @@ -2,7 +2,6 @@ import os from pathlib import Path import pytest - from unit.applications.proto import TestApplicationProto @@ -21,17 +20,27 @@ class TestStaticChroot(TestApplicationProto): self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"share": temp_dir + "/assets"}}], + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], } ) + def update_action(self, share, chroot): + return self.conf( + {"share": share, "chroot": chroot}, 'routes/0/action', + ) + + def get_custom(self, uri, host): + return self.get( + url=uri, headers={'Host': host, 'Connection': 'close'} + )['status'] + def test_static_chroot(self, temp_dir): assert self.get(url='/dir/file')['status'] == 200, 'default chroot' assert self.get(url='/index.html')['status'] == 200, 'default chroot 2' assert 'success' in self.conf( { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "chroot": temp_dir + "/assets/dir", }, 'routes/0/action', @@ -41,6 +50,30 @@ class TestStaticChroot(TestApplicationProto): assert self.get(url='/index.html')['status'] == 403, 'chroot 403 2' assert self.get(url='/file')['status'] == 403, 'chroot 403' + def test_share_chroot_array(self, temp_dir): + assert 'success' in self.conf( + { + "share": ["/blah", temp_dir + "/assets$uri"], + "chroot": temp_dir + "/assets/dir", + }, + 'routes/0/action', + ), 'configure share array' + assert self.get(url='/dir/file')['status'] == 200, 'share array' + + assert 'success' in self.update_action( + ["/blah", temp_dir + '/assets$uri'], temp_dir + '/assets/$host' + ) + assert self.get_custom('/dir/file', 'dir') == 200, 'array variable' + + assert 'success' in self.conf( + { + "share": ["/blah", "/blah2"], + "chroot": temp_dir + "/assets/dir", + }, + 'routes/0/action', + ), 'configure share array bad' + assert self.get()['status'] != 200, 'share array bad' + def test_static_chroot_permission(self, is_su, temp_dir): if is_su: pytest.skip('does\'t work under root') @@ -49,7 +82,7 @@ class TestStaticChroot(TestApplicationProto): assert 'success' in self.conf( { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "chroot": temp_dir + "/assets/dir", }, 'routes/0/action', @@ -59,7 +92,8 @@ class TestStaticChroot(TestApplicationProto): def test_static_chroot_empty(self, temp_dir): assert 'success' in self.conf( - {"share": temp_dir + "/assets", "chroot": ""}, 'routes/0/action', + {"share": temp_dir + "/assets$uri", "chroot": ""}, + 'routes/0/action', ), 'configure chroot empty absolute' assert ( @@ -67,7 +101,7 @@ class TestStaticChroot(TestApplicationProto): ), 'chroot empty absolute' assert 'success' in self.conf( - {"share": ".", "chroot": ""}, 'routes/0/action', + {"share": ".$uri", "chroot": ""}, 'routes/0/action', ), 'configure chroot empty relative' assert ( @@ -79,23 +113,99 @@ class TestStaticChroot(TestApplicationProto): pytest.skip('does\'t work under root') assert 'success' in self.conf( - {"share": temp_dir + "/assets", "chroot": "."}, 'routes/0/action', + {"share": temp_dir + "/assets$uri", "chroot": "."}, + 'routes/0/action', ), 'configure relative chroot' assert self.get(url='/dir/file')['status'] == 403, 'relative chroot' assert 'success' in self.conf( - {"share": "."}, 'routes/0/action', + {"share": ".$uri"}, 'routes/0/action', ), 'configure relative share' assert self.get(url=self.test_path)['status'] == 200, 'relative share' assert 'success' in self.conf( - {"share": ".", "chroot": "."}, 'routes/0/action', + {"share": ".$uri", "chroot": "."}, 'routes/0/action', ), 'configure relative' assert self.get(url=self.test_path)['status'] == 200, 'relative' + def test_static_chroot_variables(self, temp_dir): + assert 'success' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/assets/$host' + ) + assert self.get_custom('/dir/file', 'dir') == 200 + + assert 'success' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/assets/${host}' + ) + assert self.get_custom('/dir/file', 'dir') == 200 + + def test_static_chroot_variables_buildin_start(self, temp_dir): + assert 'success' in self.update_action( + temp_dir + '/assets/dir/$host', '$uri/assets/dir' + ) + + assert self.get_custom(temp_dir, 'file') == 200 + + def test_static_chroot_variables_buildin_mid(self, temp_dir): + assert 'success' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/$host/dir' + ) + + assert self.get_custom('/dir/file', 'assets') == 200 + + def test_static_chroot_variables_buildin_end(self, temp_dir): + assert 'success' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/assets/$host' + ) + + assert self.get_custom('/dir/file', 'dir') == 200 + + def test_static_chroot_slash(self, temp_dir): + assert 'success' in self.conf( + { + "share": temp_dir + "/assets$uri", + "chroot": temp_dir + "/assets/dir/", + }, + 'routes/0/action', + ), 'configure chroot slash end' + + assert self.get(url='/dir/file')['status'] == 200, 'slash end' + assert self.get(url='/dirxfile')['status'] == 403, 'slash end bad' + + assert 'success' in self.conf( + { + "share": temp_dir + "/assets$uri", + "chroot": temp_dir + "/assets/dir", + }, + 'routes/0/action', + ), 'configure chroot no slash end' + + assert self.get(url='/dir/file')['status'] == 200, 'no slash end' + + assert 'success' in self.conf( + { + "share": temp_dir + "/assets$uri", + "chroot": temp_dir + "/assets/dir/", + }, + 'routes/0/action', + ), 'configure chroot slash end 2' + + assert self.get(url='/dir/file')['status'] == 200, 'slash end 2' + assert self.get(url='/dirxfile')['status'] == 403, 'slash end 2 bad' + + assert 'success' in self.conf( + { + "share": temp_dir + "///assets/////$uri", + "chroot": temp_dir + "//assets////dir///", + }, + 'routes/0/action', + ), 'configure chroot multiple slashes' + + assert self.get(url='/dir/file')['status'] == 200, 'multiple slashes' + def test_static_chroot_invalid(self, temp_dir): assert 'error' in self.conf( {"share": temp_dir, "chroot": True}, 'routes/0/action', @@ -106,3 +216,10 @@ class TestStaticChroot(TestApplicationProto): assert 'error' in self.conf( {"share": temp_dir, "mount": "True"}, 'routes/0/action', ), 'configure mount error' + + assert 'error' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/assets/d$r$uri' + ) + assert 'error' in self.update_action( + temp_dir + '/assets$uri', temp_dir + '/assets/$$uri' + ) diff --git a/test/test_static_fallback.py b/test/test_static_fallback.py index dc9056b9..71b268c8 100644 --- a/test/test_static_fallback.py +++ b/test/test_static_fallback.py @@ -2,7 +2,6 @@ import os from pathlib import Path import pytest - from unit.applications.proto import TestApplicationProto @@ -23,7 +22,7 @@ class TestStaticFallback(TestApplicationProto): "*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}, }, - "routes": [{"action": {"share": temp_dir + "/assets"}}], + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], "applications": {}, } ) @@ -50,7 +49,7 @@ class TestStaticFallback(TestApplicationProto): def test_static_fallback_valid_path(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "fallback": {"return": 200}} + {"share": temp_dir + "/assets$uri", "fallback": {"return": 200}} ) resp = self.get() assert resp['status'] == 200, 'fallback status' @@ -83,7 +82,7 @@ class TestStaticFallback(TestApplicationProto): def test_static_fallback_share(self, temp_dir): self.action_update( - {"share": "/blah", "fallback": {"share": temp_dir + "/assets"},} + {"share": "/blah", "fallback": {"share": temp_dir + "/assets$uri"},} ) resp = self.get() diff --git a/test/test_static_mount.py b/test/test_static_mount.py index 570f6439..82eda956 100644 --- a/test/test_static_mount.py +++ b/test/test_static_mount.py @@ -3,7 +3,6 @@ import subprocess from pathlib import Path import pytest - from unit.applications.proto import TestApplicationProto @@ -23,7 +22,7 @@ class TestStaticMount(TestApplicationProto): Path(temp_dir + '/assets/mount/index.html').write_text('mount') try: - process = subprocess.Popen( + subprocess.check_output( [ "mount", "--bind", @@ -33,35 +32,33 @@ class TestStaticMount(TestApplicationProto): stderr=subprocess.STDOUT, ) - process.communicate() - except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: pytest.fail('Can\'t run mount process.') self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"share": temp_dir + "/assets/dir"}}], + "routes": [ + {"action": {"share": temp_dir + "/assets/dir$uri"}} + ], } ) yield try: - process = subprocess.Popen( + subprocess.check_output( ["umount", "--lazy", temp_dir + "/assets/dir/mount"], stderr=subprocess.STDOUT, ) - process.communicate() - except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: pytest.fail('Can\'t run umount process.') def test_static_mount(self, temp_dir, skip_alert): @@ -72,14 +69,14 @@ class TestStaticMount(TestApplicationProto): assert resp['body'] == 'mount' assert 'success' in self.conf( - {"share": temp_dir + "/assets/dir", "traverse_mounts": False}, + {"share": temp_dir + "/assets/dir$uri", "traverse_mounts": False}, 'routes/0/action', ), 'configure mount disable' assert self.get(url='/mount/')['status'] == 403 assert 'success' in self.conf( - {"share": temp_dir + "/assets/dir", "traverse_mounts": True}, + {"share": temp_dir + "/assets/dir$uri", "traverse_mounts": True}, 'routes/0/action', ), 'configure mount enable' @@ -97,14 +94,14 @@ class TestStaticMount(TestApplicationProto): { "match": {"method": "HEAD"}, "action": { - "share": temp_dir + "/assets/dir", + "share": temp_dir + "/assets/dir$uri", "traverse_mounts": False, }, }, { "match": {"method": "GET"}, "action": { - "share": temp_dir + "/assets/dir", + "share": temp_dir + "/assets/dir$uri", "traverse_mounts": True, }, }, @@ -120,7 +117,7 @@ class TestStaticMount(TestApplicationProto): assert 'success' in self.conf( { - "share": temp_dir + "/assets/dir", + "share": temp_dir + "/assets/dir$uri", "chroot": temp_dir + "/assets", }, 'routes/0/action', @@ -130,7 +127,7 @@ class TestStaticMount(TestApplicationProto): assert 'success' in self.conf( { - "share": temp_dir + "/assets/dir", + "share": temp_dir + "/assets/dir$uri", "chroot": temp_dir + "/assets", "traverse_mounts": False, }, diff --git a/test/test_static_share.py b/test/test_static_share.py new file mode 100644 index 00000000..5384866e --- /dev/null +++ b/test/test_static_share.py @@ -0,0 +1,72 @@ +import os +from pathlib import Path + +import pytest +from unit.applications.proto import TestApplicationProto + + +class TestStaticShare(TestApplicationProto): + prerequisites = {} + + @pytest.fixture(autouse=True) + def setup_method_fixture(self, temp_dir): + os.makedirs(temp_dir + '/assets/dir') + os.makedirs(temp_dir + '/assets/dir2') + + Path(temp_dir + '/assets/dir/file').write_text('1') + Path(temp_dir + '/assets/dir2/file2').write_text('2') + + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], + "applications": {}, + } + ) + + def action_update(self, conf): + assert 'success' in self.conf(conf, 'routes/0/action') + + def test_share_array(self, temp_dir): + assert self.get(url='/dir/file')['body'] == '1' + assert self.get(url='/dir2/file2')['body'] == '2' + + self.action_update({"share": [temp_dir + "/assets/dir$uri"]}) + + assert self.get(url='/file')['body'] == '1' + assert self.get(url='/file2')['status'] == 404 + + self.action_update( + { + "share": [ + temp_dir + "/assets/dir$uri", + temp_dir + "/assets/dir2$uri", + ] + } + ) + + assert self.get(url='/file')['body'] == '1' + assert self.get(url='/file2')['body'] == '2' + + self.action_update( + { + "share": [ + temp_dir + "/assets/dir2$uri", + temp_dir + "/assets/dir3$uri", + ] + } + ) + + assert self.get(url='/file')['status'] == 404 + assert self.get(url='/file2')['body'] == '2' + + def test_share_array_fallback(self): + self.action_update( + {"share": ["/blah", "/blah2"], "fallback": {"return": 201}} + ) + + assert self.get()['status'] == 201 + + def test_share_array_invalid(self): + assert 'error' in self.conf({"share": []}, 'routes/0/action') + assert 'error' in self.conf({"share": {}}, 'routes/0/action') diff --git a/test/test_static_symlink.py b/test/test_static_symlink.py index 35eb402a..24638e20 100644 --- a/test/test_static_symlink.py +++ b/test/test_static_symlink.py @@ -2,7 +2,6 @@ import os from pathlib import Path import pytest - from unit.applications.proto import TestApplicationProto @@ -18,7 +17,7 @@ class TestStaticSymlink(TestApplicationProto): self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"share": temp_dir + "/assets"}}], + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], } ) @@ -33,14 +32,14 @@ class TestStaticSymlink(TestApplicationProto): assert self.get(url='/link/file')['status'] == 200, 'symlink file' assert 'success' in self.conf( - {"share": temp_dir + "/assets", "follow_symlinks": False}, + {"share": temp_dir + "/assets$uri", "follow_symlinks": False}, 'routes/0/action', ), 'configure symlink disable' assert self.get(url='/link/file')['status'] == 403, 'symlink disabled' assert 'success' in self.conf( - {"share": temp_dir + "/assets", "follow_symlinks": True}, + {"share": temp_dir + "/assets$uri", "follow_symlinks": True}, 'routes/0/action', ), 'configure symlink enable' @@ -56,14 +55,14 @@ class TestStaticSymlink(TestApplicationProto): { "match": {"method": "HEAD"}, "action": { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "follow_symlinks": False, }, }, { "match": {"method": "GET"}, "action": { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "follow_symlinks": True, }, }, @@ -85,7 +84,7 @@ class TestStaticSymlink(TestApplicationProto): assert 'success' in self.conf( { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "chroot": temp_dir + "/assets/dir/dir", }, 'routes/0/action', diff --git a/test/test_static_types.py b/test/test_static_types.py index 20defddf..18564a21 100644 --- a/test/test_static_types.py +++ b/test/test_static_types.py @@ -1,7 +1,6 @@ from pathlib import Path import pytest - from unit.applications.proto import TestApplicationProto @@ -22,7 +21,7 @@ class TestStaticTypes(TestApplicationProto): "*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}, }, - "routes": [{"action": {"share": temp_dir + "/assets"}}], + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], "applications": {}, } ) @@ -36,39 +35,39 @@ class TestStaticTypes(TestApplicationProto): assert resp['body'] == body, 'body' def test_static_types_basic(self, temp_dir): - self.action_update({"share": temp_dir + "/assets"}) + self.action_update({"share": temp_dir + "/assets$uri"}) self.check_body('/index.html', 'index') self.check_body('/file.xml', '.xml') self.action_update( - {"share": temp_dir + "/assets", "types": "application/xml"} + {"share": temp_dir + "/assets$uri", "types": "application/xml"} ) self.check_body('/file.xml', '.xml') self.action_update( - {"share": temp_dir + "/assets", "types": ["application/xml"]} + {"share": temp_dir + "/assets$uri", "types": ["application/xml"]} ) self.check_body('/file.xml', '.xml') - self.action_update({"share": temp_dir + "/assets", "types": [""]}) + self.action_update({"share": temp_dir + "/assets$uri", "types": [""]}) assert self.get(url='/file.xml')['status'] == 403, 'no mtype' def test_static_types_wildcard(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "types": ["application/*"]} + {"share": temp_dir + "/assets$uri", "types": ["application/*"]} ) self.check_body('/file.xml', '.xml') assert self.get(url='/file.mp4')['status'] == 403, 'app * mtype mp4' self.action_update( - {"share": temp_dir + "/assets", "types": ["video/*"]} + {"share": temp_dir + "/assets$uri", "types": ["video/*"]} ) assert self.get(url='/file.xml')['status'] == 403, 'video * mtype xml' self.check_body('/file.mp4', '.mp4') def test_static_types_negation(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "types": ["!application/xml"]} + {"share": temp_dir + "/assets$uri", "types": ["!application/xml"]} ) assert self.get(url='/file.xml')['status'] == 403, 'forbidden negation' self.check_body('/file.mp4', '.mp4') @@ -76,7 +75,7 @@ class TestStaticTypes(TestApplicationProto): # sorting negation self.action_update( { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "types": ["!video/*", "image/png", "!image/jpg"], } ) @@ -86,7 +85,7 @@ class TestStaticTypes(TestApplicationProto): def test_static_types_regex(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "types": ["~text/(html|plain)"]} + {"share": temp_dir + "/assets$uri", "types": ["~text/(html|plain)"]} ) assert self.get(url='/file.php')['status'] == 403, 'regex fail' self.check_body('/file.html', '.html') @@ -94,7 +93,7 @@ class TestStaticTypes(TestApplicationProto): def test_static_types_case(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "types": ["!APpliCaTiOn/xMl"]} + {"share": temp_dir + "/assets$uri", "types": ["!APpliCaTiOn/xMl"]} ) self.check_body('/file.mp4', '.mp4') assert ( @@ -102,7 +101,7 @@ class TestStaticTypes(TestApplicationProto): ), 'mixed case xml negation' self.action_update( - {"share": temp_dir + "/assets", "types": ["vIdEo/mp4"]} + {"share": temp_dir + "/assets$uri", "types": ["vIdEo/mp4"]} ) assert self.get(url='/file.mp4')['status'] == 200, 'mixed case' assert ( @@ -110,7 +109,7 @@ class TestStaticTypes(TestApplicationProto): ), 'mixed case video negation' self.action_update( - {"share": temp_dir + "/assets", "types": ["vIdEo/*"]} + {"share": temp_dir + "/assets$uri", "types": ["vIdEo/*"]} ) self.check_body('/file.mp4', '.mp4') assert ( @@ -126,7 +125,7 @@ class TestStaticTypes(TestApplicationProto): }, { "action": { - "share": temp_dir + "/assets", + "share": temp_dir + "/assets$uri", "types": ["!application/x-httpd-php"], "fallback": {"proxy": "http://127.0.0.1:7081"}, } @@ -140,17 +139,18 @@ class TestStaticTypes(TestApplicationProto): def test_static_types_index(self, temp_dir): self.action_update( - {"share": temp_dir + "/assets", "types": "application/xml"} + {"share": temp_dir + "/assets$uri", "types": "application/xml"} ) self.check_body('/', 'index') self.check_body('/file.xml', '.xml') + assert self.get(url='/index.html')['status'] == 403, 'forbidden mtype' assert self.get(url='/file.mp4')['status'] == 403, 'forbidden mtype' def test_static_types_custom_mime(self, temp_dir): self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"share": temp_dir + "/assets"}}], + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], "applications": {}, "settings": { "http": { @@ -160,10 +160,10 @@ class TestStaticTypes(TestApplicationProto): } ) - self.action_update({"share": temp_dir + "/assets", "types": [""]}) + self.action_update({"share": temp_dir + "/assets$uri", "types": [""]}) assert self.get(url='/file')['status'] == 403, 'forbidden custom mime' self.action_update( - {"share": temp_dir + "/assets", "types": ["test/mime-type"]} + {"share": temp_dir + "/assets$uri", "types": ["test/mime-type"]} ) self.check_body('/file', '') diff --git a/test/test_static_variables.py b/test/test_static_variables.py new file mode 100644 index 00000000..e7e1629c --- /dev/null +++ b/test/test_static_variables.py @@ -0,0 +1,79 @@ +import os +from pathlib import Path + +import pytest +from unit.applications.proto import TestApplicationProto + + +class TestStaticVariables(TestApplicationProto): + prerequisites = {} + + @pytest.fixture(autouse=True) + def setup_method_fixture(self, temp_dir): + os.makedirs(temp_dir + '/assets/dir') + os.makedirs(temp_dir + '/assets/d$r') + Path(temp_dir + '/assets/index.html').write_text('0123456789') + Path(temp_dir + '/assets/dir/file').write_text('file') + Path(temp_dir + '/assets/d$r/file').write_text('d$r') + + self._load_conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [{"action": {"share": temp_dir + "/assets$uri"}}], + } + ) + + def update_share(self, share): + if isinstance(share, list): + return self.conf(share, 'routes/0/action/share') + + return self.conf('"' + share + '"', 'routes/0/action/share') + + def test_static_variables(self, temp_dir): + assert self.get(url='/index.html')['status'] == 200 + assert self.get(url='/d$r/file')['status'] == 200 + + assert 'success' in self.update_share('$uri') + assert self.get(url=temp_dir + '/assets/index.html')['status'] == 200 + + assert 'success' in self.update_share(temp_dir + '/assets${uri}') + assert self.get(url='/index.html')['status'] == 200 + + def test_static_variables_array(self, temp_dir): + assert 'success' in self.update_share( + [temp_dir + '/assets$uri', '$uri'] + ) + + assert self.get(url='/dir/file')['status'] == 200 + assert self.get(url=temp_dir + '/assets/index.html')['status'] == 200 + assert self.get(url='/blah')['status'] == 404 + + assert 'success' in self.conf( + { + "share": [temp_dir + '/assets$uri', '$uri'], + "fallback": {"return": 201}, + }, + 'routes/0/action', + ) + + assert self.get(url='/dir/file')['status'] == 200 + assert self.get(url=temp_dir + '/assets/index.html')['status'] == 200 + assert self.get(url='/dir/blah')['status'] == 201 + + def test_static_variables_buildin_start(self, temp_dir): + assert 'success' in self.update_share('$uri/assets/index.html') + assert self.get(url=temp_dir)['status'] == 200 + + def test_static_variables_buildin_mid(self, temp_dir): + assert 'success' in self.update_share(temp_dir + '$uri/index.html') + assert self.get(url='/assets')['status'] == 200 + + def test_static_variables_buildin_end(self): + assert self.get(url='/index.html')['status'] == 200 + + def test_static_variables_invalid(self, temp_dir): + assert 'error' in self.update_share(temp_dir + '/assets/d$r$uri') + assert 'error' in self.update_share(temp_dir + '/assets/$$uri') + assert 'error' in self.update_share( + [temp_dir + '/assets$uri', temp_dir + '/assets/dir', '$$uri'] + ) diff --git a/test/test_tls.py b/test/test_tls.py index 546f0f89..01336765 100644 --- a/test/test_tls.py +++ b/test/test_tls.py @@ -5,7 +5,6 @@ import subprocess import time import pytest - from unit.applications.tls import TestApplicationTLS from unit.option import option @@ -33,7 +32,7 @@ class TestTLS(TestApplicationTLS): def req(self, name='localhost', subject=None, x509=False): subj = subject if subject is not None else '/CN=' + name + '/' - subprocess.call( + subprocess.check_output( [ 'openssl', 'req', @@ -88,7 +87,7 @@ basicConstraints = critical,CA:TRUE""" f.write('') def ca(self, cert='root', out='localhost'): - subprocess.call( + subprocess.check_output( [ 'openssl', 'ca', @@ -221,7 +220,7 @@ basicConstraints = critical,CA:TRUE""" self.openssl_conf() - subprocess.call( + subprocess.check_output( [ 'openssl', 'ecparam', @@ -235,7 +234,7 @@ basicConstraints = critical,CA:TRUE""" stderr=subprocess.STDOUT, ) - subprocess.call( + subprocess.check_output( [ 'openssl', 'req', @@ -590,9 +589,9 @@ basicConstraints = critical,CA:TRUE""" app_id = self.findall(r'(\d+)#\d+ "mirror" application started')[0] - subprocess.call(['kill', '-9', app_id]) + subprocess.check_output(['kill', '-9', app_id]) - skip_alert(r'process %s exited on signal 9' % app_id) + skip_alert(r'process .* %s.* exited on signal 9' % app_id) self.wait_for_record( re.compile( @@ -677,4 +676,3 @@ basicConstraints = critical,CA:TRUE""" assert self.get_ssl()['status'] == 200, 'listener #1' assert self.get_ssl(port=7081)['status'] == 200, 'listener #2' - diff --git a/test/test_tls_conf_command.py b/test/test_tls_conf_command.py index ccae09ad..b414b5a0 100644 --- a/test/test_tls_conf_command.py +++ b/test/test_tls_conf_command.py @@ -1,7 +1,6 @@ import ssl import pytest - from unit.applications.tls import TestApplicationTLS diff --git a/test/test_tls_session.py b/test/test_tls_session.py new file mode 100644 index 00000000..58f11f2d --- /dev/null +++ b/test/test_tls_session.py @@ -0,0 +1,126 @@ +import socket +import time + +import pytest + +pytest.importorskip('OpenSSL.SSL') +from OpenSSL.SSL import ( + TLSv1_2_METHOD, + SESS_CACHE_CLIENT, + OP_NO_TICKET, + Context, + Connection, + _lib, +) +from unit.applications.tls import TestApplicationTLS + + +class TestTLSSession(TestApplicationTLS): + prerequisites = {'modules': {'openssl': 'any'}} + + @pytest.fixture(autouse=True) + def setup_method_fixture(self, request): + self.certificate() + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": { + "pass": "routes", + "tls": {"certificate": "default", "session": {}}, + } + }, + "routes": [{"action": {"return": 200}}], + "applications": {}, + } + ), 'load application configuration' + + def add_session(self, cache_size=None, timeout=None): + session = {} + + if cache_size is not None: + session['cache_size'] = cache_size + if timeout is not None: + session['timeout'] = timeout + + return self.conf(session, 'listeners/*:7080/tls/session') + + def connect(self, ctx=None, session=None): + sock = socket.create_connection(('127.0.0.1', 7080)) + + if ctx is None: + ctx = Context(TLSv1_2_METHOD) + ctx.set_session_cache_mode(SESS_CACHE_CLIENT) + ctx.set_options(OP_NO_TICKET) + + client = Connection(ctx, sock) + client.set_connect_state() + + if session is not None: + client.set_session(session) + + client.do_handshake() + client.shutdown() + + return ( + client, + client.get_session(), + ctx, + _lib.SSL_session_reused(client._ssl), + ) + + def test_tls_session(self): + client, sess, ctx, reused = self.connect() + assert not reused, 'new connection' + + client, _, _, reused = self.connect(ctx, sess) + assert not reused, 'no cache' + + assert 'success' in self.add_session(cache_size=2) + + client, sess, ctx, reused = self.connect() + assert not reused, 'new connection cache' + + client, _, _, reused = self.connect(ctx, sess) + assert reused, 'cache' + + client, _, _, reused = self.connect(ctx, sess) + assert reused, 'cache 2' + + # check that at least one session of four is not reused + + clients = [self.connect() for _ in range(4)] + assert True not in [c[-1] for c in clients], 'cache small all new' + + clients_again = [self.connect(c[2], c[1]) for c in clients] + assert False in [c[-1] for c in clients_again], 'cache small no reuse' + + # all four sessions are reused + + assert 'success' in self.add_session(cache_size=8) + + clients = [self.connect() for _ in range(4)] + assert True not in [c[-1] for c in clients], 'cache big all new' + + clients_again = [self.connect(c[2], c[1]) for c in clients] + assert False not in [c[-1] for c in clients_again], 'cache big reuse' + + def test_tls_session_timeout(self): + assert 'success' in self.add_session(cache_size=5, timeout=1) + + client, sess, ctx, reused = self.connect() + assert not reused, 'new connection' + + client, _, _, reused = self.connect(ctx, sess) + assert reused, 'no timeout' + + time.sleep(3) + + client, _, _, reused = self.connect(ctx, sess) + assert not reused, 'timeout' + + def test_tls_session_invalid(self): + assert 'error' in self.add_session(cache_size=-1) + assert 'error' in self.add_session(cache_size={}) + assert 'error' in self.add_session(timeout=-1) + assert 'error' in self.add_session(timeout={}) diff --git a/test/test_tls_sni.py b/test/test_tls_sni.py index eba6140a..d5f205cf 100644 --- a/test/test_tls_sni.py +++ b/test/test_tls_sni.py @@ -1,8 +1,6 @@ import ssl import subprocess -import pytest - from unit.applications.tls import TestApplicationTLS from unit.option import option @@ -76,7 +74,7 @@ basicConstraints = critical,CA:TRUE""" else '/' ) - subprocess.call( + subprocess.check_output( [ 'openssl', 'req', @@ -102,7 +100,7 @@ basicConstraints = critical,CA:TRUE""" else '/' ) - subprocess.call( + subprocess.check_output( [ 'openssl', 'ca', diff --git a/test/test_tls_tickets.py b/test/test_tls_tickets.py new file mode 100644 index 00000000..6899eaa1 --- /dev/null +++ b/test/test_tls_tickets.py @@ -0,0 +1,196 @@ +import socket + +import pytest + +pytest.importorskip('OpenSSL.SSL') +from OpenSSL.SSL import ( + TLSv1_2_METHOD, + Context, + Connection, + Session, + _lib, +) +from unit.applications.tls import TestApplicationTLS + + +class TestTLSTicket(TestApplicationTLS): + prerequisites = {'modules': {'openssl': 'any'}} + + ticket = 'U1oDTh11mMxODuw12gS0EXX1E/PkZG13cJNQ6m5+6BGlfPTjNlIEw7PSVU3X1gTE' + ticket2 = ( + '5AV0DSYIYbZWZQB7fCnTHZmMxtotb/aXjam+n2XS79lTvX3Tq9xGqpC8XKNEF2lt' + ) + ticket80 = '6Pfil8lv/k8zf8MndPpfXaO5EAV6dhME6zs6CfUyq2yziynQwSywtKQMqHGnJ2HR\ +49TZXi/Y4/8RSIO7QPsU51/HLR1gWIMhVM2m9yh93Bw=' + + @pytest.fixture(autouse=True) + def setup_method_fixture(self, request): + self.certificate() + + listener_conf = { + "pass": "routes", + "tls": { + "certificate": "default", + "session": {"cache_size": 0, "tickets": True}, + }, + } + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": listener_conf, + "*:7081": listener_conf, + "*:7082": listener_conf, + }, + "routes": [{"action": {"return": 200}}], + "applications": {}, + } + ), 'load application configuration' + + def set_tickets(self, tickets=True, port=7080): + assert 'success' in self.conf( + {"cache_size": 0, "tickets": tickets}, + 'listeners/*:' + str(port) + '/tls/session', + ) + + def connect(self, ctx=None, session=None, port=7080): + sock = socket.create_connection(('127.0.0.1', port)) + + if ctx is None: + ctx = Context(TLSv1_2_METHOD) + + client = Connection(ctx, sock) + client.set_connect_state() + + if session is not None: + client.set_session(session) + + client.do_handshake() + client.shutdown() + + return ( + client.get_session(), + ctx, + _lib.SSL_session_reused(client._ssl), + ) + + def has_ticket(self, sess): + return _lib.SSL_SESSION_has_ticket(sess._session) + + @pytest.mark.skipif( + not hasattr(_lib, 'SSL_SESSION_has_ticket'), + reason='ticket check is not supported', + ) + def test_tls_ticket(self): + sess, ctx, reused = self.connect() + assert self.has_ticket(sess), 'tickets True' + assert not reused, 'tickets True not reused' + + sess, ctx, reused = self.connect(ctx, sess) + assert self.has_ticket(sess), 'tickets True reconnect' + assert reused, 'tickets True reused' + + self.set_tickets(tickets=False) + + sess, _, _ = self.connect() + assert not self.has_ticket(sess), 'tickets False' + + assert 'success' in self.conf_delete( + 'listeners/*:7080/tls/session/tickets' + ), 'tickets default configure' + + sess, _, _ = self.connect() + assert not self.has_ticket(sess), 'tickets default (false)' + + @pytest.mark.skipif( + not hasattr(_lib, 'SSL_SESSION_has_ticket'), + reason='ticket check is not supported', + ) + def test_tls_ticket_string(self): + self.set_tickets(self.ticket) + sess, ctx, _ = self.connect() + assert self.has_ticket(sess), 'tickets string' + + sess2, _, reused = self.connect(ctx, sess) + assert self.has_ticket(sess2), 'tickets string reconnect' + assert reused, 'tickets string reused' + + sess2, _, reused = self.connect(ctx, sess, port=7081) + assert self.has_ticket(sess2), 'connect True' + assert not reused, 'connect True not reused' + + self.set_tickets(self.ticket2, port=7081) + + sess2, _, reused = self.connect(ctx, sess, port=7081) + assert self.has_ticket(sess2), 'wrong ticket' + assert not reused, 'wrong ticket not reused' + + self.set_tickets(self.ticket80) + + sess, ctx, _ = self.connect() + assert self.has_ticket(sess), 'tickets string 80' + + sess2, _, reused = self.connect(ctx, sess) + assert self.has_ticket(sess2), 'tickets string 80 reconnect' + assert reused, 'tickets string 80 reused' + + sess2, _, reused = self.connect(ctx, sess, port=7081) + assert self.has_ticket(sess2), 'wrong ticket 80' + assert not reused, 'wrong ticket 80 not reused' + + @pytest.mark.skipif( + not hasattr(_lib, 'SSL_SESSION_has_ticket'), + reason='ticket check is not supported', + ) + def test_tls_ticket_array(self): + self.set_tickets([]) + + sess, ctx, _ = self.connect() + assert not self.has_ticket(sess), 'tickets array empty' + + self.set_tickets([self.ticket, self.ticket2]) + self.set_tickets(self.ticket, port=7081) + self.set_tickets(self.ticket2, port=7082) + + sess, ctx, _ = self.connect() + _, _, reused = self.connect(ctx, sess, port=7081) + assert not reused, 'not last ticket' + _, _, reused = self.connect(ctx, sess, port=7082) + assert reused, 'last ticket' + + sess, ctx, _ = self.connect(port=7081) + _, _, reused = self.connect(ctx, sess) + assert reused, 'first ticket' + + sess, ctx, _ = self.connect(port=7082) + _, _, reused = self.connect(ctx, sess) + assert reused, 'second ticket' + + assert 'success' in self.conf_delete( + 'listeners/*:7080/tls/session/tickets/0' + ), 'removed first ticket' + assert 'success' in self.conf_post( + '"' + self.ticket + '"', 'listeners/*:7080/tls/session/tickets' + ), 'add new ticket to the end of array' + + sess, ctx, _ = self.connect() + _, _, reused = self.connect(ctx, sess, port=7082) + assert not reused, 'not last ticket 2' + _, _, reused = self.connect(ctx, sess, port=7081) + assert reused, 'last ticket 2' + + def test_tls_ticket_invalid(self): + def check_tickets(tickets): + assert 'error' in self.conf( + {"tickets": tickets}, 'listeners/*:7080/tls/session', + ) + + check_tickets({}) + check_tickets('!?&^' * 16) + check_tickets(self.ticket[:-2] + '!' + self.ticket[3:]) + check_tickets(self.ticket[:-1]) + check_tickets(self.ticket + 'b') + check_tickets(self.ticket + 'blah') + check_tickets([True, self.ticket, self.ticket2]) + check_tickets([self.ticket, 'blah', self.ticket2]) + check_tickets([self.ticket, self.ticket2, []]) diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py index 6be1667b..367059e6 100644 --- a/test/unit/applications/lang/go.py +++ b/test/unit/applications/lang/go.py @@ -40,13 +40,12 @@ class TestApplicationGo(TestApplicationProto): print("\n$ GOPATH=" + env['GOPATH'] + " " + " ".join(args)) try: - process = subprocess.Popen(args, env=env) - process.communicate() + process = subprocess.run(args, env=env) except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: return None return process diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py index 53b27b07..50998978 100644 --- a/test/unit/applications/lang/java.py +++ b/test/unit/applications/lang/java.py @@ -64,10 +64,17 @@ class TestApplicationJava(TestApplicationProto): javac = [ 'javac', - '-target', '8', '-source', '8', '-nowarn', - '-encoding', 'utf-8', - '-d', classes_path, - '-classpath', classpath + ':' + ws_jars[0], + '-target', + '8', + '-source', + '8', + '-nowarn', + '-encoding', + 'utf-8', + '-d', + classes_path, + '-classpath', + classpath + ':' + ws_jars[0], ] javac.extend(src) @@ -75,13 +82,12 @@ class TestApplicationJava(TestApplicationProto): print("\n$ " + " ".join(javac)) try: - process = subprocess.Popen(javac, stderr=subprocess.STDOUT) - process.communicate() + subprocess.check_output(javac, stderr=subprocess.STDOUT) except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: pytest.fail('Can\'t run javac process.') def load(self, script, **kwargs): diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py index 90c0078c..5319d2ca 100644 --- a/test/unit/applications/lang/php.py +++ b/test/unit/applications/lang/php.py @@ -22,18 +22,27 @@ class TestApplicationPHP(TestApplicationProto): script_path = '/app/php/' + script + app = { + "type": self.get_application_type(), + "processes": kwargs.pop('processes', {"spare": 0}), + "root": script_path, + "working_directory": script_path, + "index": index, + } + + for attr in ( + 'environment', + 'limits', + 'options', + 'targets', + ): + if attr in kwargs: + app[attr] = kwargs.pop(attr) + self._load_conf( { "listeners": {"*:7080": {"pass": "applications/" + script}}, - "applications": { - script: { - "type": self.get_application_type(), - "processes": {"spare": 0}, - "root": script_path, - "working_directory": script_path, - "index": index, - } - }, + "applications": {script: app}, }, **kwargs ) diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py index 215aa332..1e38f3fa 100644 --- a/test/unit/applications/lang/python.py +++ b/test/unit/applications/lang/python.py @@ -2,7 +2,6 @@ import os import shutil from urllib.parse import quote -import pytest from unit.applications.proto import TestApplicationProto from unit.option import option diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py index 61d50558..824bfe7f 100644 --- a/test/unit/applications/lang/ruby.py +++ b/test/unit/applications/lang/ruby.py @@ -1,4 +1,3 @@ -import os import shutil from unit.applications.proto import TestApplicationProto diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py index e30d21ff..cd8672ba 100644 --- a/test/unit/applications/proto.py +++ b/test/unit/applications/proto.py @@ -3,8 +3,8 @@ import re import time from unit.control import TestControl -from unit.option import option from unit.log import Log +from unit.option import option class TestApplicationProto(TestControl): diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py index 583b618f..c7254235 100644 --- a/test/unit/applications/tls.py +++ b/test/unit/applications/tls.py @@ -15,7 +15,7 @@ class TestApplicationTLS(TestApplicationProto): def certificate(self, name='default', load=True): self.openssl_conf() - subprocess.call( + subprocess.check_output( [ 'openssl', 'req', diff --git a/test/unit/check/go.py b/test/unit/check/go.py index 309091c0..cc17f0fe 100644 --- a/test/unit/check/go.py +++ b/test/unit/check/go.py @@ -11,7 +11,7 @@ def check_go(current_dir, temp_dir, test_dir): env['GO111MODULE'] = 'auto' try: - process = subprocess.Popen( + process = subprocess.run( [ 'go', 'build', @@ -20,8 +20,9 @@ def check_go(current_dir, temp_dir, test_dir): test_dir + '/go/empty/app.go', ], env=env, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, ) - process.communicate() if process.returncode == 0: return True @@ -29,5 +30,5 @@ def check_go(current_dir, temp_dir, test_dir): except KeyboardInterrupt: raise - except: + except subprocess.CalledProcessError: return None diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py index 43c8842f..9bd835a3 100644 --- a/test/unit/check/isolation.py +++ b/test/unit/check/isolation.py @@ -3,9 +3,8 @@ import os from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.java import TestApplicationJava -from unit.applications.lang.ruby import TestApplicationRuby from unit.applications.lang.node import TestApplicationNode -from unit.applications.proto import TestApplicationProto +from unit.applications.lang.ruby import TestApplicationRuby from unit.http import TestHTTP from unit.option import option from unit.utils import getns @@ -1,5 +1,5 @@ # Copyright (C) NGINX, Inc. -NXT_VERSION=1.25.0 -NXT_VERNUM=12500 +NXT_VERSION=1.26.0 +NXT_VERNUM=12600 |