summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.hgtags1
-rw-r--r--CHANGES40
-rw-r--r--auto/modules/java8
-rw-r--r--auto/modules/java_jar.sha51228
-rw-r--r--auto/ssltls17
-rw-r--r--docs/changes.xml115
-rw-r--r--pkg/deb/Makefile12
-rw-r--r--pkg/deb/Makefile.jsc-common2
-rw-r--r--pkg/docker/Dockerfile.go1.152
-rw-r--r--pkg/docker/Dockerfile.jsc112
-rw-r--r--pkg/docker/Dockerfile.minimal2
-rw-r--r--pkg/docker/Dockerfile.node152
-rw-r--r--pkg/docker/Dockerfile.perl5.322
-rw-r--r--pkg/docker/Dockerfile.php8.02
-rw-r--r--pkg/docker/Dockerfile.python3.92
-rw-r--r--pkg/docker/Dockerfile.ruby2.72
-rw-r--r--src/nxt_application.h1
-rw-r--r--src/nxt_buf.c2
-rw-r--r--src/nxt_buf.h6
-rw-r--r--src/nxt_conf_validation.c174
-rw-r--r--src/nxt_controller.c161
-rw-r--r--src/nxt_event_engine.c3
-rwxr-xr-xsrc/nxt_h1proto.c1
-rw-r--r--src/nxt_http.h73
-rw-r--r--src/nxt_http_chunk_parse.c1
-rw-r--r--src/nxt_http_proxy.c19
-rw-r--r--src/nxt_http_request.c154
-rw-r--r--src/nxt_http_return.c83
-rw-r--r--src/nxt_http_route.c246
-rw-r--r--src/nxt_http_route_addr.c19
-rw-r--r--src/nxt_http_static.c219
-rw-r--r--src/nxt_isolation.c4
-rw-r--r--src/nxt_log.h22
-rw-r--r--src/nxt_main_process.c7
-rw-r--r--src/nxt_openssl.c367
-rw-r--r--src/nxt_port.h3
-rw-r--r--src/nxt_port_socket.c56
-rw-r--r--src/nxt_router.c449
-rw-r--r--src/nxt_router.h11
-rw-r--r--src/nxt_sockaddr.c160
-rw-r--r--src/nxt_sockaddr.h3
-rw-r--r--src/nxt_tls.h36
-rw-r--r--src/nxt_upstream.c4
-rw-r--r--src/python/nxt_python.c8
-rw-r--r--src/python/nxt_python.h2
-rw-r--r--src/python/nxt_python_asgi.c28
-rw-r--r--src/python/nxt_python_asgi_http.c132
-rw-r--r--src/python/nxt_python_wsgi.c4
-rw-r--r--src/ruby/nxt_ruby.c155
-rw-r--r--test/conftest.py169
-rw-r--r--test/python/client_ip/wsgi.py4
-rw-r--r--test/python/restart/longstart.py10
-rw-r--r--test/python/restart/v1.py7
-rw-r--r--test/python/restart/v2.py7
-rw-r--r--test/ruby/hooks/config.ru7
-rw-r--r--test/ruby/hooks/eval.rb3
-rw-r--r--test/ruby/hooks/multiple.rb13
-rw-r--r--test/ruby/hooks/on_thread_boot.rb9
-rw-r--r--test/ruby/hooks/on_thread_shutdown.rb9
-rw-r--r--test/ruby/hooks/on_worker_boot.rb5
-rw-r--r--test/ruby/hooks/on_worker_shutdown.rb5
-rw-r--r--test/test_client_ip.py129
-rw-r--r--test/test_configuration.py82
-rw-r--r--test/test_node_es_modules.py3
-rw-r--r--test/test_python_procman.py79
-rw-r--r--test/test_respawn.py15
-rw-r--r--test/test_routing.py4
-rw-r--r--test/test_ruby_hooks.py98
-rw-r--r--test/test_ruby_isolation.py7
-rw-r--r--test/test_static_chroot.py (renamed from test/test_share_chroot.py)22
-rw-r--r--test/test_static_fallback.py (renamed from test/test_share_fallback.py)37
-rw-r--r--test/test_static_mount.py (renamed from test/test_share_mount.py)18
-rw-r--r--test/test_static_symlink.py (renamed from test/test_share_symlink.py)16
-rw-r--r--test/test_static_types.py (renamed from test/test_share_types.py)21
-rw-r--r--test/test_tls.py13
-rw-r--r--test/test_tls_sni.py20
-rw-r--r--test/test_variables.py19
-rw-r--r--test/unit/applications/lang/java.py2
-rw-r--r--test/unit/applications/lang/python.py1
-rw-r--r--test/unit/applications/lang/ruby.py38
-rw-r--r--test/unit/applications/proto.py16
-rw-r--r--test/unit/check/isolation.py7
-rw-r--r--test/unit/http.py2
-rw-r--r--test/unit/utils.py28
-rw-r--r--version4
85 files changed, 2919 insertions, 862 deletions
diff --git a/.hgtags b/.hgtags
index 48413e05..7f41d3d1 100644
--- a/.hgtags
+++ b/.hgtags
@@ -55,3 +55,4 @@ e3f504b6082ee97ed0d6c8660890585ef6a5796f 1.21.0-1
ad6aad2450c256d4f1a3c32f7091a78dbbc4a6d1 1.23.0-1
847c88d10f26765b45149c14f88c2274adfc3f42 1.24.0
5c7ce0da580ef6e83c729dd012e976f22acbac27 1.24.0-1
+54ffe5ce4fb3c4304faf6d342d9b17dee2c745ac 1.25.0
diff --git a/CHANGES b/CHANGES
index cbc6678e..aab3a557 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,44 @@
+Changes with Unit 1.25.0 19 Aug 2021
+
+ *) Feature: client IP address replacement from a specified HTTP header
+ field.
+
+ *) Feature: TLS sessions cache.
+
+ *) Feature: TLS session tickets.
+
+ *) Feature: application restart control.
+
+ *) Feature: process and thread lifecycle hooks in Ruby.
+
+ *) Bugfix: the router process could crash on TLS connection open when
+ multiple listeners with TLS certificates were configured; the bug had
+ appeared in 1.23.0.
+
+ *) Bugfix: TLS connections were rejected for configurations with
+ multiple certificate bundles in a listener if the client did not use
+ SNI.
+
+ *) Bugfix: the router process could crash with frequent mutithreaded
+ application reconfiguration.
+
+ *) Bugfix: compatibility issues with some Python ASGI apps, notably
+ based on the Starlette framework.
+
+ *) Bugfix: a descriptor and memory leak occurred in the router process
+ when an app process stopped or crashed.
+
+ *) Bugfix: the controller or router process could crash if the
+ configuration contained a full-form IPv6 in a listener address.
+
+ *) Bugfix: the router process crashed when a request was passed to an
+ empty "routes" or "upstreams" using a variable "pass" option.
+
+ *) Bugfix: the router process crashed while matching a request to an
+ empty array of source or destination address patterns.
+
+
Changes with Unit 1.24.0 27 May 2021
*) Change: PHP added to the default MIME type list.
diff --git a/auto/modules/java b/auto/modules/java
index 05b3100c..e8137217 100644
--- a/auto/modules/java
+++ b/auto/modules/java
@@ -238,7 +238,7 @@ cat << END > $NXT_JAVA_JARS
static const char *nxt_java_system_jars[] = {
END
-NXT_TOMCAT_VERSION=9.0.44
+NXT_TOMCAT_VERSION=9.0.52
NXT_JAR_VERSION=$NXT_TOMCAT_VERSION
@@ -271,7 +271,7 @@ NXT_JAR_NAME=tomcat-util
. auto/modules/java_get_jar
NXT_JAR_NAME=ecj
-NXT_JAR_VERSION=3.25.0
+NXT_JAR_VERSION=3.26.0
NXT_JAR_NAMESPACE=org/eclipse/jdt/
. auto/modules/java_get_jar
@@ -284,7 +284,7 @@ static const char *nxt_java_unit_jars[] = {
"$NXT_UNIT_JAR",
END
-NXT_JAR_VERSION=9.4.38.v20210224
+NXT_JAR_VERSION=9.4.43.v20210629
NXT_JAR_NAMESPACE=org/eclipse/jetty/
NXT_JAR_NAME=jetty-util
@@ -297,7 +297,7 @@ NXT_JAR_NAME=jetty-http
. auto/modules/java_get_jar
NXT_JAR_NAME=classgraph
-NXT_JAR_VERSION=4.8.102
+NXT_JAR_VERSION=4.8.112
NXT_JAR_NAMESPACE=io/github/classgraph/
. auto/modules/java_get_jar
diff --git a/auto/modules/java_jar.sha512 b/auto/modules/java_jar.sha512
index 98d27149..5289081c 100644
--- a/auto/modules/java_jar.sha512
+++ b/auto/modules/java_jar.sha512
@@ -1,14 +1,14 @@
-5105e9edf0fc6a4a51eb2bb1e2c23bb78a604fe2df3b1e814cf638ce22845a6ae57e75af31db9dd00d5c650403e751659cca8d3bc465fb96525c695188d2055f classgraph-4.8.102.jar
-48cee25d195a5c713a962b035ecba633797753474f290c107a0ee96272cd9ae1b6b62b060ef37e0699f6a5af4d4b45e514ef710309f41bdd9440925cf60a111a ecj-3.25.0.jar
-3f2a4a5b7f71c6a1317cec29ea12eb04eac68f4cee94c30ffb75fdd36dbc9c622059fb762e27f74f7b7a38eb84966732ba963cc294f5aaf8b7f5f8c9d60c8899 jetty-http-9.4.38.v20210224.jar
-cc416b324841e9c259538a3ec678a9d8e07d3bea9b0bb687bc15a759c5065c7de6ea7bbbb15b369b8c6d2023c3294906d8f9cfda28e1b7859283008790581a4d jetty-server-9.4.38.v20210224.jar
-efdbeae97c959459c10b05aae62189c535a0935dadf7debf4257257c31a98a4167ca1318daa81dae32161f51117b17468d5fb5a6bb4eea40864ce1265623d684 jetty-util-9.4.38.v20210224.jar
-f2534f246c0cec9be576f18d67759b5a997074bb7870cbf7b9469f062ddc6932e5cec45fd439a5beacc8c6248d39dc458af81245a6eb988e294667f2bcd251df tomcat-api-9.0.44.jar
-0c8050b04b02a6a1f1384d1a4041594c09868cfeb139978274c5e5797eb95e0668495f489eb61cfe742905e453184edc0ad8a9fd3b2796e960981a20ae4a0334 tomcat-el-api-9.0.44.jar
-cc81a06795553f06d3dfe930429fdf47a8e0c1813c554b640d98090cc661c1eb27a91b9a9055bd14d918fc9451073632a635741dd413fd7b4a380d8907862e14 tomcat-jasper-9.0.44.jar
-de50107ff31b8c5a7eb97fe86c2fdfa38305be632c8d62329219884dfb6bbe76372b40a67e808263a55b510a41cde639d55e7925d89db06472392519f0410063 tomcat-jasper-el-9.0.44.jar
-69a79e58cea950b713f26a94e1db90221c297375a338f010d1332027ef5d93fd262163ec787b06d8cd41a4a31faa81bb4fc83836148eb14a6757591f6fe5f3c6 tomcat-jsp-api-9.0.44.jar
-ca036312596dbd92ae43dda79a9a5ad168cf90a12369636fdc6febbedb559b83d5fa22d77629659b588d70990f8a077fd8356be1c56b0227e07688b67dd7c76e tomcat-juli-9.0.44.jar
-952c122413805de16de603cfc233785e53b558b39f146b6fa4e11324e26572e4aee76fec2ee69adecc8f4271cd28acb917e1a9e61a908710b3582ab1f76c9706 tomcat-servlet-api-9.0.44.jar
-6920bfb2b1ae173d716fedbf4f238003f569a161f277a888c2d53dac0af80ef0480c9ca3be0ede4583716c1b97c1cfa1665439d14274b7d73ec948fd9f952b89 tomcat-util-9.0.44.jar
-915304750cbdffc4c9926fcb22eb928d2895cccd8b3f6f13aefb4e5302cccf5f513502b70812630026dd2968712c4c3b1cfe98d37ecf7b349988e7255ddc5b18 tomcat-util-scan-9.0.44.jar
+e7ad5ee436f6befaddcdd1046022a2e30444a435d9419a33f8316f66e794cf710809dbcf7e408a087f434cd9cb724682965b5e4098e34803569241eb44288322 classgraph-4.8.112.jar
+ab441acf5551a7dc81c353eaccb3b3df9e89a48987294d19e39acdb83a5b640fcdff7414cee29f5b96eaa8826647f1d5323e185018fe33a64c402d69c73c9158 ecj-3.26.0.jar
+a3ce1a5a41c9791ece4cbbf049ec4add1ec41330743d6757daea520f8b329299f5dd274f9e5741ba41fe49510f203efd19540d2404390eca53421132f5f46d4b jetty-http-9.4.43.v20210629.jar
+61a14e97baac9962bd68ece24f8b967eec8e32edfebfa27c6a13996a86650d82f8977bf1aa582fc9706a1b028cb3cec0057c97628235dfc130061939845229e6 jetty-server-9.4.43.v20210629.jar
+304fcdba2bdbf37e8f2ea69a3f5fbdffdfefd98d80fa78883b1dca1129a4907cef63eb2fa7c83eef359022a3b6a2f3ff742d8d23075c83d049ac01f1402e97f8 jetty-util-9.4.43.v20210629.jar
+f14ac948559c0b6e20f6d84e5177fea46ea1321a7a401f280ee9323f3a07e70e141d2b12c8c466c446efb55a58743af931b0584f56494f17311cab511bcd214a tomcat-api-9.0.52.jar
+a5ca293732267854a296ccc79b25051acf76fa8dea6118d43aa2e95b6d1951dfaffb941430b5080d7ab62d82d2c82c9385baf99e3c4a2bb6bf4a04372149169d tomcat-el-api-9.0.52.jar
+8660e11dd0f994de43b19ba251a789dc3194a6b82d674085fed510200c789b402b27ab97bcecfec0720f563bb0dd18c2631cd8bb5c35e604c1460d7357492123 tomcat-jasper-9.0.52.jar
+5b0b3e0edb47e3c4269736542d66d6fc099a74965fdcd5d3a6382db3f75bec7329e81f0719aaafccd318a058ec8fbba113a6ae9234ca94a00c8c39e5c8885568 tomcat-jasper-el-9.0.52.jar
+a4368d1073d79a4f8a1cb8967a5e39af87723a17b2d94466554e8e4d3e8bb2dec3ee37db9f606e0c775dd4028604d4e87921f0dda764c8ef138aa50acf03d549 tomcat-jsp-api-9.0.52.jar
+8687e108489996226a83e8310c73a2a176fac9ce365a7bd3fc23955fe968c3858a24c047cb5c7fbd1f3a890c893dcdf55e88180eefe61b98c1a3bf4e316fb07e tomcat-juli-9.0.52.jar
+f9929f433e2b2f93897a87d117af2519e44020b44e3a475dfc81662b08d08e010b14a3dd6df2d4800196cdba7cbb8db2b879341c5a0ef1d11e5abe63d872bc34 tomcat-servlet-api-9.0.52.jar
+c21ccf969378f2cad0ead32451c2527ea944207b5a894b642ee554042fe87eb0ce647aacbf8a51d12b4ecf2bf13e9380da78d8f7792486909daba72e8d0f83f2 tomcat-util-9.0.52.jar
+14b4eb31c124d22c7ea7f05808cd6a46076f9d72648afd76e2d11924874117266771a455686d704225d2eff94656f024293140a3259b108857fa6b8b218ddd63 tomcat-util-scan-9.0.52.jar
diff --git a/auto/ssltls b/auto/ssltls
index f9363dde..d678ba74 100644
--- a/auto/ssltls
+++ b/auto/ssltls
@@ -66,6 +66,23 @@ if [ $NXT_OPENSSL = YES ]; then
return 0;
}"
. auto/feature
+
+
+ nxt_feature="OpenSSL tlsext support"
+ nxt_feature_name=NXT_HAVE_OPENSSL_TLSEXT
+ nxt_feature_run=
+ nxt_feature_incs=
+ nxt_feature_libs="$NXT_OPENSSL_LIBS"
+ nxt_feature_test="#include <openssl/ssl.h>
+
+ int main() {
+ #if (OPENSSL_NO_TLSEXT)
+ #error OpenSSL: no tlsext support.
+ #else
+ return 0;
+ #endif
+ }"
+ . auto/feature
fi
diff --git a/docs/changes.xml b/docs/changes.xml
index e3711d0c..dca77068 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -5,6 +5,121 @@
<change_log title="unit">
+<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.25.0" rev="1"
+ date="2021-08-19" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.25.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.25.0" rev="1"
+ date="2021-08-19" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="feature">
+<para>
+client IP address replacement from a specified HTTP header field.
+</para>
+</change>
+
+<change type="feature">
+<para>
+TLS sessions cache.
+</para>
+</change>
+
+<change type="feature">
+<para>
+TLS session tickets.
+</para>
+</change>
+
+<change type="feature">
+<para>
+application restart control.
+</para>
+</change>
+
+<change type="feature">
+<para>
+process and thread lifecycle hooks in Ruby.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process could crash on TLS connection open when multiple listeners
+with TLS certificates were configured; the bug had appeared in 1.23.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+TLS connections were rejected for configurations with multiple certificate
+bundles in a listener if the client did not use SNI.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process could crash with frequent mutithreaded application
+reconfiguration.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+compatibility issues with some Python ASGI apps, notably based on the Starlette
+framework.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+a descriptor and memory leak occurred in the router process when an app process
+stopped or crashed.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the controller or router process could crash if the configuration contained
+a full-form IPv6 in a listener address.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process crashed when a request was passed to an empty "routes"
+or "upstreams" using a variable "pass" option.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process crashed while matching a request to an empty array of source
+or destination address patterns.
+</para>
+</change>
+
+</changes>
+
+
<changes apply="unit-jsc17" ver="1.24.0" rev="1"
date="2021-05-27" time="18:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index c343eb53..85d5545e 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -135,6 +135,18 @@ include Makefile.python
include Makefile.perl
endif
+# Debian 11
+ifeq ($(CODENAME),bullseye)
+include Makefile.php
+include Makefile.python27
+include Makefile.python39
+include Makefile.go
+include Makefile.perl
+include Makefile.ruby
+include Makefile.jsc-common
+include Makefile.jsc11
+endif
+
# Debian 10
ifeq ($(CODENAME),buster)
include Makefile.php
diff --git a/pkg/deb/Makefile.jsc-common b/pkg/deb/Makefile.jsc-common
index 1c4a77b5..5f727124 100644
--- a/pkg/deb/Makefile.jsc-common
+++ b/pkg/deb/Makefile.jsc-common
@@ -6,7 +6,7 @@ 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))
+ifneq (,$(findstring $(CODENAME),hirsute groovy focal eoan disco buster bullseye))
JAVA_MINVERSION= 11
else
JAVA_MINVERSION= 8
diff --git a/pkg/docker/Dockerfile.go1.15 b/pkg/docker/Dockerfile.go1.15
index d446a934..0c88ff64 100644
--- a/pkg/docker/Dockerfile.go1.15
+++ b/pkg/docker/Dockerfile.go1.15
@@ -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.24.0 \
+ && hg up 1.25.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.jsc11 b/pkg/docker/Dockerfile.jsc11
index b66ebe73..8f62ad3e 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.24.0 \
+ && hg up 1.25.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 69a70e33..00875732 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -8,7 +8,7 @@ RUN set -ex \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& hg clone https://hg.nginx.org/unit \
&& cd unit \
- && hg up 1.24.0 \
+ && hg up 1.25.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.node15 b/pkg/docker/Dockerfile.node15
index 1e3846a3..f98d0ef3 100644
--- a/pkg/docker/Dockerfile.node15
+++ b/pkg/docker/Dockerfile.node15
@@ -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.24.0 \
+ && hg up 1.25.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.perl5.32 b/pkg/docker/Dockerfile.perl5.32
index 2fccbf63..244eb076 100644
--- a/pkg/docker/Dockerfile.perl5.32
+++ b/pkg/docker/Dockerfile.perl5.32
@@ -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.24.0 \
+ && hg up 1.25.0 \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
diff --git a/pkg/docker/Dockerfile.php8.0 b/pkg/docker/Dockerfile.php8.0
index 02db27cf..ba85ce0e 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.24.0 \
+ && hg up 1.25.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 44472a12..a3ca8d4a 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.24.0 \
+ && hg up 1.25.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.ruby2.7
index 7875c470..d5140288 100644
--- a/pkg/docker/Dockerfile.ruby2.7
+++ b/pkg/docker/Dockerfile.ruby2.7
@@ -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.24.0 \
+ && hg up 1.25.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/src/nxt_application.h b/src/nxt_application.h
index 45e7fa48..6fbdc4be 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -74,6 +74,7 @@ typedef struct {
typedef struct {
nxt_str_t script;
uint32_t threads;
+ nxt_str_t hooks;
} nxt_ruby_app_conf_t;
diff --git a/src/nxt_buf.c b/src/nxt_buf.c
index 83be0fac..cbde069e 100644
--- a/src/nxt_buf.c
+++ b/src/nxt_buf.c
@@ -201,7 +201,6 @@ nxt_buf_completion(nxt_task_t *task, void *obj, void *data)
nxt_buf_t *b, *next, *parent;
b = obj;
- parent = data;
nxt_debug(task, "buf completion: %p %p", b, b->mem.start);
@@ -275,7 +274,6 @@ nxt_buf_ts_completion(nxt_task_t *task, void *obj, void *data)
nxt_buf_t *b, *next, *parent;
b = obj;
- parent = data;
if (nxt_buf_ts_handle(task, obj, data)) {
return;
diff --git a/src/nxt_buf.h b/src/nxt_buf.h
index 25e8499a..5121d659 100644
--- a/src/nxt_buf.h
+++ b/src/nxt_buf.h
@@ -288,4 +288,10 @@ nxt_buf_cpystr(nxt_buf_t *b, const nxt_str_t *str)
}
+nxt_inline void
+nxt_buf_dummy_completion(nxt_task_t *task, void *obj, void *data)
+{
+}
+
+
#endif /* _NXT_BUF_H_INCLIDED_ */
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 06ae2847..a53fff74 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -95,6 +95,16 @@ static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
#endif
static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+static nxt_int_t nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
+#endif
#endif
static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
@@ -204,8 +214,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[];
#if (NXT_TLS)
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[];
#endif
static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[];
@@ -346,6 +358,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
.name = nxt_string("application"),
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_app_name,
+ }, {
+ .name = nxt_string("client_ip"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_client_ip_members
},
#if (NXT_TLS)
@@ -361,6 +378,25 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[] = {
+ {
+ .name = nxt_string("source"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_addrs,
+ .flags = NXT_CONF_VLDT_REQUIRED
+ }, {
+ .name = nxt_string("header"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED
+ }, {
+ .name = nxt_string("recursive"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+
#if (NXT_TLS)
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
@@ -378,11 +414,132 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
.validator = nxt_conf_vldt_unsupported,
.u.string = "conf_commands",
#endif
+ }, {
+ .name = nxt_string("session"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_session_members,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[] = {
+ {
+ .name = nxt_string("cache_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_tls_cache_size,
+ }, {
+ .name = nxt_string("timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_tls_timeout,
+ }, {
+ .name = nxt_string("tickets"),
+ .type = NXT_CONF_VLDT_STRING
+ | NXT_CONF_VLDT_ARRAY
+ | NXT_CONF_VLDT_BOOLEAN,
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+ .validator = nxt_conf_vldt_ticket_key,
+#else
+ .validator = nxt_conf_vldt_unsupported,
+ .u.string = "tickets",
+#endif
},
NXT_CONF_VLDT_END
};
+
+static nxt_int_t
+nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ int64_t cache_size;
+
+ cache_size = nxt_conf_get_number(value);
+
+ if (cache_size < 0) {
+ return nxt_conf_vldt_error(vldt, "The \"cache_size\" number must not "
+ "be negative.");
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ int64_t timeout;
+
+ timeout = nxt_conf_get_number(value);
+
+ if (timeout <= 0) {
+ return nxt_conf_vldt_error(vldt, "The \"timeout\" number must be "
+ "greater than zero.");
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+
+static nxt_int_t
+nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_BOOLEAN) {
+ return NXT_OK;
+ }
+
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_ticket_key_element);
+ }
+
+ /* NXT_CONF_STRING */
+
+ return nxt_conf_vldt_ticket_key_element(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ 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 "
+ "contain only string values.");
+ }
+
+ 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) {
+ return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket "
+ "key \"%V\".", &key);
+ }
+
+ if (ret != 48 && ret != 80) {
+ return nxt_conf_vldt_error(vldt, "Invalid length %d of the ticket "
+ "key \"%V\". Must be 48 or 80 bytes.",
+ ret, &key);
+ }
+
+ return NXT_OK;
+}
+
#endif
@@ -732,6 +889,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = {
.name = nxt_string("threads"),
.type = NXT_CONF_VLDT_INTEGER,
.validator = nxt_conf_vldt_threads,
+ }, {
+ .name = nxt_string("hooks"),
+ .type = NXT_CONF_VLDT_STRING
},
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
@@ -1215,7 +1375,7 @@ static nxt_int_t
nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value)
{
- nxt_str_t ext, *dup_type;
+ nxt_str_t exten, *dup_type;
nxt_conf_vldt_mtypes_ctx_t *ctx;
ctx = vldt->ctx;
@@ -1225,24 +1385,24 @@ nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
"contain only strings.", ctx->type);
}
- nxt_conf_get_string(value, &ext);
+ nxt_conf_get_string(value, &exten);
- if (ext.length == 0) {
+ if (exten.length == 0) {
return nxt_conf_vldt_error(vldt, "An empty file extension for "
"the \"%V\" MIME type.", ctx->type);
}
- dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext);
+ dup_type = nxt_http_static_mtype_get(&ctx->hash, &exten);
if (dup_type->length != 0) {
return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been "
"declared for \"%V\" and \"%V\" "
"MIME types at the same time.",
- &ext, dup_type, ctx->type);
+ &exten, dup_type, ctx->type);
}
- return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash,
- &ext, ctx->type);
+ return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, &exten,
+ ctx->type);
}
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index 772d10c8..779a625d 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -92,6 +92,10 @@ static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name);
static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj,
void *data);
#endif
+static void nxt_controller_process_control(nxt_task_t *task,
+ nxt_controller_request_t *req, nxt_str_t *path);
+static void nxt_controller_app_restart_handler(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
static void nxt_controller_conf_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
static void nxt_controller_conf_store(nxt_task_t *task,
@@ -1022,6 +1026,14 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
#endif
+ if (nxt_str_start(&path, "/control/", 9)) {
+ path.length -= 9;
+ path.start += 9;
+
+ nxt_controller_process_control(task, req, &path);
+ return;
+ }
+
nxt_memzero(&resp, sizeof(nxt_controller_response_t));
if (path.length == 1 && path.start[0] == '/') {
@@ -1684,6 +1696,155 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
static void
+nxt_controller_process_control(nxt_task_t *task,
+ nxt_controller_request_t *req, nxt_str_t *path)
+{
+ uint32_t stream;
+ nxt_buf_t *b;
+ nxt_int_t rc;
+ nxt_port_t *router_port, *controller_port;
+ nxt_runtime_t *rt;
+ nxt_conf_value_t *value;
+ nxt_controller_response_t resp;
+
+ static nxt_str_t applications = nxt_string("applications");
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ if (!nxt_str_eq(&req->parser.method, "GET", 3)) {
+ goto not_allowed;
+ }
+
+ if (!nxt_str_start(path, "applications/", 13)
+ || nxt_memcmp(path->start + path->length - 8, "/restart", 8) != 0)
+ {
+ goto not_found;
+ }
+
+ path->start += 13;
+ path->length -= 13 + 8;
+
+ if (nxt_controller_check_postpone_request(task)) {
+ nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link);
+ return;
+ }
+
+ value = nxt_controller_conf.root;
+ if (value == NULL) {
+ goto not_found;
+ }
+
+ value = nxt_conf_get_object_member(value, &applications, NULL);
+ if (value == NULL) {
+ goto not_found;
+ }
+
+ value = nxt_conf_get_object_member(value, path, NULL);
+ if (value == NULL) {
+ goto not_found;
+ }
+
+ b = nxt_buf_mem_alloc(req->conn->mem_pool, path->length, 0);
+ if (nxt_slow_path(b == NULL)) {
+ goto alloc_fail;
+ }
+
+ b->mem.free = nxt_cpymem(b->mem.pos, path->start, path->length);
+
+ rt = task->thread->runtime;
+
+ controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
+ router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
+
+ stream = nxt_port_rpc_register_handler(task, controller_port,
+ nxt_controller_app_restart_handler,
+ nxt_controller_app_restart_handler,
+ router_port->pid, req);
+ if (nxt_slow_path(stream == 0)) {
+ goto alloc_fail;
+ }
+
+ rc = nxt_port_socket_write(task, router_port, NXT_PORT_MSG_APP_RESTART,
+ -1, stream, 0, b);
+ if (nxt_slow_path(rc != NXT_OK)) {
+ nxt_port_rpc_cancel(task, controller_port, stream);
+
+ goto fail;
+ }
+
+ nxt_queue_insert_head(&nxt_controller_waiting_requests, &req->link);
+
+ return;
+
+not_allowed:
+
+ resp.status = 405;
+ resp.title = (u_char *) "Method isn't allowed.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+not_found:
+
+ resp.status = 404;
+ resp.title = (u_char *) "Value doesn't exist.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+alloc_fail:
+
+ resp.status = 500;
+ resp.title = (u_char *) "Memory allocation failed.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+fail:
+
+ resp.status = 500;
+ resp.title = (u_char *) "Send restart failed.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+}
+
+
+static void
+nxt_controller_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data)
+{
+ nxt_controller_request_t *req;
+ nxt_controller_response_t resp;
+
+ req = data;
+
+ nxt_debug(task, "controller app restart handler");
+
+ nxt_queue_remove(&req->link);
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ if (msg->port_msg.type == NXT_PORT_MSG_RPC_READY) {
+ resp.status = 200;
+ resp.title = (u_char *) "Ok";
+
+ } else {
+ resp.status = 500;
+ resp.title = (u_char *) "Failed to restart app.";
+ resp.offset = -1;
+ }
+
+ nxt_controller_response(task, req, &resp);
+
+ nxt_controller_flush_requests(task);
+}
+
+
+static void
nxt_controller_conf_store(nxt_task_t *task, nxt_conf_value_t *conf)
{
void *mem;
diff --git a/src/nxt_event_engine.c b/src/nxt_event_engine.c
index 4384d3b1..78c79bb1 100644
--- a/src/nxt_event_engine.c
+++ b/src/nxt_event_engine.c
@@ -720,11 +720,10 @@ nxt_event_engine_buf_mem_free(nxt_event_engine_t *engine, nxt_buf_t *b)
void
nxt_event_engine_buf_mem_completion(nxt_task_t *task, void *obj, void *data)
{
- nxt_event_engine_t *engine;
nxt_buf_t *b, *next, *parent;
+ nxt_event_engine_t *engine;
b = obj;
- parent = data;
nxt_debug(task, "buf completion: %p %p", b, b->mem.start);
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index d3da6942..b683cb22 100755
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -955,7 +955,6 @@ nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r)
} else {
size = nxt_min(body_buffer_size, size);
b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size);
- body_buffer_size -= size;
}
in->mem.pos += size;
diff --git a/src/nxt_http.h b/src/nxt_http.h
index f82d837e..3bc2fd61 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -197,8 +197,23 @@ struct nxt_http_request_s {
};
-typedef struct nxt_http_route_s nxt_http_route_t;
-typedef struct nxt_http_route_rule_s nxt_http_route_rule_t;
+typedef struct nxt_http_route_s nxt_http_route_t;
+typedef struct nxt_http_route_rule_s nxt_http_route_rule_t;
+typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t;
+
+
+typedef struct {
+ nxt_conf_value_t *pass;
+ nxt_conf_value_t *ret;
+ nxt_str_t location;
+ nxt_conf_value_t *proxy;
+ nxt_conf_value_t *share;
+ nxt_str_t chroot;
+ nxt_conf_value_t *follow_symlinks;
+ nxt_conf_value_t *traverse_mounts;
+ nxt_conf_value_t *types;
+ nxt_conf_value_t *fallback;
+} nxt_http_action_conf_t;
struct nxt_http_action_s {
@@ -206,26 +221,15 @@ struct nxt_http_action_s {
nxt_http_request_t *r,
nxt_http_action_t *action);
union {
+ void *conf;
nxt_http_route_t *route;
nxt_upstream_t *upstream;
uint32_t upstream_number;
- nxt_http_status_t return_code;
nxt_var_t *var;
-
- struct {
- nxt_app_t *application;
- nxt_int_t target;
- } app;
-
- struct {
- nxt_str_t chroot;
- nxt_uint_t resolve;
- nxt_http_route_rule_t *types;
- nxt_http_action_t *fallback;
- } share;
} u;
nxt_str_t name;
+ nxt_http_action_t *fallback;
};
@@ -251,6 +255,14 @@ typedef struct {
} nxt_http_proto_table_t;
+struct nxt_http_client_ip_s {
+ nxt_http_route_addr_rule_t *source;
+ nxt_str_t *header;
+ uint32_t header_hash;
+ uint8_t recursive; /* 1 bit */
+};
+
+
#define NXT_HTTP_DATE_LEN nxt_length("Wed, 31 Dec 1986 16:40:00 GMT")
nxt_inline u_char *
@@ -308,27 +320,34 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass,
nxt_str_t *segments, nxt_uint_t n);
nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_str_t *name);
+nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create(
+ nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv);
+nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r,
+ nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr);
+nxt_http_route_rule_t *nxt_http_route_types_rule_create(nxt_task_t *task,
+ nxt_mp_t *mp, nxt_conf_value_t *types);
nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r,
nxt_http_route_rule_t *rule, u_char *start, size_t length);
+nxt_int_t nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+ nxt_conf_value_t *cv, nxt_http_action_t *action);
+void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
+ nxt_http_action_t *action);
+
nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *conf);
nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf,
nxt_upstream_t ***upstream_joint);
-void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
- nxt_http_action_t *action);
-
-nxt_http_action_t *nxt_http_return_handler(nxt_task_t *task,
- nxt_http_request_t *r, nxt_http_action_t *action);
+nxt_int_t nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action,
+ nxt_http_action_conf_t *acf);
-nxt_http_action_t *nxt_http_static_handler(nxt_task_t *task,
- nxt_http_request_t *r, nxt_http_action_t *action);
+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);
nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash);
nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
- nxt_str_t *extension, nxt_str_t *type);
-nxt_str_t *nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash,
- nxt_str_t *extension);
+ nxt_str_t *exten, nxt_str_t *type);
+nxt_str_t *nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten);
nxt_http_action_t *nxt_http_application_handler(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_action_t *action);
@@ -337,8 +356,8 @@ nxt_int_t nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name,
nxt_http_action_t *nxt_upstream_proxy_handler(nxt_task_t *task,
nxt_http_request_t *r, nxt_upstream_t *upstream);
-
-nxt_int_t nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action);
+nxt_int_t nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action,
+ nxt_http_action_conf_t *acf);
nxt_int_t nxt_http_proxy_date(void *ctx, nxt_http_field_t *field,
uintptr_t data);
nxt_int_t nxt_http_proxy_content_length(void *ctx, nxt_http_field_t *field,
diff --git a/src/nxt_http_chunk_parse.c b/src/nxt_http_chunk_parse.c
index be3a2023..deab116d 100644
--- a/src/nxt_http_chunk_parse.c
+++ b/src/nxt_http_chunk_parse.c
@@ -253,7 +253,6 @@ nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj, void *data)
nxt_buf_t *b, *next, *parent;
b = obj;
- parent = data;
nxt_debug(task, "buf completion: %p %p", b, b->mem.start);
diff --git a/src/nxt_http_proxy.c b/src/nxt_http_proxy.c
index 338d9fce..6aa3aabb 100644
--- a/src/nxt_http_proxy.c
+++ b/src/nxt_http_proxy.c
@@ -21,7 +21,7 @@ static void nxt_http_proxy_upstream_ready(nxt_task_t *task,
nxt_upstream_server_t *us);
static void nxt_http_proxy_upstream_error(nxt_task_t *task,
nxt_upstream_server_t *us);
-static nxt_http_action_t *nxt_http_proxy_handler(nxt_task_t *task,
+static nxt_http_action_t *nxt_http_proxy(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_action_t *action);
static void nxt_http_proxy_header_send(nxt_task_t *task, void *obj, void *data);
static void nxt_http_proxy_header_sent(nxt_task_t *task, void *obj, void *data);
@@ -50,7 +50,8 @@ static const nxt_upstream_peer_state_t nxt_upstream_proxy_state = {
nxt_int_t
-nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action)
+nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action,
+ nxt_http_action_conf_t *acf)
{
nxt_str_t name;
nxt_sockaddr_t *sa;
@@ -58,7 +59,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action)
nxt_upstream_proxy_t *proxy;
sa = NULL;
- name = action->name;
+ nxt_conf_get_string(acf->proxy, &name);
if (nxt_str_start(&name, "http://", 7)) {
name.length -= 7;
@@ -92,7 +93,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action)
up->type.proxy = proxy;
action->u.upstream = up;
- action->handler = nxt_http_proxy_handler;
+ action->handler = nxt_http_proxy;
}
return NXT_OK;
@@ -100,10 +101,16 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action)
static nxt_http_action_t *
-nxt_http_proxy_handler(nxt_task_t *task, nxt_http_request_t *r,
+nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
- return nxt_upstream_proxy_handler(task, r, action->u.upstream);
+ nxt_upstream_t *u;
+
+ u = action->u.upstream;
+
+ nxt_debug(task, "http proxy: \"%V\"", &u->name);
+
+ return nxt_upstream_proxy_handler(task, r, u);
}
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 779cfcf8..b71b25d9 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -10,6 +10,10 @@
static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp);
static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data);
+static nxt_int_t nxt_http_request_client_ip(nxt_task_t *task,
+ nxt_http_request_t *r);
+static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr(
+ nxt_http_request_t *r, u_char *start, size_t len);
static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data);
static void nxt_http_request_proto_info(nxt_task_t *task,
nxt_http_request_t *r);
@@ -272,16 +276,162 @@ static const nxt_http_request_state_t nxt_http_request_init_state
static void
nxt_http_request_start(nxt_task_t *task, void *obj, void *data)
{
+ nxt_int_t ret;
nxt_http_request_t *r;
r = obj;
r->state = &nxt_http_request_body_state;
+ ret = nxt_http_request_client_ip(task, r);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
nxt_http_request_read_body(task, r);
}
+static nxt_int_t
+nxt_http_request_client_ip(nxt_task_t *task, nxt_http_request_t *r)
+{
+ u_char *start, *p;
+ nxt_int_t ret, i, len;
+ nxt_str_t *header;
+ nxt_array_t *fields_arr; /* of nxt_http_field_t * */
+ nxt_sockaddr_t *sa, *prev_sa;
+ nxt_http_field_t *f, **fields;
+ nxt_http_client_ip_t *client_ip;
+
+ client_ip = r->conf->socket_conf->client_ip;
+
+ if (client_ip == NULL) {
+ return NXT_OK;
+ }
+
+ ret = nxt_http_route_addr_rule(r, client_ip->source, r->remote);
+ if (ret <= 0) {
+ return NXT_OK;
+ }
+
+ header = client_ip->header;
+
+ fields_arr = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_field_t *));
+ if (nxt_slow_path(fields_arr == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_list_each(f, r->fields) {
+ if (f->hash == client_ip->header_hash
+ && f->name_length == client_ip->header->length
+ && f->value_length > 0
+ && nxt_memcasecmp(f->name, header->start, header->length) == 0)
+ {
+ fields = nxt_array_add(fields_arr);
+ if (nxt_slow_path(fields == NULL)) {
+ return NXT_ERROR;
+ }
+
+ *fields = f;
+ }
+ } nxt_list_loop;
+
+ prev_sa = r->remote;
+ fields = (nxt_http_field_t **) fields_arr->elts;
+
+ i = fields_arr->nelts;
+
+ while (i-- > 0) {
+ f = fields[i];
+ start = f->value;
+ len = f->value_length;
+
+ do {
+ for (p = start + len - 1; p > start; p--, len--) {
+ if (*p != ' ' && *p != ',') {
+ break;
+ }
+ }
+
+ for (/* void */; p > start; p--) {
+ if (*p == ' ' || *p == ',') {
+ p++;
+ break;
+ }
+ }
+
+ sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start));
+ if (nxt_slow_path(sa == NULL)) {
+ if (prev_sa != NULL) {
+ r->remote = prev_sa;
+ }
+
+ return NXT_OK;
+ }
+
+ if (!client_ip->recursive) {
+ r->remote = sa;
+
+ return NXT_OK;
+ }
+
+ ret = nxt_http_route_addr_rule(r, client_ip->source, sa);
+ if (ret <= 0 || (i == 0 && p == start)) {
+ r->remote = sa;
+
+ return NXT_OK;
+ }
+
+ prev_sa = sa;
+ len = p - 1 - start;
+
+ } while (len > 0);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_sockaddr_t *
+nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start,
+ size_t len)
+{
+ nxt_str_t addr;
+ nxt_sockaddr_t *sa;
+
+ addr.start = start;
+ addr.length = len;
+
+ sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr);
+ if (nxt_slow_path(sa == NULL)) {
+ return NULL;
+ }
+
+ switch (sa->u.sockaddr.sa_family) {
+ case AF_INET:
+ if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) {
+ return NULL;
+ }
+
+ break;
+
+#if (NXT_INET6)
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) {
+ return NULL;
+ }
+
+ break;
+#endif /* NXT_INET6 */
+
+ default:
+ return NULL;
+ }
+
+ return sa;
+}
+
+
static const nxt_http_request_state_t nxt_http_request_body_state
nxt_aligned(64) =
{
@@ -348,9 +498,7 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_set(&r->server_name, "localhost");
}
- r->app_target = action->u.app.target;
-
- nxt_router_process_http_request(task, r, action->u.app.application);
+ nxt_router_process_http_request(task, r, action);
return NULL;
}
diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c
index c466cc25..18fd490d 100644
--- a/src/nxt_http_return.c
+++ b/src/nxt_http_return.c
@@ -7,29 +7,90 @@
#include <nxt_http.h>
+typedef struct {
+ nxt_http_status_t status;
+ nxt_str_t location;
+} nxt_http_return_conf_t;
+
+
+static nxt_http_action_t *nxt_http_return(nxt_task_t *task,
+ nxt_http_request_t *r, nxt_http_action_t *action);
+
+
static const nxt_http_request_state_t nxt_http_return_send_state;
+nxt_int_t
+nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action,
+ nxt_http_action_conf_t *acf)
+{
+ nxt_str_t *loc;
+ nxt_uint_t encode;
+ nxt_http_return_conf_t *conf;
+
+ conf = nxt_mp_zget(mp, sizeof(nxt_http_return_conf_t));
+ if (nxt_slow_path(conf == NULL)) {
+ return NXT_ERROR;
+ }
+
+ action->handler = nxt_http_return;
+ action->u.conf = conf;
+
+ conf->status = nxt_conf_get_number(acf->ret);
+
+ if (acf->location.length > 0) {
+ if (nxt_is_complex_uri_encoded(acf->location.start,
+ acf->location.length))
+ {
+ loc = nxt_str_dup(mp, &conf->location, &acf->location);
+ if (nxt_slow_path(loc == NULL)) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ loc = &conf->location;
+
+ encode = nxt_encode_complex_uri(NULL, acf->location.start,
+ acf->location.length);
+ loc->length = acf->location.length + encode * 2;
+
+ loc->start = nxt_mp_nget(mp, loc->length);
+ if (nxt_slow_path(loc->start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_encode_complex_uri(loc->start, acf->location.start,
+ acf->location.length);
+ }
+ }
+
+ return NXT_OK;
+}
+
+
nxt_http_action_t *
-nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r,
+nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
- nxt_http_field_t *field;
- nxt_http_status_t status;
+ nxt_http_field_t *field;
+ nxt_http_return_conf_t *conf;
+
+ conf = action->u.conf;
- status = action->u.return_code;
+ nxt_debug(task, "http return: %d (loc: \"%V\")",
+ conf->status, &conf->location);
- if (status >= NXT_HTTP_BAD_REQUEST
- && status <= NXT_HTTP_SERVER_ERROR_MAX)
+ if (conf->status >= NXT_HTTP_BAD_REQUEST
+ && conf->status <= NXT_HTTP_SERVER_ERROR_MAX)
{
- nxt_http_request_error(task, r, status);
+ nxt_http_request_error(task, r, conf->status);
return NULL;
}
- r->status = status;
+ r->status = conf->status;
r->resp.content_length_n = 0;
- if (action->name.length > 0) {
+ if (conf->location.length > 0) {
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
@@ -38,8 +99,8 @@ nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_field_name_set(field, "Location");
- field->value = action->name.start;
- field->value_length = action->name.length;
+ field->value = conf->location.start;
+ field->value_length = conf->location.length;
}
r->state = &nxt_http_return_send_state;
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 15b85544..cff69f96 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -47,20 +47,6 @@ typedef enum {
typedef struct {
- nxt_conf_value_t *pass;
- nxt_conf_value_t *ret;
- nxt_str_t location;
- nxt_conf_value_t *proxy;
- nxt_conf_value_t *share;
- nxt_str_t chroot;
- nxt_conf_value_t *follow_symlinks;
- nxt_conf_value_t *traverse_mounts;
- nxt_conf_value_t *types;
- nxt_conf_value_t *fallback;
-} nxt_http_route_action_conf_t;
-
-
-typedef struct {
nxt_conf_value_t *host;
nxt_conf_value_t *uri;
nxt_conf_value_t *method;
@@ -149,12 +135,12 @@ typedef struct {
} nxt_http_route_table_t;
-typedef struct {
+struct nxt_http_route_addr_rule_s {
/* The object must be the first field. */
nxt_http_route_object_t object:8;
uint32_t items;
nxt_http_route_addr_pattern_t addr_pattern[0];
-} nxt_http_route_addr_rule_t;
+};
typedef union {
@@ -199,9 +185,6 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
-static nxt_int_t nxt_http_route_action_create(nxt_task_t *task,
- nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
- nxt_http_action_t *action);
static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
@@ -211,8 +194,6 @@ static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task,
static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name,
nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
-static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create(
- nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv);
static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
nxt_http_route_pattern_case_t pattern_case,
@@ -254,8 +235,6 @@ static nxt_int_t nxt_http_route_table(nxt_http_request_t *r,
nxt_http_route_table_t *table);
static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r,
nxt_http_route_ruleset_t *ruleset);
-static nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r,
- nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr);
static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r,
nxt_http_route_rule_t *rule);
static nxt_int_t nxt_http_route_header(nxt_http_request_t *r,
@@ -476,7 +455,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NULL;
}
- ret = nxt_http_route_action_create(task, tmcf, action_conf, &match->action);
+ ret = nxt_http_action_init(task, tmcf, action_conf, &match->action);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
}
@@ -617,77 +596,69 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = {
{
nxt_string("pass"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, pass)
+ offsetof(nxt_http_action_conf_t, pass)
},
{
nxt_string("return"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, ret)
+ offsetof(nxt_http_action_conf_t, ret)
},
{
nxt_string("location"),
NXT_CONF_MAP_STR,
- offsetof(nxt_http_route_action_conf_t, location)
+ offsetof(nxt_http_action_conf_t, location)
},
{
nxt_string("proxy"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, proxy)
+ offsetof(nxt_http_action_conf_t, proxy)
},
{
nxt_string("share"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, share)
+ offsetof(nxt_http_action_conf_t, share)
},
{
nxt_string("chroot"),
NXT_CONF_MAP_STR,
- offsetof(nxt_http_route_action_conf_t, chroot)
+ offsetof(nxt_http_action_conf_t, chroot)
},
{
nxt_string("follow_symlinks"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, follow_symlinks)
+ offsetof(nxt_http_action_conf_t, follow_symlinks)
},
{
nxt_string("traverse_mounts"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, traverse_mounts)
+ offsetof(nxt_http_action_conf_t, traverse_mounts)
},
{
nxt_string("types"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, types)
+ offsetof(nxt_http_action_conf_t, types)
},
{
nxt_string("fallback"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, fallback)
+ offsetof(nxt_http_action_conf_t, fallback)
},
};
-static nxt_int_t
-nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+nxt_int_t
+nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *cv, nxt_http_action_t *action)
{
-#if (NXT_HAVE_OPENAT2)
- u_char *p;
- uint8_t slash;
- nxt_str_t *chroot;
-#endif
- nxt_mp_t *mp;
- nxt_int_t ret;
- nxt_str_t name, *string;
- nxt_uint_t encode;
- nxt_conf_value_t *conf;
- nxt_http_route_rule_t *rule;
- nxt_http_route_action_conf_t accf;
+ nxt_mp_t *mp;
+ nxt_int_t ret;
+ nxt_str_t name, *string;
+ nxt_http_action_conf_t acf;
- nxt_memzero(&accf, sizeof(accf));
+ nxt_memzero(&acf, sizeof(acf));
ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf,
- nxt_nitems(nxt_http_route_action_conf), &accf);
+ nxt_nitems(nxt_http_route_action_conf), &acf);
if (ret != NXT_OK) {
return ret;
}
@@ -696,126 +667,25 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
mp = tmcf->router_conf->mem_pool;
- if (accf.ret != NULL) {
- action->handler = nxt_http_return_handler;
- action->u.return_code = nxt_conf_get_number(accf.ret);
-
- if (accf.location.length > 0) {
- if (nxt_is_complex_uri_encoded(accf.location.start,
- accf.location.length))
- {
- string = nxt_str_dup(mp, &action->name, &accf.location);
- if (nxt_slow_path(string == NULL)) {
- return NXT_ERROR;
- }
-
- } else {
- string = &action->name;
-
- encode = nxt_encode_complex_uri(NULL, accf.location.start,
- accf.location.length);
- string->length = accf.location.length + encode * 2;
-
- string->start = nxt_mp_nget(mp, string->length);
- if (nxt_slow_path(string->start == NULL)) {
- return NXT_ERROR;
- }
-
- nxt_encode_complex_uri(string->start, accf.location.start,
- accf.location.length);
- }
- }
-
- return NXT_OK;
+ if (acf.ret != NULL) {
+ return nxt_http_return_init(mp, action, &acf);
}
- if (accf.share != NULL) {
- conf = accf.share;
-
- } else if (accf.proxy != NULL) {
- conf = accf.proxy;
+ if (acf.share != NULL) {
+ return nxt_http_static_init(task, tmcf, action, &acf);
+ }
- } else {
- conf = accf.pass;
+ if (acf.proxy != NULL) {
+ return nxt_http_proxy_init(mp, action, &acf);
}
- nxt_conf_get_string(conf, &name);
+ nxt_conf_get_string(acf.pass, &name);
string = nxt_str_dup(mp, &action->name, &name);
if (nxt_slow_path(string == NULL)) {
return NXT_ERROR;
}
- if (accf.share != NULL) {
- action->handler = nxt_http_static_handler;
-
-#if (NXT_HAVE_OPENAT2)
- string = &accf.chroot;
- chroot = &action->u.share.chroot;
-
- if (string->length > 0) {
- action->u.share.resolve |= RESOLVE_IN_ROOT;
-
- slash = (string->start[string->length - 1] != '/');
-
- chroot->length = string->length + (slash ? 1 : 0);
-
- chroot->start = nxt_mp_alloc(mp, chroot->length + 1);
- if (nxt_slow_path(chroot->start == NULL)) {
- return NXT_ERROR;
- }
-
- p = nxt_cpymem(chroot->start, string->start, string->length);
-
- if (slash) {
- *p++ = '/';
- }
-
- *p = '\0';
- }
-
- if (accf.follow_symlinks != NULL
- && !nxt_conf_get_boolean(accf.follow_symlinks))
- {
- action->u.share.resolve |= RESOLVE_NO_SYMLINKS;
- }
-
- if (accf.traverse_mounts != NULL
- && !nxt_conf_get_boolean(accf.traverse_mounts))
- {
- action->u.share.resolve |= RESOLVE_NO_XDEV;
- }
-#endif
-
- if (accf.types != NULL) {
- rule = nxt_http_route_rule_create(task, mp, accf.types, 0,
- NXT_HTTP_ROUTE_PATTERN_LOWCASE,
- NXT_HTTP_ROUTE_ENCODING_NONE);
- if (nxt_slow_path(rule == NULL)) {
- return NXT_ERROR;
- }
-
- action->u.share.types = rule;
- }
-
- if (accf.fallback != NULL) {
- action->u.share.fallback = nxt_mp_alloc(mp,
- sizeof(nxt_http_action_t));
- if (nxt_slow_path(action->u.share.fallback == NULL)) {
- return NXT_ERROR;
- }
-
- return nxt_http_route_action_create(task, tmcf, accf.fallback,
- action->u.share.fallback);
- }
-
- return NXT_OK;
- }
-
- if (accf.proxy != NULL) {
- return nxt_http_proxy_create(mp, action);
- }
-
return NXT_OK;
}
@@ -1066,7 +936,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
}
-static nxt_http_route_addr_rule_t *
+nxt_http_route_addr_rule_t *
nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv)
{
@@ -1119,6 +989,16 @@ nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
}
+nxt_http_route_rule_t *
+nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *types)
+{
+ return nxt_http_route_rule_create(task, mp, types, 0,
+ NXT_HTTP_ROUTE_PATTERN_LOWCASE,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
+}
+
+
static int
nxt_http_pattern_compare(const void *one, const void *two)
{
@@ -1491,15 +1371,12 @@ static nxt_int_t
nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_action_t *action)
{
- nxt_var_t *var;
nxt_int_t ret;
+ nxt_var_t *var;
if (action->handler != NULL) {
- if (action->handler == nxt_http_static_handler
- && action->u.share.fallback != NULL)
- {
- return nxt_http_action_resolve(task, tmcf,
- action->u.share.fallback);
+ if (action->fallback != NULL) {
+ return nxt_http_action_resolve(task, tmcf, action->fallback);
}
return NXT_OK;
@@ -1601,9 +1478,7 @@ 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)
{
- nxt_str_t *targets;
nxt_int_t ret;
- nxt_uint_t i;
nxt_str_t segments[3];
ret = nxt_http_pass_segments(mp, &action->name, segments, 3);
@@ -1612,24 +1487,8 @@ nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf,
}
if (nxt_str_eq(&segments[0], "applications", 12)) {
- ret = nxt_router_listener_application(rtcf, &segments[1], action);
-
- if (ret != NXT_OK) {
- return ret;
- }
-
- if (segments[2].length != 0) {
- targets = action->u.app.application->targets;
-
- for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++);
-
- action->u.app.target = i;
-
- } else {
- action->u.app.target = 0;
- }
-
- return NXT_OK;
+ return nxt_router_application_init(rtcf, &segments[1], &segments[2],
+ action);
}
if (segments[2].length == 0) {
@@ -1704,6 +1563,10 @@ nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
{
nxt_http_route_t **route, **end;
+ if (routes == NULL) {
+ return NXT_DECLINED;
+ }
+
route = &routes->route[0];
end = route + routes->items;
@@ -1762,9 +1625,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
action->name = *name;
- (void) nxt_router_listener_application(rtcf, name, action);
-
- action->u.app.target = 0;
+ (void) nxt_router_application_init(rtcf, name, NULL, action);
return action;
}
@@ -2062,7 +1923,7 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
}
-static nxt_int_t
+nxt_int_t
nxt_http_route_addr_rule(nxt_http_request_t *r,
nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
{
@@ -2071,6 +1932,11 @@ nxt_http_route_addr_rule(nxt_http_request_t *r,
nxt_http_route_addr_pattern_t *p;
n = addr_rule->items;
+
+ if (n == 0) {
+ return 0;
+ }
+
p = &addr_rule->addr_pattern[0] - 1;
do {
diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c
index 6d4955ed..2907a902 100644
--- a/src/nxt_http_route_addr.c
+++ b/src/nxt_http_route_addr.c
@@ -8,7 +8,6 @@
#include <nxt_http_route_addr.h>
-static nxt_bool_t nxt_str_looks_like_ipv6(const nxt_str_t *str);
#if (NXT_INET6)
static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len);
#endif
@@ -57,7 +56,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
goto parse_port;
}
- if (nxt_str_looks_like_ipv6(&addr)) {
+ if (nxt_inet6_probe(&addr)) {
#if (NXT_INET6)
u_char *end;
uint8_t i;
@@ -304,22 +303,6 @@ parse_port:
}
-static nxt_bool_t
-nxt_str_looks_like_ipv6(const nxt_str_t *str)
-{
- u_char *colon, *end;
-
- colon = nxt_memchr(str->start, ':', str->length);
-
- if (colon != NULL) {
- end = str->start + str->length;
- colon = nxt_memchr(colon + 1, ':', end - (colon + 1));
- }
-
- return (colon != NULL);
-}
-
-
#if (NXT_INET6)
static nxt_bool_t
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index c8b73fac..9b79a666 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -7,12 +7,22 @@
#include <nxt_http.h>
+typedef struct {
+ nxt_str_t share;
+ nxt_str_t chroot;
+ nxt_uint_t resolve;
+ nxt_http_route_rule_t *types;
+} nxt_http_static_conf_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_extract_extension(nxt_str_t *path,
- nxt_str_t *extension);
+ nxt_str_t *exten);
static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
void *data);
static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
@@ -27,30 +37,122 @@ static void nxt_http_static_mtypes_hash_free(void *data, void *p);
static const nxt_http_request_state_t nxt_http_static_send_state;
-nxt_http_action_t *
-nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
+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)
+{
+ nxt_mp_t *mp;
+ nxt_str_t *str, value;
+ nxt_http_static_conf_t *conf;
+
+ mp = tmcf->router_conf->mem_pool;
+
+ conf = nxt_mp_zget(mp, sizeof(nxt_http_static_conf_t));
+ if (nxt_slow_path(conf == NULL)) {
+ return NXT_ERROR;
+ }
+
+ action->handler = nxt_http_static;
+ action->u.conf = conf;
+
+ nxt_conf_get_string(acf->share, &value);
+
+ str = nxt_str_dup(mp, &conf->share, &value);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
+
+#if (NXT_HAVE_OPENAT2)
+ if (acf->chroot.length > 0) {
+ u_char *p;
+ nxt_str_t slash;
+
+ if (acf->chroot.start[acf->chroot.length - 1] != '/') {
+ nxt_str_set(&slash, "/");
+
+ } else {
+ nxt_str_set(&slash, "");
+ }
+
+ value.length = acf->chroot.length + slash.length;
+
+ value.start = nxt_mp_alloc(mp, value.length + 1);
+ if (nxt_slow_path(value.start == 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->chroot = value;
+ conf->resolve |= RESOLVE_IN_ROOT;
+ }
+
+ if (acf->follow_symlinks != NULL
+ && !nxt_conf_get_boolean(acf->follow_symlinks))
+ {
+ conf->resolve |= RESOLVE_NO_SYMLINKS;
+ }
+
+ if (acf->traverse_mounts != NULL
+ && !nxt_conf_get_boolean(acf->traverse_mounts))
+ {
+ conf->resolve |= RESOLVE_NO_XDEV;
+ }
+#endif
+
+ if (acf->types != NULL) {
+ conf->types = nxt_http_route_types_rule_create(task, mp, acf->types);
+ if (nxt_slow_path(conf->types == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+
+ if (acf->fallback != NULL) {
+ action->fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
+ if (nxt_slow_path(action->fallback == NULL)) {
+ return NXT_ERROR;
+ }
+
+ return nxt_http_action_init(task, tmcf, acf->fallback,
+ action->fallback);
+ }
+
+ return NXT_OK;
+}
+
+
+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, extension, *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;
+ 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);
if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
if (!nxt_str_eq(r->method, "HEAD", 4)) {
- if (action->u.share.fallback != NULL) {
- return action->u.share.fallback;
+ if (action->fallback != NULL) {
+ return action->fallback;
}
nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
@@ -66,11 +168,11 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
if (r->path->start[r->path->length - 1] == '/') {
/* TODO: dynamic index setting. */
nxt_str_set(&index, "index.html");
- nxt_str_set(&extension, ".html");
+ nxt_str_set(&exten, ".html");
} else {
nxt_str_set(&index, "");
- nxt_str_null(&extension);
+ nxt_str_null(&exten);
}
f = NULL;
@@ -79,20 +181,19 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
mtype = NULL;
- if (action->u.share.types != NULL && extension.start == NULL) {
- nxt_http_static_extract_extension(r->path, &extension);
- mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
- &extension);
+ 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);
- ret = nxt_http_route_test_rule(r, action->u.share.types,
- mtype->start, mtype->length);
+ ret = nxt_http_route_test_rule(r, conf->types, mtype->start,
+ mtype->length);
if (nxt_slow_path(ret == NXT_ERROR)) {
goto fail;
}
if (ret == 0) {
- if (action->u.share.fallback != NULL) {
- return action->u.share.fallback;
+ if (action->fallback != NULL) {
+ return action->fallback;
}
nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN);
@@ -100,7 +201,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
}
}
- length = action->name.length + r->path->length + index.length;
+ length = conf->share.length + r->path->length + index.length;
fname = nxt_mp_nget(r->mem_pool, length + 1);
if (nxt_slow_path(fname == NULL)) {
@@ -108,7 +209,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
}
p = fname;
- p = nxt_cpymem(p, action->name.start, action->name.length);
+ 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';
@@ -117,11 +218,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
file.name = fname;
- chroot = &action->u.share.chroot;
+ chroot = &conf->chroot;
#if (NXT_HAVE_OPENAT2)
-
- if (action->u.share.resolve != 0) {
+ if (conf->resolve != 0) {
if (chroot->length > 0) {
file.name = chroot->start;
@@ -156,8 +256,7 @@ nxt_http_static_handler(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,
- action->u.share.resolve);
+ NXT_FILE_OPEN, 0, af.fd, conf->resolve);
if (af.fd != AT_FDCWD) {
nxt_file_close(task, &af);
@@ -169,9 +268,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
}
#else
-
ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
-
#endif
if (nxt_slow_path(ret != NXT_OK)) {
@@ -211,8 +308,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
break;
}
- if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) {
- return action->u.share.fallback;
+ if (level == NXT_LOG_ERR && action->fallback != NULL) {
+ return action->fallback;
}
if (status != NXT_HTTP_NOT_FOUND) {
@@ -283,13 +380,12 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_file_size(&fi))
- p;
- if (extension.start == NULL) {
- nxt_http_static_extract_extension(r->path, &extension);
+ if (exten.start == NULL) {
+ nxt_http_static_extract_extension(r->path, &exten);
}
if (mtype == NULL) {
- mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
- &extension);
+ mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten);
}
if (mtype->length != 0) {
@@ -328,8 +424,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_file_close(task, f);
if (nxt_slow_path(!nxt_is_dir(&fi))) {
- if (action->u.share.fallback != NULL) {
- return action->u.share.fallback;
+ if (action->fallback != NULL) {
+ return action->fallback;
}
nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
@@ -401,7 +497,7 @@ fail:
static void
-nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension)
+nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten)
{
u_char ch, *p, *end;
@@ -419,8 +515,8 @@ nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension)
p++;
/* Fall through. */
case '.':
- extension->length = end - p;
- extension->start = p;
+ exten->length = end - p;
+ exten->start = p;
return;
}
}
@@ -571,13 +667,13 @@ clean:
nxt_int_t
nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
{
- nxt_str_t *type, extension;
+ nxt_str_t *type, exten;
nxt_int_t ret;
nxt_uint_t i;
static const struct {
nxt_str_t type;
- const char *extension;
+ const char *exten;
} default_types[] = {
{ nxt_string("text/html"), ".html" },
@@ -644,10 +740,10 @@ nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
for (i = 0; i < nxt_nitems(default_types); i++) {
type = (nxt_str_t *) &default_types[i].type;
- extension.start = (u_char *) default_types[i].extension;
- extension.length = nxt_strlen(extension.start);
+ exten.start = (u_char *) default_types[i].exten;
+ exten.length = nxt_strlen(exten.start);
- ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type);
+ ret = nxt_http_static_mtypes_hash_add(mp, hash, &exten, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
@@ -668,14 +764,14 @@ static const nxt_lvlhsh_proto_t nxt_http_static_mtypes_hash_proto
typedef struct {
- nxt_str_t extension;
+ nxt_str_t exten;
nxt_str_t *type;
} nxt_http_static_mtype_t;
nxt_int_t
nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
- nxt_str_t *extension, nxt_str_t *type)
+ nxt_str_t *exten, nxt_str_t *type)
{
nxt_lvlhsh_query_t lhq;
nxt_http_static_mtype_t *mtype;
@@ -685,10 +781,10 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
return NXT_ERROR;
}
- mtype->extension = *extension;
+ mtype->exten = *exten;
mtype->type = type;
- lhq.key = *extension;
+ lhq.key = *exten;
lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
lhq.replace = 1;
lhq.value = mtype;
@@ -700,14 +796,14 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
nxt_str_t *
-nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
+nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten)
{
nxt_lvlhsh_query_t lhq;
nxt_http_static_mtype_t *mtype;
static nxt_str_t empty = nxt_string("");
- lhq.key = *extension;
+ lhq.key = *exten;
lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
lhq.proto = &nxt_http_static_mtypes_hash_proto;
@@ -727,8 +823,7 @@ nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
mtype = data;
- return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK
- : NXT_DECLINED;
+ return nxt_strcasestr_eq(&lhq->key, &mtype->exten) ? NXT_OK : NXT_DECLINED;
}
diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c
index cab0074b..e3cb1f22 100644
--- a/src/nxt_isolation.c
+++ b/src/nxt_isolation.c
@@ -126,10 +126,10 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process,
return ret;
}
- has_mnt = 0;
-
#if (NXT_HAVE_CLONE_NEWNS)
has_mnt = nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS);
+#else
+ has_mnt = 0;
#endif
if (process->user_cred->uid == 0 && !has_mnt) {
diff --git a/src/nxt_log.h b/src/nxt_log.h
index 48742721..0cf10b5c 100644
--- a/src/nxt_log.h
+++ b/src/nxt_log.h
@@ -48,29 +48,29 @@ nxt_log_level_enough(log, level) \
#define nxt_alert(task, ...) \
do { \
- nxt_log_t *log = (task)->log; \
+ nxt_log_t *_log = (task)->log; \
\
- log->handler(NXT_LOG_ALERT, log, __VA_ARGS__); \
+ _log->handler(NXT_LOG_ALERT, _log, __VA_ARGS__); \
} while (0)
#define nxt_log(task, _level, ...) \
do { \
- nxt_log_t *log = (task)->log; \
+ nxt_log_t *_log = (task)->log; \
nxt_uint_t _level_ = (_level); \
\
- if (nxt_slow_path(log->level >= _level_)) { \
- log->handler(_level_, log, __VA_ARGS__); \
+ if (nxt_slow_path(_log->level >= _level_)) { \
+ _log->handler(_level_, _log, __VA_ARGS__); \
} \
} while (0)
#define nxt_trace(task, ...) \
do { \
- nxt_log_t *log = (task)->log; \
+ nxt_log_t *_log = (task)->log; \
\
- if (nxt_slow_path(log->level >= NXT_LOG_NOTICE || nxt_trace)) { \
- log->handler(NXT_LOG_NOTICE, log, __VA_ARGS__); \
+ if (nxt_slow_path(_log->level >= NXT_LOG_NOTICE || nxt_trace)) { \
+ _log->handler(NXT_LOG_NOTICE, _log, __VA_ARGS__); \
} \
} while (0)
@@ -99,10 +99,10 @@ nxt_log_error(_level, _log, ...) \
#define nxt_debug(task, ...) \
do { \
- nxt_log_t *log = (task)->log; \
+ nxt_log_t *_log = (task)->log; \
\
- if (nxt_slow_path(log->level == NXT_LOG_DEBUG || nxt_debug)) { \
- log->handler(NXT_LOG_DEBUG, log, __VA_ARGS__); \
+ if (nxt_slow_path(_log->level == NXT_LOG_DEBUG || nxt_debug)) { \
+ _log->handler(NXT_LOG_DEBUG, _log, __VA_ARGS__); \
} \
} while (0)
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 00f336f6..16c6a297 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -271,6 +271,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = {
NXT_CONF_MAP_INT32,
offsetof(nxt_common_app_conf_t, u.ruby.threads),
},
+ {
+ nxt_string("hooks"),
+ NXT_CONF_MAP_STR,
+ offsetof(nxt_common_app_conf_t, u.ruby.hooks),
+ }
};
@@ -342,8 +347,6 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_process_init_t *init;
nxt_common_app_conf_t *app_conf;
- ret = NXT_ERROR;
-
rt = task->thread->runtime;
process = nxt_main_process_new(task, rt);
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index 2fd5d1bf..273ca7f4 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -11,6 +11,8 @@
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
typedef struct {
@@ -42,15 +44,22 @@ static void nxt_openssl_lock(int mode, int type, const char *file, int line);
static unsigned long nxt_openssl_thread_id(void);
static void nxt_openssl_locks_free(void);
#endif
-static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
- nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds,
- nxt_bool_t last);
+static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_tls_init_t *tls_init, nxt_bool_t last);
static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
#if (NXT_HAVE_OPENSSL_CONF_CMD)
static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
nxt_conf_value_t *value, nxt_mp_t *mp);
#endif
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+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);
+static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name,
+ unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc);
+#endif
+static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size,
+ time_t timeout);
static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
nxt_tls_conf_t *conf, nxt_mp_t *mp);
static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
@@ -265,11 +274,12 @@ nxt_openssl_locks_free(void)
static nxt_int_t
-nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
- nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last)
+nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_tls_init_t *tls_init, nxt_bool_t last)
{
SSL_CTX *ctx;
const char *ciphers, *ca_certificate;
+ nxt_tls_conf_t *conf;
STACK_OF(X509_NAME) *list;
nxt_tls_bundle_conf_t *bundle;
@@ -279,6 +289,8 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
return NXT_ERROR;
}
+ conf = tls_init->conf;
+
bundle = conf->bundle;
nxt_assert(bundle != NULL);
@@ -337,13 +349,21 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
}
#if (NXT_HAVE_OPENSSL_CONF_CMD)
- if (conf_cmds != NULL
- && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK)
+ if (tls_init->conf_cmds != NULL
+ && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK)
{
goto fail;
}
#endif
+ nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout);
+
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+ if (nxt_tls_ticket_keys(task, ctx, tls_init, mp) != NXT_OK) {
+ goto fail;
+ }
+#endif
+
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (conf->ca_certificate != NULL) {
@@ -581,6 +601,257 @@ fail:
#endif
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+
+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)
+{
+ uint32_t i;
+ nxt_int_t ret;
+ nxt_str_t value;
+ nxt_uint_t count;
+ nxt_conf_value_t *member, *tickets_conf;
+ nxt_tls_ticket_t *ticket;
+ nxt_tls_tickets_t *tickets;
+ u_char buf[80];
+
+ tickets_conf = tls_init->tickets_conf;
+
+ if (tickets_conf == NULL) {
+ goto no_ticket;
+ }
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_BOOLEAN) {
+ if (nxt_conf_get_boolean(tickets_conf) == 0) {
+ goto no_ticket;
+ }
+
+ return NXT_OK;
+ }
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
+ count = nxt_conf_array_elements_count(tickets_conf);
+
+ if (count == 0) {
+ goto no_ticket;
+ }
+
+ } else {
+ /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
+ count = 1;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+ tickets = nxt_mp_get(mp, sizeof(nxt_tls_tickets_t)
+ + count * sizeof(nxt_tls_ticket_t));
+ if (nxt_slow_path(tickets == NULL)) {
+ return NXT_ERROR;
+ }
+
+ tickets->count = count;
+ tls_init->conf->tickets = tickets;
+ i = 0;
+
+ do {
+ ticket = &tickets->tickets[i];
+
+ i++;
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
+ member = nxt_conf_get_array_element(tickets_conf, count - i);
+ if (member == NULL) {
+ break;
+ }
+
+ } else {
+ /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
+ member = tickets_conf;
+ }
+
+ 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;
+ }
+
+ if (ret == 48) {
+ ticket->aes128 = 1;
+ nxt_memcpy(ticket->aes_key, buf + 16, 16);
+ nxt_memcpy(ticket->hmac_key, buf + 32, 16);
+
+ } else {
+ ticket->aes128 = 0;
+ nxt_memcpy(ticket->hmac_key, buf + 16, 32);
+ nxt_memcpy(ticket->aes_key, buf + 48, 32);
+ }
+
+ nxt_memcpy(ticket->name, buf, 16);
+ } while (i < count);
+
+ if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback)
+ == 0)
+ {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "Unit was built with Session Tickets support, however, "
+ "now it is linked dynamically to an OpenSSL library "
+ "which has no tlsext support, therefore Session Tickets "
+ "are not available");
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+
+#else
+ nxt_alert(task, "Setting custom session ticket keys is not supported with "
+ "this version of OpenSSL library");
+
+ return NXT_ERROR;
+
+#endif
+
+no_ticket:
+
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+
+ return NXT_OK;
+}
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+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;
+ const EVP_CIPHER *cipher;
+ nxt_tls_ticket_t *ticket;
+ nxt_openssl_conn_t *tls;
+
+ c = SSL_get_ex_data(s, nxt_openssl_connection_index);
+
+ if (nxt_slow_path(c == NULL)) {
+ nxt_thread_log_alert("SSL_get_ex_data() failed");
+ return -1;
+ }
+
+ tls = c->u.tls;
+ ticket = tls->conf->tickets->tickets;
+
+#ifdef OPENSSL_NO_SHA256
+ digest = EVP_sha1();
+#else
+ digest = EVP_sha256();
+#endif
+
+ 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;
+ }
+
+ if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "RAND_bytes() failed");
+ return -1;
+ }
+
+ 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++) {
+ if (nxt_memcmp(name, ticket[i].name, 16) == 0) {
+ goto found;
+ }
+ }
+
+ nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found");
+
+ return 0;
+
+ found:
+
+ 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;
+
+ } else {
+ cipher = EVP_aes_256_cbc();
+ size = 32;
+ }
+
+ 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;
+ }
+
+ return (i == 0) ? 1 : 2 /* renew */;
+ }
+}
+
+#endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */
+
+#endif /* NXT_HAVE_OPENSSL_TLSEXT */
+
+
+static void
+nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout)
+{
+ if (cache_size == 0) {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ return;
+ }
+
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+
+ SSL_CTX_sess_set_cache_size(ctx, cache_size);
+
+ SSL_CTX_set_timeout(ctx, (long) timeout);
+}
+
static nxt_uint_t
nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
@@ -782,15 +1053,15 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg)
}
servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
- if (nxt_slow_path(servername == NULL)) {
- nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
- "NULL in server name callback");
- return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ if (servername == NULL) {
+ nxt_debug(c->socket.task, "SSL_get_servername(): NULL");
+ goto done;
}
str.length = nxt_strlen(servername);
if (str.length == 0) {
- nxt_debug(c->socket.task, "client sent zero-length server name");
+ nxt_debug(c->socket.task, "SSL_get_servername(): \"\" is empty");
goto done;
}
@@ -882,6 +1153,11 @@ nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
bundle = bundle->next;
} while (bundle != NULL);
+ if (conf->tickets) {
+ nxt_memzero(conf->tickets->tickets,
+ conf->tickets->count * sizeof(nxt_tls_ticket_t));
+ }
+
#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
&& OPENSSL_VERSION_NUMBER < 0x1010101fL)
RAND_keep_random_devices_open(0);
@@ -1543,3 +1819,70 @@ 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_port.h b/src/nxt_port.h
index 5ece3bfa..a0bc2512 100644
--- a/src/nxt_port.h
+++ b/src/nxt_port.h
@@ -50,6 +50,7 @@ struct nxt_port_handlers_s {
/* Various data. */
nxt_port_handler_t data;
+ nxt_port_handler_t app_restart;
nxt_port_handler_t oosm;
nxt_port_handler_t shm_ack;
@@ -100,6 +101,7 @@ typedef enum {
_NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame),
_NXT_PORT_MSG_DATA = nxt_port_handler_idx(data),
+ _NXT_PORT_MSG_APP_RESTART = nxt_port_handler_idx(app_restart),
_NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm),
_NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack),
@@ -139,6 +141,7 @@ typedef enum {
NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA,
NXT_PORT_MSG_DATA_LAST = nxt_msg_last(_NXT_PORT_MSG_DATA),
+ NXT_PORT_MSG_APP_RESTART = nxt_msg_last(_NXT_PORT_MSG_APP_RESTART),
NXT_PORT_MSG_OOSM = nxt_msg_last(_NXT_PORT_MSG_OOSM),
NXT_PORT_MSG_SHM_ACK = nxt_msg_last(_NXT_PORT_MSG_SHM_ACK),
diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c
index 3cf2e79a..ba1b7081 100644
--- a/src/nxt_port_socket.c
+++ b/src/nxt_port_socket.c
@@ -21,6 +21,7 @@ static nxt_int_t nxt_port_msg_chk_insert(nxt_task_t *task, nxt_port_t *port,
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);
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,
@@ -449,19 +450,7 @@ next_fragment:
goto fail;
}
- if (msg->close_fd) {
- if (msg->fd[0] != -1) {
- nxt_fd_close(msg->fd[0]);
-
- msg->fd[0] = -1;
- }
-
- if (msg->fd[1] != -1) {
- nxt_fd_close(msg->fd[1]);
-
- msg->fd[1] = -1;
- }
- }
+ nxt_port_msg_close_fd(msg);
msg->buf = nxt_port_buf_completion(task, wq, msg->buf, plain_size,
m == NXT_PORT_METHOD_MMAP);
@@ -524,6 +513,12 @@ next_fragment:
} else {
if (nxt_slow_path(n == NXT_ERROR)) {
+ if (msg->link.next == NULL) {
+ nxt_port_msg_close_fd(msg);
+
+ nxt_port_release_send_msg(msg);
+ }
+
goto fail;
}
@@ -591,6 +586,27 @@ nxt_port_msg_first(nxt_port_t *port)
}
+nxt_inline void
+nxt_port_msg_close_fd(nxt_port_send_msg_t *msg)
+{
+ if (!msg->close_fd) {
+ return;
+ }
+
+ if (msg->fd[0] != -1) {
+ nxt_fd_close(msg->fd[0]);
+
+ msg->fd[0] = -1;
+ }
+
+ if (msg->fd[1] != -1) {
+ nxt_fd_close(msg->fd[1]);
+
+ msg->fd[1] = -1;
+ }
+}
+
+
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)
@@ -1315,19 +1331,7 @@ nxt_port_error_handler(nxt_task_t *task, void *obj, void *data)
nxt_queue_each(msg, &port->messages, nxt_port_send_msg_t, link) {
- if (msg->close_fd) {
- if (msg->fd[0] != -1) {
- nxt_fd_close(msg->fd[0]);
-
- msg->fd[0] = -1;
- }
-
- if (msg->fd[1] != -1) {
- nxt_fd_close(msg->fd[1]);
-
- msg->fd[1] = -1;
- }
- }
+ nxt_port_msg_close_fd(msg);
for (b = msg->buf; b != NULL; b = next) {
next = b->next;
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 015ae226..39d375f8 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -18,6 +18,8 @@
#include <nxt_app_queue.h>
#include <nxt_port_queue.h>
+#define NXT_SHARED_PORT_ID 0xFFFFu
+
typedef struct {
nxt_str_t type;
uint32_t processes;
@@ -44,10 +46,10 @@ typedef struct {
nxt_str_t name;
nxt_socket_conf_t *socket_conf;
nxt_router_temp_conf_t *temp_conf;
- nxt_conf_value_t *conf_cmds;
+ nxt_tls_init_t *tls_init;
nxt_bool_t last;
- nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */
+ nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */
} nxt_router_tlssock_t;
#endif
@@ -67,6 +69,12 @@ typedef struct {
} nxt_app_rpc_t;
+typedef struct {
+ nxt_app_joint_t *app_joint;
+ uint32_t generation;
+} nxt_app_joint_rpc_t;
+
+
static nxt_int_t nxt_router_prefork(nxt_task_t *task, nxt_process_t *process,
nxt_mp_t *mp);
static nxt_int_t nxt_router_start(nxt_task_t *task, nxt_process_data_t *data);
@@ -79,6 +87,8 @@ static void nxt_router_new_port_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static void nxt_router_conf_data_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
+static void nxt_router_app_restart_handler(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg);
static void nxt_router_remove_pid_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static void nxt_router_access_log_reopen_handler(nxt_task_t *task,
@@ -97,6 +107,9 @@ static nxt_int_t nxt_router_conf_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end);
static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_conf_value_t *conf);
+static nxt_int_t nxt_router_conf_process_client_ip(nxt_task_t *task,
+ nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf,
+ nxt_conf_value_t *conf);
static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name);
static nxt_int_t nxt_router_apps_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
@@ -123,8 +136,8 @@ static void nxt_router_listen_socket_error(nxt_task_t *task,
static void nxt_router_tls_rpc_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
- nxt_conf_value_t *value, nxt_socket_conf_t *skcf,
- nxt_conf_value_t * conf_cmds);
+ nxt_conf_value_t *value, nxt_socket_conf_t *skcf, nxt_tls_init_t *tls_init,
+ nxt_bool_t last);
#endif
static void nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app);
@@ -224,8 +237,6 @@ static void nxt_router_http_request_error(nxt_task_t *task, void *obj,
static void nxt_router_http_request_done(nxt_task_t *task, void *obj,
void *data);
-static void nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj,
- void *data);
static void nxt_router_app_prepare_request(nxt_task_t *task,
nxt_request_rpc_data_t *req_rpc_data);
static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task,
@@ -281,6 +292,7 @@ static const nxt_port_handlers_t nxt_router_process_port_handlers = {
.mmap = nxt_port_mmap_handler,
.get_mmap = nxt_router_get_mmap_handler,
.data = nxt_router_conf_data_handler,
+ .app_restart = nxt_router_app_restart_handler,
.remove_pid = nxt_router_remove_pid_handler,
.access_log = nxt_router_access_log_reopen_handler,
.rpc_ready = nxt_port_rpc_handler,
@@ -379,14 +391,15 @@ static void
nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port,
void *data)
{
- 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_runtime_t *rt;
+ 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_runtime_t *rt;
+ nxt_app_joint_rpc_t *app_joint_rpc;
app = data;
@@ -407,30 +420,29 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port,
*b->mem.free++ = '\0';
nxt_buf_cpystr(b, &app->conf);
- nxt_router_app_joint_use(task, app->joint, 1);
-
- stream = nxt_port_rpc_register_handler(task, port,
- nxt_router_app_port_ready,
- nxt_router_app_port_error,
- -1, app->joint);
-
- if (nxt_slow_path(stream == 0)) {
- nxt_router_app_joint_use(task, app->joint, -1);
-
+ app_joint_rpc = nxt_port_rpc_register_handler_ex(task, port,
+ nxt_router_app_port_ready,
+ nxt_router_app_port_error,
+ sizeof(nxt_app_joint_rpc_t));
+ if (nxt_slow_path(app_joint_rpc == NULL)) {
goto failed;
}
+ stream = nxt_port_rpc_ex_stream(app_joint_rpc);
+
ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS,
-1, stream, port->id, b);
-
if (nxt_slow_path(ret != NXT_OK)) {
nxt_port_rpc_cancel(task, port, stream);
- nxt_router_app_joint_use(task, app->joint, -1);
-
goto failed;
}
+ app_joint_rpc->app_joint = app->joint;
+ app_joint_rpc->generation = app->generation;
+
+ nxt_router_app_joint_use(task, app->joint, 1);
+
nxt_router_app_use(task, app, -1);
return;
@@ -504,6 +516,7 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data)
{
nxt_buf_t *b, *next;
nxt_bool_t cancelled;
+ nxt_port_t *app_port;
nxt_msg_info_t *msg_info;
msg_info = &req_rpc_data->msg_info;
@@ -512,13 +525,20 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data)
return 0;
}
- cancelled = nxt_app_queue_cancel(req_rpc_data->app->shared_port->queue,
- msg_info->tracking_cookie,
- req_rpc_data->stream);
+ app_port = req_rpc_data->app_port;
+
+ if (app_port != NULL && app_port->id == NXT_SHARED_PORT_ID) {
+ cancelled = nxt_app_queue_cancel(app_port->queue,
+ msg_info->tracking_cookie,
+ req_rpc_data->stream);
- if (cancelled) {
- nxt_debug(task, "stream #%uD: cancelled by router",
- req_rpc_data->stream);
+ if (cancelled) {
+ nxt_debug(task, "stream #%uD: cancelled by router",
+ req_rpc_data->stream);
+ }
+
+ } else {
+ cancelled = 0;
}
for (b = msg_info->buf; b != NULL; b = next) {
@@ -680,18 +700,20 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_assert(main_app_port != NULL);
app = main_app_port->app;
- nxt_assert(app != NULL);
- nxt_thread_mutex_lock(&app->mutex);
+ if (nxt_fast_path(app != NULL)) {
+ nxt_thread_mutex_lock(&app->mutex);
- /* TODO here should be find-and-add code because there can be
- port waiters in port_hash */
- nxt_port_hash_add(&app->port_hash, port);
- app->port_hash_count++;
+ /* TODO here should be find-and-add code because there can be
+ port waiters in port_hash */
+ nxt_port_hash_add(&app->port_hash, port);
+ app->port_hash_count++;
- nxt_thread_mutex_unlock(&app->mutex);
+ nxt_thread_mutex_unlock(&app->mutex);
+
+ port->app = app;
+ }
- port->app = app;
port->main_app_port = main_app_port;
nxt_port_socket_write(task, port, NXT_PORT_MSG_PORT_ACK, -1, 0, 0, NULL);
@@ -792,6 +814,90 @@ cleanup:
static void
+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_msg_type_t reply;
+
+ reply_port = nxt_runtime_port_find(task->thread->runtime,
+ msg->port_msg.pid,
+ msg->port_msg.reply_port);
+ if (nxt_slow_path(reply_port == NULL)) {
+ nxt_alert(task, "app_restart_handler: reply port not found");
+ return;
+ }
+
+ app_name.length = nxt_buf_mem_used_size(&msg->buf->mem);
+ app_name.start = msg->buf->mem.pos;
+
+ nxt_debug(task, "app_restart_handler: %V", &app_name);
+
+ app = nxt_router_app_find(&nxt_router->apps, &app_name);
+
+ if (nxt_fast_path(app != NULL)) {
+ shared_port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid,
+ NXT_PROCESS_APP);
+ if (nxt_slow_path(shared_port == NULL)) {
+ goto fail;
+ }
+
+ ret = nxt_port_socket_init(task, shared_port, 0);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_port_use(task, shared_port, -1);
+ goto fail;
+ }
+
+ ret = nxt_router_app_queue_init(task, shared_port);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_port_write_close(shared_port);
+ nxt_port_read_close(shared_port);
+ nxt_port_use(task, shared_port, -1);
+ goto fail;
+ }
+
+ nxt_port_write_enable(task, shared_port);
+
+ nxt_thread_mutex_lock(&app->mutex);
+
+ nxt_queue_each(port, &app->ports, nxt_port_t, app_link) {
+
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1,
+ 0, 0, NULL);
+
+ } nxt_queue_loop;
+
+ app->generation++;
+
+ shared_port->app = app;
+
+ old_shared_port = app->shared_port;
+ old_shared_port->app = NULL;
+
+ app->shared_port = shared_port;
+
+ nxt_thread_mutex_unlock(&app->mutex);
+
+ nxt_port_close(task, old_shared_port);
+ nxt_port_use(task, old_shared_port, -1);
+
+ reply = NXT_PORT_MSG_RPC_READY_LAST;
+
+ } else {
+
+fail:
+
+ reply = NXT_PORT_MSG_RPC_ERROR;
+ }
+
+ nxt_port_socket_write(task, reply_port, reply, -1, msg->port_msg.stream,
+ 0, NULL);
+}
+
+
+static void
nxt_router_app_process_remove_pid(nxt_task_t *task, nxt_port_t *port,
void *data)
{
@@ -956,8 +1062,6 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link);
- tls->last = nxt_queue_is_empty(&tmcf->tls);
-
nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
nxt_router_tls_rpc_handler, tls);
return;
@@ -1341,12 +1445,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_router_t *router;
nxt_app_joint_t *app_joint;
#if (NXT_TLS)
- nxt_conf_value_t *certificate, *conf_cmds;
+ nxt_tls_init_t *tls_init;
+ nxt_conf_value_t *certificate;
#endif
nxt_conf_value_t *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener;
- nxt_conf_value_t *routes_conf, *static_conf;
+ nxt_conf_value_t *routes_conf, *static_conf, *client_ip_conf;
nxt_socket_conf_t *skcf;
nxt_http_routes_t *routes;
nxt_event_engine_t *engine;
@@ -1363,9 +1468,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
#if (NXT_TLS)
static nxt_str_t certificate_path = nxt_string("/tls/certificate");
static nxt_str_t conf_commands_path = nxt_string("/tls/conf_commands");
+ static nxt_str_t conf_cache_path = nxt_string("/tls/session/cache_size");
+ static nxt_str_t conf_timeout_path = nxt_string("/tls/session/timeout");
+ static nxt_str_t conf_tickets = nxt_string("/tls/session/tickets");
#endif
static nxt_str_t static_path = nxt_string("/settings/http/static");
static nxt_str_t websocket_path = nxt_string("/settings/http/websocket");
+ static nxt_str_t client_ip_path = nxt_string("/client_ip");
conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL);
if (conf == NULL) {
@@ -1604,7 +1713,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
app_joint->free_app_work.task = &engine->task;
app_joint->free_app_work.obj = app_joint;
- port = nxt_port_new(task, (nxt_port_id_t) -1, nxt_pid,
+ port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid,
NXT_PROCESS_APP);
if (nxt_slow_path(port == NULL)) {
return NXT_ERROR;
@@ -1737,11 +1846,40 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
t->length = nxt_strlen(t->start);
}
+ client_ip_conf = nxt_conf_get_path(listener, &client_ip_path);
+ ret = nxt_router_conf_process_client_ip(task, tmcf, skcf,
+ client_ip_conf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
#if (NXT_TLS)
certificate = nxt_conf_get_path(listener, &certificate_path);
if (certificate != NULL) {
- conf_cmds = nxt_conf_get_path(listener, &conf_commands_path);
+ tls_init = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_tls_init_t));
+ if (nxt_slow_path(tls_init == NULL)) {
+ return NXT_ERROR;
+ }
+
+ tls_init->cache_size = 0;
+ tls_init->timeout = 300;
+
+ value = nxt_conf_get_path(listener, &conf_cache_path);
+ if (value != NULL) {
+ tls_init->cache_size = nxt_conf_get_number(value);
+ }
+
+ value = nxt_conf_get_path(listener, &conf_timeout_path);
+ if (value != NULL) {
+ tls_init->timeout = nxt_conf_get_number(value);
+ }
+
+ tls_init->conf_cmds = nxt_conf_get_path(listener,
+ &conf_commands_path);
+
+ tls_init->tickets_conf = nxt_conf_get_path(listener,
+ &conf_tickets);
if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) {
n = nxt_conf_array_elements_count(certificate);
@@ -1752,7 +1890,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_assert(value != NULL);
ret = nxt_router_conf_tls_insert(tmcf, value, skcf,
- conf_cmds);
+ tls_init, i == 0);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -1761,7 +1899,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
} else {
/* NXT_CONF_STRING */
ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf,
- conf_cmds);
+ tls_init, 1);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -1856,7 +1994,7 @@ fail:
static nxt_int_t
nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *value, nxt_socket_conf_t *skcf,
- nxt_conf_value_t *conf_cmds)
+ nxt_tls_init_t *tls_init, nxt_bool_t last)
{
nxt_router_tlssock_t *tls;
@@ -1865,9 +2003,10 @@ nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
return NXT_ERROR;
}
+ tls->tls_init = tls_init;
tls->socket_conf = skcf;
- tls->conf_cmds = conf_cmds;
tls->temp_conf = tmcf;
+ tls->last = last;
nxt_conf_get_string(value, &tls->name);
nxt_queue_insert_tail(&tmcf->tls, &tls->link);
@@ -1884,7 +2023,7 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
{
uint32_t next, i;
nxt_mp_t *mp;
- nxt_str_t *type, extension, str;
+ nxt_str_t *type, exten, str;
nxt_int_t ret;
nxt_uint_t exts;
nxt_conf_value_t *mtypes_conf, *ext_conf, *value;
@@ -1922,12 +2061,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) {
nxt_conf_get_string(ext_conf, &str);
- if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) {
+ if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) {
return NXT_ERROR;
}
ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash,
- &extension, type);
+ &exten, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
@@ -1942,12 +2081,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
nxt_conf_get_string(value, &str);
- if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) {
+ if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) {
return NXT_ERROR;
}
ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash,
- &extension, type);
+ &exten, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
@@ -1959,6 +2098,79 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
}
+static nxt_int_t
+nxt_router_conf_process_client_ip(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+ nxt_socket_conf_t *skcf, nxt_conf_value_t *conf)
+{
+ char c;
+ size_t i;
+ nxt_mp_t *mp;
+ uint32_t hash;
+ nxt_str_t header;
+ nxt_conf_value_t *source_conf, *header_conf, *recursive_conf;
+ nxt_http_client_ip_t *client_ip;
+ nxt_http_route_addr_rule_t *source;
+
+ static nxt_str_t header_path = nxt_string("/header");
+ static nxt_str_t source_path = nxt_string("/source");
+ static nxt_str_t recursive_path = nxt_string("/recursive");
+
+ if (conf == NULL) {
+ skcf->client_ip = NULL;
+
+ return NXT_OK;
+ }
+
+ mp = tmcf->router_conf->mem_pool;
+
+ source_conf = nxt_conf_get_path(conf, &source_path);
+ header_conf = nxt_conf_get_path(conf, &header_path);
+ recursive_conf = nxt_conf_get_path(conf, &recursive_path);
+
+ if (source_conf == NULL || header_conf == NULL) {
+ return NXT_ERROR;
+ }
+
+ client_ip = nxt_mp_zget(mp, sizeof(nxt_http_client_ip_t));
+ if (nxt_slow_path(client_ip == NULL)) {
+ return NXT_ERROR;
+ }
+
+ source = nxt_http_route_addr_rule_create(task, mp, source_conf);
+ if (nxt_slow_path(source == NULL)) {
+ return NXT_ERROR;
+ }
+
+ client_ip->source = source;
+
+ nxt_conf_get_string(header_conf, &header);
+
+ if (recursive_conf != NULL) {
+ client_ip->recursive = nxt_conf_get_boolean(recursive_conf);
+ }
+
+ client_ip->header = nxt_str_dup(mp, NULL, &header);
+ if (nxt_slow_path(client_ip->header == NULL)) {
+ return NXT_ERROR;
+ }
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+
+ for (i = 0; i < client_ip->header->length; i++) {
+ c = client_ip->header->start[i];
+ hash = nxt_http_field_hash_char(hash, nxt_lowcase(c));
+ }
+
+ hash = nxt_http_field_hash_end(hash) & 0xFFFF;
+
+ client_ip->header_hash = hash;
+
+ skcf->client_ip = client_ip;
+
+ return NXT_OK;
+}
+
+
static nxt_app_t *
nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name)
{
@@ -2135,21 +2347,46 @@ nxt_router_apps_hash_use(nxt_task_t *task, nxt_router_conf_t *rtcf, int i)
}
+typedef struct {
+ nxt_app_t *app;
+ nxt_int_t target;
+} nxt_http_app_conf_t;
+
nxt_int_t
-nxt_router_listener_application(nxt_router_conf_t *rtcf, nxt_str_t *name,
- nxt_http_action_t *action)
+nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name,
+ nxt_str_t *target, nxt_http_action_t *action)
{
- nxt_app_t *app;
+ nxt_app_t *app;
+ nxt_str_t *targets;
+ nxt_uint_t i;
+ nxt_http_app_conf_t *conf;
app = nxt_router_apps_hash_get(rtcf, name);
-
if (app == NULL) {
return NXT_DECLINED;
}
- action->u.app.application = app;
+ conf = nxt_mp_get(rtcf->mem_pool, sizeof(nxt_http_app_conf_t));
+ if (nxt_slow_path(conf == NULL)) {
+ return NXT_ERROR;
+ }
+
action->handler = nxt_http_application_handler;
+ action->u.conf = conf;
+
+ conf->app = app;
+
+ if (target != NULL && target->length != 0) {
+ targets = app->targets;
+
+ for (i = 0; !nxt_strstr_eq(target, &targets[i]); i++);
+
+ conf->target = i;
+
+ } else {
+ conf->target = 0;
+ }
return NXT_OK;
}
@@ -2297,7 +2534,7 @@ nxt_router_listen_socket_rpc_create(nxt_task_t *task,
goto fail;
}
- b->completion_handler = nxt_router_dummy_buf_completion;
+ b->completion_handler = nxt_buf_dummy_completion;
b->mem.free = nxt_cpymem(b->mem.free, skcf->listen->sockaddr, size);
@@ -2466,6 +2703,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
tlscf = tls->socket_conf->tls;
}
+ tls->tls_init->conf = tlscf;
+
bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t));
if (nxt_slow_path(bundle == NULL)) {
goto fail;
@@ -2479,8 +2718,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
bundle->next = tlscf->bundle;
tlscf->bundle = bundle;
- ret = task->thread->runtime->tls->server_init(task, tlscf, mp,
- tls->conf_cmds, tls->last);
+ ret = task->thread->runtime->tls->server_init(task, mp, tls->tls_init,
+ tls->last);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -2526,7 +2765,7 @@ nxt_router_app_rpc_create(nxt_task_t *task,
goto fail;
}
- b->completion_handler = nxt_router_dummy_buf_completion;
+ b->completion_handler = nxt_buf_dummy_completion;
nxt_buf_cpystr(b, &app->name);
*b->mem.free++ = '\0';
@@ -3555,7 +3794,7 @@ nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
goto fail;
}
- b->completion_handler = nxt_router_dummy_buf_completion;
+ b->completion_handler = nxt_buf_dummy_completion;
nxt_buf_cpystr(b, &access_log->path);
*b->mem.free++ = '\0';
@@ -4183,11 +4422,16 @@ static void
nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
- nxt_app_t *app;
- nxt_port_t *port;
- nxt_app_joint_t *app_joint;
+ nxt_app_t *app;
+ nxt_bool_t start_process;
+ nxt_port_t *port;
+ nxt_app_joint_t *app_joint;
+ nxt_app_joint_rpc_t *app_joint_rpc;
+
+ nxt_assert(data != NULL);
- app_joint = data;
+ app_joint_rpc = data;
+ app_joint = app_joint_rpc->app_joint;
port = msg->u.new_port;
nxt_assert(app_joint != NULL);
@@ -4207,14 +4451,37 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
return;
}
- port->app = app;
- port->main_app_port = port;
-
nxt_thread_mutex_lock(&app->mutex);
nxt_assert(app->pending_processes != 0);
app->pending_processes--;
+
+ if (nxt_slow_path(app->generation != app_joint_rpc->generation)) {
+ nxt_debug(task, "new port ready for restarted app, send QUIT");
+
+ start_process = !task->thread->engine->shutdown
+ && nxt_router_app_can_start(app)
+ && nxt_router_app_need_start(app);
+
+ if (start_process) {
+ app->pending_processes++;
+ }
+
+ nxt_thread_mutex_unlock(&app->mutex);
+
+ nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL);
+
+ if (start_process) {
+ nxt_router_start_app_process(task, app);
+ }
+
+ return;
+ }
+
+ port->app = app;
+ port->main_app_port = port;
+
app->processes++;
nxt_port_hash_add(&app->port_hash, port);
app->port_hash_count++;
@@ -4268,12 +4535,16 @@ static void
nxt_router_app_port_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
- nxt_app_t *app;
- nxt_app_joint_t *app_joint;
- nxt_queue_link_t *link;
- nxt_http_request_t *r;
+ nxt_app_t *app;
+ nxt_app_joint_t *app_joint;
+ nxt_queue_link_t *link;
+ nxt_http_request_t *r;
+ nxt_app_joint_rpc_t *app_joint_rpc;
+
+ nxt_assert(data != NULL);
- app_joint = data;
+ app_joint_rpc = data;
+ app_joint = app_joint_rpc->app_joint;
nxt_assert(app_joint != NULL);
@@ -4440,7 +4711,7 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port,
port->pid, port->id,
(int) inc_use, (int) got_response);
- if (port == app->shared_port) {
+ if (port->id == NXT_SHARED_PORT_ID) {
nxt_thread_mutex_lock(&app->mutex);
app->active_requests -= got_response + dec_requests;
@@ -4810,6 +5081,8 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data)
app->shared_port->app = NULL;
nxt_port_close(task, app->shared_port);
nxt_port_use(task, app->shared_port, -1);
+
+ app->shared_port = NULL;
}
nxt_thread_mutex_destroy(&app->mutex);
@@ -4876,13 +5149,17 @@ nxt_router_app_port_get(nxt_task_t *task, nxt_app_t *app,
void
nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
- nxt_app_t *app)
+ nxt_http_action_t *action)
{
nxt_event_engine_t *engine;
+ nxt_http_app_conf_t *conf;
nxt_request_rpc_data_t *req_rpc_data;
+ conf = action->u.conf;
engine = task->thread->engine;
+ r->app_target = conf->target;
+
req_rpc_data = nxt_port_rpc_register_handler_ex(task, engine->port,
nxt_router_response_ready_handler,
nxt_router_response_error_handler,
@@ -4913,11 +5190,11 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
r->err_work.obj = r;
req_rpc_data->stream = nxt_port_rpc_ex_stream(req_rpc_data);
- req_rpc_data->app = app;
+ req_rpc_data->app = conf->app;
req_rpc_data->msg_info.body_fd = -1;
req_rpc_data->rpc_cancel = 1;
- nxt_router_app_use(task, app, 1);
+ nxt_router_app_use(task, conf->app, 1);
req_rpc_data->request = r;
r->req_rpc_data = req_rpc_data;
@@ -4926,7 +5203,7 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
r->last->completion_handler = nxt_router_http_request_done;
}
- nxt_router_app_port_get(task, app, req_rpc_data);
+ nxt_router_app_port_get(task, conf->app, req_rpc_data);
nxt_router_app_prepare_request(task, req_rpc_data);
}
@@ -4968,12 +5245,6 @@ nxt_router_http_request_done(nxt_task_t *task, void *obj, void *data)
static void
-nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, void *data)
-{
-}
-
-
-static void
nxt_router_app_prepare_request(nxt_task_t *task,
nxt_request_rpc_data_t *req_rpc_data)
{
diff --git a/src/nxt_router.h b/src/nxt_router.h
index b1ccdf51..fc068b53 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -18,6 +18,7 @@ typedef struct nxt_http_request_s nxt_http_request_t;
typedef struct nxt_http_action_s nxt_http_action_t;
typedef struct nxt_http_routes_s nxt_http_routes_t;
+typedef struct nxt_http_client_ip_s nxt_http_client_ip_t;
typedef struct nxt_upstream_s nxt_upstream_t;
typedef struct nxt_upstreams_s nxt_upstreams_t;
typedef struct nxt_router_access_log_s nxt_router_access_log_t;
@@ -125,6 +126,8 @@ struct nxt_app_s {
uint32_t max_pending_processes;
uint32_t max_requests;
+ uint32_t generation;
+
nxt_msec_t timeout;
nxt_msec_t idle_timeout;
@@ -193,6 +196,8 @@ typedef struct {
uint8_t discard_unsafe_fields; /* 1 bit */
+ nxt_http_client_ip_t *client_ip;
+
#if (NXT_TLS)
nxt_tls_conf_t *tls;
#endif
@@ -223,10 +228,10 @@ struct nxt_router_access_log_s {
void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
- nxt_app_t *app);
+ nxt_http_action_t *action);
void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port);
-nxt_int_t nxt_router_listener_application(nxt_router_conf_t *rtcf,
- nxt_str_t *name, nxt_http_action_t *action);
+nxt_int_t nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name,
+ nxt_str_t *target, nxt_http_action_t *action);
void nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev,
nxt_socket_conf_joint_t *joint);
void nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint);
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c
index af696a6b..730428e4 100644
--- a/src/nxt_sockaddr.c
+++ b/src/nxt_sockaddr.c
@@ -525,9 +525,9 @@ nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
return buf;
}
- zero_start = 8;
+ zero_start = 16;
zero_groups = 0;
- last_zero_start = 8;
+ last_zero_start = 16;
last_zero_groups = 0;
for (i = 0; i < 16; i += 2) {
@@ -605,10 +605,35 @@ nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr)
{
nxt_sockaddr_t *sa;
+ sa = nxt_sockaddr_parse_optport(mp, addr);
+
+ if (sa != NULL
+ && sa->u.sockaddr.sa_family != AF_UNIX
+ && nxt_sockaddr_port_number(sa) == 0)
+ {
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "The address \"%V\" must specify a port.", addr);
+ return NULL;
+ }
+
+ return sa;
+}
+
+
+nxt_sockaddr_t *
+nxt_sockaddr_parse_optport(nxt_mp_t *mp, nxt_str_t *addr)
+{
+ nxt_sockaddr_t *sa;
+
+ if (addr->length == 0) {
+ nxt_thread_log_error(NXT_LOG_ERR, "socket address cannot be empty");
+ return NULL;
+ }
+
if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) {
sa = nxt_sockaddr_unix_parse(mp, addr);
- } else if (addr->length != 0 && addr->start[0] == '[') {
+ } else if (addr->start[0] == '[' || nxt_inet6_probe(addr)) {
sa = nxt_sockaddr_inet6_parse(mp, addr);
} else {
@@ -703,44 +728,60 @@ nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr)
nxt_int_t ret, port;
nxt_sockaddr_t *sa;
- length = addr->length - 1;
- start = addr->start + 1;
+ if (addr->start[0] == '[') {
+ length = addr->length - 1;
+ start = addr->start + 1;
- end = nxt_memchr(start, ']', length);
-
- if (end != NULL) {
- sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
- NXT_INET6_ADDR_STR_LEN);
- if (nxt_slow_path(sa == NULL)) {
+ end = nxt_memchr(start, ']', length);
+ if (nxt_slow_path(end == NULL)) {
return NULL;
}
- ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start);
+ p = end + 1;
+
+ } else {
+ length = addr->length;
+ start = addr->start;
+ end = addr->start + addr->length;
+ p = NULL;
+ }
- if (nxt_fast_path(ret == NXT_OK)) {
- p = end + 1;
- length = (start + length) - p;
+ port = 0;
- if (length > 2 && *p == ':') {
- port = nxt_int_parse(p + 1, length - 1);
+ if (p != NULL) {
+ length = (start + length) - p;
- if (port > 0 && port < 65536) {
- sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port);
- sa->u.sockaddr_in6.sin6_family = AF_INET6;
+ if (length < 2 || *p != ':') {
+ nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"",
+ addr);
+ return NULL;
+ }
- return sa;
- }
- }
+ port = nxt_int_parse(p + 1, length - 1);
+ if (port < 1 || port > 65535) {
nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
-
return NULL;
}
}
- nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr);
+ sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6),
+ NXT_INET6_ADDR_STR_LEN);
+ if (nxt_slow_path(sa == NULL)) {
+ return NULL;
+ }
- return NULL;
+ ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"",
+ addr);
+ return NULL;
+ }
+
+ sa->u.sockaddr_in6.sin6_family = AF_INET6;
+ sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port);
+
+ return sa;
#else /* !(NXT_INET6) */
@@ -763,41 +804,48 @@ nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr)
p = nxt_memchr(addr->start, ':', addr->length);
- if (nxt_fast_path(p != NULL)) {
- inaddr = INADDR_ANY;
+ if (p == NULL) {
+ length = addr->length;
+
+ } else {
length = p - addr->start;
+ }
- if (length != 1 || addr->start[0] != '*') {
- inaddr = nxt_inet_addr(addr->start, length);
+ inaddr = INADDR_ANY;
- if (nxt_slow_path(inaddr == INADDR_NONE)) {
- nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"",
- addr);
- return NULL;
- }
+ if (length != 1 || addr->start[0] != '*') {
+ inaddr = nxt_inet_addr(addr->start, length);
+ if (nxt_slow_path(inaddr == INADDR_NONE)) {
+ nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", addr);
+ return NULL;
}
+ }
+
+ port = 0;
+ if (p != NULL) {
p++;
length = (addr->start + addr->length) - p;
- port = nxt_int_parse(p, length);
-
- if (port > 0 && port < 65536) {
- sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
- NXT_INET_ADDR_STR_LEN);
- if (nxt_fast_path(sa != NULL)) {
- sa->u.sockaddr_in.sin_family = AF_INET;
- sa->u.sockaddr_in.sin_port = htons((in_port_t) port);
- sa->u.sockaddr_in.sin_addr.s_addr = inaddr;
- }
+ port = nxt_int_parse(p, length);
- return sa;
+ if (port < 1 || port > 65535) {
+ nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
+ return NULL;
}
}
- nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr);
+ sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in),
+ NXT_INET_ADDR_STR_LEN);
+ if (nxt_slow_path(sa == NULL)) {
+ return NULL;
+ }
- return NULL;
+ sa->u.sockaddr_in.sin_family = AF_INET;
+ sa->u.sockaddr_in.sin_addr.s_addr = inaddr;
+ sa->u.sockaddr_in.sin_port = htons((in_port_t) port);
+
+ return sa;
}
@@ -1320,3 +1368,19 @@ nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length)
}
#endif
+
+
+nxt_bool_t
+nxt_inet6_probe(nxt_str_t *str)
+{
+ u_char *colon, *end;
+
+ colon = nxt_memchr(str->start, ':', str->length);
+
+ if (colon != NULL) {
+ end = str->start + str->length;
+ colon = nxt_memchr(colon + 1, ':', end - (colon + 1));
+ }
+
+ return (colon != NULL);
+}
diff --git a/src/nxt_sockaddr.h b/src/nxt_sockaddr.h
index aa4da5d2..a8f1b393 100644
--- a/src/nxt_sockaddr.h
+++ b/src/nxt_sockaddr.h
@@ -91,12 +91,15 @@ NXT_EXPORT nxt_bool_t nxt_sockaddr_cmp(nxt_sockaddr_t *sa1,
NXT_EXPORT size_t nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf,
u_char *end, nxt_bool_t port);
NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr);
+NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse_optport(nxt_mp_t *mp,
+ nxt_str_t *addr);
NXT_EXPORT void nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs);
NXT_EXPORT in_addr_t nxt_inet_addr(u_char *buf, size_t len);
#if (NXT_INET6)
NXT_EXPORT nxt_int_t nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf,
size_t len);
#endif
+NXT_EXPORT nxt_bool_t nxt_inet6_probe(nxt_str_t *addr);
#define NXT_INET_ADDR_STR_LEN nxt_length("255.255.255.255:65535")
diff --git a/src/nxt_tls.h b/src/nxt_tls.h
index 63c49ee4..eeb4e7ba 100644
--- a/src/nxt_tls.h
+++ b/src/nxt_tls.h
@@ -28,14 +28,16 @@
typedef struct nxt_tls_conf_s nxt_tls_conf_t;
typedef struct nxt_tls_bundle_conf_s nxt_tls_bundle_conf_t;
+typedef struct nxt_tls_init_s nxt_tls_init_t;
+typedef struct nxt_tls_ticket_s nxt_tls_ticket_t;
+typedef struct nxt_tls_tickets_s nxt_tls_tickets_t;
typedef struct {
nxt_int_t (*library_init)(nxt_task_t *task);
void (*library_free)(nxt_task_t *task);
- nxt_int_t (*server_init)(nxt_task_t *task,
- nxt_tls_conf_t *conf, nxt_mp_t *mp,
- nxt_conf_value_t *conf_cmds,
+ nxt_int_t (*server_init)(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_tls_init_t *tls_init,
nxt_bool_t last);
void (*server_free)(nxt_task_t *task,
nxt_tls_conf_t *conf);
@@ -63,6 +65,8 @@ struct nxt_tls_conf_s {
nxt_tls_bundle_conf_t *bundle;
nxt_lvlhsh_t bundle_hash;
+ nxt_tls_tickets_t *tickets;
+
void (*conn_init)(nxt_task_t *task,
nxt_tls_conf_t *conf, nxt_conn_t *c);
@@ -78,12 +82,38 @@ struct nxt_tls_conf_s {
};
+struct nxt_tls_init_s {
+ size_t cache_size;
+ nxt_time_t timeout;
+ nxt_conf_value_t *conf_cmds;
+ nxt_conf_value_t *tickets_conf;
+
+ nxt_tls_conf_t *conf;
+};
+
+
+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_upstream.c b/src/nxt_upstream.c
index 9f81b286..de9b1d49 100644
--- a/src/nxt_upstream.c
+++ b/src/nxt_upstream.c
@@ -78,6 +78,10 @@ nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name,
uint32_t i, n;
nxt_upstream_t *upstream;
+ if (upstreams == NULL) {
+ return NXT_DECLINED;
+ }
+
upstream = &upstreams->upstream[0];
n = upstreams->items;
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
index 588a147a..abb04194 100644
--- a/src/python/nxt_python.c
+++ b/src/python/nxt_python.c
@@ -264,7 +264,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
goto fail;
}
- rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data);
+ rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
@@ -364,13 +364,13 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
- callable, module);
+ callable, module_name);
goto fail;
}
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
- callable, module);
+ callable, module_name);
goto fail;
}
@@ -504,7 +504,7 @@ nxt_python_init_threads(nxt_python_app_conf_t *c)
for (i = 0; i < c->threads - 1; i++) {
ti = &nxt_py_threads[i];
- res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data);
+ res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
if (nxt_slow_path(res != NXT_UNIT_OK)) {
return NXT_UNIT_ERROR;
}
diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h
index a5c1d9a6..e4eac9dc 100644
--- a/src/python/nxt_python.h
+++ b/src/python/nxt_python.h
@@ -60,7 +60,7 @@ typedef struct {
typedef struct {
- int (*ctx_data_alloc)(void **pdata);
+ int (*ctx_data_alloc)(void **pdata, int main);
void (*ctx_data_free)(void *data);
int (*startup)(void *data);
int (*run)(nxt_unit_ctx_t *ctx);
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index 1d220678..26003805 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -17,7 +17,7 @@
static PyObject *nxt_python_asgi_get_func(PyObject *obj);
-static int nxt_python_asgi_ctx_data_alloc(void **pdata);
+static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main);
static void nxt_python_asgi_ctx_data_free(void *data);
static int nxt_python_asgi_startup(void *data);
static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
@@ -194,10 +194,11 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
static int
-nxt_python_asgi_ctx_data_alloc(void **pdata)
+nxt_python_asgi_ctx_data_alloc(void **pdata, int main)
{
uint32_t i;
- PyObject *asyncio, *loop, *new_event_loop, *obj;
+ PyObject *asyncio, *loop, *event_loop, *obj;
+ const char *event_loop_func;
nxt_py_asgi_ctx_data_t *ctx_data;
ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
@@ -232,23 +233,28 @@ nxt_python_asgi_ctx_data_alloc(void **pdata)
goto fail;
}
- new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
- "new_event_loop");
- if (nxt_slow_path(new_event_loop == NULL)) {
+ event_loop_func = main ? "get_event_loop" : "new_event_loop";
+
+ event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
+ event_loop_func);
+ if (nxt_slow_path(event_loop == NULL)) {
nxt_unit_alert(NULL,
- "Python failed to get 'new_event_loop' from module 'asyncio'");
+ "Python failed to get '%s' from module 'asyncio'",
+ event_loop_func);
goto fail;
}
- if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) {
+ if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) {
nxt_unit_alert(NULL,
- "'asyncio.new_event_loop' is not a callable object");
+ "'asyncio.%s' is not a callable object",
+ event_loop_func);
goto fail;
}
- loop = PyObject_CallObject(new_event_loop, NULL);
+ loop = PyObject_CallObject(event_loop, NULL);
if (nxt_slow_path(loop == NULL)) {
- nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'");
+ nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'",
+ event_loop_func);
goto fail;
}
diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c
index d88c4b00..c4a77d53 100644
--- a/src/python/nxt_python_asgi_http.c
+++ b/src/python/nxt_python_asgi_http.c
@@ -23,10 +23,11 @@ typedef struct {
PyObject *send_future;
uint64_t content_length;
uint64_t bytes_sent;
- int complete;
- int closed;
PyObject *send_body;
Py_ssize_t send_body_off;
+ uint8_t complete;
+ uint8_t closed;
+ uint8_t empty_body_received;
} nxt_py_asgi_http_t;
@@ -37,6 +38,9 @@ static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http,
PyObject *dict);
static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http,
PyObject *dict);
+static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http);
+static void nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http,
+ PyObject *future, PyObject *msg);
static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future);
@@ -94,10 +98,11 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req)
http->send_future = NULL;
http->content_length = -1;
http->bytes_sent = 0;
- http->complete = 0;
- http->closed = 0;
http->send_body = NULL;
http->send_body_off = 0;
+ http->complete = 0;
+ http->closed = 0;
+ http->empty_body_received = 0;
}
return (PyObject *) http;
@@ -117,7 +122,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none)
nxt_unit_req_debug(req, "asgi_http_receive");
- if (nxt_slow_path(http->closed || nxt_unit_response_is_sent(req))) {
+ if (nxt_slow_path(http->closed || http->complete )) {
msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str);
} else {
@@ -171,6 +176,14 @@ nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http)
size = nxt_py_asgi_http_body_buf_size;
}
+ if (size == 0) {
+ if (http->empty_body_received) {
+ Py_RETURN_NONE;
+ }
+
+ http->empty_body_received = 1;
+ }
+
if (size > 0) {
body = PyBytes_FromStringAndSize(NULL, size);
if (nxt_slow_path(body == NULL)) {
@@ -442,6 +455,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
if (more_body == NULL || more_body == Py_False) {
http->complete = 1;
+
+ nxt_py_asgi_http_emit_disconnect(http);
}
Py_INCREF(http);
@@ -449,10 +464,67 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
}
+static void
+nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http)
+{
+ PyObject *msg, *future;
+
+ if (http->receive_future == NULL) {
+ return;
+ }
+
+ msg = nxt_py_asgi_new_msg(http->req, nxt_py_http_disconnect_str);
+ if (nxt_slow_path(msg == NULL)) {
+ return;
+ }
+
+ if (msg == Py_None) {
+ Py_DECREF(msg);
+ return;
+ }
+
+ future = http->receive_future;
+ http->receive_future = NULL;
+
+ nxt_py_asgi_http_set_result(http, future, msg);
+
+ Py_DECREF(msg);
+}
+
+
+static void
+nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, PyObject *future,
+ PyObject *msg)
+{
+ PyObject *res;
+
+ res = PyObject_CallMethodObjArgs(future, nxt_py_done_str, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_req_alert(http->req, "'done' call failed");
+ nxt_python_print_exception();
+ }
+
+ if (nxt_fast_path(res == Py_False)) {
+ res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg,
+ NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_req_alert(http->req, "'set_result' call failed");
+ nxt_python_print_exception();
+ }
+
+ } else {
+ res = NULL;
+ }
+
+ Py_XDECREF(res);
+ Py_DECREF(future);
+}
+
+
void
nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req)
{
- PyObject *msg, *future, *res;
+ PyObject *msg, *future;
nxt_py_asgi_http_t *http;
http = req->data;
@@ -476,14 +548,7 @@ nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req)
future = http->receive_future;
http->receive_future = NULL;
- res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL);
- if (nxt_slow_path(res == NULL)) {
- nxt_unit_req_alert(req, "'set_result' call failed");
- nxt_python_print_exception();
- }
-
- Py_XDECREF(res);
- Py_DECREF(future);
+ nxt_py_asgi_http_set_result(http, future, msg);
Py_DECREF(msg);
}
@@ -527,15 +592,7 @@ nxt_py_asgi_http_drain(nxt_queue_link_t *lnk)
future = http->send_future;
http->send_future = NULL;
- res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, Py_None,
- NULL);
- if (nxt_slow_path(res == NULL)) {
- nxt_unit_req_alert(http->req, "'set_result' call failed");
- nxt_python_print_exception();
- }
-
- Py_XDECREF(res);
- Py_DECREF(future);
+ nxt_py_asgi_http_set_result(http, future, Py_None);
return NXT_UNIT_OK;
@@ -573,7 +630,6 @@ fail:
void
nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req)
{
- PyObject *msg, *future, *res;
nxt_py_asgi_http_t *http;
http = req->data;
@@ -582,33 +638,7 @@ nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req)
http->closed = 1;
- if (http->receive_future == NULL) {
- return;
- }
-
- msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str);
- if (nxt_slow_path(msg == NULL)) {
- return;
- }
-
- if (msg == Py_None) {
- Py_DECREF(msg);
- return;
- }
-
- future = http->receive_future;
- http->receive_future = NULL;
-
- res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL);
- if (nxt_slow_path(res == NULL)) {
- nxt_unit_req_alert(req, "'set_result' call failed");
- nxt_python_print_exception();
- }
-
- Py_XDECREF(res);
- Py_DECREF(future);
-
- Py_DECREF(msg);
+ nxt_py_asgi_http_emit_disconnect(http);
}
diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c
index b80d10fa..87dcfaa2 100644
--- a/src/python/nxt_python_wsgi.c
+++ b/src/python/nxt_python_wsgi.c
@@ -51,7 +51,7 @@ typedef struct {
} nxt_python_ctx_t;
-static int nxt_python_wsgi_ctx_data_alloc(void **pdata);
+static int nxt_python_wsgi_ctx_data_alloc(void **pdata, int main);
static void nxt_python_wsgi_ctx_data_free(void *data);
static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
static void nxt_python_wsgi_done(void);
@@ -210,7 +210,7 @@ fail:
static int
-nxt_python_wsgi_ctx_data_alloc(void **pdata)
+nxt_python_wsgi_ctx_data_alloc(void **pdata, int main)
{
nxt_python_ctx_t *pctx;
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index ca14af5b..522869b5 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -29,6 +29,11 @@ typedef struct {
static nxt_int_t nxt_ruby_start(nxt_task_t *task,
nxt_process_data_t *data);
static VALUE nxt_ruby_init_basic(VALUE arg);
+
+static VALUE nxt_ruby_hook_procs_load(VALUE path);
+static VALUE nxt_ruby_hook_register(VALUE arg);
+static VALUE nxt_ruby_hook_call(VALUE name);
+
static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
static VALUE nxt_ruby_require_rubygems(VALUE arg);
@@ -78,6 +83,7 @@ static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG,
};
+static VALUE nxt_ruby_hook_procs;
static VALUE nxt_ruby_rackup;
static VALUE nxt_ruby_call;
@@ -115,6 +121,10 @@ static VALUE nxt_rb_server_addr_str;
static VALUE nxt_rb_server_name_str;
static VALUE nxt_rb_server_port_str;
static VALUE nxt_rb_server_protocol_str;
+static VALUE nxt_rb_on_worker_boot;
+static VALUE nxt_rb_on_worker_shutdown;
+static VALUE nxt_rb_on_thread_boot;
+static VALUE nxt_rb_on_thread_shutdown;
static nxt_ruby_string_t nxt_rb_strings[] = {
{ nxt_string("80"), &nxt_rb_80_str },
@@ -132,6 +142,10 @@ static nxt_ruby_string_t nxt_rb_strings[] = {
{ nxt_string("SERVER_NAME"), &nxt_rb_server_name_str },
{ nxt_string("SERVER_PORT"), &nxt_rb_server_port_str },
{ nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str },
+ { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot },
+ { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown },
+ { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot },
+ { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown },
{ nxt_null_string, NULL },
};
@@ -183,11 +197,70 @@ nxt_ruby_done_strings(void)
}
+static VALUE
+nxt_ruby_hook_procs_load(VALUE path)
+{
+ VALUE module, file, file_obj;
+
+ module = rb_define_module("Unit");
+
+ nxt_ruby_hook_procs = rb_hash_new();
+
+ rb_gc_register_address(&nxt_ruby_hook_procs);
+
+ rb_define_module_function(module, "on_worker_boot",
+ &nxt_ruby_hook_register, 0);
+ rb_define_module_function(module, "on_worker_shutdown",
+ &nxt_ruby_hook_register, 0);
+ rb_define_module_function(module, "on_thread_boot",
+ &nxt_ruby_hook_register, 0);
+ rb_define_module_function(module, "on_thread_shutdown",
+ &nxt_ruby_hook_register, 0);
+
+ file = rb_const_get(rb_cObject, rb_intern("File"));
+ file_obj = rb_funcall(file, rb_intern("read"), 1, path);
+
+ return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path,
+ INT2NUM(1));
+}
+
+
+static VALUE
+nxt_ruby_hook_register(VALUE arg)
+{
+ VALUE kernel, callee, callee_str;
+
+ rb_need_block();
+
+ kernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
+ callee = rb_funcall(kernel, rb_intern("__callee__"), 0);
+ callee_str = rb_funcall(callee, rb_intern("to_s"), 0);
+
+ rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc());
+
+ return Qnil;
+}
+
+
+static VALUE
+nxt_ruby_hook_call(VALUE name)
+{
+ VALUE proc;
+
+ proc = rb_hash_lookup(nxt_ruby_hook_procs, name);
+ if (proc == Qnil) {
+ return Qnil;
+ }
+
+ return rb_funcall(proc, rb_intern("call"), 0);
+}
+
+
static nxt_int_t
nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
{
int state, rc;
- VALUE res;
+ VALUE res, path;
nxt_ruby_ctx_t ruby_ctx;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t ruby_unit_init;
@@ -231,6 +304,29 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
}
nxt_ruby_call = Qnil;
+ nxt_ruby_hook_procs = Qnil;
+
+ if (c->hooks.start != NULL) {
+ path = rb_str_new((const char *) c->hooks.start,
+ (long) c->hooks.length);
+
+ rb_protect(nxt_ruby_hook_procs_load, path, &state);
+ rb_str_free(path);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
+ "Failed to setup hooks");
+ return NXT_ERROR;
+ }
+ }
+
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_worker_boot()");
+ return NXT_ERROR;
+ }
+ }
nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init);
if (nxt_slow_path(nxt_ruby_rackup == Qnil)) {
@@ -274,11 +370,35 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
goto fail;
}
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_thread_boot()");
+ }
+ }
+
rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx,
nxt_ruby_ubf, unit_ctx);
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_thread_shutdown()");
+ }
+ }
+
nxt_ruby_join_threads(unit_ctx, c);
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_worker_shutdown()");
+ }
+ }
+
nxt_unit_done(unit_ctx);
nxt_ruby_ctx_done(&ruby_ctx);
@@ -1069,14 +1189,18 @@ nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level,
return;
}
+ eclass = rb_class_name(rb_class_of(err));
+
+ msg = rb_funcall(err, rb_intern("message"), 0);
ary = rb_funcall(err, rb_intern("backtrace"), 0);
- if (nxt_slow_path(RARRAY_LEN(ary) == 0)) {
+
+ if (RARRAY_LEN(ary) == 0) {
+ nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg),
+ RSTRING_PTR(eclass));
+
return;
}
- eclass = rb_class_name(rb_class_of(err));
- msg = rb_funcall(err, rb_intern("message"), 0);
-
nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)",
RSTRING_PTR(RARRAY_PTR(ary)[0]),
RSTRING_PTR(msg), RSTRING_PTR(eclass));
@@ -1116,6 +1240,10 @@ nxt_ruby_atexit(void)
rb_gc_unregister_address(&nxt_ruby_call);
}
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_gc_unregister_address(&nxt_ruby_hook_procs);
+ }
+
nxt_ruby_done_strings();
ruby_cleanup(0);
@@ -1178,6 +1306,7 @@ nxt_ruby_thread_create_gvl(void *rctx)
static VALUE
nxt_ruby_thread_func(VALUE arg)
{
+ int state;
nxt_unit_ctx_t *ctx;
nxt_ruby_ctx_t *rctx;
@@ -1190,9 +1319,25 @@ nxt_ruby_thread_func(VALUE arg)
goto fail;
}
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_thread_boot()");
+ }
+ }
+
(void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx,
nxt_ruby_ubf, ctx);
+ if (nxt_ruby_hook_procs != Qnil) {
+ rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state);
+ if (nxt_slow_path(state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ "Failed to call on_thread_shutdown()");
+ }
+ }
+
nxt_unit_done(ctx);
fail:
diff --git a/test/conftest.py b/test/conftest.py
index 5ea4e49d..4d46e2fc 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -22,8 +22,8 @@ from unit.check.node import check_node
from unit.check.regex import check_regex
from unit.check.tls import check_openssl
from unit.http import TestHTTP
-from unit.option import option
from unit.log import Log
+from unit.option import option
from unit.utils import public_dir
from unit.utils import waitforfiles
@@ -74,7 +74,7 @@ def pytest_addoption(parser):
unit_instance = {}
_processes = []
-_fds_check = {
+_fds_info = {
'main': {'fds': 0, 'skip': False},
'router': {'name': 'unit: router', 'pid': -1, 'fds': 0, 'skip': False},
'controller': {
@@ -115,6 +115,17 @@ def pytest_configure(config):
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
+def print_log_on_assert(func):
+ def inner_function(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except AssertionError as e:
+ _print_log(kwargs.get('log', None))
+ raise e
+
+ return inner_function
+
+
def pytest_generate_tests(metafunc):
cls = metafunc.cls
if (
@@ -275,9 +286,9 @@ def run(request):
]
option.skip_sanitizer = False
- _fds_check['main']['skip'] = False
- _fds_check['router']['skip'] = False
- _fds_check['controller']['skip'] = False
+ _fds_info['main']['skip'] = False
+ _fds_info['router']['skip'] = False
+ _fds_info['controller']['skip'] = False
yield
@@ -299,7 +310,7 @@ def run(request):
# clean temp_dir before the next test
if not option.restart:
- _clear_conf(unit['temp_dir'] + '/control.unit.sock', log)
+ _clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log)
for item in os.listdir(unit['temp_dir']):
if item not in [
@@ -317,53 +328,18 @@ def run(request):
):
os.remove(path)
else:
- shutil.rmtree(path)
-
- # check descriptors (wait for some time before check)
-
- def waitforfds(diff):
- for i in range(600):
- fds_diff = diff()
-
- if fds_diff <= option.fds_threshold:
- break
-
- time.sleep(0.1)
-
- return fds_diff
-
- ps = _fds_check['main']
- if not ps['skip']:
- fds_diff = waitforfds(
- lambda: _count_fds(unit_instance['pid']) - ps['fds']
- )
- ps['fds'] += fds_diff
-
- assert (
- fds_diff <= option.fds_threshold
- ), 'descriptors leak main process'
-
- else:
- ps['fds'] = _count_fds(unit_instance['pid'])
-
- for name in ['controller', 'router']:
- ps = _fds_check[name]
- ps_pid = ps['pid']
- ps['pid'] = pid_by_name(ps['name'])
-
- if not ps['skip']:
- fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds'])
- ps['fds'] += fds_diff
-
- if not option.restart:
- assert ps['pid'] == ps_pid, 'same pid %s' % name
+ for attempt in range(10):
+ try:
+ shutil.rmtree(path)
+ break
+ except OSError as err:
+ if err.errno != 16:
+ raise
+ time.sleep(1)
- assert fds_diff <= option.fds_threshold, (
- 'descriptors leak %s' % name
- )
+ # check descriptors
- else:
- ps['fds'] = _count_fds(ps['pid'])
+ _check_fds(log=log)
# print unit.log in case of error
@@ -424,6 +400,8 @@ def unit_run():
with open(temp_dir + '/unit.log', 'w') as log:
unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log)
+ Log.temp_dir = temp_dir
+
if not waitforfiles(temp_dir + '/control.unit.sock'):
_print_log()
exit('Could not start unit')
@@ -433,20 +411,19 @@ def unit_run():
unit_instance['unitd'] = unitd
option.temp_dir = temp_dir
- Log.temp_dir = temp_dir
with open(temp_dir + '/unit.pid', 'r') as f:
unit_instance['pid'] = f.read().rstrip()
_clear_conf(unit_instance['temp_dir'] + '/control.unit.sock')
- _fds_check['main']['fds'] = _count_fds(unit_instance['pid'])
+ _fds_info['main']['fds'] = _count_fds(unit_instance['pid'])
- router = _fds_check['router']
+ router = _fds_info['router']
router['pid'] = pid_by_name(router['name'])
router['fds'] = _count_fds(router['pid'])
- controller = _fds_check['controller']
+ controller = _fds_info['controller']
controller['pid'] = pid_by_name(controller['name'])
controller['fds'] = _count_fds(controller['pid'])
@@ -481,7 +458,8 @@ def unit_stop():
return 'Could not terminate unit'
-def _check_alerts(log=None):
+@print_log_on_assert
+def _check_alerts(*, log=None):
if log is None:
with Log.open(encoding='utf-8') as f:
log = f.read()
@@ -499,22 +477,18 @@ def _check_alerts(log=None):
for skip in option.skip_alerts:
alerts = [al for al in alerts if re.search(skip, al) is None]
- if alerts:
- _print_log(log)
- assert not alerts, 'alert(s)'
+ assert not alerts, 'alert(s)'
if not option.skip_sanitizer:
sanitizer_errors = re.findall('.+Sanitizer.+', log)
- if sanitizer_errors:
- _print_log(log)
- assert not sanitizer_errors, 'sanitizer error(s)'
+ assert not sanitizer_errors, 'sanitizer error(s)'
if found:
print('skipped.')
-def _print_log(data=None):
+def _print_log(log=None):
path = Log.get_path()
print('Path to unit.log:\n' + path + '\n')
@@ -523,19 +497,15 @@ def _print_log(data=None):
os.set_blocking(sys.stdout.fileno(), True)
sys.stdout.flush()
- if data is None:
+ if log is None:
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
shutil.copyfileobj(f, sys.stdout)
else:
- sys.stdout.write(data)
+ sys.stdout.write(log)
-def _clear_conf(sock, log=None):
- def check_success(resp):
- if 'success' not in resp:
- _print_log(log)
- assert 'success' in resp
-
+@print_log_on_assert
+def _clear_conf(sock, *, log=None):
resp = http.put(
url='/config',
sock_type='unix',
@@ -543,7 +513,7 @@ def _clear_conf(sock, log=None):
body=json.dumps({"listeners": {}, "applications": {}}),
)['body']
- check_success(resp)
+ assert 'success' in resp, 'clear conf'
if 'openssl' not in option.available['modules']:
return
@@ -561,7 +531,54 @@ def _clear_conf(sock, log=None):
url='/certificates/' + cert, sock_type='unix', addr=sock,
)['body']
- check_success(resp)
+ assert 'success' in resp, 'remove certificate'
+
+
+@print_log_on_assert
+def _check_fds(*, log=None):
+ def waitforfds(diff):
+ for i in range(600):
+ fds_diff = diff()
+
+ if fds_diff <= option.fds_threshold:
+ break
+
+ time.sleep(0.1)
+
+ return fds_diff
+
+ ps = _fds_info['main']
+ if not ps['skip']:
+ fds_diff = waitforfds(
+ lambda: _count_fds(unit_instance['pid']) - ps['fds']
+ )
+ ps['fds'] += fds_diff
+
+ assert (
+ fds_diff <= option.fds_threshold
+ ), 'descriptors leak main process'
+
+ else:
+ ps['fds'] = _count_fds(unit_instance['pid'])
+
+ for name in ['controller', 'router']:
+ ps = _fds_info[name]
+ ps_pid = ps['pid']
+ ps['pid'] = pid_by_name(ps['name'])
+
+ if not ps['skip']:
+ fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds'])
+ ps['fds'] += fds_diff
+
+ if not option.restart:
+ assert ps['pid'] == ps_pid, 'same pid %s' % name
+
+ assert fds_diff <= option.fds_threshold, (
+ 'descriptors leak %s' % name
+ )
+
+ else:
+ ps['fds'] = _count_fds(ps['pid'])
def _count_fds(pid):
@@ -639,9 +656,9 @@ def skip_alert():
@pytest.fixture()
def skip_fds_check():
def _skip(main=False, router=False, controller=False):
- _fds_check['main']['skip'] = main
- _fds_check['router']['skip'] = router
- _fds_check['controller']['skip'] = controller
+ _fds_info['main']['skip'] = main
+ _fds_info['router']['skip'] = router
+ _fds_info['controller']['skip'] = controller
return _skip
diff --git a/test/python/client_ip/wsgi.py b/test/python/client_ip/wsgi.py
new file mode 100644
index 00000000..0e12db0a
--- /dev/null
+++ b/test/python/client_ip/wsgi.py
@@ -0,0 +1,4 @@
+def application(env, start_response):
+ ip = env['REMOTE_ADDR'].encode()
+ start_response('200', [('Content-Length', str(len(ip)))])
+ return ip
diff --git a/test/python/restart/longstart.py b/test/python/restart/longstart.py
new file mode 100644
index 00000000..777398ac
--- /dev/null
+++ b/test/python/restart/longstart.py
@@ -0,0 +1,10 @@
+import os
+import time
+
+time.sleep(2)
+
+def application(environ, start_response):
+ body = str(os.getpid()).encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/python/restart/v1.py b/test/python/restart/v1.py
new file mode 100644
index 00000000..2e45b269
--- /dev/null
+++ b/test/python/restart/v1.py
@@ -0,0 +1,7 @@
+import os
+
+def application(environ, start_response):
+ body = "v1".encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/python/restart/v2.py b/test/python/restart/v2.py
new file mode 100644
index 00000000..59e3d30f
--- /dev/null
+++ b/test/python/restart/v2.py
@@ -0,0 +1,7 @@
+import os
+
+def application(environ, start_response):
+ body = "v2".encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/ruby/hooks/config.ru b/test/ruby/hooks/config.ru
new file mode 100644
index 00000000..f3069558
--- /dev/null
+++ b/test/ruby/hooks/config.ru
@@ -0,0 +1,7 @@
+app = Proc.new do |env|
+ ['200', {
+ 'Content-Length' => '0'
+ }, ['']]
+end
+
+run app
diff --git a/test/ruby/hooks/eval.rb b/test/ruby/hooks/eval.rb
new file mode 100644
index 00000000..ce7329c1
--- /dev/null
+++ b/test/ruby/hooks/eval.rb
@@ -0,0 +1,3 @@
+require 'securerandom'
+
+File.write("./cookie_eval.#{SecureRandom.hex}", "evaluated")
diff --git a/test/ruby/hooks/multiple.rb b/test/ruby/hooks/multiple.rb
new file mode 100644
index 00000000..b1b659a5
--- /dev/null
+++ b/test/ruby/hooks/multiple.rb
@@ -0,0 +1,13 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_worker_boot do
+ File.write("./cookie_worker_boot.#{SecureRandom.hex}", "worker booted")
+end
+
+on_thread_boot do
+ @mutex.synchronize do
+ File.write("./cookie_thread_boot.#{SecureRandom.hex}", "thread booted")
+ end
+end
diff --git a/test/ruby/hooks/on_thread_boot.rb b/test/ruby/hooks/on_thread_boot.rb
new file mode 100644
index 00000000..4f88424e
--- /dev/null
+++ b/test/ruby/hooks/on_thread_boot.rb
@@ -0,0 +1,9 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_thread_boot do
+ @mutex.synchronize do
+ File.write("./cookie_thread_boot.#{SecureRandom.hex}", "booted")
+ end
+end
diff --git a/test/ruby/hooks/on_thread_shutdown.rb b/test/ruby/hooks/on_thread_shutdown.rb
new file mode 100644
index 00000000..d953b8b7
--- /dev/null
+++ b/test/ruby/hooks/on_thread_shutdown.rb
@@ -0,0 +1,9 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_thread_shutdown do
+ @mutex.synchronize do
+ File.write("./cookie_thread_shutdown.#{SecureRandom.hex}", "shutdown")
+ end
+end
diff --git a/test/ruby/hooks/on_worker_boot.rb b/test/ruby/hooks/on_worker_boot.rb
new file mode 100644
index 00000000..b6529f60
--- /dev/null
+++ b/test/ruby/hooks/on_worker_boot.rb
@@ -0,0 +1,5 @@
+require 'securerandom'
+
+on_worker_boot do
+ File.write("./cookie_worker_boot.#{SecureRandom.hex}", "booted")
+end
diff --git a/test/ruby/hooks/on_worker_shutdown.rb b/test/ruby/hooks/on_worker_shutdown.rb
new file mode 100644
index 00000000..9ffaad93
--- /dev/null
+++ b/test/ruby/hooks/on_worker_shutdown.rb
@@ -0,0 +1,5 @@
+require 'securerandom'
+
+on_worker_shutdown do
+ File.write("./cookie_worker_shutdown.#{SecureRandom.hex}", "shutdown")
+end
diff --git a/test/test_client_ip.py b/test/test_client_ip.py
new file mode 100644
index 00000000..0084574e
--- /dev/null
+++ b/test/test_client_ip.py
@@ -0,0 +1,129 @@
+import pytest
+
+from unit.applications.lang.python import TestApplicationPython
+
+
+class TestClientIP(TestApplicationPython):
+ prerequisites = {'modules': {'python': 'any'}}
+
+ def client_ip(self, options):
+ assert 'success' in self.conf(
+ {
+ "127.0.0.1:7081":
+ {"client_ip": options, "pass": "applications/client_ip"},
+ "[::1]:7082":
+ {"client_ip": options, "pass": "applications/client_ip"},
+ },
+ 'listeners',
+ ), 'listeners configure'
+
+ def get_xff(self, xff, sock_type='ipv4'):
+ port = 7081 if sock_type == 'ipv4' else 7082
+
+ return self.get(
+ sock_type=sock_type,
+ port=port,
+ headers={'Connection': 'close', 'X-Forwarded-For': xff},
+ )['body']
+
+ def setup_method(self):
+ self.load('client_ip')
+
+ def test_settings_client_ip_single_ip(self):
+ self.client_ip(
+ {'header': 'X-Forwarded-For', 'source': '123.123.123.123'}
+ )
+
+ assert self.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default'
+ assert (
+ self.get(sock_type='ipv6', port=7082)['body'] == '::1'
+ ), 'ipv6 default'
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source'
+ assert self.get_xff('blah') == '127.0.0.1', 'bad header'
+ assert self.get_xff('1.1.1.1', 'ipv6') == '::1', 'bad source ipv6'
+
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '127.0.0.1'})
+
+ assert self.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default 2'
+ assert (
+ self.get(sock_type='ipv6', port=7082)['body'] == '::1'
+ ), 'ipv6 default 2'
+ assert self.get_xff('1.1.1.1') == '1.1.1.1', 'replace'
+ assert self.get_xff('blah') == '127.0.0.1', 'bad header 2'
+ assert (
+ self.get_xff('1.1.1.1', 'ipv6') == '::1'
+ ), 'bad source ipv6 2'
+
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '!127.0.0.1'})
+
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source 3'
+ assert self.get_xff('1.1.1.1', 'ipv6') == '1.1.1.1', 'replace 2'
+
+ def test_settings_client_ip_ipv4(self):
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '127.0.0.1'})
+
+ assert (
+ self.get_xff('8.8.8.8, 84.23.23.11') == '84.23.23.11'
+ ), 'xff replace'
+ assert (
+ self.get_xff('8.8.8.8, 84.23.23.11, 127.0.0.1') == '127.0.0.1'
+ ), 'xff replace 2'
+ assert (
+ self.get_xff(['8.8.8.8', '127.0.0.1, 10.0.1.1']) == '10.0.1.1'
+ ), 'xff replace multi'
+
+ def test_settings_client_ip_ipv6(self):
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '::1'})
+
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source ipv4'
+
+ for ip in [
+ 'f607:7403:1e4b:6c66:33b2:843f:2517:da27',
+ '2001:db8:3c4d:15::1a2f:1a2b',
+ '2001::3c4d:15:1a2f:1a2b',
+ '::11.22.33.44',
+ ]:
+ assert self.get_xff(ip, 'ipv6') == ip, 'replace'
+
+ def test_settings_client_ip_recursive(self):
+ self.client_ip(
+ {
+ 'header': 'X-Forwarded-For',
+ 'recursive': True,
+ 'source': ['127.0.0.1', '10.50.0.17', '10.5.2.1'],
+ }
+ )
+
+ assert self.get_xff('1.1.1.1') == '1.1.1.1', 'xff chain'
+ assert self.get_xff('1.1.1.1, 10.5.2.1') == '1.1.1.1', 'xff chain 2'
+ assert (
+ self.get_xff('8.8.8.8, 1.1.1.1, 10.5.2.1') == '1.1.1.1'
+ ), 'xff chain 3'
+ assert (
+ self.get_xff('10.50.0.17, 10.5.2.1, 10.5.2.1') == '10.50.0.17'
+ ), 'xff chain 4'
+ assert (
+ self.get_xff(['8.8.8.8', '1.1.1.1, 127.0.0.1']) == '1.1.1.1'
+ ), 'xff replace multi'
+ assert (
+ self.get_xff(['8.8.8.8', '1.1.1.1, 127.0.0.1', '10.5.2.1'])
+ == '1.1.1.1'
+ ), 'xff replace multi 2'
+ assert (
+ self.get_xff(['10.5.2.1', '10.50.0.17, 1.1.1.1', '10.5.2.1'])
+ == '1.1.1.1'
+ ), 'xff replace multi 3'
+ assert (
+ self.get_xff('8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1')
+ == '2001:db8:3c4d:15::1a2f:1a2b'
+ ), 'xff chain ipv6'
+
+ def test_settings_client_ip_invalid(self):
+ assert 'error' in self.conf(
+ {"http": {"client_ip": {'header': 'X-Forwarded-For', 'source': []}}},
+ 'settings',
+ ), 'empty array source'
+ assert 'error' in self.conf(
+ {"http":{"client_ip": {'header': 'X-Forwarded-For', 'source': 'a'}}},
+ 'settings',
+ ), 'empty source invalid'
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 880aef6c..8655968f 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -1,12 +1,22 @@
+import socket
+
import pytest
-import socket
from unit.control import TestControl
class TestConfiguration(TestControl):
prerequisites = {'modules': {'python': 'any'}}
+ def try_addr(self, addr):
+ return self.conf(
+ {
+ "listeners": {addr: {"pass": "routes"}},
+ "routes": [{"action": {"return": 200}}],
+ "applications": {},
+ }
+ )
+
def test_json_empty(self):
assert 'error' in self.conf(''), 'empty'
@@ -217,50 +227,20 @@ class TestConfiguration(TestControl):
{"*:7080": {"pass": "applications/app"}}, 'listeners'
), 'listeners no app'
- def test_listeners_wildcard(self):
- assert 'success' in self.conf(
- {
- "listeners": {"*:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'listeners wildcard'
+ def test_listeners_addr(self):
+ assert 'success' in self.try_addr("*:7080"), 'wildcard'
+ assert 'success' in self.try_addr("127.0.0.1:7081"), 'explicit'
+ assert 'success' in self.try_addr("[::1]:7082"), 'explicit ipv6'
- def test_listeners_explicit(self):
- assert 'success' in self.conf(
- {
- "listeners": {"127.0.0.1:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'explicit'
+ def test_listeners_addr_error(self):
+ assert 'error' in self.try_addr("127.0.0.1"), 'no port'
- def test_listeners_explicit_ipv6(self):
- assert 'success' in self.conf(
- {
- "listeners": {"[::1]:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'explicit ipv6'
+ def test_listeners_addr_error_2(self, skip_alert):
+ skip_alert(r'bind.*failed', r'failed to apply new conf')
+
+ assert 'error' in self.try_addr(
+ "[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:7080"
+ )
def test_listeners_port_release(self):
for i in range(10):
@@ -289,22 +269,6 @@ class TestConfiguration(TestControl):
assert 'success' in resp, 'port release'
- @pytest.mark.skip('not yet, unsafe')
- def test_listeners_no_port(self):
- assert 'error' in self.conf(
- {
- "listeners": {"127.0.0.1": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'no port'
-
def test_json_application_name_large(self):
name = "X" * 1024 * 1024
diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py
index 0945a967..5464d4a6 100644
--- a/test/test_node_es_modules.py
+++ b/test/test_node_es_modules.py
@@ -1,6 +1,7 @@
+from distutils.version import LooseVersion
+
import pytest
-from distutils.version import LooseVersion
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index b0d0f5af..a95c5680 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -1,4 +1,5 @@
import re
+import shutil
import subprocess
import time
@@ -201,3 +202,81 @@ class TestPythonProcman(TestApplicationPython):
assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert len(self.pids_for_process()) == 0, 'stop all'
+
+ def test_python_restart(self, temp_dir):
+ shutil.copyfile(
+ option.test_dir + '/python/restart/v1.py', temp_dir + '/wsgi.py'
+ )
+
+ self.load(
+ temp_dir,
+ name=self.app_name,
+ processes=1,
+ environment={'PYTHONDONTWRITEBYTECODE': '1'},
+ )
+
+ b = self.get()['body']
+ assert b == "v1", 'process started'
+
+ shutil.copyfile(
+ option.test_dir + '/python/restart/v2.py', temp_dir + '/wsgi.py'
+ )
+
+ b = self.get()['body']
+ assert b == "v1", 'still old process'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ b = self.get()['body']
+ assert b == "v2", 'new process started'
+
+ assert 'error' in self.conf_get(
+ '/control/applications/blah/restart'
+ ), 'application incorrect'
+
+ assert 'error' in self.conf_delete(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'method incorrect'
+
+ def test_python_restart_multi(self):
+ self.conf_proc('2')
+
+ pids = self.pids_for_process()
+ assert len(pids) == 2, 'restart 2 started'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ new_pids = self.pids_for_process()
+ assert len(new_pids) == 2, 'restart still 2'
+
+ assert len(new_pids.intersection(pids)) == 0, 'restart all new'
+
+ def test_python_restart_longstart(self):
+ self.load(
+ 'restart',
+ name=self.app_name,
+ module="longstart",
+ processes={"spare": 1, "max": 2, "idle_timeout": 5},
+ )
+
+ assert len(self.pids_for_process()) == 1, 'longstarts == 1'
+
+ pid = self.get()['body']
+ pids = self.pids_for_process()
+ assert len(pids) == 2, 'longstarts == 2'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ # wait for longstarted app
+ time.sleep(2)
+
+ new_pids = self.pids_for_process()
+ assert len(new_pids) == 1, 'restart 1'
+
+ assert len(new_pids.intersection(pids)) == 0, 'restart all new'
diff --git a/test/test_respawn.py b/test/test_respawn.py
index edbfa2a8..5a5d6126 100644
--- a/test/test_respawn.py
+++ b/test/test_respawn.py
@@ -44,11 +44,16 @@ class TestRespawn(TestApplicationPython):
return re.findall(str(ppid) + r'.*' + name, ps_output)
def smoke_test(self, unit_pid):
- for _ in range(5):
- assert 'success' in self.conf(
- '1', 'applications/' + self.app_name + '/processes'
- )
- assert self.get()['status'] == 200
+ for _ in range(10):
+ r = self.conf('1', 'applications/' + self.app_name + '/processes')
+
+ if 'success' in r:
+ break
+
+ time.sleep(0.1)
+
+ assert 'success' in r
+ assert self.get()['status'] == 200
# Check if the only one router, controller,
# and application processes running.
diff --git a/test/test_routing.py b/test/test_routing.py
index eaa0a134..ef5622c2 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1751,6 +1751,10 @@ class TestRouting(TestApplicationProto):
self.route_match_invalid({"source": "*:1-a"})
self.route_match_invalid({"source": "*:65536"})
+ def test_routes_match_source_none(self):
+ self.route_match({"source": []})
+ assert self.get()['status'] == 404, 'source none'
+
def test_routes_match_destination(self):
assert 'success' in self.conf(
{"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
diff --git a/test/test_ruby_hooks.py b/test/test_ruby_hooks.py
new file mode 100644
index 00000000..af8ce337
--- /dev/null
+++ b/test/test_ruby_hooks.py
@@ -0,0 +1,98 @@
+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
+
+
+class TestRubyHooks(TestApplicationRuby):
+ prerequisites = {'modules': {'ruby': 'all'}}
+
+ def _wait_cookie(self, pattern, count):
+ return waitforglob(
+ option.temp_dir + '/ruby/hooks/cookie_' + pattern, count
+ )
+
+ def test_ruby_hooks_eval(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='eval.rb')
+
+ hooked = self._wait_cookie('eval.*', processes)
+
+ assert hooked, 'hooks evaluated'
+
+ def test_ruby_hooks_on_worker_boot(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='on_worker_boot.rb')
+
+ hooked = self._wait_cookie('worker_boot.*', processes)
+
+ assert hooked, 'on_worker_boot called'
+
+ def test_ruby_hooks_on_worker_shutdown(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='on_worker_shutdown.rb')
+
+ assert self.get()['status'] == 200, 'app response'
+
+ self.load('empty')
+
+ hooked = self._wait_cookie('worker_shutdown.*', processes)
+
+ assert hooked, 'on_worker_shutdown called'
+
+ def test_ruby_hooks_on_thread_boot(self):
+ processes = 1
+ threads = 2
+
+ self.load(
+ 'hooks',
+ processes=processes,
+ threads=threads,
+ hooks='on_thread_boot.rb',
+ )
+
+ hooked = self._wait_cookie('thread_boot.*', processes * threads)
+
+ assert hooked, 'on_thread_boot called'
+
+ def test_ruby_hooks_on_thread_shutdown(self):
+ processes = 1
+ threads = 2
+
+ self.load(
+ 'hooks',
+ processes=processes,
+ threads=threads,
+ hooks='on_thread_shutdown.rb',
+ )
+
+ assert self.get()['status'] == 200, 'app response'
+
+ self.load('empty')
+
+ hooked = self._wait_cookie('thread_shutdown.*', processes * threads)
+
+ assert hooked, 'on_thread_shutdown called'
+
+ def test_ruby_hooks_multiple(self):
+ processes = 1
+ threads = 1
+
+ self.load(
+ 'hooks', processes=processes, threads=threads, hooks='multiple.rb',
+ )
+
+ hooked = self._wait_cookie('worker_boot.*', processes)
+ assert hooked, 'on_worker_boot called'
+
+ hooked = self._wait_cookie('thread_boot.*', threads)
+ assert hooked, 'on_thread_boot called'
diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py
index 8443d857..f414d610 100644
--- a/test/test_ruby_isolation.py
+++ b/test/test_ruby_isolation.py
@@ -35,13 +35,6 @@ class TestRubyIsolation(TestApplicationRuby):
'pid': True,
}
- os.mkdir(option.temp_dir + '/ruby')
-
- shutil.copytree(
- option.test_dir + '/ruby/status_int',
- option.temp_dir + '/ruby/status_int',
- )
-
self.load('status_int', isolation=isolation)
assert 'success' in self.conf(
diff --git a/test/test_share_chroot.py b/test/test_static_chroot.py
index 7e53d3f7..f9bc93a8 100644
--- a/test/test_share_chroot.py
+++ b/test/test_static_chroot.py
@@ -6,17 +6,14 @@ import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareChroot(TestApplicationProto):
+class TestStaticChroot(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(temp_dir + '/assets/dir')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/file', 'w'
- ) as file:
- index.write('0123456789')
- file.write('blah')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
+ Path(temp_dir + '/assets/dir/file').write_text('blah')
test = Path(__file__)
self.test_path = '/' + test.parent.name + '/' + test.name
@@ -28,7 +25,7 @@ class TestShareChroot(TestApplicationProto):
}
)
- def test_share_chroot(self, temp_dir):
+ 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'
@@ -44,7 +41,10 @@ class TestShareChroot(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_permission(self, temp_dir):
+ def test_static_chroot_permission(self, is_su, temp_dir):
+ if is_su:
+ pytest.skip('does\'t work under root')
+
os.chmod(temp_dir + '/assets/dir', 0o100)
assert 'success' in self.conf(
@@ -57,7 +57,7 @@ class TestShareChroot(TestApplicationProto):
assert self.get(url='/dir/file')['status'] == 200, 'chroot'
- def test_share_chroot_empty(self, temp_dir):
+ def test_static_chroot_empty(self, temp_dir):
assert 'success' in self.conf(
{"share": temp_dir + "/assets", "chroot": ""}, 'routes/0/action',
), 'configure chroot empty absolute'
@@ -74,7 +74,7 @@ class TestShareChroot(TestApplicationProto):
self.get(url=self.test_path)['status'] == 200
), 'chroot empty relative'
- def test_share_chroot_relative(self, is_su, temp_dir):
+ def test_static_chroot_relative(self, is_su, temp_dir):
if is_su:
pytest.skip('does\'t work under root')
@@ -96,7 +96,7 @@ class TestShareChroot(TestApplicationProto):
assert self.get(url=self.test_path)['status'] == 200, 'relative'
- def test_share_chroot_invalid(self, temp_dir):
+ def test_static_chroot_invalid(self, temp_dir):
assert 'error' in self.conf(
{"share": temp_dir, "chroot": True}, 'routes/0/action',
), 'configure chroot error'
diff --git a/test/test_share_fallback.py b/test/test_static_fallback.py
index 0b1c270e..dc9056b9 100644
--- a/test/test_share_fallback.py
+++ b/test/test_static_fallback.py
@@ -1,21 +1,21 @@
import os
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-from unit.option import option
-class TestStatic(TestApplicationProto):
+class TestStaticFallback(TestApplicationProto):
prerequisites = {}
- def setup_method(self):
- os.makedirs(option.temp_dir + '/assets/dir')
- with open(option.temp_dir + '/assets/index.html', 'w') as index:
- index.write('0123456789')
+ @pytest.fixture(autouse=True)
+ def setup_method_fixture(self, temp_dir):
+ os.makedirs(temp_dir + '/assets/dir')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
- os.makedirs(option.temp_dir + '/assets/403')
- os.chmod(option.temp_dir + '/assets/403', 0o000)
+ os.makedirs(temp_dir + '/assets/403')
+ os.chmod(temp_dir + '/assets/403', 0o000)
self._load_conf(
{
@@ -23,21 +23,22 @@ class TestStatic(TestApplicationProto):
"*:7080": {"pass": "routes"},
"*:7081": {"pass": "routes"},
},
- "routes": [{"action": {"share": option.temp_dir + "/assets"}}],
+ "routes": [{"action": {"share": temp_dir + "/assets"}}],
"applications": {},
}
)
- def teardown_method(self):
+ yield
+
try:
- os.chmod(option.temp_dir + '/assets/403', 0o777)
+ os.chmod(temp_dir + '/assets/403', 0o777)
except FileNotFoundError:
pass
def action_update(self, conf):
assert 'success' in self.conf(conf, 'routes/0/action')
- def test_fallback(self):
+ def test_static_fallback(self):
self.action_update({"share": "/blah"})
assert self.get()['status'] == 404, 'bad path no fallback'
@@ -47,7 +48,7 @@ class TestStatic(TestApplicationProto):
assert resp['status'] == 200, 'bad path fallback status'
assert resp['body'] == '', 'bad path fallback'
- def test_fallback_valid_path(self, temp_dir):
+ def test_static_fallback_valid_path(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "fallback": {"return": 200}}
)
@@ -65,7 +66,7 @@ class TestStatic(TestApplicationProto):
assert self.get(url='/dir')['status'] == 301, 'fallback status 301'
- def test_fallback_nested(self):
+ def test_static_fallback_nested(self):
self.action_update(
{
"share": "/blah",
@@ -80,7 +81,7 @@ class TestStatic(TestApplicationProto):
assert resp['status'] == 200, 'fallback nested status'
assert resp['body'] == '', 'fallback nested'
- def test_fallback_share(self, temp_dir):
+ def test_static_fallback_share(self, temp_dir):
self.action_update(
{"share": "/blah", "fallback": {"share": temp_dir + "/assets"},}
)
@@ -97,7 +98,7 @@ class TestStatic(TestApplicationProto):
self.get(url='/dir')['status'] == 301
), 'fallback share status 301'
- def test_fallback_proxy(self):
+ def test_static_fallback_proxy(self):
assert 'success' in self.conf(
[
{
@@ -119,7 +120,7 @@ class TestStatic(TestApplicationProto):
assert resp['body'] == '', 'fallback proxy'
@pytest.mark.skip('not yet')
- def test_fallback_proxy_loop(self, skip_alert):
+ def test_static_fallback_proxy_loop(self, skip_alert):
skip_alert(
r'open.*/blah/index.html.*failed',
r'accept.*failed',
@@ -135,7 +136,7 @@ class TestStatic(TestApplicationProto):
assert 'success' in self.conf_delete('listeners/*:7081')
self.get(read_timeout=1)
- def test_fallback_invalid(self):
+ def test_static_fallback_invalid(self):
def check_error(conf):
assert 'error' in self.conf(conf, 'routes/0/action')
diff --git a/test/test_share_mount.py b/test/test_static_mount.py
index f22fbe75..570f6439 100644
--- a/test/test_share_mount.py
+++ b/test/test_static_mount.py
@@ -1,12 +1,13 @@
import os
import subprocess
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareMount(TestApplicationProto):
+class TestStaticMount(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
@@ -17,12 +18,9 @@ class TestShareMount(TestApplicationProto):
os.makedirs(temp_dir + '/assets/dir/mount')
os.makedirs(temp_dir + '/assets/dir/dir')
os.makedirs(temp_dir + '/assets/mount')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/dir/file', 'w'
- ) as file, open(temp_dir + '/assets/mount/index.html', 'w') as mount:
- index.write('index')
- file.write('file')
- mount.write('mount')
+ Path(temp_dir + '/assets/index.html').write_text('index')
+ Path(temp_dir + '/assets/dir/dir/file').write_text('file')
+ Path(temp_dir + '/assets/mount/index.html').write_text('mount')
try:
process = subprocess.Popen(
@@ -66,7 +64,7 @@ class TestShareMount(TestApplicationProto):
except:
pytest.fail('Can\'t run umount process.')
- def test_share_mount(self, temp_dir, skip_alert):
+ def test_static_mount(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
resp = self.get(url='/mount/')
@@ -89,7 +87,7 @@ class TestShareMount(TestApplicationProto):
assert resp['status'] == 200
assert resp['body'] == 'mount'
- def test_share_mount_two_blocks(self, temp_dir, skip_alert):
+ def test_static_mount_two_blocks(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -117,7 +115,7 @@ class TestShareMount(TestApplicationProto):
assert self.get(url='/mount/')['status'] == 200, 'block enabled'
assert self.head(url='/mount/')['status'] == 403, 'block disabled'
- def test_share_mount_chroot(self, temp_dir, skip_alert):
+ def test_static_mount_chroot(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
assert 'success' in self.conf(
diff --git a/test/test_share_symlink.py b/test/test_static_symlink.py
index 3970b605..35eb402a 100644
--- a/test/test_share_symlink.py
+++ b/test/test_static_symlink.py
@@ -1,21 +1,19 @@
import os
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareSymlink(TestApplicationProto):
+class TestStaticSymlink(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(temp_dir + '/assets/dir/dir')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/file', 'w'
- ) as file:
- index.write('0123456789')
- file.write('blah')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
+ Path(temp_dir + '/assets/dir/file').write_text('blah')
self._load_conf(
{
@@ -24,7 +22,7 @@ class TestShareSymlink(TestApplicationProto):
}
)
- def test_share_symlink(self, temp_dir, skip_alert):
+ def test_static_symlink(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -48,7 +46,7 @@ class TestShareSymlink(TestApplicationProto):
assert self.get(url='/link/file')['status'] == 200, 'symlink enabled'
- def test_share_symlink_two_blocks(self, temp_dir, skip_alert):
+ def test_static_symlink_two_blocks(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -76,7 +74,7 @@ class TestShareSymlink(TestApplicationProto):
assert self.get(url='/link/file')['status'] == 200, 'block enabled'
assert self.head(url='/link/file')['status'] == 403, 'block disabled'
- def test_share_symlink_chroot(self, temp_dir, skip_alert):
+ def test_static_symlink_chroot(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(
diff --git a/test/test_share_types.py b/test/test_static_types.py
index b5ed97a0..20defddf 100644
--- a/test/test_share_types.py
+++ b/test/test_static_types.py
@@ -1,12 +1,11 @@
-import os
from pathlib import Path
import pytest
+
from unit.applications.proto import TestApplicationProto
-from unit.option import option
-class TestShareTypes(TestApplicationProto):
+class TestStaticTypes(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
@@ -36,7 +35,7 @@ class TestShareTypes(TestApplicationProto):
assert resp['status'] == 200, 'status'
assert resp['body'] == body, 'body'
- def test_share_types_basic(self, temp_dir):
+ def test_static_types_basic(self, temp_dir):
self.action_update({"share": temp_dir + "/assets"})
self.check_body('/index.html', 'index')
self.check_body('/file.xml', '.xml')
@@ -54,7 +53,7 @@ class TestShareTypes(TestApplicationProto):
self.action_update({"share": temp_dir + "/assets", "types": [""]})
assert self.get(url='/file.xml')['status'] == 403, 'no mtype'
- def test_share_types_wildcard(self, temp_dir):
+ def test_static_types_wildcard(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["application/*"]}
)
@@ -67,7 +66,7 @@ class TestShareTypes(TestApplicationProto):
assert self.get(url='/file.xml')['status'] == 403, 'video * mtype xml'
self.check_body('/file.mp4', '.mp4')
- def test_share_types_negation(self, temp_dir):
+ def test_static_types_negation(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["!application/xml"]}
)
@@ -85,7 +84,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.png', '.png')
assert self.get(url='/file.jpg')['status'] == 403, 'negation sort jpg'
- def test_share_types_regex(self, temp_dir):
+ def test_static_types_regex(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["~text/(html|plain)"]}
)
@@ -93,7 +92,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.html', '.html')
self.check_body('/file.txt', '.txt')
- def test_share_types_case(self, temp_dir):
+ def test_static_types_case(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["!APpliCaTiOn/xMl"]}
)
@@ -118,7 +117,7 @@ class TestShareTypes(TestApplicationProto):
self.get(url='/file.xml')['status'] == 403
), 'mixed case video * negation'
- def test_share_types_fallback(self, temp_dir):
+ def test_static_types_fallback(self, temp_dir):
assert 'success' in self.conf(
[
{
@@ -139,7 +138,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.php', '')
self.check_body('/file.mp4', '.mp4')
- def test_share_types_index(self, temp_dir):
+ def test_static_types_index(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": "application/xml"}
)
@@ -147,7 +146,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.xml', '.xml')
assert self.get(url='/file.mp4')['status'] == 403, 'forbidden mtype'
- def test_share_types_custom_mime(self, temp_dir):
+ def test_static_types_custom_mime(self, temp_dir):
self._load_conf(
{
"listeners": {"*:7080": {"pass": "routes"}},
diff --git a/test/test_tls.py b/test/test_tls.py
index 0cfeaded..546f0f89 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -665,3 +665,16 @@ basicConstraints = critical,CA:TRUE"""
)
assert res['status'] == 200, 'status ok'
assert res['body'] == filename + data
+
+ def test_tls_multi_listener(self):
+ self.load('empty')
+
+ self.certificate()
+
+ self.add_tls()
+ self.add_tls(port=7081)
+
+ assert self.get_ssl()['status'] == 200, 'listener #1'
+
+ assert self.get_ssl(port=7081)['status'] == 200, 'listener #2'
+
diff --git a/test/test_tls_sni.py b/test/test_tls_sni.py
index 2e5424e2..eba6140a 100644
--- a/test/test_tls_sni.py
+++ b/test/test_tls_sni.py
@@ -168,6 +168,26 @@ basicConstraints = critical,CA:TRUE"""
self.check_cert('alt2.example.com', bundles['example.com']['subj'])
self.check_cert('blah', bundles['default']['subj'])
+ def test_tls_sni_no_hostname(self):
+ bundles = {
+ "localhost.com": {"subj": "localhost.com", "alt_names": []},
+ "example.com": {
+ "subj": "example.com",
+ "alt_names": ["example.com"],
+ },
+ }
+ self.config_bundles(bundles)
+ self.add_tls(["localhost.com", "example.com"])
+
+ resp, sock = self.get_ssl(
+ headers={'Content-Length': '0', 'Connection': 'close'}, start=True,
+ )
+ assert resp['status'] == 200
+ assert (
+ sock.getpeercert()['subject'][0][0][1]
+ == bundles['localhost.com']['subj']
+ )
+
def test_tls_sni_upper_case(self):
bundles = {
"localhost.com": {"subj": "LOCALHOST.COM", "alt_names": []},
diff --git a/test/test_variables.py b/test/test_variables.py
index 139d867e..d8547b7b 100644
--- a/test/test_variables.py
+++ b/test/test_variables.py
@@ -100,6 +100,25 @@ class TestVariables(TestApplicationProto):
assert self.get(url='/1')['status'] == 200
assert self.get(url='/2')['status'] == 404
+ def test_variables_empty(self):
+ def update_pass(prefix):
+ assert 'success' in self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": prefix + "/$method"},
+ },
+ },
+ ), 'variables empty'
+
+ update_pass("routes");
+ assert self.get(url='/1')['status'] == 404
+
+ update_pass("upstreams");
+ assert self.get(url='/2')['status'] == 404
+
+ update_pass("applications");
+ assert self.get(url='/3')['status'] == 404
+
def test_variables_invalid(self):
def check_variables(routes):
assert 'error' in self.conf(
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index c9c2095e..53b27b07 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -52,7 +52,7 @@ class TestApplicationJava(TestApplicationProto):
os.makedirs(classes_path)
classpath = (
- option.current_dir + '/build/tomcat-servlet-api-9.0.44.jar'
+ option.current_dir + '/build/tomcat-servlet-api-9.0.52.jar'
)
ws_jars = glob.glob(
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index b399dffd..215aa332 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -44,6 +44,7 @@ class TestApplicationPython(TestApplicationProto):
for attr in (
'callable',
+ 'environment',
'home',
'limits',
'path',
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
index 02644584..61d50558 100644
--- a/test/unit/applications/lang/ruby.py
+++ b/test/unit/applications/lang/ruby.py
@@ -1,24 +1,44 @@
+import os
+import shutil
+
from unit.applications.proto import TestApplicationProto
from unit.option import option
+from unit.utils import public_dir
class TestApplicationRuby(TestApplicationProto):
application_type = "ruby"
+ def prepare_env(self, script):
+ shutil.copytree(
+ option.test_dir + '/ruby/' + script,
+ option.temp_dir + '/ruby/' + script,
+ )
+
+ public_dir(option.temp_dir + '/ruby/' + script)
+
def load(self, script, name='config.ru', **kwargs):
- script_path = option.test_dir + '/ruby/' + script
+ self.prepare_env(script)
+
+ script_path = option.temp_dir + '/ruby/' + script
+
+ app = {
+ "type": self.get_application_type(),
+ "processes": {"spare": 0},
+ "working_directory": script_path,
+ "script": script_path + '/' + name,
+ }
+
+ for key in [
+ 'hooks',
+ ]:
+ if key in kwargs:
+ app[key] = kwargs[key]
self._load_conf(
{
"listeners": {"*:7080": {"pass": "applications/" + script}},
- "applications": {
- script: {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "working_directory": script_path,
- "script": script_path + '/' + name,
- }
- },
+ "applications": {script: app},
},
**kwargs
)
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index 92754c03..e30d21ff 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -47,13 +47,15 @@ class TestApplicationProto(TestControl):
if 'applications' in conf:
for app in conf['applications'].keys():
app_conf = conf['applications'][app]
- if 'user' in kwargs:
- app_conf['user'] = kwargs['user']
- if 'group' in kwargs:
- app_conf['group'] = kwargs['group']
-
- if 'isolation' in kwargs:
- app_conf['isolation'] = kwargs['isolation']
+ for key in [
+ 'user',
+ 'group',
+ 'isolation',
+ 'processes',
+ 'threads',
+ ]:
+ if key in kwargs:
+ app_conf[key] = kwargs[key]
assert 'success' in self.conf(conf), 'load application configuration'
diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py
index 7c83ae35..43c8842f 100644
--- a/test/unit/check/isolation.py
+++ b/test/unit/check/isolation.py
@@ -3,6 +3,7 @@ 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.http import TestHTTP
@@ -65,14 +66,16 @@ def check_isolation():
}
elif 'ruby' in available['modules']:
+ TestApplicationRuby().prepare_env('empty')
+
conf = {
"listeners": {"*:7080": {"pass": "applications/empty"}},
"applications": {
"empty": {
"type": "ruby",
"processes": {"spare": 0},
- "working_directory": option.test_dir + "/ruby/empty",
- "script": option.test_dir + "/ruby/empty/config.ru",
+ "working_directory": option.temp_dir + "/ruby/empty",
+ "script": option.temp_dir + "/ruby/empty/config.ru",
"isolation": {"namespaces": {"credential": True}},
}
},
diff --git a/test/unit/http.py b/test/unit/http.py
index 797b7681..dcfcd232 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -45,7 +45,7 @@ class TestHTTP:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if 'wrapper' in kwargs:
- server_hostname = headers.get('Host', 'localhost')
+ server_hostname = headers.get('Host', None)
sock = kwargs['wrapper'](sock, server_hostname=server_hostname)
connect_args = addr if sock_type == 'unix' else (addr, port)
diff --git a/test/unit/utils.py b/test/unit/utils.py
index a627e9f5..43aaa81b 100644
--- a/test/unit/utils.py
+++ b/test/unit/utils.py
@@ -1,3 +1,4 @@
+import glob
import os
import socket
import subprocess
@@ -16,8 +17,8 @@ def public_dir(path):
os.chmod(os.path.join(root, f), 0o777)
-def waitforfiles(*files):
- for i in range(50):
+def waitforfiles(*files, timeout=50):
+ for i in range(timeout):
wait = False
for f in files:
@@ -33,6 +34,21 @@ def waitforfiles(*files):
return False
+def waitforglob(pattern, count=1, timeout=50):
+ for i in range(timeout):
+ n = 0
+
+ for f in glob.glob(pattern):
+ n += 1
+
+ if n == count:
+ return True
+
+ time.sleep(0.1)
+
+ return False
+
+
def waitforsocket(port):
for i in range(50):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
@@ -72,8 +88,8 @@ def sysctl():
return out
-def waitformount(template, wait=50):
- for i in range(wait):
+def waitformount(template, timeout=50):
+ for i in range(timeout):
if findmnt().find(template) != -1:
return True
@@ -82,8 +98,8 @@ def waitformount(template, wait=50):
return False
-def waitforunmount(template, wait=50):
- for i in range(wait):
+def waitforunmount(template, timeout=50):
+ for i in range(timeout):
if findmnt().find(template) == -1:
return True
diff --git a/version b/version
index 4483727a..b8cd3458 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.24.0
-NXT_VERNUM=12400
+NXT_VERSION=1.25.0
+NXT_VERNUM=12500