summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.hgtags1
-rw-r--r--CHANGES16
-rw-r--r--auto/isolation5
-rw-r--r--auto/modules/go34
-rw-r--r--auto/modules/java_get_jar9
-rw-r--r--auto/sources8
-rw-r--r--auto/unix20
-rw-r--r--docs/Makefile4
-rw-r--r--docs/changes.xml59
-rw-r--r--go/ldflags-lrt.go (renamed from src/go/unit/ldflags-lrt.go)0
-rw-r--r--go/ldflags.go10
-rw-r--r--go/nxt_cgo_lib.c (renamed from src/go/unit/nxt_cgo_lib.c)20
-rw-r--r--go/nxt_cgo_lib.h (renamed from src/go/unit/nxt_cgo_lib.h)0
-rw-r--r--go/observable.go32
-rw-r--r--go/port.go (renamed from src/go/unit/port.go)0
-rw-r--r--go/request.go (renamed from src/go/unit/request.go)0
-rw-r--r--go/response.go (renamed from src/go/unit/response.go)36
-rw-r--r--go/unit.go (renamed from src/go/unit/unit.go)0
-rw-r--r--pkg/deb/Makefile19
-rw-r--r--pkg/deb/Makefile.go6
-rw-r--r--pkg/deb/Makefile.go11048
-rw-r--r--pkg/deb/Makefile.go11148
-rw-r--r--pkg/deb/Makefile.go11248
-rw-r--r--pkg/deb/Makefile.go11348
-rw-r--r--pkg/deb/Makefile.go1748
-rw-r--r--pkg/deb/Makefile.go1848
-rw-r--r--pkg/deb/Makefile.go1948
-rw-r--r--pkg/deb/debian.module/rules-noarch.in1
-rwxr-xr-xpkg/deb/debian.module/rules.in1
-rw-r--r--pkg/deb/debian.module/unit.example-go-app2
-rw-r--r--pkg/deb/debian.module/unit.example-go1.10-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.11-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.12-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.13-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.7-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.8-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.9-config15
-rw-r--r--pkg/deb/debian/rules.in1
-rw-r--r--pkg/deb/debian/unit.example-go-app2
-rw-r--r--pkg/docker/Dockerfile.full10
-rw-r--r--pkg/docker/Dockerfile.go1.11-dev (renamed from pkg/docker/Dockerfile.go1.8-dev)10
-rw-r--r--pkg/docker/Dockerfile.go1.7-dev93
-rw-r--r--pkg/docker/Dockerfile.minimal8
-rw-r--r--pkg/docker/Dockerfile.perl5.28 (renamed from pkg/docker/Dockerfile.perl5.24)8
-rw-r--r--pkg/docker/Dockerfile.php7.3 (renamed from pkg/docker/Dockerfile.php7.0)8
-rw-r--r--pkg/docker/Dockerfile.python2.78
-rw-r--r--pkg/docker/Dockerfile.python3.7 (renamed from pkg/docker/Dockerfile.python3.5)10
-rw-r--r--pkg/docker/Dockerfile.ruby2.5 (renamed from pkg/docker/Dockerfile.ruby2.3)8
-rw-r--r--pkg/docker/Dockerfile.tmpl6
-rw-r--r--pkg/docker/Makefile29
-rw-r--r--pkg/rpm/Makefile8
-rw-r--r--pkg/rpm/Makefile.go10
-rw-r--r--pkg/rpm/Makefile.python279
-rw-r--r--pkg/rpm/Makefile.python362
-rw-r--r--pkg/rpm/Makefile.python372
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-go-app2
-rw-r--r--src/nodejs/unit-http/http_server.js121
-rw-r--r--src/nodejs/unit-http/unit.cpp90
-rw-r--r--src/nodejs/unit-http/unit.h3
-rw-r--r--src/nxt_application.h3
-rw-r--r--src/nxt_capability.c12
-rw-r--r--src/nxt_clone.c311
-rw-r--r--src/nxt_clone.h40
-rw-r--r--src/nxt_conf_validation.c78
-rw-r--r--src/nxt_credential.c350
-rw-r--r--src/nxt_credential.h30
-rw-r--r--src/nxt_external.c4
-rw-r--r--src/nxt_h1proto.c19
-rw-r--r--src/nxt_h1proto.h2
-rw-r--r--src/nxt_http_route.c278
-rw-r--r--src/nxt_http_route_addr.c349
-rw-r--r--src/nxt_http_route_addr.h73
-rw-r--r--src/nxt_java.c1
-rw-r--r--src/nxt_lib.c2
-rw-r--r--src/nxt_main.h1
-rw-r--r--src/nxt_main_process.c551
-rw-r--r--src/nxt_main_process.h5
-rw-r--r--src/nxt_php_sapi.c1
-rw-r--r--src/nxt_port.c2
-rw-r--r--src/nxt_port.h11
-rw-r--r--src/nxt_port_memory.c19
-rw-r--r--src/nxt_port_memory_int.h1
-rw-r--r--src/nxt_process.c291
-rw-r--r--src/nxt_process.h51
-rw-r--r--src/nxt_python_wsgi.c195
-rw-r--r--src/nxt_router.c138
-rw-r--r--src/nxt_runtime.c7
-rw-r--r--src/nxt_runtime.h2
-rw-r--r--src/nxt_sockaddr.c4
-rw-r--r--src/nxt_unit.c828
-rw-r--r--src/nxt_unit.h7
-rw-r--r--src/nxt_worker_process.c19
-rw-r--r--src/perl/nxt_perl_psgi.c1
-rw-r--r--src/ruby/nxt_ruby.c1
-rw-r--r--src/test/nxt_clone_test.c601
-rw-r--r--src/test/nxt_tests.c6
-rw-r--r--src/test/nxt_tests.h1
-rw-r--r--test/go/404/app.go2
-rw-r--r--test/go/command_line_arguments/app.go2
-rw-r--r--test/go/cookies/app.go2
-rw-r--r--test/go/empty/app.go2
-rw-r--r--test/go/get_variables/app.go2
-rw-r--r--test/go/mirror/app.go2
-rw-r--r--test/go/ns_inspect/app.go4
-rw-r--r--test/go/post_variables/app.go2
-rw-r--r--test/go/variables/app.go2
-rw-r--r--test/python/user_group/wsgi.py18
-rw-r--r--test/test_go_isolation.py207
-rw-r--r--test/test_java_application.py21
-rw-r--r--test/test_node_application.py6
-rw-r--r--test/test_perl_application.py2
-rw-r--r--test/test_python_application.py97
-rw-r--r--test/test_python_environment.py16
-rw-r--r--test/test_routing.py2418
-rw-r--r--test/test_routing_tls.py6
-rw-r--r--test/test_ruby_application.py2
-rw-r--r--test/unit/applications/lang/go.py7
-rw-r--r--test/unit/applications/lang/java.py5
-rw-r--r--test/unit/applications/lang/node.py11
-rw-r--r--test/unit/applications/lang/perl.py5
-rw-r--r--test/unit/applications/lang/php.py5
-rw-r--r--test/unit/applications/lang/python.py5
-rw-r--r--test/unit/applications/lang/ruby.py5
-rw-r--r--test/unit/applications/proto.py14
-rw-r--r--test/unit/feature/isolation.py3
-rw-r--r--test/unit/http.py82
-rw-r--r--test/unit/main.py21
-rw-r--r--version4
128 files changed, 5182 insertions, 3265 deletions
diff --git a/.hgtags b/.hgtags
index d7384b08..2118096b 100644
--- a/.hgtags
+++ b/.hgtags
@@ -33,3 +33,4 @@ b391df5f0102aa6afe660cfc863729c1b1111c9e 1.12.0
c1625c52dd6444ed613348719fbb54c7abcc6619 1.12.0-1
3313bf222e6e0a91213946dfcbd70bb5079f4cef 1.13.0
439bd957eeb48dbbffa4cd7eecf3829d497f69b0 1.13.0-1
+6e28966ed1f26e119bf333229ea5e6686c60a469 1.14.0
diff --git a/CHANGES b/CHANGES
index 58e91d4f..409de839 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,20 @@
+Changes with Unit 1.14.0 26 Dec 2019
+
+ *) Change: the Go package import name changed to "unit.nginx.org/go".
+
+ *) Change: Go package now links to libunit instead of including library
+ sources.
+
+ *) Feature: ability to change user and group for isolated applications
+ when Unit daemon runs as an unprivileged user.
+
+ *) Feature: request routing by source and destination addresses and
+ ports.
+
+ *) Bugfix: memory bloat on large responses.
+
+
Changes with Unit 1.13.0 14 Nov 2019
*) Feature: basic support for HTTP reverse proxying.
diff --git a/auto/isolation b/auto/isolation
index c26a4991..d231de12 100644
--- a/auto/isolation
+++ b/auto/isolation
@@ -5,6 +5,7 @@
NXT_ISOLATION=NO
NXT_HAVE_CLONE=NO
+NXT_HAVE_CLONE_NEWUSER=NO
nsflags="USER NS PID NET UTS CGROUP"
@@ -42,6 +43,10 @@ if [ $nxt_found = yes ]; then
. auto/feature
if [ $nxt_found = yes ]; then
+ if [ $flag = "USER" ]; then
+ NXT_HAVE_CLONE_NEWUSER=YES
+ fi
+
if [ "$NXT_ISOLATION" = "NO" ]; then
NXT_ISOLATION=$flag
else
diff --git a/auto/modules/go b/auto/modules/go
index 51b5979d..2d53dd65 100644
--- a/auto/modules/go
+++ b/auto/modules/go
@@ -72,7 +72,9 @@ fi
NXT_GO_PATH=${NXT_GO_PATH=`${NXT_GO} env GOPATH`}
-NXT_GO_PATH=${NXT_GO_PATH:-`pwd`/${NXT_GO}}
+NXT_GO_PATH=${NXT_GO_PATH:-${PWD}/${NXT_BUILD_DIR}/${NXT_GO}}
+
+NXT_GO_PKG=unit.nginx.org/go
$echo " + Go package path: \"${NXT_GO_PATH}\""
@@ -88,7 +90,6 @@ cat << END >> $NXT_MAKEFILE
.PHONY: ${NXT_GO}
.PHONY: ${NXT_GO}-install
.PHONY: ${NXT_GO}-install-src
-.PHONY: ${NXT_GO}-install-build
.PHONY: ${NXT_GO}-uninstall
GOPATH = $NXT_GO_PATH
@@ -101,23 +102,30 @@ install: ${NXT_GO}-install
${NXT_GO}:
-${NXT_GO}-install: ${NXT_GO}-install-build
+${NXT_GO}-install: ${NXT_GO}-install-src ${NXT_GO}-install-env
+ GOPATH=\$(DESTDIR)\$(GOPATH) ${NXT_GO} build ${NXT_GO_PKG}
+
+${NXT_GO}-install-src:
+ install -d \$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}
+ install -p -m644 ./go/* \$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}/
-${NXT_GO}-install-src: ${NXT_VERSION_H}
- install -d \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit
- install -p -m644 ./src/*.h ./build/*.h ./src/go/unit/* \
- ./src/nxt_unit.c ./src/nxt_lvlhsh.c ./src/nxt_murmur_hash.c \
- ./src/nxt_websocket.c \
- \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit/
+${NXT_GO}-install-env: \$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}/env.go \
+ ${NXT_VERSION_H} ${NXT_BUILD_DIR}/${NXT_LIB_UNIT_STATIC}
-${NXT_GO}-install-build: ${NXT_GO}-install-src
- GOPATH=\$(DESTDIR)\$(GOPATH) ${NXT_GO} build nginx/unit
+\$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}/env.go:
+ install -d \$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}
+ $echo "package unit" > \$@
+ $echo "/*" >> \$@
+ $echo "#cgo CPPFLAGS: -I${PWD}/src -I${PWD}/${NXT_BUILD_DIR}" >> \$@
+ $echo "#cgo LDFLAGS: -L${PWD}/${NXT_BUILD_DIR}" >> \$@
+ $echo "*/" >> \$@
+ $echo 'import "C"' >> \$@
uninstall: ${NXT_GO}-uninstall
${NXT_GO}-uninstall:
- rm -rf \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit
- rm -rf \$(DESTDIR)\$(NXT_GO_DST)/pkg/\$(GOOS)_\$(GOARCH)/nginx/unit
+ rm -rf \$(DESTDIR)\$(NXT_GO_DST)/src/${NXT_GO_PKG}
+ rm -rf \$(DESTDIR)\$(NXT_GO_DST)/pkg/\$(GOOS)_\$(GOARCH)/${NXT_GO_PKG}
END
diff --git a/auto/modules/java_get_jar b/auto/modules/java_get_jar
index c61d0a53..52cd146f 100644
--- a/auto/modules/java_get_jar
+++ b/auto/modules/java_get_jar
@@ -8,7 +8,9 @@
# NXT_JAR_LOCAL_REPO=$HOME/.m2/repository/
NXT_JAR_FILE=${NXT_JAR_NAME}-${NXT_JAR_VERSION}.jar
-NXT_JAR_LOCAL="${NXT_JAR_LOCAL_REPO}${NXT_JAR_NAMESPACE}${NXT_JAR_NAME}/${NXT_JAR_VERSION}/${NXT_JAR_FILE}"
+NXT_JAR_LOCAL_DIR="${NXT_JAR_LOCAL_REPO}${NXT_JAR_NAMESPACE}${NXT_JAR_NAME}/${NXT_JAR_VERSION}"
+NXT_JAR_LOCAL="${NXT_JAR_LOCAL_DIR}/${NXT_JAR_FILE}"
+NXT_JAR_LOCAL_TMP="${NXT_JAR_LOCAL_DIR}/.${NXT_JAR_FILE}.$$"
NXT_JAR_URL=${NXT_JAR_REPO}${NXT_JAR_NAMESPACE}${NXT_JAR_NAME}/${NXT_JAR_VERSION}/${NXT_JAR_FILE}
if [ ! -f "$NXT_BUILD_DIR/$NXT_JAR_FILE" ]; then
@@ -16,8 +18,9 @@ if [ ! -f "$NXT_BUILD_DIR/$NXT_JAR_FILE" ]; then
$echo "getting remote $NXT_JAR_FILE ... "
$echo "getting remote $NXT_JAR_FILE ..." >> $NXT_AUTOCONF_ERR
- mkdir -p "${NXT_JAR_LOCAL_REPO}${NXT_JAR_NAMESPACE}${NXT_JAR_NAME}/${NXT_JAR_VERSION}/"
- curl --progress-bar "$NXT_JAR_URL" -o "$NXT_JAR_LOCAL"
+ mkdir -p "${NXT_JAR_LOCAL_DIR}"
+ curl --progress-bar "$NXT_JAR_URL" -o "$NXT_JAR_LOCAL_TMP"
+ mv "$NXT_JAR_LOCAL_TMP" "$NXT_JAR_LOCAL"
else
$echo "getting local $NXT_JAR_FILE"
$echo "getting local $NXT_JAR_FILE ..." >> $NXT_AUTOCONF_ERR
diff --git a/auto/sources b/auto/sources
index 155e388b..98e4a1f4 100644
--- a/auto/sources
+++ b/auto/sources
@@ -13,6 +13,7 @@ NXT_LIB_SRCS=" \
src/nxt_mem_map.c \
src/nxt_socket.c \
src/nxt_socketpair.c \
+ src/nxt_credential.c \
src/nxt_process.c \
src/nxt_process_title.c \
src/nxt_signal.c \
@@ -84,6 +85,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_response.c \
src/nxt_http_error.c \
src/nxt_http_route.c \
+ src/nxt_http_route_addr.c \
src/nxt_http_static.c \
src/nxt_http_proxy.c \
src/nxt_application.c \
@@ -162,6 +164,12 @@ NXT_TEST_SRCS=" \
src/test/nxt_strverscmp_test.c \
"
+
+if [ $NXT_HAVE_CLONE_NEWUSER = YES ]; then
+ NXT_TEST_SRCS="$NXT_TEST_SRCS src/test/nxt_clone_test.c"
+fi
+
+
NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
src/test/nxt_utf8_file_name_test.c \
"
diff --git a/auto/unix b/auto/unix
index 5813e5e8..7c241650 100644
--- a/auto/unix
+++ b/auto/unix
@@ -186,8 +186,8 @@ nxt_feature_libs=
nxt_feature_test="#include <spawn.h>
#include <unistd.h>
- int main() {
- (void) posix_spawn(NULL, NULL, NULL, NULL, NULL, NULL);
+ int main(int argc, char *argv[]) {
+ (void) posix_spawn(NULL, \"\", NULL, NULL, argv, NULL);
return 0;
}"
. auto/feature
@@ -208,3 +208,19 @@ nxt_feature_test="#include <stdlib.h>
return 0;
}"
. auto/feature
+
+
+# Linux, FreeBSD, Solaris getgrouplist()
+nxt_feature="getgrouplist()"
+nxt_feature_name=NXT_HAVE_GETGROUPLIST
+nxt_feature_run=
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <unistd.h>
+ #include <grp.h>
+
+ int main() {
+ getgrouplist(\"root\", 0, NULL, NULL);
+ return 0;
+ }"
+. auto/feature
diff --git a/docs/Makefile b/docs/Makefile
index bb4a1446..aa8aeb9b 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,7 +1,5 @@
+#!/usr/bin/make
-VER= $(shell grep 'define NXT_VERSION' src/nxt_main.h \
- | sed -e 's/^.*"\(.*\)".*/\1/')
-UNIT= unit-$(VER)
DEST= ../build
XSLS?= xslscript.pl
diff --git a/docs/changes.xml b/docs/changes.xml
index 6efb014d..e43bfc87 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -5,6 +5,65 @@
<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-go
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
+ ver="1.14.0" rev="1"
+ date="2019-12-26" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.14.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.14.0" rev="1"
+ date="2019-12-26" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="change">
+<para>
+the Go package import name changed to "unit.nginx.org/go".
+</para>
+</change>
+
+<change type="change">
+<para>
+Go package now links to libunit instead of including library sources.
+</para>
+</change>
+
+<change type="feature">
+<para>
+ability to change user and group for isolated applications when Unit daemon
+runs as an unprivileged user.
+</para>
+</change>
+
+<change type="feature">
+<para>
+request routing by source and destination addresses and ports.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+memory bloat on large responses.
+</para>
+</change>
+
+</changes>
+
+
<changes apply="unit-go1.13" ver="1.13.0" rev="1"
date="2019-11-20" time="09:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/src/go/unit/ldflags-lrt.go b/go/ldflags-lrt.go
index f5a63508..f5a63508 100644
--- a/src/go/unit/ldflags-lrt.go
+++ b/go/ldflags-lrt.go
diff --git a/go/ldflags.go b/go/ldflags.go
new file mode 100644
index 00000000..68f2ab78
--- /dev/null
+++ b/go/ldflags.go
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+package unit
+
+/*
+#cgo LDFLAGS: -lunit
+*/
+import "C"
diff --git a/src/go/unit/nxt_cgo_lib.c b/go/nxt_cgo_lib.c
index 5cb31b5a..a4fef9ea 100644
--- a/src/go/unit/nxt_cgo_lib.c
+++ b/go/nxt_cgo_lib.c
@@ -19,6 +19,7 @@ static ssize_t nxt_cgo_port_send(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id,
const void *buf, size_t buf_size, const void *oob, size_t oob_size);
static ssize_t nxt_cgo_port_recv(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id,
void *buf, size_t buf_size, void *oob, size_t oob_size);
+static void nxt_cgo_shm_ack_handler(nxt_unit_ctx_t *ctx);
int
nxt_cgo_run(uintptr_t handler)
@@ -34,6 +35,7 @@ nxt_cgo_run(uintptr_t handler)
init.callbacks.remove_port = nxt_cgo_remove_port;
init.callbacks.port_send = nxt_cgo_port_send;
init.callbacks.port_recv = nxt_cgo_port_recv;
+ init.callbacks.shm_ack_handler = nxt_cgo_shm_ack_handler;
init.data = (void *) handler;
@@ -137,6 +139,13 @@ nxt_cgo_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id,
}
+static void
+nxt_cgo_shm_ack_handler(nxt_unit_ctx_t *ctx)
+{
+ return nxt_go_shm_ack_handler();
+}
+
+
int
nxt_cgo_response_create(uintptr_t req, int status, int fields,
uint32_t fields_size)
@@ -166,15 +175,8 @@ nxt_cgo_response_send(uintptr_t req)
ssize_t
nxt_cgo_response_write(uintptr_t req, uintptr_t start, uint32_t len)
{
- int rc;
-
- rc = nxt_unit_response_write((nxt_unit_request_info_t *) req,
- (void *) start, len);
- if (rc != NXT_UNIT_OK) {
- return -1;
- }
-
- return len;
+ return nxt_unit_response_write_nb((nxt_unit_request_info_t *) req,
+ (void *) start, len, 0);
}
diff --git a/src/go/unit/nxt_cgo_lib.h b/go/nxt_cgo_lib.h
index 5317380b..5317380b 100644
--- a/src/go/unit/nxt_cgo_lib.h
+++ b/go/nxt_cgo_lib.h
diff --git a/go/observable.go b/go/observable.go
new file mode 100644
index 00000000..9a38802c
--- /dev/null
+++ b/go/observable.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+package unit
+
+import (
+ "sync"
+)
+
+type observable struct {
+ sync.Mutex
+ observers []chan int
+}
+
+func (o *observable) attach(c chan int) {
+ o.Lock()
+ defer o.Unlock()
+
+ o.observers = append(o.observers, c)
+}
+
+func (o *observable) notify(e int) {
+ o.Lock()
+ defer o.Unlock()
+
+ for _, v := range o.observers {
+ v <- e
+ }
+
+ o.observers = nil
+}
diff --git a/src/go/unit/port.go b/go/port.go
index a68cae74..a68cae74 100644
--- a/src/go/unit/port.go
+++ b/go/port.go
diff --git a/src/go/unit/request.go b/go/request.go
index 1d8c6702..1d8c6702 100644
--- a/src/go/unit/request.go
+++ b/go/request.go
diff --git a/src/go/unit/response.go b/go/response.go
index 767d66b7..bfa79656 100644
--- a/src/go/unit/response.go
+++ b/go/response.go
@@ -19,6 +19,7 @@ type response struct {
headerSent bool
req *http.Request
c_req C.uintptr_t
+ ch chan int
}
func new_response(c_req C.uintptr_t, req *http.Request) *response {
@@ -40,8 +41,26 @@ func (r *response) Write(p []byte) (n int, err error) {
r.WriteHeader(http.StatusOK)
}
- res := C.nxt_cgo_response_write(r.c_req, buf_ref(p), C.uint32_t(len(p)))
- return int(res), nil
+ l := len(p)
+ written := int(0)
+ br := buf_ref(p)
+
+ for written < l {
+ res := C.nxt_cgo_response_write(r.c_req, br, C.uint32_t(l - written))
+
+ written += int(res)
+ br += C.uintptr_t(res)
+
+ if (written < l) {
+ if r.ch == nil {
+ r.ch = make(chan int, 2)
+ }
+
+ wait_shm_ack(r.ch)
+ }
+ }
+
+ return written, nil
}
func (r *response) WriteHeader(code int) {
@@ -85,3 +104,16 @@ func (r *response) Flush() {
r.WriteHeader(http.StatusOK)
}
}
+
+var observer_registry_ observable
+
+func wait_shm_ack(c chan int) {
+ observer_registry_.attach(c)
+
+ _ = <-c
+}
+
+//export nxt_go_shm_ack_handler
+func nxt_go_shm_ack_handler() {
+ observer_registry_.notify(1)
+}
diff --git a/src/go/unit/unit.go b/go/unit.go
index 1534479e..1534479e 100644
--- a/src/go/unit/unit.go
+++ b/go/unit.go
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index 855dd6f7..13063fd8 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -22,8 +22,7 @@ include Makefile.php
include Makefile.python27
include Makefile.python37
include Makefile.python38
-include Makefile.go112
-include Makefile.go113
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -36,8 +35,7 @@ include Makefile.php
include Makefile.python27
include Makefile.python37
include Makefile.python38
-include Makefile.go110
-include Makefile.go111
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -50,8 +48,7 @@ include Makefile.php
include Makefile.python27
include Makefile.python36
include Makefile.python37
-include Makefile.go19
-include Makefile.go110
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -66,8 +63,7 @@ include Makefile.python27
include Makefile.python36
include Makefile.python37
include Makefile.python38
-include Makefile.go19
-include Makefile.go110
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -99,7 +95,7 @@ ifeq ($(CODENAME),buster)
include Makefile.php
include Makefile.python27
include Makefile.python37
-include Makefile.go111
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -111,8 +107,7 @@ ifeq ($(CODENAME),stretch)
include Makefile.php
include Makefile.python27
include Makefile.python35
-include Makefile.go17
-include Makefile.go18
+include Makefile.go
include Makefile.perl
include Makefile.ruby
include Makefile.jsc-common
@@ -200,7 +195,7 @@ endif
debuild/unit_$(VERSION).orig.tar.gz: | debuild/$(SRCDIR)/debian
cd ../.. && tar -czf pkg/deb/debuild/$(SRCDIR).tar.gz \
--transform "s#^#$(SRCDIR)/#" \
- LICENSE NOTICE CHANGES README configure auto src test version
+ LICENSE NOTICE CHANGES README configure auto src test version go
mv debuild/$(SRCDIR).tar.gz debuild/unit_$(VERSION).orig.tar.gz
cd debuild && tar zxf unit_$(VERSION).orig.tar.gz
diff --git a/pkg/deb/Makefile.go b/pkg/deb/Makefile.go
index 3399f5ca..cdccb5ed 100644
--- a/pkg/deb/Makefile.go
+++ b/pkg/deb/Makefile.go
@@ -8,7 +8,7 @@ MODULE_RELEASE_go= 1
MODULE_CONFARGS_go= go --go-path=/usr/share/gocode
MODULE_MAKEARGS_go= go
-MODULE_INSTARGS_go= go-install
+MODULE_INSTARGS_go= go-install-src
MODULE_SOURCES_go= unit.example-go-app \
unit.example-go-config
@@ -17,7 +17,9 @@ BUILD_DEPENDS_go= golang
BUILD_DEPENDS+= $(BUILD_DEPENDS_go)
MODULE_BUILD_DEPENDS_go=,golang
-MODULE_DEPENDS_go=,golang
+MODULE_DEPENDS_go=,golang,unit-dev (= $(VERSION)-$(RELEASE)~$(CODENAME))
+
+MODULE_NOARCH_go= true
define MODULE_PREINSTALL_go
mkdir -p debian/unit-go/usr/share/doc/unit-go/examples/go-app
diff --git a/pkg/deb/Makefile.go110 b/pkg/deb/Makefile.go110
deleted file mode 100644
index 0e956d9f..00000000
--- a/pkg/deb/Makefile.go110
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go110
-MODULE_SUFFIX_go110= go1.10
-
-MODULE_SUMMARY_go110= Go 1.10 module for NGINX Unit
-
-MODULE_VERSION_go110= $(VERSION)
-MODULE_RELEASE_go110= 1
-
-MODULE_CONFARGS_go110= go --go=/usr/lib/go-1.10/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go110= /usr/lib/go-1.10/bin/go
-MODULE_INSTARGS_go110= /usr/lib/go-1.10/bin/go-install
-
-MODULE_SOURCES_go110= unit.example-go-app \
- unit.example-go1.10-config
-
-BUILD_DEPENDS_go110= golang-1.10
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go110)
-
-MODULE_BUILD_DEPENDS_go110=,golang-1.10
-MODULE_DEPENDS_go110=,golang-1.10
-
-define MODULE_PREINSTALL_go110
- mkdir -p debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.10-config debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/unit.config
-endef
-export MODULE_PREINSTALL_go110
-
-define MODULE_POST_go110
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go110) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.10/bin/go build -o /tmp/go1.10-app /usr/share/doc/unit-$(MODULE_SUFFIX_go110)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go110)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go110
diff --git a/pkg/deb/Makefile.go111 b/pkg/deb/Makefile.go111
deleted file mode 100644
index f8ed5ae8..00000000
--- a/pkg/deb/Makefile.go111
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go111
-MODULE_SUFFIX_go111= go1.11
-
-MODULE_SUMMARY_go111= Go 1.11 module for NGINX Unit
-
-MODULE_VERSION_go111= $(VERSION)
-MODULE_RELEASE_go111= 1
-
-MODULE_CONFARGS_go111= go --go=/usr/lib/go-1.11/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go111= /usr/lib/go-1.11/bin/go
-MODULE_INSTARGS_go111= /usr/lib/go-1.11/bin/go-install
-
-MODULE_SOURCES_go111= unit.example-go-app \
- unit.example-go1.11-config
-
-BUILD_DEPENDS_go111= golang-1.11
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go111)
-
-MODULE_BUILD_DEPENDS_go111=,golang-1.11
-MODULE_DEPENDS_go111=,golang-1.11
-
-define MODULE_PREINSTALL_go111
- mkdir -p debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.11-config debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/unit.config
-endef
-export MODULE_PREINSTALL_go111
-
-define MODULE_POST_go111
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go111) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.11/bin/go build -o /tmp/go1.11-app /usr/share/doc/unit-$(MODULE_SUFFIX_go111)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go111)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go111
diff --git a/pkg/deb/Makefile.go112 b/pkg/deb/Makefile.go112
deleted file mode 100644
index f64ab4c5..00000000
--- a/pkg/deb/Makefile.go112
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go112
-MODULE_SUFFIX_go112= go1.12
-
-MODULE_SUMMARY_go112= Go 1.12 module for NGINX Unit
-
-MODULE_VERSION_go112= $(VERSION)
-MODULE_RELEASE_go112= 1
-
-MODULE_CONFARGS_go112= go --go=/usr/lib/go-1.12/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go112= /usr/lib/go-1.12/bin/go
-MODULE_INSTARGS_go112= /usr/lib/go-1.12/bin/go-install
-
-MODULE_SOURCES_go112= unit.example-go-app \
- unit.example-go1.12-config
-
-BUILD_DEPENDS_go112= golang-1.12
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go112)
-
-MODULE_BUILD_DEPENDS_go112=,golang-1.12
-MODULE_DEPENDS_go112=,golang-1.12
-
-define MODULE_PREINSTALL_go112
- mkdir -p debian/unit-go1.12/usr/share/doc/unit-go1.12/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.12/usr/share/doc/unit-go1.12/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.12-config debian/unit-go1.12/usr/share/doc/unit-go1.12/examples/unit.config
-endef
-export MODULE_PREINSTALL_go112
-
-define MODULE_POST_go112
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go112) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.12/bin/go build -o /tmp/go1.12-app /usr/share/doc/unit-$(MODULE_SUFFIX_go112)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go112)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go112
diff --git a/pkg/deb/Makefile.go113 b/pkg/deb/Makefile.go113
deleted file mode 100644
index ded2e45a..00000000
--- a/pkg/deb/Makefile.go113
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go113
-MODULE_SUFFIX_go113= go1.13
-
-MODULE_SUMMARY_go113= Go 1.13 module for NGINX Unit
-
-MODULE_VERSION_go113= $(VERSION)
-MODULE_RELEASE_go113= 1
-
-MODULE_CONFARGS_go113= go --go=/usr/lib/go-1.13/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go113= /usr/lib/go-1.13/bin/go
-MODULE_INSTARGS_go113= /usr/lib/go-1.13/bin/go-install
-
-MODULE_SOURCES_go113= unit.example-go-app \
- unit.example-go1.13-config
-
-BUILD_DEPENDS_go113= golang-1.13
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go113)
-
-MODULE_BUILD_DEPENDS_go113=,golang-1.13
-MODULE_DEPENDS_go113=,golang-1.13
-
-define MODULE_PREINSTALL_go113
- mkdir -p debian/unit-go1.13/usr/share/doc/unit-go1.13/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.13/usr/share/doc/unit-go1.13/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.13-config debian/unit-go1.13/usr/share/doc/unit-go1.13/examples/unit.config
-endef
-export MODULE_PREINSTALL_go113
-
-define MODULE_POST_go113
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go113) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.13/bin/go build -o /tmp/go1.13-app /usr/share/doc/unit-$(MODULE_SUFFIX_go113)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go113)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go113
diff --git a/pkg/deb/Makefile.go17 b/pkg/deb/Makefile.go17
deleted file mode 100644
index d014edd1..00000000
--- a/pkg/deb/Makefile.go17
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go17
-MODULE_SUFFIX_go17= go1.7
-
-MODULE_SUMMARY_go17= Go 1.7 module for NGINX Unit
-
-MODULE_VERSION_go17= $(VERSION)
-MODULE_RELEASE_go17= 1
-
-MODULE_CONFARGS_go17= go --go=/usr/lib/go-1.7/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go17= /usr/lib/go-1.7/bin/go
-MODULE_INSTARGS_go17= /usr/lib/go-1.7/bin/go-install
-
-MODULE_SOURCES_go17= unit.example-go-app \
- unit.example-go1.7-config
-
-BUILD_DEPENDS_go17= golang-1.7
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go17)
-
-MODULE_BUILD_DEPENDS_go17=,golang-1.7
-MODULE_DEPENDS_go17=,golang-1.7
-
-define MODULE_PREINSTALL_go17
- mkdir -p debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.7-config debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/unit.config
-endef
-export MODULE_PREINSTALL_go17
-
-define MODULE_POST_go17
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go17) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.7/bin/go build -o /tmp/go1.7-app /usr/share/doc/unit-$(MODULE_SUFFIX_go17)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go17)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go17
diff --git a/pkg/deb/Makefile.go18 b/pkg/deb/Makefile.go18
deleted file mode 100644
index 597f4804..00000000
--- a/pkg/deb/Makefile.go18
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go18
-MODULE_SUFFIX_go18= go1.8
-
-MODULE_SUMMARY_go18= Go 1.8 module for NGINX Unit
-
-MODULE_VERSION_go18= $(VERSION)
-MODULE_RELEASE_go18= 1
-
-MODULE_CONFARGS_go18= go --go=/usr/lib/go-1.8/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go18= /usr/lib/go-1.8/bin/go
-MODULE_INSTARGS_go18= /usr/lib/go-1.8/bin/go-install
-
-MODULE_SOURCES_go18= unit.example-go-app \
- unit.example-go1.8-config
-
-BUILD_DEPENDS_go18= golang-1.8
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go18)
-
-MODULE_BUILD_DEPENDS_go18=,golang-1.8
-MODULE_DEPENDS_go18=,golang-1.8
-
-define MODULE_PREINSTALL_go18
- mkdir -p debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.8-config debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/unit.config
-endef
-export MODULE_PREINSTALL_go18
-
-define MODULE_POST_go18
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go18) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.8/bin/go build -o /tmp/go1.8-app /usr/share/doc/unit-$(MODULE_SUFFIX_go18)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go18)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go18
diff --git a/pkg/deb/Makefile.go19 b/pkg/deb/Makefile.go19
deleted file mode 100644
index 60d4b239..00000000
--- a/pkg/deb/Makefile.go19
+++ /dev/null
@@ -1,48 +0,0 @@
-MODULES+= go19
-MODULE_SUFFIX_go19= go1.9
-
-MODULE_SUMMARY_go19= Go 1.9 module for NGINX Unit
-
-MODULE_VERSION_go19= $(VERSION)
-MODULE_RELEASE_go19= 1
-
-MODULE_CONFARGS_go19= go --go=/usr/lib/go-1.9/bin/go --go-path=/usr/share/gocode
-MODULE_MAKEARGS_go19= /usr/lib/go-1.9/bin/go
-MODULE_INSTARGS_go19= /usr/lib/go-1.9/bin/go-install
-
-MODULE_SOURCES_go19= unit.example-go-app \
- unit.example-go1.9-config
-
-BUILD_DEPENDS_go19= golang-1.9
-BUILD_DEPENDS+= $(BUILD_DEPENDS_go19)
-
-MODULE_BUILD_DEPENDS_go19=,golang-1.9
-MODULE_DEPENDS_go19=,golang-1.9
-
-define MODULE_PREINSTALL_go19
- mkdir -p debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app
- install -m 644 -p debian/unit.example-go-app debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app/let-my-people.go
- install -m 644 -p debian/unit.example-go1.9-config debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/unit.config
-endef
-export MODULE_PREINSTALL_go19
-
-define MODULE_POST_go19
-cat <<BANNER
-----------------------------------------------------------------------
-
-The $(MODULE_SUMMARY_go19) has been installed.
-
-To check out the sample app, run these commands:
-
- GOPATH=/usr/share/gocode /usr/lib/go-1.9/bin/go build -o /tmp/go1.9-app /usr/share/doc/unit-$(MODULE_SUFFIX_go19)/examples/go-app/let-my-people.go
- sudo service unit restart
- cd /usr/share/doc/unit-$(MODULE_SUFFIX_go19)/examples
- sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
- curl http://localhost:8500/
-
-Online documentation is available at https://unit.nginx.org
-
-----------------------------------------------------------------------
-BANNER
-endef
-export MODULE_POST_go19
diff --git a/pkg/deb/debian.module/rules-noarch.in b/pkg/deb/debian.module/rules-noarch.in
index 823675ba..a24602c4 100644
--- a/pkg/deb/debian.module/rules-noarch.in
+++ b/pkg/deb/debian.module/rules-noarch.in
@@ -27,6 +27,7 @@ config.env.%:
cp -Pa $(CURDIR)/LICENSE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/NOTICE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/README $(BUILDDIR_$*)/
+ cp -Pa $(CURDIR)/go $(BUILDDIR_$*)/
touch $@
configure.unit: config.env.unit
diff --git a/pkg/deb/debian.module/rules.in b/pkg/deb/debian.module/rules.in
index 1391e01a..8ee277b3 100755
--- a/pkg/deb/debian.module/rules.in
+++ b/pkg/deb/debian.module/rules.in
@@ -27,6 +27,7 @@ config.env.%:
cp -Pa $(CURDIR)/LICENSE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/NOTICE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/README $(BUILDDIR_$*)/
+ cp -Pa $(CURDIR)/go $(BUILDDIR_$*)/
touch $@
configure.unit: config.env.unit
diff --git a/pkg/deb/debian.module/unit.example-go-app b/pkg/deb/debian.module/unit.example-go-app
index 7ca0c9fd..6eec1dbb 100644
--- a/pkg/deb/debian.module/unit.example-go-app
+++ b/pkg/deb/debian.module/unit.example-go-app
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/deb/debian.module/unit.example-go1.10-config b/pkg/deb/debian.module/unit.example-go1.10-config
deleted file mode 100644
index 61790b73..00000000
--- a/pkg/deb/debian.module/unit.example-go1.10-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.10-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.11-config b/pkg/deb/debian.module/unit.example-go1.11-config
deleted file mode 100644
index 5e063a4c..00000000
--- a/pkg/deb/debian.module/unit.example-go1.11-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.11-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.12-config b/pkg/deb/debian.module/unit.example-go1.12-config
deleted file mode 100644
index b49f0aba..00000000
--- a/pkg/deb/debian.module/unit.example-go1.12-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.12-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.13-config b/pkg/deb/debian.module/unit.example-go1.13-config
deleted file mode 100644
index 7dbffb17..00000000
--- a/pkg/deb/debian.module/unit.example-go1.13-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.13-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.7-config b/pkg/deb/debian.module/unit.example-go1.7-config
deleted file mode 100644
index e1a8e1a4..00000000
--- a/pkg/deb/debian.module/unit.example-go1.7-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.7-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.8-config b/pkg/deb/debian.module/unit.example-go1.8-config
deleted file mode 100644
index e570f38c..00000000
--- a/pkg/deb/debian.module/unit.example-go1.8-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.8-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian.module/unit.example-go1.9-config b/pkg/deb/debian.module/unit.example-go1.9-config
deleted file mode 100644
index 90ef7d5f..00000000
--- a/pkg/deb/debian.module/unit.example-go1.9-config
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "applications": {
- "example_go": {
- "type": "external",
- "user": "nobody",
- "executable": "/tmp/go1.9-app"
- }
- },
-
- "listeners": {
- "*:8500": {
- "pass": "applications/example_go"
- }
- }
-}
diff --git a/pkg/deb/debian/rules.in b/pkg/deb/debian/rules.in
index bee9223f..a4696793 100644
--- a/pkg/deb/debian/rules.in
+++ b/pkg/deb/debian/rules.in
@@ -31,6 +31,7 @@ config.env.%:
cp -Pa $(CURDIR)/LICENSE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/NOTICE $(BUILDDIR_$*)/
cp -Pa $(CURDIR)/README $(BUILDDIR_$*)/
+ cp -Pa $(CURDIR)/go $(BUILDDIR_$*)/
touch $@
configure.unit: config.env.unit
diff --git a/pkg/deb/debian/unit.example-go-app b/pkg/deb/debian/unit.example-go-app
index 7ca0c9fd..6eec1dbb 100644
--- a/pkg/deb/debian/unit.example-go-app
+++ b/pkg/deb/debian/unit.example-go-app
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
index cf3b1150..b71b74fb 100644
--- a/pkg/docker/Dockerfile.full
+++ b/pkg/docker/Dockerfile.full
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -22,17 +22,17 @@ RUN set -x \
test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-php=${UNIT_VERSION} unit-python2.7=${UNIT_VERSION} unit-python3.5=${UNIT_VERSION} unit-perl=${UNIT_VERSION} unit-ruby=${UNIT_VERSION}" \
+ && unitPackages="unit=${UNIT_VERSION} unit-php=${UNIT_VERSION} unit-python2.7=${UNIT_VERSION} unit-python3.7=${UNIT_VERSION} unit-perl=${UNIT_VERSION} unit-ruby=${UNIT_VERSION}" \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.go1.8-dev b/pkg/docker/Dockerfile.go1.11-dev
index d38b669b..92fa56f6 100644
--- a/pkg/docker/Dockerfile.go1.8-dev
+++ b/pkg/docker/Dockerfile.go1.11-dev
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -22,17 +22,17 @@ RUN set -x \
test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-go1.8=${UNIT_VERSION} gcc" \
+ && unitPackages="unit=${UNIT_VERSION} unit-go=${UNIT_VERSION} gcc" \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.go1.7-dev b/pkg/docker/Dockerfile.go1.7-dev
deleted file mode 100644
index 5622271e..00000000
--- a/pkg/docker/Dockerfile.go1.7-dev
+++ /dev/null
@@ -1,93 +0,0 @@
-FROM debian:stretch-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.13.0-1~stretch
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-go1.7=${UNIT_VERSION} gcc" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index 4567b2da..26a55d9c 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.perl5.24 b/pkg/docker/Dockerfile.perl5.28
index 64d68a4e..3be4a6dc 100644
--- a/pkg/docker/Dockerfile.perl5.24
+++ b/pkg/docker/Dockerfile.perl5.28
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.php7.0 b/pkg/docker/Dockerfile.php7.3
index 649de902..36a2dfbf 100644
--- a/pkg/docker/Dockerfile.php7.0
+++ b/pkg/docker/Dockerfile.php7.3
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
index e9866349..0dd2f9a5 100644
--- a/pkg/docker/Dockerfile.python2.7
+++ b/pkg/docker/Dockerfile.python2.7
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.python3.5 b/pkg/docker/Dockerfile.python3.7
index 831bc54a..fd6f1639 100644
--- a/pkg/docker/Dockerfile.python3.5
+++ b/pkg/docker/Dockerfile.python3.7
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -22,17 +22,17 @@ RUN set -x \
test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-python3.5=${UNIT_VERSION}" \
+ && unitPackages="unit=${UNIT_VERSION} unit-python3.7=${UNIT_VERSION}" \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.ruby2.3 b/pkg/docker/Dockerfile.ruby2.5
index 181b2525..034ae542 100644
--- a/pkg/docker/Dockerfile.ruby2.3
+++ b/pkg/docker/Dockerfile.ruby2.5
@@ -1,8 +1,8 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.13.0-1~stretch
+ENV UNIT_VERSION 1.14.0-1~buster
RUN set -x \
&& apt-get update \
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Dockerfile.tmpl b/pkg/docker/Dockerfile.tmpl
index c721931b..3372ef6f 100644
--- a/pkg/docker/Dockerfile.tmpl
+++ b/pkg/docker/Dockerfile.tmpl
@@ -1,4 +1,4 @@
-FROM debian:stretch-slim
+FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
@@ -26,13 +26,13 @@ RUN set -x \
&& case "$dpkgArch" in \
amd64|i386) \
# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ stretch unit" >> /etc/apt/sources.list.d/unit.list \
+ echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile
index e826cb95..d80b8763 100644
--- a/pkg/docker/Makefile
+++ b/pkg/docker/Makefile
@@ -6,42 +6,29 @@ DEFAULT_RELEASE := 1
VERSION ?= $(NXT_VERSION)
RELEASE ?= $(DEFAULT_RELEASE)
-CODENAME := stretch
+CODENAME := buster
UNIT_VERSION = $(VERSION)-$(RELEASE)~$(CODENAME)
-MODULES = python2.7 python3.5 php7.0 go1.7-dev go1.8-dev perl5.24 ruby2.3 \
+MODULES = python2.7 python3.7 php7.3 go1.11-dev perl5.28 ruby2.5 \
full minimal
-MODULE_php7.0="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION}"
+MODULE_php7.3="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION}"
MODULE_python2.7="unit=$${UNIT_VERSION} unit-python2.7=$${UNIT_VERSION}"
-MODULE_python3.5="unit=$${UNIT_VERSION} unit-python3.5=$${UNIT_VERSION}"
+MODULE_python3.7="unit=$${UNIT_VERSION} unit-python3.7=$${UNIT_VERSION}"
-MODULE_go1.7-dev="unit=$${UNIT_VERSION} unit-go1.7=$${UNIT_VERSION} gcc"
+MODULE_go1.11-dev="unit=$${UNIT_VERSION} unit-go=$${UNIT_VERSION} gcc"
-MODULE_go1.8-dev="unit=$${UNIT_VERSION} unit-go1.8=$${UNIT_VERSION} gcc"
+MODULE_perl5.28="unit=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION}"
-MODULE_perl5.24="unit=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION}"
+MODULE_ruby2.5="unit=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION}"
-MODULE_ruby2.3="unit=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION}"
-
-MODULE_full="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION} unit-python2.7=$${UNIT_VERSION} unit-python3.5=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION}"
+MODULE_full="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION} unit-python2.7=$${UNIT_VERSION} unit-python3.7=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION}"
MODULE_minimal="unit=$${UNIT_VERSION}"
-export \
- MODULE_python2.7 \
- MODULE_python3.5 \
- MODULE_php7.0 \
- MODULE_go1.7-dev \
- MODULE_go1.8-dev \
- MODULE_perl5.24 \
- MODULE_ruby2.3 \
- MODULE_full \
- MODULE_minimal
-
default:
@echo "valid targets: all build dockerfiles push clean"
diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile
index 4e970e45..34f79bcc 100644
--- a/pkg/rpm/Makefile
+++ b/pkg/rpm/Makefile
@@ -57,7 +57,8 @@ endif
ifeq ($(OSVER), centos7)
include Makefile.php
-include Makefile.python
+include Makefile.python27
+include Makefile.python36
include Makefile.go
include Makefile.perl
include Makefile.jsc-common
@@ -90,7 +91,8 @@ endif
ifeq ($(OSVER), amazonlinux2)
include Makefile.php
-include Makefile.python
+include Makefile.python27
+include Makefile.python37
include Makefile.go
include Makefile.perl
include Makefile.jsc-common
@@ -202,7 +204,7 @@ endif
rpmbuild/SOURCES/unit-$(VERSION).tar.gz:
cd ../.. && tar -czf pkg/rpm/rpmbuild/SOURCES/unit-$(VERSION).tar.gz \
--transform "s#^#unit-$(VERSION)/#" \
- LICENSE NOTICE CHANGES README configure auto src test version
+ LICENSE NOTICE CHANGES README configure auto src test version go
unit: check-build-depends-unit rpmbuild/SPECS/unit.spec rpmbuild/SOURCES/unit-$(VERSION).tar.gz
@echo "===> Building $@ package" ; \
diff --git a/pkg/rpm/Makefile.go b/pkg/rpm/Makefile.go
index 09dffd21..e3891ab4 100644
--- a/pkg/rpm/Makefile.go
+++ b/pkg/rpm/Makefile.go
@@ -8,7 +8,7 @@ MODULE_RELEASE_go= 1
MODULE_CONFARGS_go= go --go-path=%{gopath}
MODULE_MAKEARGS_go= go
-MODULE_INSTARGS_go= go-install
+MODULE_INSTARGS_go= go-install-src
MODULE_SOURCES_go= unit.example-go-app \
unit.example-go-config
@@ -25,11 +25,15 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go)
ifneq (,$(findstring $(OSVER),opensuse-leap opensuse-tumbleweed))
define MODULE_DEFINITIONS_go
+BuildArch: noarch
+Requires: unit-devel == $(VERSION)-$(RELEASE)%{?dist}.ngx
BuildRequires: $(BUILD_DEPENDS_go)
%define gopath /usr/share/go/contrib
endef
else
define MODULE_DEFINITIONS_go
+BuildArch: noarch
+Requires: unit-devel == $(VERSION)-$(RELEASE)%{?dist}.ngx
BuildRequires: $(BUILD_DEPENDS_go)
endef
endif
@@ -48,8 +52,8 @@ endef
export MODULE_PREINSTALL_go
define MODULE_FILES_go
-%dir %{gopath}/src/nginx/unit
-%{gopath}/src/nginx/unit/*
+%dir %{gopath}/src/unit.nginx.org/go
+%{gopath}/src/unit.nginx.org/go/*
endef
export MODULE_FILES_go
diff --git a/pkg/rpm/Makefile.python27 b/pkg/rpm/Makefile.python27
index 95b392a9..079a8512 100644
--- a/pkg/rpm/Makefile.python27
+++ b/pkg/rpm/Makefile.python27
@@ -17,12 +17,21 @@ ifneq (,$(findstring $(OSVER),opensuse-leap opensuse-tumbleweed sles))
BUILD_DEPENDS_python27= python-devel
else ifneq (,$(findstring $(OSVER),fedora centos8))
BUILD_DEPENDS_python27= python2-devel
+else ifneq (,$(findstring $(OSVER),centos7 amazonlinux2))
+BUILD_DEPENDS_python27= python-devel
else
BUILD_DEPENDS_python27= python27-devel
endif
BUILD_DEPENDS+= $(BUILD_DEPENDS_python27)
+define MODULE_DEFINITIONS_python27
+%if (0%{?rhel} == 7) || (0%{?amzn} == 2)
+Obsoletes: unit-python
+%endif
+endef
+export MODULE_DEFINITIONS_python27
+
define MODULE_PREINSTALL_python27
%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python27/examples/python-app
%{__install} -m 644 -p %{SOURCE100} \
diff --git a/pkg/rpm/Makefile.python36 b/pkg/rpm/Makefile.python36
index 25e33968..827d2253 100644
--- a/pkg/rpm/Makefile.python36
+++ b/pkg/rpm/Makefile.python36
@@ -13,7 +13,7 @@ MODULE_INSTARGS_python36= python3.6-install
MODULE_SOURCES_python36= unit.example-python-app \
unit.example-python36-config
-ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora))
+ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora centos7))
BUILD_DEPENDS_python36= python3-devel
else
BUILD_DEPENDS_python36= python36-devel
diff --git a/pkg/rpm/Makefile.python37 b/pkg/rpm/Makefile.python37
index ed9462b8..563d4539 100644
--- a/pkg/rpm/Makefile.python37
+++ b/pkg/rpm/Makefile.python37
@@ -13,7 +13,7 @@ MODULE_INSTARGS_python37= python3.7-install
MODULE_SOURCES_python37= unit.example-python-app \
unit.example-python37-config
-ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora))
+ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora amazonlinux2))
BUILD_DEPENDS_python37= python3-devel
else
BUILD_DEPENDS_python37= python37-devel
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-app b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-app
index 7ca0c9fd..6eec1dbb 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-app
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-app
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index c42149a5..2f324329 100644
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -227,6 +227,7 @@ ServerResponse.prototype._write = unit_lib.response_write;
ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
var contentLength = 0;
+ var res, o;
this._sendHeaders();
@@ -247,11 +248,32 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
if (typeof chunk === 'string') {
contentLength = Buffer.byteLength(chunk, encoding);
+ if (contentLength > unit_lib.buf_min) {
+ chunk = Buffer.from(chunk, encoding);
+
+ contentLength = chunk.length;
+ }
+
} else {
contentLength = chunk.length;
}
- this._write(chunk, contentLength);
+ if (this.server._output.length > 0 || !this.socket.writable) {
+ o = new BufferedOutput(this, 0, chunk, encoding, callback);
+ this.server._output.push(o);
+
+ return false;
+ }
+
+ res = this._write(chunk, 0, contentLength);
+ if (res < contentLength) {
+ this.socket.writable = false;
+
+ o = new BufferedOutput(this, res, chunk, encoding, callback);
+ this.server._output.push(o);
+
+ return false;
+ }
}
if (typeof callback === 'function') {
@@ -265,29 +287,48 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
* the event loop. All callbacks passed to process.nextTick()
* will be resolved before the event loop continues.
*/
- process.nextTick(function () {
- callback(this);
- }.bind(this));
+ process.nextTick(callback);
}
+
+ return true;
};
ServerResponse.prototype.write = function write(chunk, encoding, callback) {
if (this.finished) {
- throw new Error("Write after end");
- }
+ if (typeof encoding === 'function') {
+ callback = encoding;
+ encoding = null;
+ }
- this._writeBody(chunk, encoding, callback);
+ var err = new Error("Write after end");
+ process.nextTick(() => {
+ this.emit('error', err);
- return true;
+ if (typeof callback === 'function') {
+ callback(err);
+ }
+ })
+ }
+
+ return this._writeBody(chunk, encoding, callback);
};
ServerResponse.prototype._end = unit_lib.response_end;
ServerResponse.prototype.end = function end(chunk, encoding, callback) {
if (!this.finished) {
- this._writeBody(chunk, encoding, callback);
+ if (typeof encoding === 'function') {
+ callback = encoding;
+ encoding = null;
+ }
- this._end();
+ this._writeBody(chunk, encoding, () => {
+ this._end();
+
+ if (typeof callback === 'function') {
+ callback();
+ }
+ });
this.finished = true;
}
@@ -393,6 +434,9 @@ function Server(requestListener) {
this._upgradeListenerCount--;
}
});
+
+ this._output = [];
+ this._drain_resp = new Set();
}
util.inherits(Server, EventEmitter);
@@ -429,6 +473,63 @@ Server.prototype.emit_close = function () {
this.emit('close');
};
+Server.prototype.emit_drain = function () {
+ var res, o, l;
+
+ if (this._output.length <= 0) {
+ return;
+ }
+
+ while (this._output.length > 0) {
+ o = this._output[0];
+
+ if (typeof o.chunk === 'string') {
+ l = Buffer.byteLength(o.chunk, o.encoding);
+
+ } else {
+ l = o.chunk.length;
+ }
+
+ res = o.resp._write(o.chunk, o.offset, l);
+
+ o.offset += res;
+ if (o.offset < l) {
+ return;
+ }
+
+ this._drain_resp.add(o.resp);
+
+ if (typeof o.callback === 'function') {
+ process.nextTick(o.callback);
+ }
+
+ this._output.shift();
+ }
+
+ for (var resp of this._drain_resp) {
+
+ if (resp.socket.writable) {
+ continue;
+ }
+
+ resp.socket.writable = true;
+
+ process.nextTick(() => {
+ resp.emit("drain");
+ });
+ }
+
+ this._drain_resp.clear();
+};
+
+function BufferedOutput(resp, offset, chunk, encoding, callback) {
+ this.resp = resp;
+ this.offset = offset;
+ this.chunk = chunk;
+ this.encoding = encoding;
+ this.callback = callback;
+}
+
function connectionListener(socket) {
}
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index 10875703..1fa73689 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -70,6 +70,8 @@ Unit::init(napi_env env, napi_value exports)
websocket_send_frame);
napi.set_named_property(exports, "websocket_set_sock",
websocket_set_sock);
+ napi.set_named_property(exports, "buf_min", nxt_unit_buf_min());
+ napi.set_named_property(exports, "buf_max", nxt_unit_buf_max());
} catch (exception &e) {
napi.throw_error(e);
@@ -148,6 +150,7 @@ Unit::create_server(napi_env env, napi_callback_info info)
unit_init.callbacks.request_handler = request_handler_cb;
unit_init.callbacks.websocket_handler = websocket_handler_cb;
unit_init.callbacks.close_handler = close_handler_cb;
+ unit_init.callbacks.shm_ack_handler = shm_ack_handler_cb;
unit_init.callbacks.add_port = add_port;
unit_init.callbacks.remove_port = remove_port;
unit_init.callbacks.quit = quit_cb;
@@ -308,6 +311,40 @@ Unit::close_handler(nxt_unit_request_info_t *req)
}
+void
+Unit::shm_ack_handler_cb(nxt_unit_ctx_t *ctx)
+{
+ Unit *obj;
+
+ obj = reinterpret_cast<Unit *>(ctx->unit->data);
+
+ obj->shm_ack_handler(ctx);
+}
+
+
+void
+Unit::shm_ack_handler(nxt_unit_ctx_t *ctx)
+{
+ napi_value server_obj, emit_drain;
+
+ try {
+ nxt_handle_scope scope(env());
+
+ server_obj = get_server_object();
+
+ emit_drain = get_named_property(server_obj, "emit_drain");
+
+ nxt_async_context async_context(env(), "shm_ack_handler");
+ nxt_callback_scope async_scope(async_context);
+
+ make_callback(async_context, server_obj, emit_drain);
+
+ } catch (exception &e) {
+ nxt_unit_warn(ctx, "shm_ack_handler: %s", e.str);
+ }
+}
+
+
static void
nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
{
@@ -748,47 +785,68 @@ Unit::response_write(napi_env env, napi_callback_info info)
int ret;
void *ptr;
size_t argc, have_buf_len;
- uint32_t buf_len;
+ ssize_t res_len;
+ uint32_t buf_start, buf_len;
nxt_napi napi(env);
napi_value this_arg;
nxt_unit_buf_t *buf;
napi_valuetype buf_type;
nxt_unit_request_info_t *req;
- napi_value argv[2];
+ napi_value argv[3];
- argc = 2;
+ argc = 3;
try {
this_arg = napi.get_cb_info(info, argc, argv);
- if (argc != 2) {
+ if (argc != 3) {
throw exception("Wrong args count. Expected: "
- "chunk, chunk length");
+ "chunk, start, length");
}
req = napi.get_request_info(this_arg);
buf_type = napi.type_of(argv[0]);
- buf_len = napi.get_value_uint32(argv[1]) + 1;
-
- buf = nxt_unit_response_buf_alloc(req, buf_len);
- if (buf == NULL) {
- throw exception("Failed to allocate response buffer");
- }
+ buf_start = napi.get_value_uint32(argv[1]);
+ buf_len = napi.get_value_uint32(argv[2]) + 1;
if (buf_type == napi_string) {
/* TODO: will work only for utf8 content-type */
+ if (req->response_buf != NULL
+ && (req->response_buf->end - req->response_buf->free)
+ >= buf_len)
+ {
+ buf = req->response_buf;
+
+ } else {
+ buf = nxt_unit_response_buf_alloc(req, buf_len);
+ if (buf == NULL) {
+ throw exception("Failed to allocate response buffer");
+ }
+ }
+
have_buf_len = napi.get_value_string_utf8(argv[0], buf->free,
buf_len);
+ buf->free += have_buf_len;
+
+ ret = nxt_unit_buf_send(buf);
+ if (ret == NXT_UNIT_OK) {
+ res_len = have_buf_len;
+ }
+
} else {
ptr = napi.get_buffer_info(argv[0], have_buf_len);
- memcpy(buf->free, ptr, have_buf_len);
- }
+ if (buf_start > 0) {
+ ptr = ((uint8_t *) ptr) + buf_start;
+ have_buf_len -= buf_start;
+ }
- buf->free += have_buf_len;
+ res_len = nxt_unit_response_write_nb(req, ptr, have_buf_len, 0);
+
+ ret = res_len < 0 ? -res_len : NXT_UNIT_OK;
+ }
- ret = nxt_unit_buf_send(buf);
if (ret != NXT_UNIT_OK) {
throw exception("Failed to send body buf");
}
@@ -797,7 +855,7 @@ Unit::response_write(napi_env env, napi_callback_info info)
return nullptr;
}
- return this_arg;
+ return napi.create((int64_t) res_len);
}
diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h
index f5eaf9fd..18359118 100644
--- a/src/nodejs/unit-http/unit.h
+++ b/src/nodejs/unit-http/unit.h
@@ -36,6 +36,9 @@ private:
static void close_handler_cb(nxt_unit_request_info_t *req);
void close_handler(nxt_unit_request_info_t *req);
+ static void shm_ack_handler_cb(nxt_unit_ctx_t *ctx);
+ void shm_ack_handler(nxt_unit_ctx_t *ctx);
+
static int add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
static void remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id);
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 2a1fa39e..e7177887 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -89,6 +89,9 @@ struct nxt_common_app_conf_s {
nxt_conf_value_t *environment;
nxt_conf_value_t *isolation;
+ nxt_conf_value_t *limits;
+
+ size_t shm_limit;
union {
nxt_external_app_conf_t external;
diff --git a/src/nxt_capability.c b/src/nxt_capability.c
index 805faff6..dfa7a834 100644
--- a/src/nxt_capability.c
+++ b/src/nxt_capability.c
@@ -10,6 +10,16 @@
#include <linux/capability.h>
#include <sys/syscall.h>
+
+#if (_LINUX_CAPABILITY_VERSION_3)
+#define NXT_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_3
+#elif (_LINUX_CAPABILITY_VERSION_2)
+#define NXT_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_2
+#else
+#define NXT_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION
+#endif
+
+
#define nxt_capget(hdrp, datap) \
syscall(SYS_capget, hdrp, datap)
#define nxt_capset(hdrp, datap) \
@@ -43,7 +53,7 @@ nxt_capability_linux_get_version()
{
struct __user_cap_header_struct hdr;
- hdr.version = _LINUX_CAPABILITY_VERSION;
+ hdr.version = NXT_CAPABILITY_VERSION;
hdr.pid = nxt_pid;
nxt_capget(&hdr, NULL);
diff --git a/src/nxt_clone.c b/src/nxt_clone.c
index a2c376a3..9ee3c012 100644
--- a/src/nxt_clone.c
+++ b/src/nxt_clone.c
@@ -25,26 +25,18 @@ nxt_clone(nxt_int_t flags)
#if (NXT_HAVE_CLONE_NEWUSER)
-/* map uid 65534 to unit pid */
-#define NXT_DEFAULT_UNPRIV_MAP "65534 %d 1"
-
-nxt_int_t nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid,
+nxt_int_t nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid,
const char *str);
-nxt_int_t nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile,
- pid_t pid, nxt_int_t defval, nxt_conf_value_t *mapobj);
-nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile,
+nxt_int_t nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile,
+ pid_t pid, nxt_int_t default_container, nxt_int_t default_host,
+ nxt_clone_credential_map_t *map);
+nxt_int_t nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile,
pid_t pid, u_char *mapinfo);
-typedef struct {
- nxt_int_t container;
- nxt_int_t host;
- nxt_int_t size;
-} nxt_clone_procmap_t;
-
-
nxt_int_t
-nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str)
+nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid,
+ const char *str)
{
int fd, n;
u_char *p, *end;
@@ -89,8 +81,8 @@ nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str)
nxt_int_t
-nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid,
- u_char *mapinfo)
+nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile,
+ pid_t pid, u_char *mapinfo)
{
int len, mapfd;
u_char *p, *end;
@@ -139,17 +131,13 @@ nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid,
nxt_int_t
-nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
- nxt_int_t defval, nxt_conf_value_t *mapobj)
+nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
+ nxt_int_t default_container, nxt_int_t default_host,
+ nxt_clone_credential_map_t *map)
{
- u_char *p, *end, *mapinfo;
- nxt_int_t container, host, size;
- nxt_int_t ret, len, count, i;
- nxt_conf_value_t *obj, *value;
-
- static nxt_str_t str_cont = nxt_string("container");
- static nxt_str_t str_host = nxt_string("host");
- static nxt_str_t str_size = nxt_string("size");
+ u_char *p, *end, *mapinfo;
+ nxt_int_t ret, len;
+ nxt_uint_t i;
/*
* uid_map one-entry size:
@@ -157,44 +145,28 @@ nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
*/
len = sizeof(u_char) * (10 + 10 + 10 + 2 + 1);
- if (mapobj != NULL) {
- count = nxt_conf_array_elements_count(mapobj);
-
- if (count == 0) {
- goto default_map;
- }
-
- len = len * count + 1;
+ if (map->size > 0) {
+ len = len * map->size + 1;
mapinfo = nxt_malloc(len);
if (nxt_slow_path(mapinfo == NULL)) {
- nxt_alert(task, "failed to allocate uid_map buffer");
return NXT_ERROR;
}
p = mapinfo;
end = mapinfo + len;
- for (i = 0; i < count; i++) {
- obj = nxt_conf_get_array_element(mapobj, i);
+ for (i = 0; i < map->size; i++) {
+ p = nxt_sprintf(p, end, "%d %d %d", map->map[i].container,
+ map->map[i].host, map->map[i].size);
- value = nxt_conf_get_object_member(obj, &str_cont, NULL);
- container = nxt_conf_get_integer(value);
-
- value = nxt_conf_get_object_member(obj, &str_host, NULL);
- host = nxt_conf_get_integer(value);
-
- value = nxt_conf_get_object_member(obj, &str_size, NULL);
- size = nxt_conf_get_integer(value);
-
- p = nxt_sprintf(p, end, "%d %d %d", container, host, size);
if (nxt_slow_path(p == end)) {
- nxt_alert(task, "write past the uid_map buffer");
+ nxt_alert(task, "write past the mapinfo buffer");
nxt_free(mapinfo);
return NXT_ERROR;
}
- if (i+1 < count) {
+ if (i+1 < map->size) {
*p++ = '\n';
} else {
@@ -203,27 +175,24 @@ nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
}
} else {
-
-default_map:
-
mapinfo = nxt_malloc(len);
if (nxt_slow_path(mapinfo == NULL)) {
- nxt_alert(task, "failed to allocate uid_map buffer");
return NXT_ERROR;
}
end = mapinfo + len;
- p = nxt_sprintf(mapinfo, end, NXT_DEFAULT_UNPRIV_MAP, defval);
+ p = nxt_sprintf(mapinfo, end, "%d %d 1",
+ default_container, default_host);
*p = '\0';
if (nxt_slow_path(p == end)) {
- nxt_alert(task, "write past the %s buffer", mapfile);
+ nxt_alert(task, "write past mapinfo buffer");
nxt_free(mapinfo);
return NXT_ERROR;
}
}
- ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo);
+ ret = nxt_clone_credential_map_write(task, mapfile, pid, mapinfo);
nxt_free(mapinfo);
@@ -232,31 +201,50 @@ default_map:
nxt_int_t
-nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone)
+nxt_clone_credential_map(nxt_task_t *task, pid_t pid,
+ nxt_credential_t *app_creds, nxt_clone_t *clone)
{
nxt_int_t ret;
- nxt_int_t uid, gid;
+ nxt_int_t default_host_uid;
+ nxt_int_t default_host_gid;
const char *rule;
nxt_runtime_t *rt;
rt = task->thread->runtime;
- uid = geteuid();
- gid = getegid();
- rule = rt->capabilities.setid ? "allow" : "deny";
+ if (rt->capabilities.setid) {
+ rule = "allow";
+
+ /*
+ * By default we don't map a privileged user
+ */
+ default_host_uid = app_creds->uid;
+ default_host_gid = app_creds->base_gid;
+ } else {
+ rule = "deny";
+
+ default_host_uid = nxt_euid;
+ default_host_gid = nxt_egid;
+ }
+
+ ret = nxt_clone_credential_map_set(task, "uid_map", pid, app_creds->uid,
+ default_host_uid,
+ &clone->uidmap);
- ret = nxt_clone_proc_map_set(task, "uid_map", pid, uid, clone->uidmap);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
- ret = nxt_clone_proc_setgroups(task, pid, rule);
+ ret = nxt_clone_credential_setgroups(task, pid, rule);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_alert(task, "failed to write /proc/%d/setgroups", pid);
return NXT_ERROR;
}
- ret = nxt_clone_proc_map_set(task, "gid_map", pid, gid, clone->gidmap);
+ ret = nxt_clone_credential_map_set(task, "gid_map", pid, app_creds->base_gid,
+ default_host_gid,
+ &clone->gidmap);
+
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
@@ -264,4 +252,197 @@ nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone)
return NXT_OK;
}
+
+nxt_int_t
+nxt_clone_vldt_credential_uidmap(nxt_task_t *task,
+ nxt_clone_credential_map_t *map, nxt_credential_t *creds)
+{
+ nxt_int_t id;
+ nxt_uint_t i;
+ nxt_runtime_t *rt;
+ nxt_clone_map_entry_t m;
+
+ if (map->size == 0) {
+ return NXT_OK;
+ }
+
+ rt = task->thread->runtime;
+
+ if (!rt->capabilities.setid) {
+ if (nxt_slow_path(map->size > 1)) {
+ nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has %d entries "
+ "but unprivileged unit has a maximum of 1 map.",
+ map->size);
+
+ return NXT_ERROR;
+ }
+
+ id = map->map[0].host;
+
+ if (nxt_slow_path((nxt_uid_t) id != nxt_euid)) {
+ nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has an entry for "
+ "host uid %d but unprivileged unit can only map itself "
+ "(uid %d) into child namespaces.", id, nxt_euid);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ for (i = 0; i < map->size; i++) {
+ m = map->map[i];
+
+ if (creds->uid >= (nxt_uid_t) m.container
+ && creds->uid < (nxt_uid_t) (m.container + m.size))
+ {
+ return NXT_OK;
+ }
+ }
+
+ nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has no \"container\" "
+ "entry for user \"%s\" (uid %d)", creds->user, creds->uid);
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_clone_vldt_credential_gidmap(nxt_task_t *task,
+ nxt_clone_credential_map_t *map, nxt_credential_t *creds)
+{
+ nxt_uint_t base_ok, gid_ok, gids_ok;
+ nxt_uint_t i, j;
+ nxt_runtime_t *rt;
+ nxt_clone_map_entry_t m;
+
+ rt = task->thread->runtime;
+
+ if (!rt->capabilities.setid) {
+ if (creds->ngroups > 0
+ && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid)) {
+ nxt_log(task, NXT_LOG_NOTICE,
+ "unprivileged unit disallow supplementary groups for "
+ "new namespace (user \"%s\" has %d group%s).",
+ creds->user, creds->ngroups,
+ creds->ngroups > 1 ? "s" : "");
+
+ return NXT_ERROR;
+ }
+
+ if (map->size == 0) {
+ return NXT_OK;
+ }
+
+ if (nxt_slow_path(map->size > 1)) {
+ nxt_log(task, NXT_LOG_NOTICE, "\"gidmap\" field has %d entries "
+ "but unprivileged unit has a maximum of 1 map.",
+ map->size);
+
+ return NXT_ERROR;
+ }
+
+ m = map->map[0];
+
+ if (nxt_slow_path((nxt_gid_t) m.host != nxt_egid)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry for "
+ "host gid %d but unprivileged unit can only map itself "
+ "(gid %d) into child namespaces.", m.host, nxt_egid);
+
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(m.size > 1)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry with "
+ "\"size\": %d, but for unprivileged unit it must be 1.",
+ m.size);
+
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path((nxt_gid_t) m.container != creds->base_gid)) {
+ nxt_log(task, NXT_LOG_ERR,
+ "\"gidmap\" field has no \"container\" entry for gid %d.",
+ creds->base_gid);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ if (map->size == 0) {
+ if (creds->ngroups > 0
+ && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid))
+ {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no entries "
+ "but user \"%s\" has %d suplementary group%s.",
+ creds->user, creds->ngroups,
+ creds->ngroups > 1 ? "s" : "");
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ base_ok = 0;
+ gids_ok = 0;
+
+ for (i = 0; i < creds->ngroups; i++) {
+ gid_ok = 0;
+
+ for (j = 0; j < map->size; j++) {
+ m = map->map[j];
+
+ if (!base_ok && creds->base_gid >= (nxt_gid_t) m.container
+ && creds->base_gid < (nxt_gid_t) (m.container+m.size))
+ {
+ base_ok = 1;
+ }
+
+ if (creds->gids[i] >= (nxt_gid_t) m.container
+ && creds->gids[i] < (nxt_gid_t) (m.container+m.size))
+ {
+ gid_ok = 1;
+ break;
+ }
+ }
+
+ if (nxt_fast_path(gid_ok)) {
+ gids_ok++;
+ }
+ }
+
+ if (!base_ok) {
+ for (i = 0; i < map->size; i++) {
+ m = map->map[i];
+
+ if (creds->base_gid >= (nxt_gid_t) m.container
+ && creds->base_gid < (nxt_gid_t) (m.container+m.size))
+ {
+ base_ok = 1;
+ break;
+ }
+ }
+ }
+
+ if (nxt_slow_path(!base_ok)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no \"container\" "
+ "entry for gid %d.", creds->base_gid);
+
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(gids_ok < creds->ngroups)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has missing "
+ "suplementary gid mappings (found %d out of %d).", gids_ok,
+ creds->ngroups);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
#endif
diff --git a/src/nxt_clone.h b/src/nxt_clone.h
index 50dec0b4..dcccf1db 100644
--- a/src/nxt_clone.h
+++ b/src/nxt_clone.h
@@ -7,11 +7,47 @@
#define _NXT_CLONE_INCLUDED_
+#if (NXT_HAVE_CLONE_NEWUSER)
+
+typedef struct {
+ nxt_int_t container;
+ nxt_int_t host;
+ nxt_int_t size;
+} nxt_clone_map_entry_t;
+
+typedef struct {
+ nxt_uint_t size;
+ nxt_clone_map_entry_t *map;
+} nxt_clone_credential_map_t;
+
+#endif
+
+typedef struct {
+ nxt_int_t flags;
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ nxt_clone_credential_map_t uidmap;
+ nxt_clone_credential_map_t gidmap;
+#endif
+
+} nxt_clone_t;
+
+
pid_t nxt_clone(nxt_int_t flags);
+
#if (NXT_HAVE_CLONE_NEWUSER)
-nxt_int_t nxt_clone_proc_map(nxt_task_t *task, pid_t pid,
- nxt_process_clone_t *clone);
+
+#define NXT_CLONE_USER(flags) \
+ ((flags & CLONE_NEWUSER) == CLONE_NEWUSER)
+
+NXT_EXPORT nxt_int_t nxt_clone_credential_map(nxt_task_t *task, pid_t pid,
+ nxt_credential_t *creds, nxt_clone_t *clone);
+NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_uidmap(nxt_task_t *task,
+ nxt_clone_credential_map_t *map, nxt_credential_t *creds);
+NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_gidmap(nxt_task_t *task,
+ nxt_clone_credential_map_t *map, nxt_credential_t *creds);
+
#endif
#endif /* _NXT_CLONE_INCLUDED_ */
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 105af675..5a1f7839 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -9,6 +9,8 @@
#include <nxt_cert.h>
#include <nxt_router.h>
#include <nxt_http.h>
+#include <nxt_sockaddr.h>
+#include <nxt_http_route_addr.h>
typedef enum {
@@ -82,6 +84,10 @@ static nxt_int_t nxt_conf_vldt_match_patterns_set_member(
nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
@@ -283,6 +289,16 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
&nxt_conf_vldt_match_patterns,
NULL },
+ { nxt_string("source"),
+ NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_addrs,
+ NULL },
+
+ { nxt_string("destination"),
+ NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_addrs,
+ NULL },
+
{ nxt_string("uri"),
NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
&nxt_conf_vldt_match_patterns,
@@ -358,6 +374,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = {
NULL,
NULL },
+ { nxt_string("shm"),
+ NXT_CONF_VLDT_INTEGER,
+ NULL,
+ NULL },
+
NXT_CONF_VLDT_END
};
@@ -1150,6 +1171,63 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
static nxt_int_t
+nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_match_addr);
+ }
+
+ return nxt_conf_vldt_match_addr(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ nxt_http_route_addr_pattern_t pattern;
+
+ switch (nxt_http_route_addr_pattern_parse(vldt->pool, &pattern, value)) {
+
+ case NXT_OK:
+ return NXT_OK;
+
+ case NXT_ADDR_PATTERN_PORT_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" port an invalid "
+ "port.");
+
+ case NXT_ADDR_PATTERN_CV_TYPE_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
+ "\"address\" must be a string.");
+
+ case NXT_ADDR_PATTERN_LENGTH_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" is too short.");
+
+ case NXT_ADDR_PATTERN_FORMAT_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" format is invalid.");
+
+ case NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" range is "
+ "overlapping.");
+
+ case NXT_ADDR_PATTERN_CIDR_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" has an invalid CIDR "
+ "prefix.");
+
+ case NXT_ADDR_PATTERN_NO_IPv6_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" does not support "
+ "IPv6 with your configuration.");
+
+ default:
+ return nxt_conf_vldt_error(vldt, "The \"address\" has an unknown "
+ "format.");
+ }
+}
+
+
+static nxt_int_t
nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
diff --git a/src/nxt_credential.c b/src/nxt_credential.c
new file mode 100644
index 00000000..168db9cf
--- /dev/null
+++ b/src/nxt_credential.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+static nxt_int_t nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_credential_t *uc);
+
+
+nxt_int_t
+nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc,
+ const char *group)
+{
+ struct group *grp;
+ struct passwd *pwd;
+
+ nxt_errno = 0;
+
+ pwd = getpwnam(uc->user);
+
+ if (nxt_slow_path(pwd == NULL)) {
+
+ if (nxt_errno == 0) {
+ nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
+ uc->user, uc->user);
+ } else {
+ nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
+ }
+
+ return NXT_ERROR;
+ }
+
+ uc->uid = pwd->pw_uid;
+ uc->base_gid = pwd->pw_gid;
+
+ if (group != NULL && group[0] != '\0') {
+ nxt_errno = 0;
+
+ grp = getgrnam(group);
+
+ if (nxt_slow_path(grp == NULL)) {
+
+ if (nxt_errno == 0) {
+ nxt_alert(task,
+ "getgrnam(\"%s\") failed, group \"%s\" not found",
+ group, group);
+ } else {
+ nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
+ }
+
+ return NXT_ERROR;
+ }
+
+ uc->base_gid = grp->gr_gid;
+ }
+
+ nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)",
+ uc->user, uc->uid, uc->base_gid);
+
+ if (nxt_credential_groups_get(task, mp, uc) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+#if (NXT_DEBUG)
+ {
+ u_char *p, *end;
+ nxt_uint_t i;
+ u_char msg[NXT_MAX_ERROR_STR];
+
+ p = msg;
+ end = msg + NXT_MAX_ERROR_STR;
+
+ for (i = 0; i < uc->ngroups; i++) {
+ p = nxt_sprintf(p, end, "%d%c", uc->gids[i],
+ i+1 < uc->ngroups ? ',' : '\0');
+ }
+
+ nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg);
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+#if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX)
+
+#define NXT_NGROUPS nxt_min(256, NGROUPS_MAX)
+
+
+static nxt_int_t
+nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_credential_t *uc)
+{
+ int ngroups;
+ gid_t groups[NXT_NGROUPS];
+
+ ngroups = NXT_NGROUPS;
+
+ if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) {
+ if (nxt_slow_path(ngroups <= NXT_NGROUPS)) {
+ nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user,
+ uc->base_gid, nxt_errno);
+
+ return NXT_ERROR;
+ }
+ }
+
+ if (ngroups > NXT_NGROUPS) {
+ if (ngroups > NGROUPS_MAX) {
+ ngroups = NGROUPS_MAX;
+ }
+
+ uc->ngroups = ngroups;
+
+ uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
+ if (nxt_slow_path(uc->gids == NULL)) {
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids,
+ &ngroups) < 0)) {
+
+ nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user,
+ uc->base_gid, nxt_errno);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ uc->ngroups = ngroups;
+
+ uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
+ if (nxt_slow_path(uc->gids == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t));
+
+ return NXT_OK;
+}
+
+
+#else
+
+/*
+ * For operating systems that lack getgrouplist(3) or it's buggy (MacOS),
+ * nxt_credential_groups_get() stores an array of groups IDs which should be
+ * set by the setgroups() function for a given user. The initgroups()
+ * may block a just forked worker process for some time if LDAP or NDIS+
+ * is used, so nxt_credential_groups_get() allows to get worker user groups in
+ * main process. In a nutshell the initgroups() calls getgrouplist()
+ * followed by setgroups(). However older Solaris lacks the getgrouplist().
+ * Besides getgrouplist() does not allow to query the exact number of
+ * groups in some platforms, while NGROUPS_MAX can be quite large (e.g.
+ * 65536 on Linux).
+ * So nxt_credential_groups_get() emulates getgrouplist(): at first the
+ * function saves the super-user groups IDs, then calls initgroups() and saves
+ * the specified user groups IDs, and then restores the super-user groups IDs.
+ * This works at least on Linux, FreeBSD, and Solaris, but does not work
+ * on MacOSX, getgroups(2):
+ *
+ * To provide compatibility with applications that use getgroups() in
+ * environments where users may be in more than {NGROUPS_MAX} groups,
+ * a variant of getgroups(), obtained when compiling with either the
+ * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
+ * be used that is not limited to {NGROUPS_MAX} groups. However, this
+ * variant only returns the user's default group access list and not
+ * the group list modified by a call to setgroups(2).
+ *
+ * For such cases initgroups() is used in worker process as fallback.
+ */
+
+static nxt_int_t
+nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc)
+{
+ int nsaved, ngroups;
+ nxt_int_t ret;
+ nxt_gid_t *saved;
+
+ nsaved = getgroups(0, NULL);
+
+ if (nxt_slow_path(nsaved == -1)) {
+ nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
+
+ if (nsaved > NGROUPS_MAX) {
+ /* MacOSX case. */
+
+ uc->gids = NULL;
+ uc->ngroups = 0;
+
+ return NXT_OK;
+ }
+
+ saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t));
+
+ if (nxt_slow_path(saved == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ret = NXT_ERROR;
+
+ nsaved = getgroups(nsaved, saved);
+
+ if (nxt_slow_path(nsaved == -1)) {
+ nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
+ goto free;
+ }
+
+ nxt_debug(task, "getgroups(): %d", nsaved);
+
+ if (initgroups(uc->user, uc->base_gid) != 0) {
+ if (nxt_errno == NXT_EPERM) {
+ nxt_log(task, NXT_LOG_NOTICE,
+ "initgroups(%s, %d) failed %E, ignored",
+ uc->user, uc->base_gid, nxt_errno);
+
+ ret = NXT_OK;
+
+ goto free;
+
+ } else {
+ nxt_alert(task, "initgroups(%s, %d) failed %E",
+ uc->user, uc->base_gid, nxt_errno);
+ goto restore;
+ }
+ }
+
+ ngroups = getgroups(0, NULL);
+
+ if (nxt_slow_path(ngroups == -1)) {
+ nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
+ goto restore;
+ }
+
+ nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
+
+ uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t));
+
+ if (nxt_slow_path(uc->gids == NULL)) {
+ goto restore;
+ }
+
+ ngroups = getgroups(ngroups, uc->gids);
+
+ if (nxt_slow_path(ngroups == -1)) {
+ nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
+ goto restore;
+ }
+
+ uc->ngroups = ngroups;
+
+ ret = NXT_OK;
+
+restore:
+
+ if (nxt_slow_path(setgroups(nsaved, saved) != 0)) {
+ nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
+ ret = NXT_ERROR;
+ }
+
+free:
+
+ nxt_mp_free(mp, saved);
+
+ return ret;
+}
+
+
+#endif
+
+
+nxt_int_t
+nxt_credential_setuid(nxt_task_t *task, nxt_credential_t *uc)
+{
+ nxt_debug(task, "user cred set: \"%s\" uid:%d", uc->user, uc->uid);
+
+ if (setuid(uc->uid) != 0) {
+
+#if (NXT_HAVE_CLONE)
+ if (nxt_errno == EINVAL) {
+ nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't "
+ "valid in the application namespace.", uc->uid, uc->user);
+ return NXT_ERROR;
+ }
+#endif
+
+ nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_credential_setgids(nxt_task_t *task, nxt_credential_t *uc)
+{
+ nxt_runtime_t *rt;
+
+ nxt_debug(task, "user cred set gids: base gid:%d, ngroups: %d",
+ uc->base_gid, uc->ngroups);
+
+ rt = task->thread->runtime;
+
+ if (setgid(uc->base_gid) != 0) {
+
+#if (NXT_HAVE_CLONE)
+ if (nxt_errno == EINVAL) {
+ nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the "
+ "application namespace.", uc->base_gid);
+ return NXT_ERROR;
+ }
+#endif
+
+ nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (!rt->capabilities.setid) {
+ return NXT_OK;
+ }
+
+ if (nxt_slow_path(uc->ngroups > 0
+ && setgroups(uc->ngroups, uc->gids) != 0)) {
+
+#if (NXT_HAVE_CLONE)
+ if (nxt_errno == EINVAL) {
+ nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has "
+ "supplementary group ids not valid in the application "
+ "namespace.", uc->user, uc->uid);
+ return NXT_ERROR;
+ }
+#endif
+
+ nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
diff --git a/src/nxt_credential.h b/src/nxt_credential.h
new file mode 100644
index 00000000..243eba83
--- /dev/null
+++ b/src/nxt_credential.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_CREDENTIAL_H_INCLUDED_
+#define _NXT_CREDENTIAL_H_INCLUDED_
+
+
+typedef uid_t nxt_uid_t;
+typedef gid_t nxt_gid_t;
+
+typedef struct {
+ const char *user;
+ nxt_uid_t uid;
+ nxt_gid_t base_gid;
+ nxt_uint_t ngroups;
+ nxt_gid_t *gids;
+} nxt_credential_t;
+
+
+NXT_EXPORT nxt_int_t nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_credential_t *uc, const char *group);
+NXT_EXPORT nxt_int_t nxt_credential_setuid(nxt_task_t *task,
+ nxt_credential_t *uc);
+NXT_EXPORT nxt_int_t nxt_credential_setgids(nxt_task_t *task,
+ nxt_credential_t *uc);
+
+
+#endif /* _NXT_CREDENTIAL_H_INCLUDED_ */
diff --git a/src/nxt_external.c b/src/nxt_external.c
index 89fe08c8..e498a938 100644
--- a/src/nxt_external.c
+++ b/src/nxt_external.c
@@ -98,11 +98,11 @@ nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
"%s;%uD;"
"%PI,%ud,%d;"
"%PI,%ud,%d;"
- "%d,%Z",
+ "%d,%z,%Z",
NXT_VERSION, my_port->process->init->stream,
main_port->pid, main_port->id, main_port->pair[1],
my_port->pid, my_port->id, my_port->pair[0],
- 2);
+ 2, conf->shm_limit);
if (nxt_slow_path(p == end)) {
nxt_alert(task, "internal error: buffer too small for NXT_UNIT_INIT");
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index b07eaf84..8ce57893 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -1230,6 +1230,7 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
c = h1p->conn;
c->write = header;
+ h1p->conn_write_tail = &header->next;
c->write_state = &nxt_h1p_request_send_state;
if (body_handler != NULL) {
@@ -1342,8 +1343,14 @@ nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
nxt_conn_write(task->thread->engine, c);
} else {
- nxt_buf_chain_add(&c->write, out);
+ *h1p->conn_write_tail = out;
}
+
+ while (out->next != NULL) {
+ out = out->next;
+ }
+
+ h1p->conn_write_tail = &out->next;
}
@@ -1730,9 +1737,10 @@ nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data)
static void
nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c)
{
- u_char *p;
- size_t size;
- nxt_buf_t *out, *last;
+ u_char *p;
+ size_t size;
+ nxt_buf_t *out, *last;
+ nxt_h1proto_t *h1p;
size = nxt_length(NXT_H1P_IDLE_TIMEOUT)
+ nxt_http_date_cache.size
@@ -1762,6 +1770,9 @@ nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c)
last->completion_handler = nxt_h1p_idle_response_sent;
last->parent = c;
+ h1p = c->socket.data;
+ h1p->conn_write_tail = &last->next;
+
c->write = out;
c->write_state = &nxt_h1p_timeout_response_state;
diff --git a/src/nxt_h1proto.h b/src/nxt_h1proto.h
index 61da6770..3294713f 100644
--- a/src/nxt_h1proto.h
+++ b/src/nxt_h1proto.h
@@ -40,6 +40,8 @@ struct nxt_h1proto_s {
nxt_http_request_t *request;
nxt_buf_t *buffers;
+
+ nxt_buf_t **conn_write_tail;
/*
* All fields before the conn field will
* be zeroed in a keep-alive connection.
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 18b352ea..ef9593b7 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -6,6 +6,8 @@
#include <nxt_router.h>
#include <nxt_http.h>
+#include <nxt_sockaddr.h>
+#include <nxt_http_route_addr.h>
typedef enum {
@@ -16,6 +18,8 @@ typedef enum {
NXT_HTTP_ROUTE_ARGUMENT,
NXT_HTTP_ROUTE_COOKIE,
NXT_HTTP_ROUTE_SCHEME,
+ NXT_HTTP_ROUTE_SOURCE,
+ NXT_HTTP_ROUTE_DESTINATION,
} nxt_http_route_object_t;
@@ -50,6 +54,8 @@ typedef struct {
nxt_conf_value_t *arguments;
nxt_conf_value_t *cookies;
nxt_conf_value_t *scheme;
+ nxt_conf_value_t *source;
+ nxt_conf_value_t *destination;
} nxt_http_route_match_conf_t;
@@ -118,9 +124,18 @@ typedef struct {
} nxt_http_route_table_t;
+typedef struct {
+ /* 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 {
nxt_http_route_rule_t *rule;
nxt_http_route_table_t *table;
+ nxt_http_route_addr_rule_t *addr_rule;
} nxt_http_route_test_t;
@@ -170,6 +185,8 @@ 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);
+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);
@@ -190,12 +207,14 @@ static void nxt_http_route_cleanup(nxt_task_t *task, nxt_http_route_t *routes);
static nxt_http_action_t *nxt_http_route_handler(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_action_t *start);
-static nxt_http_action_t *nxt_http_route_match(nxt_http_request_t *r,
- nxt_http_route_match_t *match);
+static nxt_http_action_t *nxt_http_route_match(nxt_task_t *task,
+ nxt_http_request_t *r, nxt_http_route_match_t *match);
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,
@@ -329,6 +348,18 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = {
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_match_conf_t, cookies),
},
+
+ {
+ nxt_string("source"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, source),
+ },
+
+ {
+ nxt_string("destination"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, destination),
+ },
};
@@ -381,6 +412,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_rule_t *rule;
nxt_http_route_table_t *table;
nxt_http_route_match_t *match;
+ nxt_http_route_addr_rule_t *addr_rule;
nxt_http_route_match_conf_t mtcf;
static nxt_str_t match_path = nxt_string("/match");
@@ -505,6 +537,28 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
test++;
}
+ if (mtcf.source != NULL) {
+ addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.source);
+ if (addr_rule == NULL) {
+ return NULL;
+ }
+
+ addr_rule->object = NXT_HTTP_ROUTE_SOURCE;
+ test->addr_rule = addr_rule;
+ test++;
+ }
+
+ if (mtcf.destination != NULL) {
+ addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.destination);
+ if (addr_rule == NULL) {
+ return NULL;
+ }
+
+ addr_rule->object = NXT_HTTP_ROUTE_DESTINATION;
+ test->addr_rule = addr_rule;
+ test++;
+ }
+
return match;
}
@@ -770,6 +824,53 @@ 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_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *cv)
+{
+ size_t size;
+ uint32_t i, n;
+ nxt_bool_t array;
+ nxt_conf_value_t *value;
+ nxt_http_route_addr_rule_t *addr_rule;
+ nxt_http_route_addr_pattern_t *pattern;
+
+ array = (nxt_conf_type(cv) == NXT_CONF_ARRAY);
+ n = array ? nxt_conf_array_elements_count(cv) : 1;
+
+ size = sizeof(nxt_http_route_addr_rule_t)
+ + n * sizeof(nxt_http_route_addr_pattern_t);
+
+ addr_rule = nxt_mp_alloc(mp, size);
+ if (nxt_slow_path(addr_rule == NULL)) {
+ return NULL;
+ }
+
+ addr_rule->items = n;
+
+ if (!array) {
+ pattern = &addr_rule->addr_pattern[0];
+
+ if (nxt_http_route_addr_pattern_parse(mp, pattern, cv) != NXT_OK) {
+ return NULL;
+ }
+
+ return addr_rule;
+ }
+
+ for (i = 0; i < n; i++) {
+ pattern = &addr_rule->addr_pattern[i];
+ value = nxt_conf_get_array_element(cv, i);
+
+ if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
+ return NULL;
+ }
+ }
+
+ return addr_rule;
+}
+
+
static int
nxt_http_pattern_compare(const void *one, const void *two)
{
@@ -1117,7 +1218,7 @@ nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
end = match + route->items;
while (match < end) {
- action = nxt_http_route_match(r, *match);
+ action = nxt_http_route_match(task, r, *match);
if (action != NULL) {
return action;
}
@@ -1132,7 +1233,8 @@ nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
static nxt_http_action_t *
-nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match)
+nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
+ nxt_http_route_match_t *match)
{
nxt_int_t ret;
nxt_http_route_test_t *test, *end;
@@ -1141,11 +1243,23 @@ nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match)
end = test + match->items;
while (test < end) {
- if (test->rule->object != NXT_HTTP_ROUTE_TABLE) {
- ret = nxt_http_route_rule(r, test->rule);
-
- } else {
+ switch (test->rule->object) {
+ case NXT_HTTP_ROUTE_TABLE:
ret = nxt_http_route_table(r, test->table);
+ break;
+ case NXT_HTTP_ROUTE_SOURCE:
+ ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
+ break;
+ case NXT_HTTP_ROUTE_DESTINATION:
+ if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
+ nxt_http_proto[r->protocol].local_addr(task, r);
+ }
+
+ ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
+ break;
+ default:
+ ret = nxt_http_route_rule(r, test->rule);
+ break;
}
if (ret <= 0) {
@@ -1256,6 +1370,154 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
static nxt_int_t
+nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
+ nxt_sockaddr_t *sa)
+{
+#if (NXT_INET6)
+ uint32_t i;
+#endif
+ in_port_t in_port;
+ nxt_int_t match;
+ struct sockaddr_in *sin;
+#if (NXT_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ nxt_http_route_addr_base_t *base;
+
+ base = &p->base;
+
+ switch (sa->u.sockaddr.sa_family) {
+
+ case AF_INET:
+
+ match = (base->addr_family == AF_INET
+ || base->addr_family == AF_UNSPEC);
+ if (!match) {
+ break;
+ }
+
+ sin = &sa->u.sockaddr_in;
+ in_port = ntohs(sin->sin_port);
+
+ match = (in_port >= base->port.start && in_port <= base->port.end);
+ if (!match) {
+ break;
+ }
+
+ switch (base->match_type) {
+
+ case NXT_HTTP_ROUTE_ADDR_ANY:
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_EXACT:
+ match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ sizeof(struct in_addr))
+ == 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_RANGE:
+ match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ sizeof(struct in_addr)) >= 0
+ && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
+ sizeof(struct in_addr)) <= 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_CIDR:
+ match = ((sin->sin_addr.s_addr & p->addr.v4.end)
+ == p->addr.v4.start);
+ break;
+
+ default:
+ nxt_unreachable();
+ }
+
+ break;
+
+#if (NXT_INET6)
+ case AF_INET6:
+
+ match = (base->addr_family == AF_INET6
+ || base->addr_family == AF_UNSPEC);
+ if (!match) {
+ break;
+ }
+
+ sin6 = &sa->u.sockaddr_in6;
+ in_port = ntohs(sin6->sin6_port);
+
+ match = (in_port >= base->port.start && in_port <= base->port.end);
+ if (!match) {
+ break;
+ }
+
+ switch (base->match_type) {
+
+ case NXT_HTTP_ROUTE_ADDR_ANY:
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_EXACT:
+ match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ sizeof(struct in6_addr))
+ == 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_RANGE:
+ match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ sizeof(struct in6_addr)) >= 0
+ && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
+ sizeof(struct in6_addr)) <= 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_CIDR:
+ for (i = 0; i < 16; i++) {
+ match = ((sin6->sin6_addr.s6_addr[i]
+ & p->addr.v6.end.s6_addr[i])
+ == p->addr.v6.start.s6_addr[i]);
+
+ if (!match) {
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ nxt_unreachable();
+ }
+
+ break;
+#endif
+
+ default:
+ match = 0;
+ break;
+ }
+
+ return match ^ base->negative;
+}
+
+
+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 *sa)
+{
+ uint32_t i, n;
+ nxt_http_route_addr_pattern_t *p;
+
+ n = addr_rule->items;
+
+ for (i = 0; i < n; i++) {
+ p = &addr_rule->addr_pattern[i];
+ if (nxt_http_route_addr_pattern_match(p, sa)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static nxt_int_t
nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
{
nxt_int_t ret;
diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c
new file mode 100644
index 00000000..3c7e9c84
--- /dev/null
+++ b/src/nxt_http_route_addr.c
@@ -0,0 +1,349 @@
+
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#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
+
+
+nxt_int_t
+nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
+ nxt_http_route_addr_pattern_t *pattern, nxt_conf_value_t *cv)
+{
+ u_char *delim, *end;
+ nxt_int_t ret, cidr_prefix;
+ nxt_str_t addr, port;
+ nxt_http_route_addr_base_t *base;
+ nxt_http_route_addr_range_t *inet;
+
+ if (nxt_conf_type(cv) != NXT_CONF_STRING) {
+ return NXT_ADDR_PATTERN_CV_TYPE_ERROR;
+ }
+
+ nxt_conf_get_string(cv, &addr);
+
+ base = &pattern->base;
+
+ if (addr.length > 0 && addr.start[0] == '!') {
+ addr.start++;
+ addr.length--;
+
+ base->negative = 1;
+
+ } else {
+ base->negative = 0;
+ }
+
+ if (nxt_slow_path(addr.length < 2)) {
+ return NXT_ADDR_PATTERN_LENGTH_ERROR;
+ }
+
+ nxt_str_null(&port);
+
+ if (addr.start[0] == '*' && addr.start[1] == ':') {
+ port.start = addr.start + 2;
+ port.length = addr.length - 2;
+ base->addr_family = AF_UNSPEC;
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (nxt_str_looks_like_ipv6(&addr)) {
+#if (NXT_INET6)
+ uint8_t i;
+ nxt_int_t len;
+ nxt_http_route_in6_addr_range_t *inet6;
+
+ base->addr_family = AF_INET6;
+
+ if (addr.start[0] == '[') {
+ addr.start++;
+ addr.length--;
+
+ end = addr.start + addr.length;
+
+ port.start = nxt_rmemstrn(addr.start, end, "]:", 2);
+ if (nxt_slow_path(port.start == NULL)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ addr.length = port.start - addr.start;
+ port.start += nxt_length("]:");
+ port.length = end - port.start;
+ }
+
+ inet6 = &pattern->addr.v6;
+
+ delim = nxt_memchr(addr.start, '-', addr.length);
+ if (delim != NULL) {
+ len = delim - addr.start;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->start, addr.start, len);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ len = addr.start + addr.length - delim - 1;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(delim + 1, len))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->end, delim + 1, len);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end,
+ sizeof(struct in6_addr)) > 0))
+ {
+ return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
+
+ goto parse_port;
+ }
+
+ delim = nxt_memchr(addr.start, '/', addr.length);
+ if (delim != NULL) {
+ cidr_prefix = nxt_int_parse(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 128)) {
+ return NXT_ADDR_PATTERN_CIDR_ERROR;
+ }
+
+ addr.length = delim - addr.start;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start,
+ addr.length)))
+ {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(cidr_prefix == 0)) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (nxt_slow_path(cidr_prefix == 128)) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+ goto parse_port;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ if (cidr_prefix >= 8) {
+ inet6->end.s6_addr[i] = 0xFF;
+ cidr_prefix -= 8;
+
+ continue;
+ }
+
+ if (cidr_prefix > 0) {
+ inet6->end.s6_addr[i] = 0xFF & (0xFF << (8 - cidr_prefix));
+ inet6->start.s6_addr[i] &= inet6->end.s6_addr[i];
+ cidr_prefix = 0;
+
+ continue;
+ }
+
+ inet6->start.s6_addr[i] = 0;
+ inet6->end.s6_addr[i] = 0;
+ }
+
+ goto parse_port;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, addr.length))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ nxt_inet6_addr(&inet6->start, addr.start, addr.length);
+
+ goto parse_port;
+#endif
+ return NXT_ADDR_PATTERN_NO_IPv6_ERROR;
+ }
+
+ base->addr_family = AF_INET;
+
+ delim = nxt_memchr(addr.start, ':', addr.length);
+ if (delim != NULL) {
+ port.start = delim + 1;
+ port.length = addr.start + addr.length - port.start;
+ addr.length = delim - addr.start;
+ }
+
+ inet = &pattern->addr.v4;
+
+ delim = nxt_memchr(addr.start, '-', addr.length);
+ if (delim != NULL) {
+ inet->start = nxt_inet_addr(addr.start, delim - addr.start);
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ inet->end = nxt_inet_addr(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(inet->end == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end,
+ sizeof(struct in_addr)) > 0))
+ {
+ return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
+
+ goto parse_port;
+ }
+
+ delim = nxt_memchr(addr.start, '/', addr.length);
+ if (delim != NULL) {
+ cidr_prefix = nxt_int_parse(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 32)) {
+ return NXT_ADDR_PATTERN_CIDR_ERROR;
+ }
+
+ addr.length = delim - addr.start;
+ inet->end = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - cidr_prefix)));
+
+ inet->start = nxt_inet_addr(addr.start, addr.length) & inet->end;
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (cidr_prefix == 0) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (cidr_prefix < 32) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
+
+ goto parse_port;
+ }
+ }
+
+ inet->start = nxt_inet_addr(addr.start, addr.length);
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+parse_port:
+
+ if (port.length == 0) {
+ if (nxt_slow_path(port.start != NULL)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ base->port.start = 0;
+ base->port.end = 65535;
+
+ return NXT_OK;
+ }
+
+ delim = nxt_memchr(port.start, '-', port.length - 1);
+ if (delim != NULL) {
+ ret = nxt_int_parse(port.start, delim - port.start);
+ if (nxt_slow_path(ret < 0 || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.start = ret;
+
+ ret = nxt_int_parse(delim + 1, port.start + port.length - (delim + 1));
+ if (nxt_slow_path(ret < base->port.start || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.end = ret;
+
+ } else {
+ ret = nxt_int_parse(port.start, port.length);
+ if (nxt_slow_path(ret < 0 || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.start = ret;
+ base->port.end = ret;
+ }
+
+ return NXT_OK;
+}
+
+
+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
+nxt_valid_ipv6_blocks(u_char *c, size_t len)
+{
+ u_char *end;
+ nxt_uint_t colon_gap;
+
+ end = c + len;
+ colon_gap = 0;
+
+ while (c != end) {
+ if (*c == ':') {
+ colon_gap = 0;
+ c++;
+
+ continue;
+ }
+
+ colon_gap++;
+ c++;
+
+ if (nxt_slow_path(colon_gap > 4)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#endif
diff --git a/src/nxt_http_route_addr.h b/src/nxt_http_route_addr.h
new file mode 100644
index 00000000..3b1e1da3
--- /dev/null
+++ b/src/nxt_http_route_addr.h
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_conf.h>
+
+#ifndef _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_
+#define _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_
+
+
+enum {
+ NXT_HTTP_ROUTE_ADDR_ANY = 0,
+ NXT_HTTP_ROUTE_ADDR_RANGE,
+ NXT_HTTP_ROUTE_ADDR_EXACT,
+ NXT_HTTP_ROUTE_ADDR_CIDR,
+};
+
+
+enum {
+ NXT_ADDR_PATTERN_PORT_ERROR = NXT_OK + 1,
+ NXT_ADDR_PATTERN_CV_TYPE_ERROR,
+ NXT_ADDR_PATTERN_LENGTH_ERROR,
+ NXT_ADDR_PATTERN_FORMAT_ERROR,
+ NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR,
+ NXT_ADDR_PATTERN_CIDR_ERROR,
+ NXT_ADDR_PATTERN_NO_IPv6_ERROR,
+};
+
+
+typedef struct {
+ in_addr_t start;
+ in_addr_t end;
+} nxt_http_route_addr_range_t;
+
+
+#if (NXT_INET6)
+typedef struct {
+ struct in6_addr start;
+ struct in6_addr end;
+} nxt_http_route_in6_addr_range_t;
+#endif
+
+
+typedef struct {
+ uint8_t match_type:2;
+ uint8_t negative:1;
+ uint8_t addr_family;
+
+ struct {
+ uint16_t start;
+ uint16_t end;
+ } port;
+} nxt_http_route_addr_base_t;
+
+
+typedef struct {
+ nxt_http_route_addr_base_t base;
+
+ union {
+ nxt_http_route_addr_range_t v4;
+#if (NXT_INET6)
+ nxt_http_route_in6_addr_range_t v6;
+#endif
+ } addr;
+} nxt_http_route_addr_pattern_t;
+
+
+NXT_EXPORT nxt_int_t nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
+ nxt_http_route_addr_pattern_t *pattern, nxt_conf_value_t *cv);
+
+#endif /* _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_ */
diff --git a/src/nxt_java.c b/src/nxt_java.c
index 08e24595..004907d6 100644
--- a/src/nxt_java.c
+++ b/src/nxt_java.c
@@ -354,6 +354,7 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
java_init.callbacks.close_handler = nxt_java_close_handler;
java_init.request_data_size = sizeof(nxt_java_request_data_t);
java_init.data = &data;
+ java_init.shm_limit = conf->shm_limit;
ctx = nxt_unit_init(&java_init);
if (nxt_slow_path(ctx == NULL)) {
diff --git a/src/nxt_lib.c b/src/nxt_lib.c
index db3d29c1..1634a2b8 100644
--- a/src/nxt_lib.c
+++ b/src/nxt_lib.c
@@ -43,6 +43,8 @@ nxt_lib_start(const char *app, char **argv, char ***envp)
nxt_pid = getpid();
nxt_ppid = getppid();
+ nxt_euid = geteuid();
+ nxt_egid = getegid();
#if (NXT_DEBUG)
diff --git a/src/nxt_main.h b/src/nxt_main.h
index 0afebb96..d9e337d2 100644
--- a/src/nxt_main.h
+++ b/src/nxt_main.h
@@ -58,6 +58,7 @@ typedef uint16_t nxt_port_id_t;
#include <nxt_thread.h>
#include <nxt_process_type.h>
#include <nxt_capability.h>
+#include <nxt_credential.h>
#include <nxt_process.h>
#include <nxt_utf8.h>
#include <nxt_file_name.h>
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index cfe0341f..eed37752 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -29,6 +29,10 @@ typedef struct {
} nxt_conf_app_map_t;
+extern nxt_port_handlers_t nxt_controller_process_port_handlers;
+extern nxt_port_handlers_t nxt_router_process_port_handlers;
+
+
static nxt_int_t nxt_main_process_port_create(nxt_task_t *task,
nxt_runtime_t *rt);
static void nxt_main_process_title(nxt_task_t *task);
@@ -36,6 +40,8 @@ static nxt_int_t nxt_main_start_controller_process(nxt_task_t *task,
nxt_runtime_t *rt);
static nxt_int_t nxt_main_create_controller_process(nxt_task_t *task,
nxt_runtime_t *rt, nxt_process_init_t *init);
+static nxt_int_t nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt,
+ nxt_process_init_t *init);
static nxt_int_t nxt_main_start_router_process(nxt_task_t *task,
nxt_runtime_t *rt);
static nxt_int_t nxt_main_start_discovery_process(nxt_task_t *task,
@@ -67,11 +73,29 @@ static void nxt_main_port_conf_store_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static void nxt_main_port_access_log_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
+static nxt_process_init_t *nxt_process_init_create(nxt_task_t *task,
+ nxt_process_type_t type, const nxt_str_t *name);
+static nxt_int_t nxt_process_init_name_set(nxt_process_init_t *init,
+ nxt_process_type_t type, const nxt_str_t *name);
+static nxt_int_t nxt_process_init_creds_set(nxt_task_t *task,
+ nxt_process_init_t *init, nxt_str_t *user, nxt_str_t *group);
+
+static nxt_int_t nxt_init_isolation(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_init_t *init);
+#if (NXT_HAVE_CLONE)
+static nxt_int_t nxt_init_clone_flags(nxt_task_t *task,
+ nxt_conf_value_t *namespaces, nxt_process_init_t *init);
+#endif
-static nxt_int_t nxt_init_set_isolation(nxt_task_t *task,
- nxt_process_init_t *init, nxt_conf_value_t *isolation);
-static nxt_int_t nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init,
- nxt_conf_value_t *ns);
+#if (NXT_HAVE_CLONE_NEWUSER)
+static nxt_int_t nxt_init_isolation_creds(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_init_t *init);
+static nxt_int_t nxt_init_vldt_isolation_creds(nxt_task_t *task,
+ nxt_process_init_t *init);
+static nxt_int_t nxt_init_isolation_credential_map(nxt_task_t *task,
+ nxt_mp_t *mem_pool, nxt_conf_value_t *map_array,
+ nxt_clone_credential_map_t *map);
+#endif
const nxt_sig_event_t nxt_main_process_signals[] = {
nxt_event_signal(SIGHUP, nxt_main_process_signal_handler),
@@ -84,6 +108,54 @@ const nxt_sig_event_t nxt_main_process_signals[] = {
};
+static const nxt_port_handlers_t nxt_app_process_port_handlers = {
+ .new_port = nxt_port_new_port_handler,
+ .change_file = nxt_port_change_log_file_handler,
+ .mmap = nxt_port_mmap_handler,
+ .remove_pid = nxt_port_remove_pid_handler,
+};
+
+
+static const nxt_port_handlers_t nxt_discovery_process_port_handlers = {
+ .quit = nxt_worker_process_quit_handler,
+ .new_port = nxt_port_new_port_handler,
+ .change_file = nxt_port_change_log_file_handler,
+ .mmap = nxt_port_mmap_handler,
+ .data = nxt_port_data_handler,
+ .remove_pid = nxt_port_remove_pid_handler,
+ .rpc_ready = nxt_port_rpc_handler,
+ .rpc_error = nxt_port_rpc_handler,
+};
+
+
+static const nxt_port_handlers_t *nxt_process_port_handlers[NXT_PROCESS_MAX] =
+{
+ NULL,
+ &nxt_discovery_process_port_handlers,
+ &nxt_controller_process_port_handlers,
+ &nxt_router_process_port_handlers,
+ &nxt_app_process_port_handlers
+};
+
+
+static const nxt_process_start_t nxt_process_starts[NXT_PROCESS_MAX] = {
+ NULL,
+ nxt_discovery_start,
+ nxt_controller_start,
+ nxt_router_start,
+ nxt_app_start
+};
+
+
+static const nxt_process_restart_t nxt_process_restarts[NXT_PROCESS_MAX] = {
+ NULL,
+ NULL,
+ &nxt_main_create_controller_process,
+ &nxt_main_create_router_process,
+ NULL
+};
+
+
static nxt_bool_t nxt_exiting;
@@ -143,7 +215,24 @@ static nxt_conf_map_t nxt_common_app_conf[] = {
nxt_string("isolation"),
NXT_CONF_MAP_PTR,
offsetof(nxt_common_app_conf_t, isolation),
- }
+ },
+
+ {
+ nxt_string("limits"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_common_app_conf_t, limits),
+ },
+
+};
+
+
+static nxt_conf_map_t nxt_common_app_limits_conf[] = {
+ {
+ nxt_string("shm"),
+ NXT_CONF_MAP_SIZE,
+ offsetof(nxt_common_app_conf_t, shm_limit),
+ },
+
};
@@ -309,6 +398,7 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
app_conf.name.start = start;
app_conf.name.length = nxt_strlen(start);
+ app_conf.shm_limit = 100 * 1024 * 1024;
start += app_conf.name.length + 1;
@@ -355,6 +445,18 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
goto failed;
}
+ if (app_conf.limits != NULL) {
+ ret = nxt_conf_map_object(mp, app_conf.limits,
+ nxt_common_app_limits_conf,
+ nxt_nitems(nxt_common_app_limits_conf),
+ &app_conf);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "failed to map app limits received from router");
+ goto failed;
+ }
+ }
+
ret = nxt_main_start_worker_process(task, task->thread->runtime,
&app_conf, msg->port_msg.stream);
@@ -453,20 +555,13 @@ nxt_main_start_controller_process(nxt_task_t *task, nxt_runtime_t *rt)
{
nxt_process_init_t *init;
- init = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_init_t));
+ static const nxt_str_t name = nxt_string("controller");
+
+ init = nxt_process_init_create(task, NXT_PROCESS_CONTROLLER, &name);
if (nxt_slow_path(init == NULL)) {
return NXT_ERROR;
}
- init->start = nxt_controller_start;
- init->name = "controller";
- init->user_cred = &rt->user_cred;
- init->port_handlers = &nxt_controller_process_port_handlers;
- init->signals = nxt_worker_process_signals;
- init->type = NXT_PROCESS_CONTROLLER;
- init->stream = 0;
- init->restart = &nxt_main_create_controller_process;
-
return nxt_main_create_controller_process(task, rt, init);;
}
@@ -547,21 +642,13 @@ nxt_main_start_discovery_process(nxt_task_t *task, nxt_runtime_t *rt)
{
nxt_process_init_t *init;
- init = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_init_t));
+ static const nxt_str_t name = nxt_string("discovery");
+
+ init = nxt_process_init_create(task, NXT_PROCESS_DISCOVERY, &name);
if (nxt_slow_path(init == NULL)) {
return NXT_ERROR;
}
- init->start = nxt_discovery_start;
- init->name = "discovery";
- init->user_cred = &rt->user_cred;
- init->port_handlers = &nxt_discovery_process_port_handlers;
- init->signals = nxt_worker_process_signals;
- init->type = NXT_PROCESS_DISCOVERY;
- init->data = rt;
- init->stream = 0;
- init->restart = NULL;
-
return nxt_main_create_worker_process(task, rt, init);
}
@@ -571,72 +658,59 @@ nxt_main_start_router_process(nxt_task_t *task, nxt_runtime_t *rt)
{
nxt_process_init_t *init;
- init = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_init_t));
+ static const nxt_str_t name = nxt_string("router");
+
+ init = nxt_process_init_create(task, NXT_PROCESS_ROUTER, &name);
if (nxt_slow_path(init == NULL)) {
return NXT_ERROR;
}
- init->start = nxt_router_start;
- init->name = "router";
- init->user_cred = &rt->user_cred;
- init->port_handlers = &nxt_router_process_port_handlers;
- init->signals = nxt_worker_process_signals;
- init->type = NXT_PROCESS_ROUTER;
- init->data = rt;
- init->stream = 0;
- init->restart = &nxt_main_create_worker_process;
+ return nxt_main_create_router_process(task, rt, init);
+}
+
+
+static nxt_int_t
+nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt,
+ nxt_process_init_t *init)
+{
+ nxt_main_stop_worker_processes(task, rt);
return nxt_main_create_worker_process(task, rt, init);
}
+
static nxt_int_t
nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_common_app_conf_t *app_conf, uint32_t stream)
{
- char *user, *group;
- u_char *title, *last, *end;
- size_t size;
+ nxt_int_t cap_setid;
nxt_int_t ret;
nxt_process_init_t *init;
- size = sizeof(nxt_process_init_t)
- + app_conf->name.length
- + sizeof("\"\" application");
-
- if (rt->capabilities.setid) {
- size += sizeof(nxt_user_cred_t)
- + app_conf->user.length + 1
- + app_conf->group.length + 1;
- }
-
- init = nxt_mp_zalloc(rt->mem_pool, size);
+ init = nxt_process_init_create(task, NXT_PROCESS_WORKER, &app_conf->name);
if (nxt_slow_path(init == NULL)) {
return NXT_ERROR;
}
- if (rt->capabilities.setid) {
- init->user_cred = nxt_pointer_to(init, sizeof(nxt_process_init_t));
- user = nxt_pointer_to(init->user_cred, sizeof(nxt_user_cred_t));
-
- nxt_memcpy(user, app_conf->user.start, app_conf->user.length);
- last = nxt_pointer_to(user, app_conf->user.length);
- *last++ = '\0';
+ cap_setid = rt->capabilities.setid;
- init->user_cred->user = user;
-
- if (app_conf->group.start != NULL) {
- group = (char *) last;
-
- nxt_memcpy(group, app_conf->group.start, app_conf->group.length);
- last = nxt_pointer_to(group, app_conf->group.length);
- *last++ = '\0';
-
- } else {
- group = NULL;
+ if (app_conf->isolation != NULL) {
+ ret = nxt_init_isolation(task, app_conf->isolation, init);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
}
+ }
- ret = nxt_user_cred_get(task, init->user_cred, group);
- if (ret != NXT_OK) {
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (NXT_CLONE_USER(init->isolation.clone.flags)) {
+ cap_setid = 1;
+ }
+#endif
+
+ if (cap_setid) {
+ ret = nxt_process_init_creds_set(task, init, &app_conf->user,
+ &app_conf->group);
+ if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -658,40 +732,29 @@ nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
&app_conf->name);
goto fail;
}
-
- last = nxt_pointer_to(init, sizeof(nxt_process_init_t));
}
- title = last;
- end = title + app_conf->name.length + sizeof("\"\" application");
-
- nxt_sprintf(title, end, "\"%V\" application%Z", &app_conf->name);
-
- init->start = nxt_app_start;
- init->name = (char *) title;
- init->port_handlers = &nxt_app_process_port_handlers;
- init->signals = nxt_worker_process_signals;
- init->type = NXT_PROCESS_WORKER;
init->data = app_conf;
init->stream = stream;
- init->restart = NULL;
- ret = nxt_init_set_isolation(task, init, app_conf->isolation);
+#if (NXT_HAVE_CLONE_NEWUSER)
+ ret = nxt_init_vldt_isolation_creds(task, init);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
+#endif
return nxt_main_create_worker_process(task, rt, init);
fail:
- nxt_mp_free(rt->mem_pool, init);
+ nxt_mp_destroy(init->mem_pool);
return NXT_ERROR;
}
-static nxt_int_t
+nxt_int_t
nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_process_init_t *init)
{
@@ -706,7 +769,7 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
process = nxt_runtime_process_new(rt);
if (nxt_slow_path(process == NULL)) {
- nxt_mp_free(rt->mem_pool, init);
+ nxt_mp_destroy(init->mem_pool);
return NXT_ERROR;
}
@@ -972,12 +1035,13 @@ nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data)
static void
nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid)
{
- nxt_buf_t *buf;
- nxt_port_t *port;
- nxt_runtime_t *rt;
- nxt_process_t *process;
- nxt_process_type_t ptype;
- nxt_process_init_t *init;
+ nxt_buf_t *buf;
+ nxt_port_t *port;
+ nxt_runtime_t *rt;
+ nxt_process_t *process;
+ nxt_process_type_t ptype;
+ nxt_process_init_t *init;
+ nxt_process_restart_t restart;
rt = task->thread->runtime;
@@ -988,6 +1052,7 @@ nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid)
process->init = NULL;
ptype = nxt_process_type(process);
+ restart = nxt_process_restarts[ptype];
if (process->ready) {
init->stream = 0;
@@ -996,6 +1061,8 @@ nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid)
nxt_process_close_ports(task, process);
if (nxt_exiting) {
+ nxt_mp_destroy(init->mem_pool);
+
if (rt->nprocesses <= 2) {
nxt_runtime_quit(task, 0);
}
@@ -1030,15 +1097,11 @@ nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid)
-1, init->stream, 0, buf);
} nxt_runtime_process_loop;
- if (init->restart != NULL) {
- if (init->type == NXT_PROCESS_ROUTER) {
- nxt_main_stop_worker_processes(task, rt);
- }
-
- init->restart(task, rt, init);
+ if (restart != NULL) {
+ restart(task, rt, init);
} else {
- nxt_mp_free(rt->mem_pool, init);
+ nxt_mp_destroy(init->mem_pool);
}
}
}
@@ -1484,45 +1547,178 @@ nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
static nxt_int_t
-nxt_init_set_isolation(nxt_task_t *task, nxt_process_init_t *init,
- nxt_conf_value_t *isolation)
+nxt_init_isolation(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_init_t *init)
+{
+#if (NXT_HAVE_CLONE)
+ nxt_int_t ret;
+ nxt_conf_value_t *obj;
+
+ static nxt_str_t nsname = nxt_string("namespaces");
+
+ obj = nxt_conf_get_object_member(isolation, &nsname, NULL);
+ if (obj != NULL) {
+ ret = nxt_init_clone_flags(task, obj, init);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ ret = nxt_init_isolation_creds(task, isolation, init);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+
+static nxt_int_t
+nxt_init_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_init_t *init)
{
nxt_int_t ret;
- nxt_conf_value_t *object;
+ nxt_clone_t *clone;
+ nxt_conf_value_t *array;
- static nxt_str_t nsname = nxt_string("namespaces");
static nxt_str_t uidname = nxt_string("uidmap");
static nxt_str_t gidname = nxt_string("gidmap");
- if (isolation == NULL) {
+ clone = &init->isolation.clone;
+
+ array = nxt_conf_get_object_member(isolation, &uidname, NULL);
+ if (array != NULL) {
+ ret = nxt_init_isolation_credential_map(task, init->mem_pool, array,
+ &clone->uidmap);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ array = nxt_conf_get_object_member(isolation, &gidname, NULL);
+ if (array != NULL) {
+ ret = nxt_init_isolation_credential_map(task, init->mem_pool, array,
+ &clone->gidmap);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_init_vldt_isolation_creds(nxt_task_t *task, nxt_process_init_t *init)
+{
+ nxt_int_t ret;
+ nxt_clone_t *clone;
+
+ clone = &init->isolation.clone;
+
+ if (clone->uidmap.size == 0 && clone->gidmap.size == 0) {
return NXT_OK;
}
- object = nxt_conf_get_object_member(isolation, &nsname, NULL);
- if (object != NULL) {
- ret = nxt_init_set_ns(task, init, object);
- if (ret != NXT_OK) {
- return ret;
+ if (!NXT_CLONE_USER(clone->flags)) {
+ if (nxt_slow_path(clone->uidmap.size > 0)) {
+ nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but "
+ "\"isolation.namespaces.credential\" is false or unset");
+
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(clone->gidmap.size > 0)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but "
+ "\"isolation.namespaces.credential\" is false or unset");
+
+ return NXT_ERROR;
}
+
+ return NXT_OK;
+ }
+
+ ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap,
+ init->user_cred);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap,
+ init->user_cred);
+}
+
+
+static nxt_int_t
+nxt_init_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool,
+ nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map)
+{
+ nxt_int_t ret;
+ nxt_uint_t i;
+ nxt_conf_value_t *obj;
+
+ static nxt_conf_map_t nxt_clone_map_entry_conf[] = {
+ {
+ nxt_string("container"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, container),
+ },
+
+ {
+ nxt_string("host"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, host),
+ },
+
+ {
+ nxt_string("size"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, size),
+ },
+ };
+
+ map->size = nxt_conf_array_elements_count(map_array);
+
+ if (map->size == 0) {
+ return NXT_OK;
}
- object = nxt_conf_get_object_member(isolation, &uidname, NULL);
- if (object != NULL) {
- init->isolation.clone.uidmap = object;
+ map->map = nxt_mp_alloc(mem_pool,
+ map->size * sizeof(nxt_clone_map_entry_t));
+ if (nxt_slow_path(map->map == NULL)) {
+ return NXT_ERROR;
}
- object = nxt_conf_get_object_member(isolation, &gidname, NULL);
- if (object != NULL) {
- init->isolation.clone.gidmap = object;
+ for (i = 0; i < map->size; i++) {
+ obj = nxt_conf_get_array_element(map_array, i);
+
+ ret = nxt_conf_map_object(mem_pool, obj, nxt_clone_map_entry_conf,
+ nxt_nitems(nxt_clone_map_entry_conf),
+ map->map + i);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "clone map entry map error");
+ return NXT_ERROR;
+ }
}
return NXT_OK;
}
+#endif
+
+#if (NXT_HAVE_CLONE)
static nxt_int_t
-nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init,
- nxt_conf_value_t *namespaces)
+nxt_init_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces,
+ nxt_process_init_t *init)
{
uint32_t index;
nxt_str_t name;
@@ -1588,3 +1784,122 @@ nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init,
return NXT_OK;
}
+
+#endif
+
+
+static nxt_process_init_t *
+nxt_process_init_create(nxt_task_t *task, nxt_process_type_t type,
+ const nxt_str_t *name)
+{
+ nxt_mp_t *mp;
+ nxt_int_t ret;
+ nxt_runtime_t *rt;
+ nxt_process_init_t *init;
+
+ mp = nxt_mp_create(1024, 128, 256, 32);
+ if (nxt_slow_path(mp == NULL)) {
+ return NULL;
+ }
+
+ init = nxt_mp_zalloc(mp, sizeof(nxt_process_init_t));
+ if (nxt_slow_path(init == NULL)) {
+ goto fail;
+ }
+
+ init->mem_pool = mp;
+
+ ret = nxt_process_init_name_set(init, type, name);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
+ rt = task->thread->runtime;
+
+ init->type = type;
+ init->start = nxt_process_starts[type];
+ init->port_handlers = nxt_process_port_handlers[type];
+ init->signals = nxt_worker_process_signals;
+ init->user_cred = &rt->user_cred;
+ init->data = &rt;
+
+ return init;
+
+fail:
+
+ nxt_mp_destroy(mp);
+
+ return NULL;
+}
+
+
+static nxt_int_t
+nxt_process_init_name_set(nxt_process_init_t *init, nxt_process_type_t type,
+ const nxt_str_t *name)
+{
+ u_char *str, *end;
+ size_t size;
+ const char *fmt;
+
+ size = name->length + 1;
+
+ if (type == NXT_PROCESS_WORKER) {
+ size += nxt_length("\"\" application");
+ fmt = "\"%V\" application%Z";
+
+ } else {
+ fmt = "%V%Z";
+ }
+
+ str = nxt_mp_alloc(init->mem_pool, size);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
+
+ end = str + size;
+
+ nxt_sprintf(str, end, fmt, name);
+
+ init->name = (char *) str;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_process_init_creds_set(nxt_task_t *task, nxt_process_init_t *init,
+ nxt_str_t *user, nxt_str_t *group)
+{
+ char *str;
+
+ init->user_cred = nxt_mp_zalloc(init->mem_pool, sizeof(nxt_credential_t));
+
+ if (nxt_slow_path(init->user_cred == NULL)) {
+ return NXT_ERROR;
+ }
+
+ str = nxt_mp_zalloc(init->mem_pool, user->length + 1);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(str, user->start, user->length);
+ str[user->length] = '\0';
+
+ init->user_cred->user = str;
+
+ if (group->start != NULL) {
+ str = nxt_mp_zalloc(init->mem_pool, group->length + 1);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(str, group->start, group->length);
+ str[group->length] = '\0';
+
+ } else {
+ str = NULL;
+ }
+
+ return nxt_credential_get(task, init->mem_pool, init->user_cred, str);
+}
diff --git a/src/nxt_main_process.h b/src/nxt_main_process.h
index d932e11f..b0570a84 100644
--- a/src/nxt_main_process.h
+++ b/src/nxt_main_process.h
@@ -36,10 +36,7 @@ nxt_int_t nxt_router_start(nxt_task_t *task, void *data);
nxt_int_t nxt_discovery_start(nxt_task_t *task, void *data);
nxt_int_t nxt_app_start(nxt_task_t *task, void *data);
-extern nxt_port_handlers_t nxt_controller_process_port_handlers;
-extern nxt_port_handlers_t nxt_discovery_process_port_handlers;
-extern nxt_port_handlers_t nxt_app_process_port_handlers;
-extern nxt_port_handlers_t nxt_router_process_port_handlers;
+
extern const nxt_sig_event_t nxt_main_process_signals[];
extern const nxt_sig_event_t nxt_worker_process_signals[];
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 7a5e0a3b..0f6ce686 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -352,6 +352,7 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_fd_blocking(task, my_port->pair[0]);
php_init.log_fd = 2;
+ php_init.shm_limit = conf->shm_limit;
unit_ctx = nxt_unit_init(&php_init);
if (nxt_slow_path(unit_ctx == NULL)) {
diff --git a/src/nxt_port.c b/src/nxt_port.c
index 8d14a5e7..70cf33e6 100644
--- a/src/nxt_port.c
+++ b/src/nxt_port.c
@@ -140,7 +140,7 @@ nxt_port_reset_next_id()
void
nxt_port_enable(nxt_task_t *task, nxt_port_t *port,
- nxt_port_handlers_t *handlers)
+ const nxt_port_handlers_t *handlers)
{
port->pid = nxt_pid;
port->handler = nxt_port_handler;
diff --git a/src/nxt_port.h b/src/nxt_port.h
index eeb6caa5..c6f15238 100644
--- a/src/nxt_port.h
+++ b/src/nxt_port.h
@@ -44,6 +44,9 @@ struct nxt_port_handlers_s {
/* Various data. */
nxt_port_handler_t data;
+
+ nxt_port_handler_t oosm;
+ nxt_port_handler_t shm_ack;
};
@@ -82,6 +85,9 @@ typedef enum {
_NXT_PORT_MSG_DATA = nxt_port_handler_idx(data),
+ _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm),
+ _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack),
+
NXT_PORT_MSG_MAX = sizeof(nxt_port_handlers_t)
/ sizeof(nxt_port_handler_t),
@@ -114,6 +120,9 @@ typedef enum {
NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA,
NXT_PORT_MSG_DATA_LAST = _NXT_PORT_MSG_DATA | NXT_PORT_MSG_LAST,
+
+ NXT_PORT_MSG_OOSM = _NXT_PORT_MSG_OOSM | NXT_PORT_MSG_LAST,
+ NXT_PORT_MSG_SHM_ACK = _NXT_PORT_MSG_SHM_ACK | NXT_PORT_MSG_LAST,
} nxt_port_msg_type_t;
@@ -269,7 +278,7 @@ nxt_port_socket_write(nxt_task_t *task, nxt_port_t *port,
}
void nxt_port_enable(nxt_task_t *task, nxt_port_t *port,
- nxt_port_handlers_t *handlers);
+ const nxt_port_handlers_t *handlers);
nxt_int_t nxt_port_send_port(nxt_task_t *task, nxt_port_t *port,
nxt_port_t *new_port, uint32_t stream);
void nxt_port_change_log_file(nxt_task_t *task, nxt_runtime_t *rt,
diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c
index b7068c88..24a40406 100644
--- a/src/nxt_port_memory.c
+++ b/src/nxt_port_memory.c
@@ -112,6 +112,8 @@ nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
u_char *p;
nxt_mp_t *mp;
nxt_buf_t *b;
+ nxt_port_t *port;
+ nxt_process_t *process;
nxt_chunk_id_t c;
nxt_port_mmap_header_t *hdr;
nxt_port_mmap_handler_t *mmap_handler;
@@ -163,6 +165,21 @@ nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
c++;
}
+ if (hdr->dst_pid == nxt_pid
+ && nxt_atomic_cmp_set(&hdr->oosm, 1, 0))
+ {
+ process = nxt_runtime_process_find(task->thread->runtime, hdr->src_pid);
+
+ if (process != NULL && !nxt_queue_is_empty(&process->ports)) {
+ port = nxt_process_port_first(process);
+
+ if (port->type == NXT_PROCESS_WORKER) {
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK,
+ -1, 0, 0, NULL);
+ }
+ }
+ }
+
release_buf:
nxt_port_mmap_handler_use(mmap_handler, -1);
@@ -454,6 +471,8 @@ nxt_port_mmap_get(nxt_task_t *task, nxt_port_t *port, nxt_chunk_id_t *c,
goto unlock_return;
}
}
+
+ hdr->oosm = 1;
}
/* TODO introduce port_mmap limit and release wait. */
diff --git a/src/nxt_port_memory_int.h b/src/nxt_port_memory_int.h
index 53dfaebf..87c3d833 100644
--- a/src/nxt_port_memory_int.h
+++ b/src/nxt_port_memory_int.h
@@ -51,6 +51,7 @@ struct nxt_port_mmap_header_s {
nxt_pid_t src_pid; /* For sanity check. */
nxt_pid_t dst_pid; /* For sanity check. */
nxt_port_id_t sent_over;
+ nxt_atomic_t oosm;
nxt_free_map_t free_map[MAX_FREE_IDX];
nxt_free_map_t free_map_padding;
nxt_free_map_t free_tracking_map[MAX_FREE_IDX];
diff --git a/src/nxt_process.c b/src/nxt_process.c
index b246a58c..035f747f 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -14,7 +14,6 @@
#include <signal.h>
static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
-static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
static nxt_int_t nxt_process_worker_setup(nxt_task_t *task,
nxt_process_t *process, int parentfd);
@@ -24,6 +23,12 @@ nxt_pid_t nxt_pid;
/* An original parent process pid. */
nxt_pid_t nxt_ppid;
+/* A cached process effective uid */
+nxt_uid_t nxt_euid;
+
+/* A cached process effective gid */
+nxt_gid_t nxt_egid;
+
nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
{ 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0 },
@@ -208,8 +213,9 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process)
}
#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
- if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) {
- ret = nxt_clone_proc_map(task, pid, &init->isolation.clone);
+ if (NXT_CLONE_USER(init->isolation.clone.flags)) {
+ ret = nxt_clone_credential_map(task, pid, init->user_cred,
+ &init->isolation.clone);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -258,7 +264,7 @@ cleanup:
static void
nxt_process_start(nxt_task_t *task, nxt_process_t *process)
{
- nxt_int_t ret;
+ nxt_int_t ret, cap_setid;
nxt_port_t *port, *main_port;
nxt_thread_t *thread;
nxt_runtime_t *rt;
@@ -277,9 +283,22 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process)
nxt_random_init(&thread->random);
- if (rt->capabilities.setid && init->user_cred != NULL) {
- ret = nxt_user_cred_set(task, init->user_cred);
- if (ret != NXT_OK) {
+ cap_setid = rt->capabilities.setid;
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (!cap_setid && NXT_CLONE_USER(init->isolation.clone.flags)) {
+ cap_setid = 1;
+ }
+#endif
+
+ if (cap_setid) {
+ ret = nxt_credential_setgids(task, init->user_cred);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
+ ret = nxt_credential_setuid(task, init->user_cred);
+ if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
}
@@ -525,264 +544,6 @@ nxt_nanosleep(nxt_nsec_t ns)
}
-nxt_int_t
-nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
-{
- struct group *grp;
- struct passwd *pwd;
-
- nxt_errno = 0;
-
- pwd = getpwnam(uc->user);
-
- if (nxt_slow_path(pwd == NULL)) {
-
- if (nxt_errno == 0) {
- nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
- uc->user, uc->user);
- } else {
- nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
- }
-
- return NXT_ERROR;
- }
-
- uc->uid = pwd->pw_uid;
- uc->base_gid = pwd->pw_gid;
-
- if (group != NULL && group[0] != '\0') {
- nxt_errno = 0;
-
- grp = getgrnam(group);
-
- if (nxt_slow_path(grp == NULL)) {
-
- if (nxt_errno == 0) {
- nxt_alert(task,
- "getgrnam(\"%s\") failed, group \"%s\" not found",
- group, group);
- } else {
- nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
- }
-
- return NXT_ERROR;
- }
-
- uc->base_gid = grp->gr_gid;
- }
-
- return nxt_user_groups_get(task, uc);
-}
-
-
-/*
- * nxt_user_groups_get() stores an array of groups IDs which should be
- * set by the initgroups() function for a given user. The initgroups()
- * may block a just forked worker process for some time if LDAP or NDIS+
- * is used, so nxt_user_groups_get() allows to get worker user groups in
- * main process. In a nutshell the initgroups() calls getgrouplist()
- * followed by setgroups(). However Solaris lacks the getgrouplist().
- * Besides getgrouplist() does not allow to query the exact number of
- * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
- * So nxt_user_groups_get() emulates getgrouplist(): at first the function
- * saves the super-user groups IDs, then calls initgroups() and saves the
- * specified user groups IDs, and then restores the super-user groups IDs.
- * This works at least on Linux, FreeBSD, and Solaris, but does not work
- * on MacOSX, getgroups(2):
- *
- * To provide compatibility with applications that use getgroups() in
- * environments where users may be in more than {NGROUPS_MAX} groups,
- * a variant of getgroups(), obtained when compiling with either the
- * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
- * be used that is not limited to {NGROUPS_MAX} groups. However, this
- * variant only returns the user's default group access list and not
- * the group list modified by a call to setgroups(2).
- *
- * For such cases initgroups() is used in worker process as fallback.
- */
-
-static nxt_int_t
-nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
-{
- int nsaved, ngroups;
- nxt_int_t ret;
- nxt_gid_t *saved;
-
- nsaved = getgroups(0, NULL);
-
- if (nsaved == -1) {
- nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
- return NXT_ERROR;
- }
-
- nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
-
- if (nsaved > NGROUPS_MAX) {
- /* MacOSX case. */
-
- uc->gids = NULL;
- uc->ngroups = 0;
-
- return NXT_OK;
- }
-
- saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
-
- if (saved == NULL) {
- return NXT_ERROR;
- }
-
- ret = NXT_ERROR;
-
- nsaved = getgroups(nsaved, saved);
-
- if (nsaved == -1) {
- nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
- goto free;
- }
-
- nxt_debug(task, "getgroups(): %d", nsaved);
-
- if (initgroups(uc->user, uc->base_gid) != 0) {
- if (nxt_errno == NXT_EPERM) {
- nxt_log(task, NXT_LOG_NOTICE,
- "initgroups(%s, %d) failed %E, ignored",
- uc->user, uc->base_gid, nxt_errno);
-
- ret = NXT_OK;
-
- goto free;
-
- } else {
- nxt_alert(task, "initgroups(%s, %d) failed %E",
- uc->user, uc->base_gid, nxt_errno);
- goto restore;
- }
- }
-
- ngroups = getgroups(0, NULL);
-
- if (ngroups == -1) {
- nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
- goto restore;
- }
-
- nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
-
- uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
-
- if (uc->gids == NULL) {
- goto restore;
- }
-
- ngroups = getgroups(ngroups, uc->gids);
-
- if (ngroups == -1) {
- nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
- goto restore;
- }
-
- uc->ngroups = ngroups;
-
-#if (NXT_DEBUG)
- {
- u_char *p, *end;
- nxt_uint_t i;
- u_char msg[NXT_MAX_ERROR_STR];
-
- p = msg;
- end = msg + NXT_MAX_ERROR_STR;
-
- for (i = 0; i < uc->ngroups; i++) {
- p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
- }
-
- nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
- uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
- p - msg, msg);
- }
-#endif
-
- ret = NXT_OK;
-
-restore:
-
- if (setgroups(nsaved, saved) != 0) {
- nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
- ret = NXT_ERROR;
- }
-
-free:
-
- nxt_free(saved);
-
- return ret;
-}
-
-
-nxt_int_t
-nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
-{
- nxt_debug(task, "user cred set: \"%s\" uid:%d base gid:%d",
- uc->user, uc->uid, uc->base_gid);
-
- if (setgid(uc->base_gid) != 0) {
-
-#if (NXT_HAVE_CLONE)
- if (nxt_errno == EINVAL) {
- nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the "
- "application namespace.", uc->base_gid);
- return NXT_ERROR;
- }
-#endif
-
- nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
- return NXT_ERROR;
- }
-
- if (uc->gids != NULL) {
- if (setgroups(uc->ngroups, uc->gids) != 0) {
-
-#if (NXT_HAVE_CLONE)
- if (nxt_errno == EINVAL) {
- nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has "
- "supplementary group ids not valid in the application "
- "namespace.", uc->user, uc->uid);
- return NXT_ERROR;
- }
-#endif
-
- nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
- return NXT_ERROR;
- }
-
- } else {
- /* MacOSX fallback. */
- if (initgroups(uc->user, uc->base_gid) != 0) {
- nxt_alert(task, "initgroups(%s, %d) failed %E",
- uc->user, uc->base_gid, nxt_errno);
- return NXT_ERROR;
- }
- }
-
- if (setuid(uc->uid) != 0) {
-
-#if (NXT_HAVE_CLONE)
- if (nxt_errno == EINVAL) {
- nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't "
- "valid in the application namespace.", uc->uid, uc->user);
- return NXT_ERROR;
- }
-#endif
-
- nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
- return NXT_ERROR;
- }
-
- return NXT_OK;
-}
-
-
void
nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i)
{
diff --git a/src/nxt_process.h b/src/nxt_process.h
index d67573f1..343fffb8 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -7,27 +7,13 @@
#ifndef _NXT_PROCESS_H_INCLUDED_
#define _NXT_PROCESS_H_INCLUDED_
-#include <nxt_conf.h>
+#if (NXT_HAVE_CLONE)
+#include <nxt_clone.h>
+#endif
typedef pid_t nxt_pid_t;
-typedef uid_t nxt_uid_t;
-typedef gid_t nxt_gid_t;
-
-typedef struct {
- const char *user;
- nxt_uid_t uid;
- nxt_gid_t base_gid;
- nxt_uint_t ngroups;
- nxt_gid_t *gids;
-} nxt_user_cred_t;
-
-typedef struct {
- nxt_int_t flags;
- nxt_conf_value_t *uidmap;
- nxt_conf_value_t *gidmap;
-} nxt_process_clone_t;
typedef struct nxt_process_init_s nxt_process_init_t;
typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, void *data);
@@ -35,22 +21,23 @@ typedef nxt_int_t (*nxt_process_restart_t)(nxt_task_t *task, nxt_runtime_t *rt,
nxt_process_init_t *init);
struct nxt_process_init_s {
- nxt_process_start_t start;
- const char *name;
- nxt_user_cred_t *user_cred;
+ nxt_mp_t *mem_pool;
+ nxt_process_start_t start;
+ const char *name;
+ nxt_credential_t *user_cred;
- nxt_port_handlers_t *port_handlers;
- const nxt_sig_event_t *signals;
+ const nxt_port_handlers_t *port_handlers;
+ const nxt_sig_event_t *signals;
- nxt_process_type_t type;
+ nxt_process_type_t type;
- void *data;
- uint32_t stream;
-
- nxt_process_restart_t restart;
+ void *data;
+ uint32_t stream;
union {
- nxt_process_clone_t clone;
+#if (NXT_HAVE_CLONE)
+ nxt_clone_t clone;
+#endif
} isolation;
};
@@ -126,10 +113,11 @@ void nxt_process_connected_port_remove(nxt_process_t *process,
nxt_port_t *nxt_process_connected_port_find(nxt_process_t *process,
nxt_pid_t pid, nxt_port_id_t port_id);
-
void nxt_worker_process_quit_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
+void nxt_init_destroy(nxt_runtime_t *rt, nxt_process_init_t *init);
+
#if (NXT_HAVE_SETPROCTITLE)
@@ -155,12 +143,11 @@ NXT_EXPORT void nxt_process_title(nxt_task_t *task, const char *fmt, ...);
#define nxt_abort() \
(void) raise(SIGABRT)
-NXT_EXPORT nxt_int_t nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc,
- const char *group);
-NXT_EXPORT nxt_int_t nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc);
NXT_EXPORT extern nxt_pid_t nxt_pid;
NXT_EXPORT extern nxt_pid_t nxt_ppid;
+NXT_EXPORT extern nxt_uid_t nxt_euid;
+NXT_EXPORT extern nxt_gid_t nxt_egid;
NXT_EXPORT extern char **nxt_process_argv;
NXT_EXPORT extern char ***nxt_process_environ;
diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c
index 5bb2fb2c..ea8b6903 100644
--- a/src/nxt_python_wsgi.c
+++ b/src/nxt_python_wsgi.c
@@ -50,6 +50,8 @@
#define PyBytes_Check PyString_Check
#define PyBytes_GET_SIZE PyString_GET_SIZE
#define PyBytes_AS_STRING PyString_AS_STRING
+#define PyUnicode_InternInPlace PyString_InternInPlace
+#define PyUnicode_AsUTF8 PyString_AS_STRING
#endif
typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t;
@@ -64,15 +66,18 @@ typedef struct {
} nxt_py_error_t;
static nxt_int_t nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_python_init_strings(void);
static void nxt_python_request_handler(nxt_unit_request_info_t *req);
static void nxt_python_atexit(void);
static PyObject *nxt_python_create_environ(nxt_task_t *task);
static PyObject *nxt_python_get_environ(nxt_python_run_ctx_t *ctx);
-static int nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
+static int nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size);
-static int nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name,
- const char *str, uint32_t size);
+static int nxt_python_add_field(nxt_python_run_ctx_t *ctx,
+ nxt_unit_field_t *field);
+static int nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name,
+ PyObject *value);
static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args);
static int nxt_python_response_add_field(nxt_python_run_ctx_t *ctx,
@@ -157,6 +162,48 @@ static PyThreadState *nxt_python_thread_state;
static nxt_python_run_ctx_t *nxt_python_run_ctx;
+static PyObject *nxt_py_80_str;
+static PyObject *nxt_py_close_str;
+static PyObject *nxt_py_content_length_str;
+static PyObject *nxt_py_content_type_str;
+static PyObject *nxt_py_http_str;
+static PyObject *nxt_py_https_str;
+static PyObject *nxt_py_path_info_str;
+static PyObject *nxt_py_query_string_str;
+static PyObject *nxt_py_remote_addr_str;
+static PyObject *nxt_py_request_method_str;
+static PyObject *nxt_py_request_uri_str;
+static PyObject *nxt_py_server_addr_str;
+static PyObject *nxt_py_server_name_str;
+static PyObject *nxt_py_server_port_str;
+static PyObject *nxt_py_server_protocol_str;
+static PyObject *nxt_py_wsgi_uri_scheme_str;
+
+typedef struct {
+ nxt_str_t string;
+ PyObject **object_p;
+} nxt_python_string_t;
+
+static nxt_python_string_t nxt_python_strings[] = {
+ { nxt_string("80"), &nxt_py_80_str },
+ { nxt_string("close"), &nxt_py_close_str },
+ { nxt_string("CONTENT_LENGTH"), &nxt_py_content_length_str },
+ { nxt_string("CONTENT_TYPE"), &nxt_py_content_type_str },
+ { nxt_string("http"), &nxt_py_http_str },
+ { nxt_string("https"), &nxt_py_https_str },
+ { nxt_string("PATH_INFO"), &nxt_py_path_info_str },
+ { nxt_string("QUERY_STRING"), &nxt_py_query_string_str },
+ { nxt_string("REMOTE_ADDR"), &nxt_py_remote_addr_str },
+ { nxt_string("REQUEST_METHOD"), &nxt_py_request_method_str },
+ { nxt_string("REQUEST_URI"), &nxt_py_request_uri_str },
+ { nxt_string("SERVER_ADDR"), &nxt_py_server_addr_str },
+ { nxt_string("SERVER_NAME"), &nxt_py_server_name_str },
+ { nxt_string("SERVER_PORT"), &nxt_py_server_port_str },
+ { nxt_string("SERVER_PROTOCOL"), &nxt_py_server_protocol_str },
+ { nxt_string("wsgi.url_scheme"), &nxt_py_wsgi_uri_scheme_str },
+};
+
+
static nxt_int_t
nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
{
@@ -239,6 +286,12 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
Py_InitializeEx(0);
module = NULL;
+ obj = NULL;
+
+ if (nxt_slow_path(nxt_python_init_strings() != NXT_OK)) {
+ nxt_alert(task, "Python failed to init string objects");
+ goto fail;
+ }
obj = PySys_GetObject((char *) "stderr");
if (nxt_slow_path(obj == NULL)) {
@@ -351,6 +404,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_unit_default_init(task, &python_init);
python_init.callbacks.request_handler = nxt_python_request_handler;
+ python_init.shm_limit = conf->shm_limit;
unit_ctx = nxt_unit_init(&python_init);
if (nxt_slow_path(unit_ctx == NULL)) {
@@ -382,6 +436,31 @@ fail:
}
+static nxt_int_t
+nxt_python_init_strings(void)
+{
+ PyObject *obj;
+ nxt_uint_t i;
+ nxt_python_string_t *pstr;
+
+ for (i = 0; i < nxt_nitems(nxt_python_strings); i++) {
+ pstr = &nxt_python_strings[i];
+
+ obj = PyString_FromStringAndSize((char *) pstr->string.start,
+ pstr->string.length);
+ if (nxt_slow_path(obj == NULL)) {
+ return NXT_ERROR;
+ }
+
+ PyUnicode_InternInPlace(&obj);
+
+ *pstr->object_p = obj;
+ }
+
+ return NXT_OK;
+}
+
+
static void
nxt_python_request_handler(nxt_unit_request_info_t *req)
{
@@ -478,7 +557,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
rc = NXT_UNIT_ERROR;
}
- close = PyObject_GetAttrString(response, "close");
+ close = PyObject_GetAttr(response, nxt_py_close_str);
if (close != NULL) {
result = PyObject_CallFunction(close, NULL);
@@ -512,6 +591,12 @@ done:
static void
nxt_python_atexit(void)
{
+ nxt_uint_t i;
+
+ for (i = 0; i < nxt_nitems(nxt_python_strings); i++) {
+ Py_XDECREF(*nxt_python_strings[i].object_p);
+ }
+
Py_XDECREF(nxt_py_stderr_flush);
Py_XDECREF(nxt_py_application);
Py_XDECREF(nxt_py_start_resp_obj);
@@ -655,7 +740,6 @@ static PyObject *
nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
{
int rc;
- char *name;
uint32_t i;
PyObject *environ;
nxt_unit_field_t *f;
@@ -681,47 +765,52 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
} \
} while(0)
- RC(nxt_python_add_sptr(ctx, "REQUEST_METHOD", &r->method,
+ RC(nxt_python_add_sptr(ctx, nxt_py_request_method_str, &r->method,
r->method_length));
- RC(nxt_python_add_sptr(ctx, "REQUEST_URI", &r->target, r->target_length));
- RC(nxt_python_add_sptr(ctx, "QUERY_STRING", &r->query, r->query_length));
- RC(nxt_python_add_sptr(ctx, "PATH_INFO", &r->path, r->path_length));
-
- RC(nxt_python_add_sptr(ctx, "REMOTE_ADDR", &r->remote, r->remote_length));
- RC(nxt_python_add_sptr(ctx, "SERVER_ADDR", &r->local, r->local_length));
+ RC(nxt_python_add_sptr(ctx, nxt_py_request_uri_str, &r->target,
+ r->target_length));
+ RC(nxt_python_add_sptr(ctx, nxt_py_query_string_str, &r->query,
+ r->query_length));
+ RC(nxt_python_add_sptr(ctx, nxt_py_path_info_str, &r->path,
+ r->path_length));
+
+ RC(nxt_python_add_sptr(ctx, nxt_py_remote_addr_str, &r->remote,
+ r->remote_length));
+ RC(nxt_python_add_sptr(ctx, nxt_py_server_addr_str, &r->local,
+ r->local_length));
if (r->tls) {
- RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "https", 5));
-
+ RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str,
+ nxt_py_https_str));
} else {
- RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "http", 4));
+ RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str,
+ nxt_py_http_str));
}
- RC(nxt_python_add_sptr(ctx, "SERVER_PROTOCOL", &r->version,
+ RC(nxt_python_add_sptr(ctx, nxt_py_server_protocol_str, &r->version,
r->version_length));
- RC(nxt_python_add_sptr(ctx, "SERVER_NAME", &r->server_name,
+ RC(nxt_python_add_sptr(ctx, nxt_py_server_name_str, &r->server_name,
r->server_name_length));
- RC(nxt_python_add_str(ctx, "SERVER_PORT", "80", 2));
+ RC(nxt_python_add_obj(ctx, nxt_py_server_port_str, nxt_py_80_str));
for (i = 0; i < r->fields_count; i++) {
f = r->fields + i;
- name = nxt_unit_sptr_get(&f->name);
- RC(nxt_python_add_sptr(ctx, name, &f->value, f->value_length));
+ RC(nxt_python_add_field(ctx, f));
}
if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_length_field;
- RC(nxt_python_add_sptr(ctx, "CONTENT_LENGTH", &f->value,
+ RC(nxt_python_add_sptr(ctx, nxt_py_content_length_str, &f->value,
f->value_length));
}
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_type_field;
- RC(nxt_python_add_sptr(ctx, "CONTENT_TYPE", &f->value,
+ RC(nxt_python_add_sptr(ctx, nxt_py_content_type_str, &f->value,
f->value_length));
}
@@ -738,7 +827,7 @@ fail:
static int
-nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
+nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size)
{
char *src;
@@ -756,10 +845,10 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
return NXT_UNIT_ERROR;
}
- if (nxt_slow_path(PyDict_SetItemString(ctx->environ, name, value) != 0)) {
+ if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
nxt_unit_req_error(ctx->req,
"Python failed to set the \"%s\" environ value",
- name);
+ PyUnicode_AsUTF8(name));
Py_DECREF(value);
return NXT_UNIT_ERROR;
@@ -772,37 +861,67 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
static int
-nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name,
- const char *str, uint32_t size)
+nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field)
{
- PyObject *value;
+ char *src;
+ PyObject *name, *value;
- if (nxt_slow_path(str == NULL)) {
- return NXT_UNIT_OK;
+ src = nxt_unit_sptr_get(&field->name);
+
+ name = PyString_FromStringAndSize(src, field->name_length);
+ if (nxt_slow_path(name == NULL)) {
+ nxt_unit_req_error(ctx->req,
+ "Python failed to create name string \"%.*s\"",
+ (int) field->name_length, src);
+ nxt_python_print_exception();
+
+ return NXT_UNIT_ERROR;
}
- value = PyString_FromStringAndSize(str, size);
+ src = nxt_unit_sptr_get(&field->value);
+
+ value = PyString_FromStringAndSize(src, field->value_length);
if (nxt_slow_path(value == NULL)) {
nxt_unit_req_error(ctx->req,
"Python failed to create value string \"%.*s\"",
- (int) size, str);
+ (int) field->value_length, src);
nxt_python_print_exception();
- return NXT_UNIT_ERROR;
+ goto fail;
}
- if (nxt_slow_path(PyDict_SetItemString(ctx->environ, name, value) != 0)) {
+ if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
nxt_unit_req_error(ctx->req,
"Python failed to set the \"%s\" environ value",
- name);
+ PyUnicode_AsUTF8(name));
+ goto fail;
+ }
- Py_DECREF(value);
+ Py_DECREF(name);
+ Py_DECREF(value);
+
+ return NXT_UNIT_OK;
+
+fail:
+
+ Py_DECREF(name);
+ Py_XDECREF(value);
+
+ return NXT_UNIT_ERROR;
+}
+
+
+static int
+nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, PyObject *value)
+{
+ if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
+ nxt_unit_req_error(ctx->req,
+ "Python failed to set the \"%s\" environ value",
+ PyUnicode_AsUTF8(name));
return NXT_UNIT_ERROR;
}
- Py_DECREF(value);
-
return NXT_UNIT_OK;
}
diff --git a/src/nxt_router.c b/src/nxt_router.c
index b9f5d921..6a1f3792 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -95,16 +95,16 @@ nxt_request_app_link_inc_use(nxt_request_app_link_t *req_app_link)
}
nxt_inline void
-nxt_request_app_link_dec_use(nxt_request_app_link_t *req_app_link)
+nxt_request_app_link_chk_use(nxt_request_app_link_t *req_app_link, int i)
{
#if (NXT_DEBUG)
int c;
- c = nxt_atomic_fetch_add(&req_app_link->use_count, -1);
+ c = nxt_atomic_fetch_add(&req_app_link->use_count, i);
- nxt_assert(c > 1);
+ nxt_assert((c + i) > 0);
#else
- (void) nxt_atomic_fetch_add(&req_app_link->use_count, -1);
+ (void) nxt_atomic_fetch_add(&req_app_link->use_count, i);
#endif
}
@@ -248,6 +248,7 @@ static nxt_int_t nxt_router_http_request_done(nxt_task_t *task,
nxt_http_request_t *r);
static void nxt_router_http_request_release(nxt_task_t *task, void *obj,
void *data);
+static void nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
extern const nxt_http_request_state_t nxt_http_websocket;
@@ -276,6 +277,7 @@ nxt_port_handlers_t nxt_router_process_port_handlers = {
.access_log = nxt_router_access_log_reopen_handler,
.rpc_ready = nxt_port_rpc_handler,
.rpc_error = nxt_port_rpc_handler,
+ .oosm = nxt_router_oosm_handler,
};
@@ -600,8 +602,6 @@ nxt_request_app_link_update_peer(nxt_task_t *task,
nxt_port_rpc_ex_set_peer(task, engine->port, req_rpc_data,
req_app_link->app_port->pid);
}
-
- nxt_request_app_link_use(task, req_app_link, -1);
}
@@ -2750,6 +2750,7 @@ static nxt_port_handlers_t nxt_router_app_port_handlers = {
.rpc_error = nxt_port_rpc_handler,
.mmap = nxt_port_mmap_handler,
.data = nxt_port_rpc_handler,
+ .oosm = nxt_router_oosm_handler,
};
@@ -3732,8 +3733,6 @@ nxt_router_response_error_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
cancelled = nxt_router_msg_cancel(task, &req_app_link->msg_info,
req_app_link->stream);
if (cancelled) {
- nxt_request_app_link_inc_use(req_app_link);
-
res = nxt_router_app_port(task, req_rpc_data->app, req_app_link);
if (res == NXT_OK) {
@@ -3751,6 +3750,8 @@ nxt_router_response_error_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_router_app_prepare_request(task, req_app_link);
}
+ nxt_request_app_link_use(task, req_app_link, -1);
+
msg->port_msg.last = 0;
return;
@@ -4015,6 +4016,8 @@ nxt_router_app_process_request(nxt_task_t *task, void *obj, void *data)
#endif
nxt_router_app_prepare_request(task, req_app_link);
+
+ nxt_request_app_link_use(task, req_app_link, -1);
}
@@ -4148,8 +4151,6 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port,
re_ra->stream);
if (cancelled) {
- nxt_request_app_link_inc_use(re_ra);
-
state.req_app_link = re_ra;
state.app = app;
@@ -4217,19 +4218,38 @@ re_ra_cancelled:
if (re_ra != NULL) {
if (nxt_router_port_post_select(task, &state) == NXT_OK) {
+ /*
+ * There should be call nxt_request_app_link_inc_use(re_ra),
+ * but we need to decrement use then. So, let's skip both.
+ */
+
nxt_work_queue_add(&task->thread->engine->fast_work_queue,
nxt_router_app_process_request,
&task->thread->engine->task, app, re_ra);
+
+ } else {
+ /*
+ * This call should be unconditional, but we want to spare
+ * couple of CPU ticks to postpone the head death of the universe.
+ */
+
+ nxt_request_app_link_use(task, re_ra, -1);
}
}
if (req_app_link != NULL) {
- nxt_request_app_link_use(task, req_app_link, -1);
+ /*
+ * Here we do the same trick as described above,
+ * but without conditions.
+ * Skip required nxt_request_app_link_inc_use(req_app_link).
+ */
nxt_work_queue_add(&task->thread->engine->fast_work_queue,
nxt_router_app_process_request,
&task->thread->engine->task, app, req_app_link);
+ /* ... skip nxt_request_app_link_use(task, req_app_link, -1) too. */
+
goto adjust_use;
}
@@ -4477,6 +4497,7 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data)
static void
nxt_router_port_select(nxt_task_t *task, nxt_port_select_state_t *state)
{
+ int ra_use_delta;
nxt_app_t *app;
nxt_bool_t can_start_process;
nxt_request_app_link_t *req_app_link;
@@ -4485,11 +4506,7 @@ nxt_router_port_select(nxt_task_t *task, nxt_port_select_state_t *state)
app = state->app;
state->failed_port_use_delta = 0;
-
- if (nxt_queue_chk_remove(&req_app_link->link_app_requests))
- {
- nxt_request_app_link_dec_use(req_app_link);
- }
+ ra_use_delta = -nxt_queue_chk_remove(&req_app_link->link_app_requests);
if (nxt_queue_chk_remove(&req_app_link->link_port_pending))
{
@@ -4498,7 +4515,7 @@ nxt_router_port_select(nxt_task_t *task, nxt_port_select_state_t *state)
nxt_queue_remove(&req_app_link->link_app_pending);
req_app_link->link_app_pending.next = NULL;
- nxt_request_app_link_dec_use(req_app_link);
+ ra_use_delta--;
}
state->failed_port = req_app_link->app_port;
@@ -4538,7 +4555,7 @@ nxt_router_port_select(nxt_task_t *task, nxt_port_select_state_t *state)
&req_app_link->link_app_requests);
}
- nxt_request_app_link_inc_use(req_app_link);
+ ra_use_delta++;
nxt_debug(task, "req_app_link stream #%uD enqueue to app->requests",
req_app_link->stream);
@@ -4569,6 +4586,8 @@ nxt_router_port_select(nxt_task_t *task, nxt_port_select_state_t *state)
}
}
+ nxt_request_app_link_chk_use(req_app_link, ra_use_delta);
+
fail:
state->shared_ra = req_app_link;
@@ -4596,7 +4615,6 @@ nxt_router_port_post_select(nxt_task_t *task, nxt_port_select_state_t *state)
nxt_request_app_link_error(state->req_app_link, 500,
"Failed to allocate shared req<->app link");
- nxt_request_app_link_use(task, state->req_app_link, -1);
return NXT_ERROR;
}
@@ -4625,7 +4643,6 @@ nxt_router_port_post_select(nxt_task_t *task, nxt_port_select_state_t *state)
if (nxt_slow_path(res != NXT_OK)) {
nxt_request_app_link_error(req_app_link, 500,
"Failed to start app process");
- nxt_request_app_link_use(task, req_app_link, -1);
return NXT_ERROR;
}
@@ -4686,19 +4703,19 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
nxt_request_app_link_init(task, req_app_link, req_rpc_data);
res = nxt_router_app_port(task, app, req_app_link);
+ req_app_link = req_rpc_data->req_app_link;
- if (res != NXT_OK) {
- return;
- }
+ if (res == NXT_OK) {
+ port = req_app_link->app_port;
- req_app_link = req_rpc_data->req_app_link;
- port = req_app_link->app_port;
+ nxt_assert(port != NULL);
- nxt_assert(port != NULL);
+ nxt_port_rpc_ex_set_peer(task, engine->port, req_rpc_data, port->pid);
- nxt_port_rpc_ex_set_peer(task, engine->port, req_rpc_data, port->pid);
+ nxt_router_app_prepare_request(task, req_app_link);
+ }
- nxt_router_app_prepare_request(task, req_app_link);
+ nxt_request_app_link_use(task, req_app_link, -1);
}
@@ -5172,8 +5189,6 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data)
pending_ra->stream);
if (cancelled) {
- nxt_request_app_link_inc_use(pending_ra);
-
state.req_app_link = pending_ra;
state.app = app;
@@ -5186,10 +5201,12 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data)
nxt_thread_mutex_unlock(&app->mutex);
- if (pending_ra != NULL
- && nxt_router_port_post_select(task, &state) == NXT_OK)
- {
- nxt_router_app_prepare_request(task, pending_ra);
+ if (pending_ra != NULL) {
+ if (nxt_router_port_post_select(task, &state) == NXT_OK) {
+ nxt_router_app_prepare_request(task, pending_ra);
+ }
+
+ nxt_request_app_link_use(task, pending_ra, -1);
}
nxt_debug(task, "send quit to app '%V' pid %PI", &app->name, port->pid);
@@ -5227,3 +5244,56 @@ nxt_router_http_request_release(nxt_task_t *task, void *obj, void *data)
nxt_mp_release(r->mem_pool);
}
+
+
+static void
+nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ size_t mi;
+ uint32_t i;
+ nxt_bool_t ack;
+ nxt_process_t *process;
+ nxt_free_map_t *m;
+ nxt_port_mmap_header_t *hdr;
+
+ nxt_debug(task, "oosm in %PI", msg->port_msg.pid);
+
+ process = nxt_runtime_process_find(task->thread->runtime,
+ msg->port_msg.pid);
+ if (nxt_slow_path(process == NULL)) {
+ return;
+ }
+
+ ack = 0;
+
+ /*
+ * To mitigate possible racing condition (when OOSM message received
+ * after some of the memory was already freed), need to try to find
+ * first free segment in shared memory and send ACK if found.
+ */
+
+ nxt_thread_mutex_lock(&process->incoming.mutex);
+
+ for (i = 0; i < process->incoming.size; i++) {
+ hdr = process->incoming.elts[i].mmap_handler->hdr;
+ m = hdr->free_map;
+
+ for (mi = 0; mi < MAX_FREE_IDX; mi++) {
+ if (m[mi] != 0) {
+ ack = 1;
+
+ nxt_debug(task, "oosm: already free #%uD %uz = 0x%08xA",
+ i, mi, m[mi]);
+
+ break;
+ }
+ }
+ }
+
+ nxt_thread_mutex_unlock(&process->incoming.mutex);
+
+ if (ack) {
+ (void) nxt_port_socket_write(task, msg->port, NXT_PORT_MSG_SHM_ACK,
+ -1, 0, 0, NULL);
+ }
+}
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index 096aabc4..80b25c1b 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -705,7 +705,10 @@ nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt)
}
if (rt->capabilities.setid) {
- if (nxt_user_cred_get(task, &rt->user_cred, rt->group) != NXT_OK) {
+ ret = nxt_credential_get(task, rt->mem_pool, &rt->user_cred,
+ rt->group);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
@@ -1323,7 +1326,7 @@ nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process)
nxt_thread_mutex_destroy(&process->cp_mutex);
if (process->init != NULL) {
- nxt_mp_free(rt->mem_pool, process->init);
+ nxt_mp_destroy(process->init->mem_pool);
}
nxt_mp_free(rt->mem_pool, process);
diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h
index d5b340b6..f8d19ec6 100644
--- a/src/nxt_runtime.h
+++ b/src/nxt_runtime.h
@@ -58,7 +58,7 @@ struct nxt_runtime_s {
const char *engine;
uint32_t engine_connections;
uint32_t auxiliary_threads;
- nxt_user_cred_t user_cred;
+ nxt_credential_t user_cred;
nxt_capabilities_t capabilities;
const char *group;
const char *pid;
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c
index 57dfbfa6..af696a6b 100644
--- a/src/nxt_sockaddr.c
+++ b/src/nxt_sockaddr.c
@@ -1140,6 +1140,10 @@ nxt_inet_addr(u_char *buf, size_t length)
in_addr_t addr;
nxt_uint_t digit, octet, dots;
+ if (nxt_slow_path(*(buf + length - 1) == '.')) {
+ return INADDR_NONE;
+ }
+
addr = 0;
octet = 0;
dots = 0;
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index 0cf32916..95874db3 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -19,12 +19,21 @@
#include <linux/memfd.h>
#endif
+#define NXT_UNIT_MAX_PLAIN_SIZE 1024
+#define NXT_UNIT_LOCAL_BUF_SIZE \
+ (NXT_UNIT_MAX_PLAIN_SIZE + sizeof(nxt_port_msg_t))
+
+#define NXT_UNIT_MAX_PLAIN_SIZE 1024
+#define NXT_UNIT_LOCAL_BUF_SIZE \
+ (NXT_UNIT_MAX_PLAIN_SIZE + sizeof(nxt_port_msg_t))
+
typedef struct nxt_unit_impl_s nxt_unit_impl_t;
typedef struct nxt_unit_mmap_s nxt_unit_mmap_t;
typedef struct nxt_unit_mmaps_s nxt_unit_mmaps_t;
typedef struct nxt_unit_process_s nxt_unit_process_t;
typedef struct nxt_unit_mmap_buf_s nxt_unit_mmap_buf_t;
typedef struct nxt_unit_recv_msg_s nxt_unit_recv_msg_t;
+typedef struct nxt_unit_read_buf_s nxt_unit_read_buf_t;
typedef struct nxt_unit_ctx_impl_s nxt_unit_ctx_impl_t;
typedef struct nxt_unit_port_impl_s nxt_unit_port_impl_t;
typedef struct nxt_unit_request_info_impl_s nxt_unit_request_info_impl_t;
@@ -37,9 +46,10 @@ nxt_inline void nxt_unit_mmap_buf_insert(nxt_unit_mmap_buf_t **head,
nxt_unit_mmap_buf_t *mmap_buf);
nxt_inline void nxt_unit_mmap_buf_insert_tail(nxt_unit_mmap_buf_t **prev,
nxt_unit_mmap_buf_t *mmap_buf);
-nxt_inline void nxt_unit_mmap_buf_remove(nxt_unit_mmap_buf_t *mmap_buf);
+nxt_inline void nxt_unit_mmap_buf_unlink(nxt_unit_mmap_buf_t *mmap_buf);
static int nxt_unit_read_env(nxt_unit_port_t *ready_port,
- nxt_unit_port_t *read_port, int *log_fd, uint32_t *stream);
+ nxt_unit_port_t *read_port, int *log_fd, uint32_t *stream,
+ uint32_t *shm_limit);
static int nxt_unit_ready(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id,
uint32_t stream);
static int nxt_unit_process_new_port(nxt_unit_ctx_t *ctx,
@@ -48,6 +58,7 @@ static int nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
static int nxt_unit_process_websocket(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
+static int nxt_unit_process_shm_ack(nxt_unit_ctx_t *ctx);
static nxt_unit_request_info_impl_t *nxt_unit_request_info_get(
nxt_unit_ctx_t *ctx);
static void nxt_unit_request_info_release(nxt_unit_request_info_t *req);
@@ -63,11 +74,19 @@ static void nxt_unit_mmap_buf_release(nxt_unit_mmap_buf_t *mmap_buf);
static int nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream,
nxt_unit_mmap_buf_t *mmap_buf, int last);
static void nxt_unit_mmap_buf_free(nxt_unit_mmap_buf_t *mmap_buf);
+static void nxt_unit_free_outgoing_buf(nxt_unit_mmap_buf_t *mmap_buf);
+static nxt_unit_read_buf_t *nxt_unit_read_buf_get(nxt_unit_ctx_t *ctx);
+static nxt_unit_read_buf_t *nxt_unit_read_buf_get_impl(
+ nxt_unit_ctx_impl_t *ctx_impl);
+static void nxt_unit_read_buf_release(nxt_unit_ctx_t *ctx,
+ nxt_unit_read_buf_t *rbuf);
static ssize_t nxt_unit_buf_read(nxt_unit_buf_t **b, uint64_t *len, void *dst,
size_t size);
static nxt_port_mmap_header_t *nxt_unit_mmap_get(nxt_unit_ctx_t *ctx,
nxt_unit_process_t *process, nxt_unit_port_id_t *port_id,
- nxt_chunk_id_t *c, int n);
+ nxt_chunk_id_t *c, int *n, int min_n);
+static int nxt_unit_send_oosm(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id);
+static int nxt_unit_wait_shm_ack(nxt_unit_ctx_t *ctx);
static nxt_unit_mmap_t *nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i);
static nxt_port_mmap_header_t *nxt_unit_new_mmap(nxt_unit_ctx_t *ctx,
nxt_unit_process_t *process, nxt_unit_port_id_t *port_id, int n);
@@ -75,7 +94,7 @@ static int nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id,
int fd);
static int nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx,
nxt_unit_process_t *process, nxt_unit_port_id_t *port_id, uint32_t size,
- nxt_unit_mmap_buf_t *mmap_buf);
+ uint32_t min_size, nxt_unit_mmap_buf_t *mmap_buf, char *local_buf);
static int nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd);
static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps);
@@ -88,14 +107,18 @@ static int nxt_unit_tracking_read(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
static int nxt_unit_mmap_read(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
-static int nxt_unit_mmap_release(nxt_port_mmap_header_t *hdr, void *start,
- uint32_t size);
+static void nxt_unit_mmap_release(nxt_unit_ctx_t *ctx,
+ nxt_unit_process_t *process,
+ nxt_port_mmap_header_t *hdr, void *start, uint32_t size);
+static int nxt_unit_send_shm_ack(nxt_unit_ctx_t *ctx, pid_t pid);
static nxt_unit_process_t *nxt_unit_process_get(nxt_unit_ctx_t *ctx,
pid_t pid);
static nxt_unit_process_t *nxt_unit_process_find(nxt_unit_ctx_t *ctx,
pid_t pid, int remove);
static nxt_unit_process_t *nxt_unit_process_pop_first(nxt_unit_impl_t *lib);
+static void nxt_unit_read_buf(nxt_unit_ctx_t *ctx,
+ nxt_unit_read_buf_t *rbuf);
static int nxt_unit_create_port(nxt_unit_ctx_t *ctx,
nxt_unit_port_id_t *port_id, int *fd);
@@ -135,10 +158,12 @@ struct nxt_unit_mmap_buf_s {
nxt_unit_mmap_buf_t **prev;
nxt_port_mmap_header_t *hdr;
-// nxt_queue_link_t link;
nxt_unit_port_id_t port_id;
nxt_unit_request_info_t *req;
nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_process_t *process;
+ char *free_ptr;
+ char *plain_ptr;
};
@@ -196,8 +221,14 @@ struct nxt_unit_websocket_frame_impl_s {
nxt_queue_link_t link;
nxt_unit_ctx_impl_t *ctx_impl;
+};
+
- void *retain_buf;
+struct nxt_unit_read_buf_s {
+ nxt_unit_read_buf_t *next;
+ ssize_t size;
+ char buf[16384];
+ char oob[256];
};
@@ -225,7 +256,12 @@ struct nxt_unit_ctx_impl_s {
/* of nxt_unit_request_info_impl_t */
nxt_lvlhsh_t requests;
+ nxt_unit_read_buf_t *pending_read_head;
+ nxt_unit_read_buf_t **pending_read_tail;
+ nxt_unit_read_buf_t *free_read_buf;
+
nxt_unit_mmap_buf_t ctx_buf[2];
+ nxt_unit_read_buf_t ctx_read_buf;
nxt_unit_request_info_impl_t req;
};
@@ -236,6 +272,7 @@ struct nxt_unit_impl_s {
nxt_unit_callbacks_t callbacks;
uint32_t request_data_size;
+ uint32_t shm_mmap_limit;
pthread_mutex_t mutex;
@@ -271,6 +308,7 @@ struct nxt_unit_mmaps_s {
pthread_mutex_t mutex;
uint32_t size;
uint32_t cap;
+ nxt_atomic_t allocated_chunks;
nxt_unit_mmap_t *elts;
};
@@ -302,7 +340,7 @@ nxt_unit_ctx_t *
nxt_unit_init(nxt_unit_init_t *init)
{
int rc;
- uint32_t ready_stream;
+ uint32_t ready_stream, shm_limit;
nxt_unit_ctx_t *ctx;
nxt_unit_impl_t *lib;
nxt_unit_port_t ready_port, read_port;
@@ -325,12 +363,20 @@ nxt_unit_init(nxt_unit_init_t *init)
ready_port.id.id);
nxt_unit_port_id_init(&read_port.id, read_port.id.pid,
read_port.id.id);
+
} else {
rc = nxt_unit_read_env(&ready_port, &read_port, &lib->log_fd,
- &ready_stream);
+ &ready_stream, &shm_limit);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
+
+ lib->shm_mmap_limit = (shm_limit + PORT_MMAP_DATA_SIZE - 1)
+ / PORT_MMAP_DATA_SIZE;
+ }
+
+ if (nxt_slow_path(lib->shm_mmap_limit < 1)) {
+ lib->shm_mmap_limit = 1;
}
lib->pid = read_port.id.pid;
@@ -395,6 +441,8 @@ nxt_unit_create(nxt_unit_init_t *init)
lib->callbacks = init->callbacks;
lib->request_data_size = init->request_data_size;
+ lib->shm_mmap_limit = (init->shm_limit + PORT_MMAP_DATA_SIZE - 1)
+ / PORT_MMAP_DATA_SIZE;
lib->processes.slot = NULL;
lib->ports.slot = NULL;
@@ -479,6 +527,11 @@ nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl,
nxt_queue_insert_tail(&ctx_impl->free_req, &ctx_impl->req.link);
+ ctx_impl->pending_read_head = NULL;
+ ctx_impl->pending_read_tail = &ctx_impl->pending_read_head;
+ ctx_impl->free_read_buf = &ctx_impl->ctx_read_buf;
+ ctx_impl->ctx_read_buf.next = NULL;
+
ctx_impl->req.req.ctx = &ctx_impl->ctx;
ctx_impl->req.req.unit = &lib->unit;
@@ -517,7 +570,7 @@ nxt_unit_mmap_buf_insert_tail(nxt_unit_mmap_buf_t **prev,
nxt_inline void
-nxt_unit_mmap_buf_remove(nxt_unit_mmap_buf_t *mmap_buf)
+nxt_unit_mmap_buf_unlink(nxt_unit_mmap_buf_t *mmap_buf)
{
nxt_unit_mmap_buf_t **prev;
@@ -535,7 +588,7 @@ nxt_unit_mmap_buf_remove(nxt_unit_mmap_buf_t *mmap_buf)
static int
nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *read_port,
- int *log_fd, uint32_t *stream)
+ int *log_fd, uint32_t *stream, uint32_t *shm_limit)
{
int rc;
int ready_fd, read_fd;
@@ -570,14 +623,14 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *read_port,
"%"PRIu32";"
"%"PRId64",%"PRIu32",%d;"
"%"PRId64",%"PRIu32",%d;"
- "%d",
+ "%d,%"PRIu32,
&ready_stream,
&ready_pid, &ready_id, &ready_fd,
&read_pid, &read_id, &read_fd,
- log_fd);
+ log_fd, shm_limit);
- if (nxt_slow_path(rc != 8)) {
- nxt_unit_alert(NULL, "failed to scan variables");
+ if (nxt_slow_path(rc != 9)) {
+ nxt_unit_alert(NULL, "failed to scan variables: %d", rc);
return NXT_UNIT_ERROR;
}
@@ -756,6 +809,10 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id,
rc = NXT_UNIT_OK;
break;
+ case _NXT_PORT_MSG_SHM_ACK:
+ rc = nxt_unit_process_shm_ack(ctx);
+ break;
+
default:
nxt_unit_debug(ctx, "#%"PRIu32": ignore message type: %d",
port_msg->stream, (int) port_msg->type);
@@ -961,7 +1018,6 @@ nxt_unit_process_websocket(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
ws_impl->ws.req = req;
ws_impl->buf = NULL;
- ws_impl->retain_buf = NULL;
if (recv_msg->mmap) {
for (b = recv_msg->incoming_buf; b != NULL; b = b->next) {
@@ -986,7 +1042,6 @@ nxt_unit_process_websocket(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
return NXT_UNIT_ERROR;
}
- b->hdr = NULL;
b->req = req;
b->buf.start = recv_msg->start;
b->buf.free = b->buf.start;
@@ -1038,6 +1093,23 @@ nxt_unit_process_websocket(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
}
+static int
+nxt_unit_process_shm_ack(nxt_unit_ctx_t *ctx)
+{
+ nxt_unit_impl_t *lib;
+ nxt_unit_callbacks_t *cb;
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ cb = &lib->callbacks;
+
+ if (cb->shm_ack_handler != NULL) {
+ cb->shm_ack_handler(ctx);
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
static nxt_unit_request_info_impl_t *
nxt_unit_request_info_get(nxt_unit_ctx_t *ctx)
{
@@ -1193,12 +1265,6 @@ nxt_unit_websocket_frame_release(nxt_unit_websocket_frame_t *ws)
ws->req = NULL;
- if (ws_impl->retain_buf != NULL) {
- free(ws_impl->retain_buf);
-
- ws_impl->retain_buf = NULL;
- }
-
pthread_mutex_lock(&ws_impl->ctx_impl->mutex);
nxt_queue_insert_tail(&ws_impl->ctx_impl->free_ws, &ws_impl->link);
@@ -1649,7 +1715,7 @@ nxt_unit_response_send(nxt_unit_request_info_t *req)
req->response_buf = NULL;
req_impl->state = NXT_UNIT_RS_RESPONSE_SENT;
- nxt_unit_mmap_buf_release(mmap_buf);
+ nxt_unit_mmap_buf_free(mmap_buf);
}
return rc;
@@ -1697,7 +1763,8 @@ nxt_unit_response_buf_alloc(nxt_unit_request_info_t *req, uint32_t size)
nxt_unit_mmap_buf_insert_tail(&req_impl->outgoing_buf, mmap_buf);
rc = nxt_unit_get_outgoing_buf(req->ctx, req_impl->process,
- &req->response_port, size, mmap_buf);
+ &req->response_port, size, size, mmap_buf,
+ NULL);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_unit_mmap_buf_release(mmap_buf);
@@ -1755,13 +1822,16 @@ nxt_unit_mmap_buf_get(nxt_unit_ctx_t *ctx)
} else {
mmap_buf = ctx_impl->free_buf;
- nxt_unit_mmap_buf_remove(mmap_buf);
+ nxt_unit_mmap_buf_unlink(mmap_buf);
pthread_mutex_unlock(&ctx_impl->mutex);
}
mmap_buf->ctx_impl = ctx_impl;
+ mmap_buf->hdr = NULL;
+ mmap_buf->free_ptr = NULL;
+
return mmap_buf;
}
@@ -1769,7 +1839,7 @@ nxt_unit_mmap_buf_get(nxt_unit_ctx_t *ctx)
static void
nxt_unit_mmap_buf_release(nxt_unit_mmap_buf_t *mmap_buf)
{
- nxt_unit_mmap_buf_remove(mmap_buf);
+ nxt_unit_mmap_buf_unlink(mmap_buf);
pthread_mutex_lock(&mmap_buf->ctx_impl->mutex);
@@ -1896,7 +1966,7 @@ nxt_unit_buf_send(nxt_unit_buf_t *buf)
}
}
- nxt_unit_mmap_buf_release(mmap_buf);
+ nxt_unit_mmap_buf_free(mmap_buf);
return NXT_UNIT_OK;
}
@@ -1917,7 +1987,7 @@ nxt_unit_buf_send_done(nxt_unit_buf_t *buf)
rc = nxt_unit_mmap_buf_send(req->ctx, req_impl->stream, mmap_buf, 1);
if (nxt_slow_path(rc == NXT_UNIT_OK)) {
- nxt_unit_mmap_buf_release(mmap_buf);
+ nxt_unit_mmap_buf_free(mmap_buf);
nxt_unit_request_info_release(req);
@@ -1936,7 +2006,8 @@ nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream,
nxt_port_mmap_msg_t mmap_msg;
} m;
- u_char *end, *last_used, *first_free;
+ int rc;
+ u_char *last_used, *first_free;
ssize_t res;
nxt_chunk_id_t first_free_chunk;
nxt_unit_buf_t *buf;
@@ -1960,38 +2031,85 @@ nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream,
m.msg.mf = 0;
m.msg.tracking = 0;
- if (hdr != NULL) {
+ rc = NXT_UNIT_ERROR;
+
+ if (m.msg.mmap) {
m.mmap_msg.mmap_id = hdr->id;
m.mmap_msg.chunk_id = nxt_port_mmap_chunk_id(hdr,
(u_char *) buf->start);
- }
- nxt_unit_debug(ctx, "#%"PRIu32": send mmap: (%d,%d,%d)",
- stream,
- (int) m.mmap_msg.mmap_id,
- (int) m.mmap_msg.chunk_id,
- (int) m.mmap_msg.size);
+ nxt_unit_debug(ctx, "#%"PRIu32": send mmap: (%d,%d,%d)",
+ stream,
+ (int) m.mmap_msg.mmap_id,
+ (int) m.mmap_msg.chunk_id,
+ (int) m.mmap_msg.size);
- res = lib->callbacks.port_send(ctx, &mmap_buf->port_id, &m,
- m.msg.mmap ? sizeof(m) : sizeof(m.msg),
- NULL, 0);
- if (nxt_slow_path(res != sizeof(m))) {
- return NXT_UNIT_ERROR;
- }
+ res = lib->callbacks.port_send(ctx, &mmap_buf->port_id, &m, sizeof(m),
+ NULL, 0);
+ if (nxt_slow_path(res != sizeof(m))) {
+ goto free_buf;
+ }
- if (buf->end - buf->free >= PORT_MMAP_CHUNK_SIZE && hdr != NULL) {
last_used = (u_char *) buf->free - 1;
-
first_free_chunk = nxt_port_mmap_chunk_id(hdr, last_used) + 1;
- first_free = nxt_port_mmap_chunk_start(hdr, first_free_chunk);
- end = (u_char *) buf->end;
- nxt_unit_mmap_release(hdr, first_free, (uint32_t) (end - first_free));
+ if (buf->end - buf->free >= PORT_MMAP_CHUNK_SIZE) {
+ first_free = nxt_port_mmap_chunk_start(hdr, first_free_chunk);
+
+ buf->start = (char *) first_free;
+ buf->free = buf->start;
+
+ if (buf->end < buf->start) {
+ buf->end = buf->start;
+ }
+
+ } else {
+ buf->start = NULL;
+ buf->free = NULL;
+ buf->end = NULL;
+
+ mmap_buf->hdr = NULL;
+ }
+
+ nxt_atomic_fetch_add(&mmap_buf->process->outgoing.allocated_chunks,
+ (int) m.mmap_msg.chunk_id - (int) first_free_chunk);
+
+ nxt_unit_debug(ctx, "process %d allocated_chunks %d",
+ mmap_buf->process->pid,
+ mmap_buf->process->outgoing.allocated_chunks);
+
+ } else {
+ if (nxt_slow_path(mmap_buf->plain_ptr == NULL
+ || mmap_buf->plain_ptr > buf->start - sizeof(m.msg)))
+ {
+ nxt_unit_warn(ctx, "#%"PRIu32": failed to send plain memory buffer"
+ ": no space reserved for message header", stream);
+
+ goto free_buf;
+ }
+
+ memcpy(buf->start - sizeof(m.msg), &m.msg, sizeof(m.msg));
+
+ nxt_unit_debug(ctx, "#%"PRIu32": send plain: %d",
+ stream,
+ (int) (sizeof(m.msg) + m.mmap_msg.size));
- buf->end = (char *) first_free;
+ res = lib->callbacks.port_send(ctx, &mmap_buf->port_id,
+ buf->start - sizeof(m.msg),
+ m.mmap_msg.size + sizeof(m.msg),
+ NULL, 0);
+ if (nxt_slow_path(res != (ssize_t) (m.mmap_msg.size + sizeof(m.msg)))) {
+ goto free_buf;
+ }
}
- return NXT_UNIT_OK;
+ rc = NXT_UNIT_OK;
+
+free_buf:
+
+ nxt_unit_free_outgoing_buf(mmap_buf);
+
+ return rc;
}
@@ -2005,12 +2123,83 @@ nxt_unit_buf_free(nxt_unit_buf_t *buf)
static void
nxt_unit_mmap_buf_free(nxt_unit_mmap_buf_t *mmap_buf)
{
- if (nxt_fast_path(mmap_buf->hdr != NULL)) {
- nxt_unit_mmap_release(mmap_buf->hdr, mmap_buf->buf.start,
+ nxt_unit_free_outgoing_buf(mmap_buf);
+
+ nxt_unit_mmap_buf_release(mmap_buf);
+}
+
+
+static void
+nxt_unit_free_outgoing_buf(nxt_unit_mmap_buf_t *mmap_buf)
+{
+ if (mmap_buf->hdr != NULL) {
+ nxt_unit_mmap_release(&mmap_buf->ctx_impl->ctx,
+ mmap_buf->process,
+ mmap_buf->hdr, mmap_buf->buf.start,
mmap_buf->buf.end - mmap_buf->buf.start);
+
+ mmap_buf->hdr = NULL;
+
+ return;
}
- nxt_unit_mmap_buf_release(mmap_buf);
+ if (mmap_buf->free_ptr != NULL) {
+ free(mmap_buf->free_ptr);
+
+ mmap_buf->free_ptr = NULL;
+ }
+}
+
+
+static nxt_unit_read_buf_t *
+nxt_unit_read_buf_get(nxt_unit_ctx_t *ctx)
+{
+ nxt_unit_ctx_impl_t *ctx_impl;
+
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ pthread_mutex_lock(&ctx_impl->mutex);
+
+ return nxt_unit_read_buf_get_impl(ctx_impl);
+}
+
+
+static nxt_unit_read_buf_t *
+nxt_unit_read_buf_get_impl(nxt_unit_ctx_impl_t *ctx_impl)
+{
+ nxt_unit_read_buf_t *rbuf;
+
+ if (ctx_impl->free_read_buf != NULL) {
+ rbuf = ctx_impl->free_read_buf;
+ ctx_impl->free_read_buf = rbuf->next;
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
+
+ return rbuf;
+ }
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
+
+ rbuf = malloc(sizeof(nxt_unit_read_buf_t));
+
+ return rbuf;
+}
+
+
+static void
+nxt_unit_read_buf_release(nxt_unit_ctx_t *ctx,
+ nxt_unit_read_buf_t *rbuf)
+{
+ nxt_unit_ctx_impl_t *ctx_impl;
+
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ pthread_mutex_lock(&ctx_impl->mutex);
+
+ rbuf->next = ctx_impl->free_read_buf;
+ ctx_impl->free_read_buf = rbuf;
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
}
@@ -2047,61 +2236,93 @@ int
nxt_unit_response_write(nxt_unit_request_info_t *req, const void *start,
size_t size)
{
+ ssize_t res;
+
+ res = nxt_unit_response_write_nb(req, start, size, size);
+
+ return res < 0 ? -res : NXT_UNIT_OK;
+}
+
+
+ssize_t
+nxt_unit_response_write_nb(nxt_unit_request_info_t *req, const void *start,
+ size_t size, size_t min_size)
+{
int rc;
- uint32_t part_size;
+ ssize_t sent;
+ uint32_t part_size, min_part_size, buf_size;
const char *part_start;
nxt_unit_mmap_buf_t mmap_buf;
nxt_unit_request_info_impl_t *req_impl;
+ char local_buf[NXT_UNIT_LOCAL_BUF_SIZE];
req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req);
part_start = start;
+ sent = 0;
+
+ if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) {
+ nxt_unit_req_warn(req, "write: response not initialized yet");
+
+ return -NXT_UNIT_ERROR;
+ }
/* Check if response is not send yet. */
- if (nxt_slow_path(req->response_buf)) {
+ if (nxt_slow_path(req->response_buf != NULL)) {
part_size = req->response_buf->end - req->response_buf->free;
part_size = nxt_min(size, part_size);
rc = nxt_unit_response_add_content(req, part_start, part_size);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- return rc;
+ return -rc;
}
rc = nxt_unit_response_send(req);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- return rc;
+ return -rc;
}
size -= part_size;
part_start += part_size;
+ sent += part_size;
+
+ min_size -= nxt_min(min_size, part_size);
}
while (size > 0) {
part_size = nxt_min(size, PORT_MMAP_DATA_SIZE);
+ min_part_size = nxt_min(min_size, part_size);
+ min_part_size = nxt_min(min_part_size, PORT_MMAP_CHUNK_SIZE);
rc = nxt_unit_get_outgoing_buf(req->ctx, req_impl->process,
&req->response_port, part_size,
- &mmap_buf);
+ min_part_size, &mmap_buf, local_buf);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- return rc;
+ return -rc;
}
+ buf_size = mmap_buf.buf.end - mmap_buf.buf.free;
+ if (nxt_slow_path(buf_size == 0)) {
+ return sent;
+ }
+ part_size = nxt_min(buf_size, part_size);
+
mmap_buf.buf.free = nxt_cpymem(mmap_buf.buf.free,
part_start, part_size);
rc = nxt_unit_mmap_buf_send(req->ctx, req_impl->stream, &mmap_buf, 0);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_mmap_release(mmap_buf.hdr, mmap_buf.buf.start,
- mmap_buf.buf.end - mmap_buf.buf.start);
-
- return rc;
+ return -rc;
}
size -= part_size;
part_start += part_size;
+ sent += part_size;
+
+ min_size -= nxt_min(min_size, part_size);
}
- return NXT_UNIT_OK;
+ return sent;
}
@@ -2109,9 +2330,15 @@ int
nxt_unit_response_write_cb(nxt_unit_request_info_t *req,
nxt_unit_read_info_t *read_info)
{
- int rc;
- ssize_t n;
- nxt_unit_buf_t *buf;
+ int rc;
+ ssize_t n;
+ uint32_t buf_size;
+ nxt_unit_buf_t *buf;
+ nxt_unit_mmap_buf_t mmap_buf;
+ nxt_unit_request_info_impl_t *req_impl;
+ char local_buf[NXT_UNIT_LOCAL_BUF_SIZE];
+
+ req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req);
/* Check if response is not send yet. */
if (nxt_slow_path(req->response_buf)) {
@@ -2159,20 +2386,24 @@ nxt_unit_response_write_cb(nxt_unit_request_info_t *req,
nxt_unit_req_debug(req, "write_cb, alloc %"PRIu32"",
read_info->buf_size);
- buf = nxt_unit_response_buf_alloc(req, nxt_min(read_info->buf_size,
- PORT_MMAP_DATA_SIZE));
- if (nxt_slow_path(buf == NULL)) {
- nxt_unit_req_error(req, "Failed to allocate buf for content");
+ buf_size = nxt_min(read_info->buf_size, PORT_MMAP_DATA_SIZE);
- return NXT_UNIT_ERROR;
+ rc = nxt_unit_get_outgoing_buf(req->ctx, req_impl->process,
+ &req->response_port,
+ buf_size, buf_size,
+ &mmap_buf, local_buf);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return rc;
}
+ buf = &mmap_buf.buf;
+
while (!read_info->eof && buf->end > buf->free) {
n = read_info->read(read_info, buf->free, buf->end - buf->free);
if (nxt_slow_path(n < 0)) {
nxt_unit_req_error(req, "Read error");
- nxt_unit_buf_free(buf);
+ nxt_unit_free_outgoing_buf(&mmap_buf);
return NXT_UNIT_ERROR;
}
@@ -2180,7 +2411,7 @@ nxt_unit_response_write_cb(nxt_unit_request_info_t *req,
buf->free += n;
}
- rc = nxt_unit_buf_send(buf);
+ rc = nxt_unit_mmap_buf_send(req->ctx, req_impl->stream, &mmap_buf, 0);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_unit_req_error(req, "Failed to send content");
@@ -2325,12 +2556,17 @@ int
nxt_unit_websocket_sendv(nxt_unit_request_info_t *req, uint8_t opcode,
uint8_t last, const struct iovec *iov, int iovcnt)
{
- int i, rc;
- size_t l, copy;
- uint32_t payload_len, buf_size;
- const uint8_t *b;
- nxt_unit_buf_t *buf;
- nxt_websocket_header_t *wh;
+ int i, rc;
+ size_t l, copy;
+ uint32_t payload_len, buf_size, alloc_size;
+ const uint8_t *b;
+ nxt_unit_buf_t *buf;
+ nxt_unit_mmap_buf_t mmap_buf;
+ nxt_websocket_header_t *wh;
+ nxt_unit_request_info_impl_t *req_impl;
+ char local_buf[NXT_UNIT_LOCAL_BUF_SIZE];
+
+ req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req);
payload_len = 0;
@@ -2339,18 +2575,23 @@ nxt_unit_websocket_sendv(nxt_unit_request_info_t *req, uint8_t opcode,
}
buf_size = 10 + payload_len;
+ alloc_size = nxt_min(buf_size, PORT_MMAP_DATA_SIZE);
- buf = nxt_unit_response_buf_alloc(req, nxt_min(buf_size,
- PORT_MMAP_DATA_SIZE));
- if (nxt_slow_path(buf == NULL)) {
- nxt_unit_req_error(req, "Failed to allocate buf for content");
-
- return NXT_UNIT_ERROR;
+ rc = nxt_unit_get_outgoing_buf(req->ctx, req_impl->process,
+ &req->response_port,
+ alloc_size, alloc_size,
+ &mmap_buf, local_buf);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return rc;
}
+ buf = &mmap_buf.buf;
+
buf->start[0] = 0;
buf->start[1] = 0;
+ buf_size -= buf->end - buf->start;
+
wh = (void *) buf->free;
buf->free = nxt_websocket_frame_init(wh, payload_len);
@@ -2370,32 +2611,33 @@ nxt_unit_websocket_sendv(nxt_unit_request_info_t *req, uint8_t opcode,
l -= copy;
if (l > 0) {
- buf_size -= buf->end - buf->start;
-
- rc = nxt_unit_buf_send(buf);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_req_error(req, "Failed to send content");
+ if (nxt_fast_path(buf->free > buf->start)) {
+ rc = nxt_unit_mmap_buf_send(req->ctx, req_impl->stream,
+ &mmap_buf, 0);
- return NXT_UNIT_ERROR;
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return rc;
+ }
}
- buf = nxt_unit_response_buf_alloc(req, nxt_min(buf_size,
- PORT_MMAP_DATA_SIZE));
- if (nxt_slow_path(buf == NULL)) {
- nxt_unit_req_error(req,
- "Failed to allocate buf for content");
+ alloc_size = nxt_min(buf_size, PORT_MMAP_DATA_SIZE);
- return NXT_UNIT_ERROR;
+ rc = nxt_unit_get_outgoing_buf(req->ctx, req_impl->process,
+ &req->response_port,
+ alloc_size, alloc_size,
+ &mmap_buf, local_buf);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return rc;
}
+
+ buf_size -= buf->end - buf->start;
}
}
}
if (buf->free > buf->start) {
- rc = nxt_unit_buf_send(buf);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_req_error(req, "Failed to send content");
- }
+ rc = nxt_unit_mmap_buf_send(req->ctx, req_impl->stream,
+ &mmap_buf, 0);
}
return rc;
@@ -2437,7 +2679,7 @@ nxt_unit_websocket_retain(nxt_unit_websocket_frame_t *ws)
ws_impl = nxt_container_of(ws, nxt_unit_websocket_frame_impl_t, ws);
- if (ws_impl->retain_buf != NULL || ws_impl->buf->hdr != NULL) {
+ if (ws_impl->buf->free_ptr != NULL || ws_impl->buf->hdr != NULL) {
return NXT_UNIT_OK;
}
@@ -2454,7 +2696,7 @@ nxt_unit_websocket_retain(nxt_unit_websocket_frame_t *ws)
ws_impl->buf->buf.free = b;
ws_impl->buf->buf.end = b + size;
- ws_impl->retain_buf = b;
+ ws_impl->buf->free_ptr = b;
return NXT_UNIT_OK;
}
@@ -2469,15 +2711,23 @@ nxt_unit_websocket_done(nxt_unit_websocket_frame_t *ws)
static nxt_port_mmap_header_t *
nxt_unit_mmap_get(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process,
- nxt_unit_port_id_t *port_id, nxt_chunk_id_t *c, int n)
+ nxt_unit_port_id_t *port_id, nxt_chunk_id_t *c, int *n, int min_n)
{
int res, nchunks, i;
+ uint32_t outgoing_size;
nxt_unit_mmap_t *mm, *mm_end;
+ nxt_unit_impl_t *lib;
nxt_port_mmap_header_t *hdr;
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
pthread_mutex_lock(&process->outgoing.mutex);
- mm_end = process->outgoing.elts + process->outgoing.size;
+retry:
+
+ outgoing_size = process->outgoing.size;
+
+ mm_end = process->outgoing.elts + outgoing_size;
for (mm = process->outgoing.elts; mm < mm_end; mm++) {
hdr = mm->hdr;
@@ -2491,11 +2741,17 @@ nxt_unit_mmap_get(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process,
while (nxt_port_mmap_get_free_chunk(hdr->free_map, c)) {
nchunks = 1;
- while (nchunks < n) {
+ while (nchunks < *n) {
res = nxt_port_mmap_chk_set_chunk_busy(hdr->free_map,
*c + nchunks);
if (res == 0) {
+ if (nchunks >= min_n) {
+ *n = nchunks;
+
+ goto unlock;
+ }
+
for (i = 0; i < nchunks; i++) {
nxt_port_mmap_set_chunk_free(hdr->free_map, *c + i);
}
@@ -2508,23 +2764,155 @@ nxt_unit_mmap_get(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process,
nchunks++;
}
- if (nchunks == n) {
+ if (nchunks >= min_n) {
+ *n = nchunks;
+
goto unlock;
}
}
+
+ hdr->oosm = 1;
+ }
+
+ if (outgoing_size >= lib->shm_mmap_limit) {
+ /* Cannot allocate more shared memory. */
+ pthread_mutex_unlock(&process->outgoing.mutex);
+
+ if (min_n == 0) {
+ *n = 0;
+ }
+
+ if (nxt_slow_path(process->outgoing.allocated_chunks + min_n
+ >= lib->shm_mmap_limit * PORT_MMAP_CHUNK_COUNT))
+ {
+ /* Memory allocated by application, but not send to router. */
+ return NULL;
+ }
+
+ /* Notify router about OOSM condition. */
+
+ res = nxt_unit_send_oosm(ctx, port_id);
+ if (nxt_slow_path(res != NXT_UNIT_OK)) {
+ return NULL;
+ }
+
+ /* Return if caller can handle OOSM condition. Non-blocking mode. */
+
+ if (min_n == 0) {
+ return NULL;
+ }
+
+ nxt_unit_debug(ctx, "oosm: waiting for ACK");
+
+ res = nxt_unit_wait_shm_ack(ctx);
+ if (nxt_slow_path(res != NXT_UNIT_OK)) {
+ return NULL;
+ }
+
+ nxt_unit_debug(ctx, "oosm: retry");
+
+ pthread_mutex_lock(&process->outgoing.mutex);
+
+ goto retry;
}
*c = 0;
- hdr = nxt_unit_new_mmap(ctx, process, port_id, n);
+ hdr = nxt_unit_new_mmap(ctx, process, port_id, *n);
unlock:
+ nxt_atomic_fetch_add(&process->outgoing.allocated_chunks, *n);
+
+ nxt_unit_debug(ctx, "process %d allocated_chunks %d",
+ process->pid,
+ process->outgoing.allocated_chunks);
+
pthread_mutex_unlock(&process->outgoing.mutex);
return hdr;
}
+static int
+nxt_unit_send_oosm(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
+{
+ ssize_t res;
+ nxt_port_msg_t msg;
+ nxt_unit_impl_t *lib;
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
+ msg.stream = 0;
+ msg.pid = lib->pid;
+ msg.reply_port = 0;
+ msg.type = _NXT_PORT_MSG_OOSM;
+ msg.last = 0;
+ msg.mmap = 0;
+ msg.nf = 0;
+ msg.mf = 0;
+ msg.tracking = 0;
+
+ res = lib->callbacks.port_send(ctx, port_id, &msg, sizeof(msg), NULL, 0);
+ if (nxt_slow_path(res != sizeof(msg))) {
+ nxt_unit_warn(ctx, "failed to send oosm to %d: %s (%d)",
+ (int) port_id->pid, strerror(errno), errno);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static int
+nxt_unit_wait_shm_ack(nxt_unit_ctx_t *ctx)
+{
+ nxt_port_msg_t *port_msg;
+ nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_read_buf_t *rbuf;
+
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ while (1) {
+ rbuf = nxt_unit_read_buf_get(ctx);
+ if (nxt_slow_path(rbuf == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_unit_read_buf(ctx, rbuf);
+ if (nxt_slow_path(rbuf->size < (ssize_t) sizeof(nxt_port_msg_t))) {
+ nxt_unit_read_buf_release(ctx, rbuf);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ port_msg = (nxt_port_msg_t *) rbuf->buf;
+
+ if (port_msg->type == _NXT_PORT_MSG_SHM_ACK) {
+ nxt_unit_read_buf_release(ctx, rbuf);
+
+ break;
+ }
+
+ pthread_mutex_lock(&ctx_impl->mutex);
+
+ *ctx_impl->pending_read_tail = rbuf;
+ ctx_impl->pending_read_tail = &rbuf->next;
+ rbuf->next = NULL;
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
+
+ if (port_msg->type == _NXT_PORT_MSG_QUIT) {
+ nxt_unit_debug(ctx, "oosm: quit received");
+
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
static nxt_unit_mmap_t *
nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i)
{
@@ -2759,17 +3147,55 @@ nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, int fd)
static int
nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process,
- nxt_unit_port_id_t *port_id, uint32_t size,
- nxt_unit_mmap_buf_t *mmap_buf)
+ nxt_unit_port_id_t *port_id, uint32_t size, uint32_t min_size,
+ nxt_unit_mmap_buf_t *mmap_buf, char *local_buf)
{
- uint32_t nchunks;
+ int nchunks, min_nchunks;
nxt_chunk_id_t c;
nxt_port_mmap_header_t *hdr;
+ if (size <= NXT_UNIT_MAX_PLAIN_SIZE) {
+ if (local_buf != NULL) {
+ mmap_buf->free_ptr = NULL;
+ mmap_buf->plain_ptr = local_buf;
+
+ } else {
+ mmap_buf->free_ptr = malloc(size + sizeof(nxt_port_msg_t));
+ if (nxt_slow_path(mmap_buf->free_ptr == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ mmap_buf->plain_ptr = mmap_buf->free_ptr;
+ }
+
+ mmap_buf->hdr = NULL;
+ mmap_buf->buf.start = mmap_buf->plain_ptr + sizeof(nxt_port_msg_t);
+ mmap_buf->buf.free = mmap_buf->buf.start;
+ mmap_buf->buf.end = mmap_buf->buf.start + size;
+ mmap_buf->port_id = *port_id;
+ mmap_buf->process = process;
+
+ nxt_unit_debug(ctx, "outgoing plain buffer allocation: (%p, %d)",
+ mmap_buf->buf.start, (int) size);
+
+ return NXT_UNIT_OK;
+ }
+
nchunks = (size + PORT_MMAP_CHUNK_SIZE - 1) / PORT_MMAP_CHUNK_SIZE;
+ min_nchunks = (min_size + PORT_MMAP_CHUNK_SIZE - 1) / PORT_MMAP_CHUNK_SIZE;
- hdr = nxt_unit_mmap_get(ctx, process, port_id, &c, nchunks);
+ hdr = nxt_unit_mmap_get(ctx, process, port_id, &c, &nchunks, min_nchunks);
if (nxt_slow_path(hdr == NULL)) {
+ if (nxt_fast_path(min_nchunks == 0 && nchunks == 0)) {
+ mmap_buf->hdr = NULL;
+ mmap_buf->buf.start = NULL;
+ mmap_buf->buf.free = NULL;
+ mmap_buf->buf.end = NULL;
+ mmap_buf->free_ptr = NULL;
+
+ return NXT_UNIT_OK;
+ }
+
return NXT_UNIT_ERROR;
}
@@ -2778,6 +3204,8 @@ nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process,
mmap_buf->buf.free = mmap_buf->buf.start;
mmap_buf->buf.end = mmap_buf->buf.start + nchunks * PORT_MMAP_CHUNK_SIZE;
mmap_buf->port_id = *port_id;
+ mmap_buf->process = process;
+ mmap_buf->free_ptr = NULL;
nxt_unit_debug(ctx, "outgoing mmap allocation: (%d,%d,%d)",
(int) hdr->id, (int) c,
@@ -2880,6 +3308,7 @@ nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps)
mmaps->size = 0;
mmaps->cap = 0;
mmaps->elts = NULL;
+ mmaps->allocated_chunks = 0;
}
@@ -3020,6 +3449,22 @@ nxt_unit_mmap_read(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
incoming_tail = &recv_msg->incoming_buf;
+ for (; mmap_msg < end; mmap_msg++) {
+ b = nxt_unit_mmap_buf_get(ctx);
+ if (nxt_slow_path(b == NULL)) {
+ nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: failed to allocate buf",
+ recv_msg->stream);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_unit_mmap_buf_insert(incoming_tail, b);
+ incoming_tail = &b->next;
+ }
+
+ b = recv_msg->incoming_buf;
+ mmap_msg = recv_msg->start;
+
pthread_mutex_lock(&process->incoming.mutex);
for (; mmap_msg < end; mmap_msg++) {
@@ -3043,25 +3488,13 @@ nxt_unit_mmap_read(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
recv_msg->size = size;
}
- b = nxt_unit_mmap_buf_get(ctx);
- if (nxt_slow_path(b == NULL)) {
- pthread_mutex_unlock(&process->incoming.mutex);
-
- nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: failed to allocate buf",
- recv_msg->stream);
-
- nxt_unit_mmap_release(hdr, start, size);
-
- return NXT_UNIT_ERROR;
- }
-
- nxt_unit_mmap_buf_insert(incoming_tail, b);
- incoming_tail = &b->next;
-
b->buf.start = start;
b->buf.free = start;
b->buf.end = b->buf.start + size;
b->hdr = hdr;
+ b->process = process;
+
+ b = b->next;
nxt_unit_debug(ctx, "#%"PRIu32": mmap_read: [%p,%d] %d->%d,(%d,%d,%d)",
recv_msg->stream,
@@ -3077,23 +3510,79 @@ nxt_unit_mmap_read(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
}
-static int
-nxt_unit_mmap_release(nxt_port_mmap_header_t *hdr, void *start, uint32_t size)
+static void
+nxt_unit_mmap_release(nxt_unit_ctx_t *ctx,
+ nxt_unit_process_t *process, nxt_port_mmap_header_t *hdr,
+ void *start, uint32_t size)
{
- u_char *p, *end;
- nxt_chunk_id_t c;
+ int freed_chunks;
+ u_char *p, *end;
+ nxt_chunk_id_t c;
+ nxt_unit_impl_t *lib;
memset(start, 0xA5, size);
p = start;
end = p + size;
c = nxt_port_mmap_chunk_id(hdr, p);
+ freed_chunks = 0;
while (p < end) {
nxt_port_mmap_set_chunk_free(hdr->free_map, c);
p += PORT_MMAP_CHUNK_SIZE;
c++;
+ freed_chunks++;
+ }
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
+ if (hdr->src_pid == lib->pid && freed_chunks != 0) {
+ nxt_atomic_fetch_add(&process->outgoing.allocated_chunks,
+ -freed_chunks);
+
+ nxt_unit_debug(ctx, "process %d allocated_chunks %d",
+ process->pid,
+ process->outgoing.allocated_chunks);
+ }
+
+ if (hdr->dst_pid == lib->pid
+ && freed_chunks != 0
+ && nxt_atomic_cmp_set(&hdr->oosm, 1, 0))
+ {
+ nxt_unit_send_shm_ack(ctx, hdr->src_pid);
+ }
+}
+
+
+static int
+nxt_unit_send_shm_ack(nxt_unit_ctx_t *ctx, pid_t pid)
+{
+ ssize_t res;
+ nxt_port_msg_t msg;
+ nxt_unit_impl_t *lib;
+ nxt_unit_port_id_t port_id;
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
+ nxt_unit_port_id_init(&port_id, pid, 0);
+
+ msg.stream = 0;
+ msg.pid = lib->pid;
+ msg.reply_port = 0;
+ msg.type = _NXT_PORT_MSG_SHM_ACK;
+ msg.last = 0;
+ msg.mmap = 0;
+ msg.nf = 0;
+ msg.mf = 0;
+ msg.tracking = 0;
+
+ res = lib->callbacks.port_send(ctx, &port_id, &msg, sizeof(msg), NULL, 0);
+ if (nxt_slow_path(res != sizeof(msg))) {
+ nxt_unit_warn(ctx, "failed to send ack to %d: %s (%d)",
+ (int) port_id.pid, strerror(errno), errno);
+
+ return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
@@ -3255,43 +3744,76 @@ int
nxt_unit_run_once(nxt_unit_ctx_t *ctx)
{
int rc;
- char buf[4096];
- char oob[256];
- ssize_t rsize;
- nxt_unit_impl_t *lib;
nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_read_buf_t *rbuf;
- lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
- memset(oob, 0, sizeof(struct cmsghdr));
+ pthread_mutex_lock(&ctx_impl->mutex);
+
+ if (ctx_impl->pending_read_head != NULL) {
+ rbuf = ctx_impl->pending_read_head;
+ ctx_impl->pending_read_head = rbuf->next;
+
+ if (ctx_impl->pending_read_tail == &rbuf->next) {
+ ctx_impl->pending_read_tail = &ctx_impl->pending_read_head;
+ }
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
- if (ctx_impl->read_port_fd != -1) {
- rsize = nxt_unit_port_recv(ctx, ctx_impl->read_port_fd,
- buf, sizeof(buf),
- oob, sizeof(oob));
} else {
- rsize = lib->callbacks.port_recv(ctx, &ctx_impl->read_port_id,
- buf, sizeof(buf),
- oob, sizeof(oob));
+ rbuf = nxt_unit_read_buf_get_impl(ctx_impl);
+ if (nxt_slow_path(rbuf == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_unit_read_buf(ctx, rbuf);
}
- if (nxt_fast_path(rsize > 0)) {
- rc = nxt_unit_process_msg(ctx, &ctx_impl->read_port_id, buf, rsize,
- oob, sizeof(oob));
+ if (nxt_fast_path(rbuf->size > 0)) {
+ rc = nxt_unit_process_msg(ctx, &ctx_impl->read_port_id,
+ rbuf->buf, rbuf->size,
+ rbuf->oob, sizeof(rbuf->oob));
#if (NXT_DEBUG)
- memset(buf, 0xAC, rsize);
+ memset(rbuf->buf, 0xAC, rbuf->size);
#endif
} else {
rc = NXT_UNIT_ERROR;
}
+ nxt_unit_read_buf_release(ctx, rbuf);
+
return rc;
}
+static void
+nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
+{
+ nxt_unit_impl_t *lib;
+ nxt_unit_ctx_impl_t *ctx_impl;
+
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ memset(rbuf->oob, 0, sizeof(struct cmsghdr));
+
+ if (ctx_impl->read_port_fd != -1) {
+ rbuf->size = nxt_unit_port_recv(ctx, ctx_impl->read_port_fd,
+ rbuf->buf, sizeof(rbuf->buf),
+ rbuf->oob, sizeof(rbuf->oob));
+
+ } else {
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
+ rbuf->size = lib->callbacks.port_recv(ctx, &ctx_impl->read_port_id,
+ rbuf->buf, sizeof(rbuf->buf),
+ rbuf->oob, sizeof(rbuf->oob));
+ }
+}
+
+
void
nxt_unit_done(nxt_unit_ctx_t *ctx)
{
@@ -3399,12 +3921,12 @@ nxt_unit_ctx_free(nxt_unit_ctx_t *ctx)
} nxt_queue_loop;
- nxt_unit_mmap_buf_remove(&ctx_impl->ctx_buf[0]);
- nxt_unit_mmap_buf_remove(&ctx_impl->ctx_buf[1]);
+ nxt_unit_mmap_buf_unlink(&ctx_impl->ctx_buf[0]);
+ nxt_unit_mmap_buf_unlink(&ctx_impl->ctx_buf[1]);
while (ctx_impl->free_buf != NULL) {
mmap_buf = ctx_impl->free_buf;
- nxt_unit_mmap_buf_remove(mmap_buf);
+ nxt_unit_mmap_buf_unlink(mmap_buf);
free(mmap_buf);
}
diff --git a/src/nxt_unit.h b/src/nxt_unit.h
index 3471a758..c8aaa124 100644
--- a/src/nxt_unit.h
+++ b/src/nxt_unit.h
@@ -137,6 +137,9 @@ struct nxt_unit_callbacks_s {
/* Gracefully quit the application. Optional. */
void (*quit)(nxt_unit_ctx_t *);
+ /* Shared memory release acknowledgement. */
+ void (*shm_ack_handler)(nxt_unit_ctx_t *);
+
/* Send data and control to process pid using port id. Optional. */
ssize_t (*port_send)(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id,
const void *buf, size_t buf_size,
@@ -155,6 +158,7 @@ struct nxt_unit_init_s {
int max_pending_requests;
uint32_t request_data_size;
+ uint32_t shm_limit;
nxt_unit_callbacks_t callbacks;
@@ -322,6 +326,9 @@ uint32_t nxt_unit_buf_min(void);
int nxt_unit_response_write(nxt_unit_request_info_t *req, const void *start,
size_t size);
+ssize_t nxt_unit_response_write_nb(nxt_unit_request_info_t *req,
+ const void *start, size_t size, size_t min_size);
+
int nxt_unit_response_write_cb(nxt_unit_request_info_t *req,
nxt_unit_read_info_t *read_info);
diff --git a/src/nxt_worker_process.c b/src/nxt_worker_process.c
index c068cca4..754e2ea8 100644
--- a/src/nxt_worker_process.c
+++ b/src/nxt_worker_process.c
@@ -19,25 +19,6 @@ static void nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj,
static void nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj,
void *data);
-nxt_port_handlers_t nxt_app_process_port_handlers = {
- .new_port = nxt_port_new_port_handler,
- .change_file = nxt_port_change_log_file_handler,
- .mmap = nxt_port_mmap_handler,
- .remove_pid = nxt_port_remove_pid_handler,
-};
-
-
-nxt_port_handlers_t nxt_discovery_process_port_handlers = {
- .quit = nxt_worker_process_quit_handler,
- .new_port = nxt_port_new_port_handler,
- .change_file = nxt_port_change_log_file_handler,
- .mmap = nxt_port_mmap_handler,
- .data = nxt_port_data_handler,
- .remove_pid = nxt_port_remove_pid_handler,
- .rpc_ready = nxt_port_rpc_handler,
- .rpc_error = nxt_port_rpc_handler,
-};
-
const nxt_sig_event_t nxt_worker_process_signals[] = {
nxt_event_signal(SIGHUP, nxt_worker_process_signal_handler),
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index b99d3269..16159b5b 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -1156,6 +1156,7 @@ nxt_perl_psgi_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
perl_init.callbacks.request_handler = nxt_perl_psgi_request_handler;
perl_init.data = &module;
+ perl_init.shm_limit = conf->shm_limit;
unit_ctx = nxt_unit_init(&perl_init);
if (nxt_slow_path(unit_ctx == NULL)) {
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index 45d7a7aa..e4b30319 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -134,6 +134,7 @@ nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_unit_default_init(task, &ruby_unit_init);
ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler;
+ ruby_unit_init.shm_limit = conf->shm_limit;
unit_ctx = nxt_unit_init(&ruby_unit_init);
if (nxt_slow_path(unit_ctx == NULL)) {
diff --git a/src/test/nxt_clone_test.c b/src/test/nxt_clone_test.c
new file mode 100644
index 00000000..15d36557
--- /dev/null
+++ b/src/test/nxt_clone_test.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+#include <nxt_main.h>
+#include <nxt_conf.h>
+#include "nxt_tests.h"
+
+
+#define UIDMAP 1
+#define GIDMAP 2
+
+
+typedef struct {
+ nxt_int_t map_type;
+ nxt_str_t map_data;
+ nxt_int_t setid;
+ nxt_credential_t creds;
+ nxt_uid_t unit_euid;
+ nxt_gid_t unit_egid;
+ nxt_int_t result;
+ nxt_str_t errmsg;
+} nxt_clone_creds_testcase_t;
+
+typedef struct {
+ nxt_clone_creds_testcase_t *tc;
+} nxt_clone_creds_ctx_t;
+
+
+nxt_int_t nxt_clone_test_mappings(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_clone_creds_ctx_t *ctx, nxt_clone_creds_testcase_t *tc);
+void nxt_cdecl nxt_clone_test_log_handler(nxt_uint_t level, nxt_log_t *log,
+ const char *fmt, ...);
+nxt_int_t nxt_clone_test_map_assert(nxt_task_t *task,
+ nxt_clone_creds_testcase_t *tc, nxt_clone_credential_map_t *map);
+static nxt_int_t nxt_clone_test_parse_map(nxt_task_t *task,
+ nxt_str_t *map_str, nxt_clone_credential_map_t *map);
+
+
+nxt_log_t *test_log;
+
+static nxt_gid_t gids[] = {1000, 10000, 60000};
+
+static nxt_clone_creds_testcase_t testcases[] = {
+ {
+ /*
+ * Unprivileged unit
+ *
+ * if no uid mapping and app creds and unit creds are the same,
+ * then we automatically add a map for the creds->uid.
+ * Then, child process can safely setuid(creds->uid) in
+ * the new namespace.
+ */
+ UIDMAP,
+ nxt_string(""),
+ 0,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string(""),
+ 0,
+ {"johndoe", 10000, 10000, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"root", 0, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 65534, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"nobody", 65534, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1}]"),
+ 0,
+ {"root", 0, 0, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"uidmap\" field has 2 entries but unprivileged unit has "
+ "a maximum of 1 map.")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1}]"),
+ 1, /* privileged */
+ {"root", 0, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"),
+ 1, /* privileged */
+ {"johndoe", 500, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"),
+ 1, /* privileged */
+ {"johndoe", 1000, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"),
+ 1, /* privileged */
+ {"johndoe", 1500, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"),
+ 1, /* privileged */
+ {"johndoe", 1999, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ UIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000},"
+ " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"),
+ 1, /* privileged */
+ {"johndoe", 2000, 0, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"uidmap\" field has no \"container\" entry for user "
+ "\"johndoe\" (uid 2000)")
+ },
+ {
+ /*
+ * Unprivileged unit
+ *
+ * if no gid mapping and app creds and unit creds are the same,
+ * then we automatically add a map for the creds->base_gid.
+ * Then, child process can safely setgid(creds->base_gid) in
+ * the new namespace.
+ */
+ GIDMAP,
+ nxt_string("[]"),
+ 0,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /*
+ * Unprivileged unit
+ *
+ * Inside the new namespace, we can have any gid but it
+ * should map to parent gid (in this case 1000) in parent
+ * namespace.
+ */
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"root", 0, 0, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 65534, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /*
+ * Unprivileged unit
+ *
+ * There's no mapping for "johndoe" (gid 1000) inside the namespace.
+ */
+ GIDMAP,
+ nxt_string("[{\"container\": 65535, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has no \"container\" entry for "
+ "gid 1000.")
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 2}]"),
+ 0,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has an entry with \"size\": 2, but "
+ "for unprivileged unit it must be 1.")
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 1001, \"size\": 1}]"),
+ 0,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has an entry for host gid 1001 but "
+ "unprivileged unit can only map itself (gid 1000) "
+ "into child namespaces.")
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 1}]"),
+ 0,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("unprivileged unit disallow supplementary groups for "
+ "new namespace (user \"johndoe\" has 3 groups).")
+ },
+
+ /* privileged unit */
+
+ /* not root with capabilities */
+ {
+ GIDMAP,
+ nxt_string("[]"),
+ 1,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ GIDMAP,
+ nxt_string(""),
+ 1,
+ {"johndoe", 1000, 1000, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /* missing gid of {"user": "nobody"} */
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"),
+ 1,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has no \"container\" entry for "
+ "gid 65534.")
+ },
+ {
+ /* solves the previous by mapping 65534 gids */
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 65535}]"),
+ 1,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /* solves by adding a separate mapping */
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1},"
+ " {\"container\": 65534, \"host\": 1000, \"size\": 1}]"),
+ 1,
+ {"nobody", 65534, 65534, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /*
+ * Map a big range
+ */
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 0, \"size\": 200000}]"),
+ 1,
+ {"johndoe", 100000, 100000, 0, NULL},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string("")
+ },
+ {
+ /*
+ * Validate if supplementary groups are mapped
+ */
+ GIDMAP,
+ nxt_string("[]"),
+ 1,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has no entries but user \"johndoe\" "
+ "has 3 suplementary groups."),
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 0, \"host\": 0, \"size\": 1}]"),
+ 1,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has no \"container\" entry for "
+ "gid 1000."),
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1}]"),
+ 1,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has missing suplementary gid mappings "
+ "(found 1 out of 3)."),
+ },
+ {
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1},"
+ " {\"container\": 10000, \"host\": 10000, \"size\": 1}]"),
+ 1,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_ERROR,
+ nxt_string("\"gidmap\" field has missing suplementary gid mappings "
+ "(found 2 out of 3)."),
+ },
+ {
+ /*
+ * Fix all mappings
+ */
+ GIDMAP,
+ nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1},"
+ "{\"container\": 10000, \"host\": 10000, \"size\": 1},"
+ " {\"container\": 60000, \"host\": 60000, \"size\": 1}]"),
+ 1,
+ {"johndoe", 1000, 1000, 3, gids},
+ 1000, 1000,
+ NXT_OK,
+ nxt_string(""),
+ },
+};
+
+
+void nxt_cdecl
+nxt_clone_test_log_handler(nxt_uint_t level, nxt_log_t *log,
+ const char *fmt, ...)
+{
+ u_char *p, *end;
+ va_list args;
+ nxt_clone_creds_ctx_t *ctx;
+ nxt_clone_creds_testcase_t *tc;
+ u_char msg[NXT_MAX_ERROR_STR];
+
+ p = msg;
+ end = msg + NXT_MAX_ERROR_STR;
+
+ ctx = log->ctx;
+ tc = ctx->tc;
+
+ va_start(args, fmt);
+ p = nxt_vsprintf(p, end, fmt, args);
+ va_end(args);
+
+ *p++ = '\0';
+
+ if (tc->result == NXT_OK && level == NXT_LOG_DEBUG) {
+ return;
+ }
+
+ if (tc->errmsg.length == 0) {
+ nxt_log_error(NXT_LOG_ERR, &nxt_main_log, "unexpected log: %s", msg);
+ return;
+ }
+
+ if (!nxt_str_eq(&tc->errmsg, msg, (nxt_uint_t) (p - msg - 1))) {
+ nxt_log_error(NXT_LOG_ERR, &nxt_main_log,
+ "error log mismatch: got [%s] but wants [%V]",
+ msg, &tc->errmsg);
+ return;
+ }
+}
+
+
+nxt_int_t
+nxt_clone_creds_test(nxt_thread_t *thr)
+{
+ nxt_mp_t *mp;
+ nxt_int_t ret;
+ nxt_uint_t count, i;
+ nxt_task_t *task;
+ nxt_runtime_t rt;
+ nxt_clone_creds_ctx_t ctx;
+
+ nxt_log_t nxt_clone_creds_log = {
+ NXT_LOG_INFO,
+ 0,
+ nxt_clone_test_log_handler,
+ NULL,
+ &ctx
+ };
+
+ nxt_thread_time_update(thr);
+
+ thr->runtime = &rt;
+
+ task = thr->task;
+
+ mp = nxt_mp_create(1024, 128, 256, 32);
+ if (mp == NULL) {
+ return NXT_ERROR;
+ }
+
+ rt.mem_pool = mp;
+
+ test_log = task->log;
+ task->log = &nxt_clone_creds_log;
+ task->thread = thr;
+
+ count = sizeof(testcases)/sizeof(nxt_clone_creds_testcase_t);
+
+ for (i = 0; i < count; i++) {
+ ret = nxt_clone_test_mappings(task, mp, &ctx, &testcases[i]);
+
+ if (ret != NXT_OK) {
+ goto fail;
+ }
+ }
+
+ ret = NXT_OK;
+
+ nxt_log_error(NXT_LOG_NOTICE, test_log, "clone creds test passed");
+
+fail:
+ task->log = test_log;
+ nxt_mp_destroy(mp);
+
+ return ret;
+}
+
+
+nxt_int_t
+nxt_clone_test_mappings(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_clone_creds_ctx_t *ctx, nxt_clone_creds_testcase_t *tc)
+{
+ nxt_int_t ret;
+ nxt_runtime_t *rt;
+ nxt_clone_credential_map_t map;
+
+ rt = task->thread->runtime;
+
+ map.size = 0;
+
+ if (tc->map_data.length > 0) {
+ ret = nxt_clone_test_parse_map(task, &tc->map_data, &map);
+ if (ret != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ rt->capabilities.setid = tc->setid;
+
+ nxt_euid = tc->unit_euid;
+ nxt_egid = tc->unit_egid;
+
+ ctx->tc = tc;
+
+ if (nxt_clone_test_map_assert(task, tc, &map) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ if (tc->setid && nxt_euid != 0) {
+ /*
+ * Running as root should have the same behavior as
+ * passing Linux capabilities.
+ */
+
+ nxt_euid = 0;
+ nxt_egid = 0;
+
+ if (nxt_clone_test_map_assert(task, tc, &map) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_clone_test_map_assert(nxt_task_t *task, nxt_clone_creds_testcase_t *tc,
+ nxt_clone_credential_map_t *map)
+{
+ nxt_int_t ret;
+
+ if (tc->map_type == UIDMAP) {
+ ret = nxt_clone_vldt_credential_uidmap(task, map, &tc->creds);
+ } else {
+ ret = nxt_clone_vldt_credential_gidmap(task, map, &tc->creds);
+ }
+
+ if (ret != tc->result) {
+ nxt_log_error(NXT_LOG_ERR, &nxt_main_log,
+ "return %d instead of %d (map: %V)", ret, tc->result,
+ &tc->map_data);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_clone_test_parse_map(nxt_task_t *task, nxt_str_t *map_str,
+ nxt_clone_credential_map_t *map)
+{
+ nxt_uint_t i;
+ nxt_runtime_t *rt;
+ nxt_conf_value_t *array, *obj, *value;
+
+ static nxt_str_t host_name = nxt_string("host");
+ static nxt_str_t cont_name = nxt_string("container");
+ static nxt_str_t size_name = nxt_string("size");
+
+ rt = task->thread->runtime;
+
+ array = nxt_conf_json_parse_str(rt->mem_pool, map_str);
+ if (array == NULL) {
+ return NXT_ERROR;
+ }
+
+ map->size = nxt_conf_array_elements_count(array);
+
+ if (map->size == 0) {
+ return NXT_OK;
+ }
+
+ map->map = nxt_mp_alloc(rt->mem_pool,
+ map->size * sizeof(nxt_clone_map_entry_t));
+
+ if (map->map == NULL) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < map->size; i++) {
+ obj = nxt_conf_get_array_element(array, i);
+
+ value = nxt_conf_get_object_member(obj, &host_name, NULL);
+ map->map[i].host = nxt_conf_get_integer(value);
+
+ value = nxt_conf_get_object_member(obj, &cont_name, NULL);
+ map->map[i].container = nxt_conf_get_integer(value);
+
+ value = nxt_conf_get_object_member(obj, &size_name, NULL);
+ map->map[i].size = nxt_conf_get_integer(value);
+ }
+
+ return NXT_OK;
+}
diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c
index 7cba0f69..901d76c3 100644
--- a/src/test/nxt_tests.c
+++ b/src/test/nxt_tests.c
@@ -162,5 +162,11 @@ main(int argc, char **argv)
return 1;
}
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (nxt_clone_creds_test(thr) != NXT_OK) {
+ return 1;
+ }
+#endif
+
return 0;
}
diff --git a/src/test/nxt_tests.h b/src/test/nxt_tests.h
index be4168cf..d531cc7d 100644
--- a/src/test/nxt_tests.h
+++ b/src/test/nxt_tests.h
@@ -64,6 +64,7 @@ nxt_int_t nxt_malloc_test(nxt_thread_t *thr);
nxt_int_t nxt_utf8_test(nxt_thread_t *thr);
nxt_int_t nxt_http_parse_test(nxt_thread_t *thr);
nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr);
+nxt_int_t nxt_clone_creds_test(nxt_thread_t *thr);
#endif /* _NXT_TESTS_H_INCLUDED_ */
diff --git a/test/go/404/app.go b/test/go/404/app.go
index 08fe56c9..7eba2cf4 100644
--- a/test/go/404/app.go
+++ b/test/go/404/app.go
@@ -4,7 +4,7 @@ import (
"io"
"io/ioutil"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/go/command_line_arguments/app.go b/test/go/command_line_arguments/app.go
index 234e565e..1101e1cf 100644
--- a/test/go/command_line_arguments/app.go
+++ b/test/go/command_line_arguments/app.go
@@ -4,7 +4,7 @@ import (
"fmt"
"io"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
"os"
"strings"
)
diff --git a/test/go/cookies/app.go b/test/go/cookies/app.go
index e6647ea8..2216e153 100644
--- a/test/go/cookies/app.go
+++ b/test/go/cookies/app.go
@@ -2,7 +2,7 @@ package main
import (
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/go/empty/app.go b/test/go/empty/app.go
index 6e0fce1b..9326a19b 100644
--- a/test/go/empty/app.go
+++ b/test/go/empty/app.go
@@ -2,7 +2,7 @@ package main
import (
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {}
diff --git a/test/go/get_variables/app.go b/test/go/get_variables/app.go
index 4dcc0e7b..1c0205a8 100644
--- a/test/go/get_variables/app.go
+++ b/test/go/get_variables/app.go
@@ -2,7 +2,7 @@ package main
import (
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/go/mirror/app.go b/test/go/mirror/app.go
index 748aa7ee..78f047c3 100644
--- a/test/go/mirror/app.go
+++ b/test/go/mirror/app.go
@@ -4,7 +4,7 @@ import (
"fmt"
"io"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/go/ns_inspect/app.go b/test/go/ns_inspect/app.go
index ebecbb00..d9b561c9 100644
--- a/test/go/ns_inspect/app.go
+++ b/test/go/ns_inspect/app.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
"os"
"strconv"
)
@@ -70,6 +70,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
return
}
+ w.Header().Add("Content-Type", "application/json")
+
w.Write(data)
}
diff --git a/test/go/post_variables/app.go b/test/go/post_variables/app.go
index 947976d2..e6279ac6 100644
--- a/test/go/post_variables/app.go
+++ b/test/go/post_variables/app.go
@@ -2,7 +2,7 @@ package main
import (
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/go/variables/app.go b/test/go/variables/app.go
index fdcbf7e8..4be60cb7 100644
--- a/test/go/variables/app.go
+++ b/test/go/variables/app.go
@@ -4,7 +4,7 @@ import (
"fmt"
"io"
"net/http"
- "nginx/unit"
+ "unit.nginx.org/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
diff --git a/test/python/user_group/wsgi.py b/test/python/user_group/wsgi.py
new file mode 100644
index 00000000..f5deb87d
--- /dev/null
+++ b/test/python/user_group/wsgi.py
@@ -0,0 +1,18 @@
+import json
+import os
+
+def application(environ, start_response):
+ uid = os.geteuid()
+ gid = os.getegid()
+
+ out = json.dumps({
+ 'UID': uid,
+ 'GID': gid,
+ }).encode('utf-8')
+
+ start_response('200 OK', [
+ ('Content-Length', str(len(out))),
+ ('Content-Type', 'application/json')
+ ])
+
+ return [out]
diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py
index ee5ddf47..7884274d 100644
--- a/test/test_go_isolation.py
+++ b/test/test_go_isolation.py
@@ -1,4 +1,5 @@
-import os
+import pwd
+import grp
import json
import unittest
from unit.applications.lang.go import TestApplicationGo
@@ -18,20 +19,25 @@ class TestGoIsolation(TestApplicationGo):
return unit if not complete_check else unit.complete()
+ def unpriv_creds(self):
+ nobody_uid = pwd.getpwnam('nobody').pw_uid
+
+ try:
+ nogroup_gid = grp.getgrnam('nogroup').gr_gid
+ nogroup = 'nogroup'
+ except:
+ nogroup_gid = grp.getgrnam('nobody').gr_gid
+ nogroup = 'nobody'
+
+ return (nobody_uid, nogroup_gid, nogroup)
+
def isolation_key(self, key):
return key in self.available['features']['isolation'].keys()
- def conf_isolation(self, isolation):
- self.assertIn(
- 'success',
- self.conf(isolation, 'applications/ns_inspect/isolation'),
- 'configure isolation',
- )
-
def test_isolation_values(self):
self.load('ns_inspect')
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
for ns, ns_value in self.available['features']['isolation'].items():
if ns.upper() in obj['NS']:
@@ -39,44 +45,155 @@ class TestGoIsolation(TestApplicationGo):
obj['NS'][ns.upper()], ns_value, '%s match' % ns
)
- def test_isolation_user(self):
+ def test_isolation_unpriv_user(self):
if not self.isolation_key('unprivileged_userns_clone'):
print('unprivileged clone is not available')
raise unittest.SkipTest()
+ if self.is_su:
+ print('privileged tests, skip this')
+ raise unittest.SkipTest()
+
self.load('ns_inspect')
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
- self.assertTrue(obj['UID'] != 0, 'uid not zero')
- self.assertTrue(obj['GID'] != 0, 'gid not zero')
- self.assertEqual(obj['UID'], os.getuid(), 'uid match')
- self.assertEqual(obj['GID'], os.getgid(), 'gid match')
+ self.assertEqual(obj['UID'], self.uid, 'uid match')
+ self.assertEqual(obj['GID'], self.gid, 'gid match')
- self.conf_isolation({"namespaces": {"credential": True}})
+ self.load('ns_inspect', isolation={'namespaces': {'credential': True}})
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
- # default uid and gid maps current user to nobody
- self.assertEqual(obj['UID'], 65534, 'uid nobody')
- self.assertEqual(obj['GID'], 65534, 'gid nobody')
+ nobody_uid, nogroup_gid, nogroup = self.unpriv_creds()
- self.conf_isolation(
- {
- "namespaces": {"credential": True},
- "uidmap": [
- {"container": 1000, "host": os.geteuid(), "size": 1}
- ],
- "gidmap": [
- {"container": 1000, "host": os.getegid(), "size": 1}
+ # unprivileged unit map itself to nobody in the container by default
+ self.assertEqual(obj['UID'], nobody_uid, 'uid of nobody')
+ self.assertEqual(obj['GID'], nogroup_gid, 'gid of %s' % nogroup)
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ isolation={'namespaces': {'credential': True}},
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid match user=root')
+ self.assertEqual(obj['GID'], 0, 'gid match user=root')
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ group=nogroup,
+ isolation={'namespaces': {'credential': True}},
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid match user=root group=nogroup')
+ self.assertEqual(
+ obj['GID'], nogroup_gid, 'gid match user=root group=nogroup'
+ )
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ group='root',
+ isolation={
+ 'namespaces': {'credential': True},
+ 'uidmap': [{'container': 0, 'host': self.uid, 'size': 1}],
+ 'gidmap': [{'container': 0, 'host': self.gid, 'size': 1}],
+ },
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid match uidmap')
+ self.assertEqual(obj['GID'], 0, 'gid match gidmap')
+
+ def test_isolation_priv_user(self):
+ if not self.is_su:
+ print('unprivileged tests, skip this')
+ raise unittest.SkipTest()
+
+ self.load('ns_inspect')
+
+ nobody_uid, nogroup_gid, nogroup = self.unpriv_creds()
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], nobody_uid, 'uid match')
+ self.assertEqual(obj['GID'], nogroup_gid, 'gid match')
+
+ self.load('ns_inspect', isolation={'namespaces': {'credential': True}})
+
+ obj = self.getjson()['body']
+
+ # privileged unit map app creds in the container by default
+ self.assertEqual(obj['UID'], nobody_uid, 'uid nobody')
+ self.assertEqual(obj['GID'], nogroup_gid, 'gid nobody')
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ isolation={'namespaces': {'credential': True}},
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid nobody user=root')
+ self.assertEqual(obj['GID'], 0, 'gid nobody user=root')
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ group=nogroup,
+ isolation={'namespaces': {'credential': True}},
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid match user=root group=nogroup')
+ self.assertEqual(
+ obj['GID'], nogroup_gid, 'gid match user=root group=nogroup'
+ )
+
+ self.load(
+ 'ns_inspect',
+ user='root',
+ group='root',
+ isolation={
+ 'namespaces': {'credential': True},
+ 'uidmap': [{'container': 0, 'host': 0, 'size': 1}],
+ 'gidmap': [{'container': 0, 'host': 0, 'size': 1}],
+ },
+ )
+
+ obj = self.getjson()['body']
+
+ self.assertEqual(obj['UID'], 0, 'uid match uidmap user=root')
+ self.assertEqual(obj['GID'], 0, 'gid match gidmap user=root')
+
+ # map 65535 uids
+ self.load(
+ 'ns_inspect',
+ user='nobody',
+ isolation={
+ 'namespaces': {'credential': True},
+ 'uidmap': [
+ {'container': 0, 'host': 0, 'size': nobody_uid + 1}
],
- }
+ },
)
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
- # default uid and gid maps current user to root
- self.assertEqual(obj['UID'], 1000, 'uid root')
- self.assertEqual(obj['GID'], 1000, 'gid root')
+ self.assertEqual(
+ obj['UID'], nobody_uid, 'uid match uidmap user=nobody'
+ )
+ self.assertEqual(
+ obj['GID'], nogroup_gid, 'gid match uidmap user=nobody'
+ )
def test_isolation_mnt(self):
if not self.isolation_key('mnt'):
@@ -87,12 +204,12 @@ class TestGoIsolation(TestApplicationGo):
print('unprivileged clone is not available')
raise unittest.SkipTest()
- self.load('ns_inspect')
- self.conf_isolation(
- {"namespaces": {"mount": True, "credential": True}}
+ self.load(
+ 'ns_inspect',
+ isolation={'namespaces': {'mount': True, 'credential': True}},
)
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
# all but user and mnt
allns = list(self.available['features']['isolation'].keys())
@@ -119,14 +236,16 @@ class TestGoIsolation(TestApplicationGo):
print('pid namespace is not supported')
raise unittest.SkipTest()
- if not self.isolation_key('unprivileged_userns_clone'):
- print('unprivileged clone is not available')
+ if not (self.is_su or self.isolation_key('unprivileged_userns_clone')):
+ print('requires root or unprivileged_userns_clone')
raise unittest.SkipTest()
- self.load('ns_inspect')
- self.conf_isolation({"namespaces": {"pid": True, "credential": True}})
+ self.load(
+ 'ns_inspect',
+ isolation={'namespaces': {'pid': True, 'credential': True}},
+ )
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
self.assertEqual(obj['PID'], 1, 'pid of container is 1')
@@ -150,9 +269,9 @@ class TestGoIsolation(TestApplicationGo):
else:
namespaces[ns] = False
- self.conf_isolation({"namespaces": namespaces})
+ self.load('ns_inspect', isolation={'namespaces': namespaces})
- obj = self.isolation.parsejson(self.get()['body'])
+ obj = self.getjson()['body']
for ns in allns:
if ns.upper() in obj['NS']:
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 2e937718..d2b97f88 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -1,3 +1,4 @@
+import os
import time
import unittest
from unit.applications.lang.java import TestApplicationJava
@@ -1217,7 +1218,13 @@ class TestJavaApplication(TestApplicationJava):
def test_java_application_multipart(self):
self.load('multipart')
- body = """Preamble. Should be ignored.\r
+ reldst = '/uploads'
+ fulldst = self.testdir + reldst
+ os.mkdir(fulldst)
+ self.public_dir(fulldst)
+
+ body = (
+ """Preamble. Should be ignored.\r
\r
--12345\r
Content-Disposition: form-data; name="file"; filename="sample.txt"\r
@@ -1234,7 +1241,9 @@ Content-Disposition: form-data; name="upload"\r
Upload\r
--12345--\r
\r
-Epilogue. Should be ignored.""" % self.testdir
+Epilogue. Should be ignored."""
+ % fulldst
+ )
resp = self.post(
headers={
@@ -1246,9 +1255,13 @@ Epilogue. Should be ignored.""" % self.testdir
)
self.assertEqual(resp['status'], 200, 'multipart status')
- self.assertRegex(resp['body'], r'sample\.txt created', 'multipart body')
+ self.assertRegex(
+ resp['body'], r'sample\.txt created', 'multipart body'
+ )
self.assertIsNotNone(
- self.search_in_log(r'^Data from sample file$', name='sample.txt'),
+ self.search_in_log(
+ r'^Data from sample file$', name=reldst + '/sample.txt'
+ ),
'file created',
)
diff --git a/test/test_node_application.py b/test/test_node_application.py
index a5b4a108..b80d17d3 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -141,7 +141,7 @@ class TestNodeApplication(TestApplicationNode):
self.load('write_buffer')
self.assertEqual(
- self.get()['body'], '6\r\nbuffer\r\n0\r\n\r\n', 'write buffer'
+ self.get()['body'], 'buffer', 'write buffer'
)
def test_node_application_write_callback(self):
@@ -149,7 +149,7 @@ class TestNodeApplication(TestApplicationNode):
self.assertEqual(
self.get()['body'],
- '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n',
+ 'helloworld',
'write callback order',
)
self.assertTrue(
@@ -173,7 +173,7 @@ class TestNodeApplication(TestApplicationNode):
self.assertEqual(
self.get()['body'],
- '4\r\nbody\r\n4\r\ntrue\r\n0\r\n\r\n',
+ 'bodytrue',
'write return',
)
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index bf3c65d5..a4bac623 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -157,7 +157,7 @@ class TestPerlApplication(TestApplicationPerl):
def test_perl_application_body_empty(self):
self.load('body_empty')
- self.assertEqual(self.get()['body'], '0\r\n\r\n', 'body empty')
+ self.assertEqual(self.get()['body'], '', 'body empty')
def test_perl_application_body_array(self):
self.load('body_array')
diff --git a/test/test_python_application.py b/test/test_python_application.py
index ae8f01ca..818816d0 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -1,4 +1,7 @@
import re
+import os
+import grp
+import pwd
import time
import unittest
from unit.applications.lang.python import TestApplicationPython
@@ -540,7 +543,7 @@ Connection: close
}
)
self.assertEqual(resp['status'], 200, 'status')
- self.assertEqual(resp['body'][-5:], '0\r\n\r\n', 'body')
+ self.assertEqual(resp['body'], 'XXXXXXX', 'body')
# Exception before start_response().
@@ -607,12 +610,11 @@ Connection: close
'X-Skip': '2',
'X-Chunked': '1',
'Connection': 'close',
- }
+ },
+ raw_resp=True
)
- if 'body' in resp:
- self.assertNotEqual(
- resp['body'][-5:], '0\r\n\r\n', 'incomplete body'
- )
+ if resp:
+ self.assertNotEqual(resp[-5:], '0\r\n\r\n', 'incomplete body')
self.assertEqual(
len(self.findall(r'Traceback')), 4, 'traceback count 4'
)
@@ -646,12 +648,11 @@ Connection: close
'X-Skip': '3',
'X-Chunked': '1',
'Connection': 'close',
- }
+ },
+ raw_resp=True
)
- if 'body' in resp:
- self.assertNotEqual(
- resp['body'][-5:], '0\r\n\r\n', 'incomplete body 2'
- )
+ if resp:
+ self.assertNotEqual(resp[-5:], '0\r\n\r\n', 'incomplete body 2')
self.assertEqual(
len(self.findall(r'Traceback')), 6, 'traceback count 6'
)
@@ -678,5 +679,79 @@ Connection: close
len(self.findall(r'Traceback')), 8, 'traceback count 8'
)
+ def test_python_user_group(self):
+ if not self.is_su:
+ print("requires root")
+ raise unittest.SkipTest()
+
+ nobody_uid = pwd.getpwnam('nobody').pw_uid
+
+ group = 'nobody'
+
+ try:
+ group_id = grp.getgrnam(group).gr_gid
+ except:
+ group = 'nogroup'
+ group_id = grp.getgrnam(group).gr_gid
+
+ self.load('user_group')
+
+ obj = self.getjson()['body']
+ self.assertEqual(obj['UID'], nobody_uid, 'nobody uid')
+ self.assertEqual(obj['GID'], group_id, 'nobody gid')
+
+ self.load('user_group', user='nobody')
+
+ obj = self.getjson()['body']
+ self.assertEqual(obj['UID'], nobody_uid, 'nobody uid user=nobody')
+ self.assertEqual(obj['GID'], group_id, 'nobody gid user=nobody')
+
+ self.load('user_group', user='nobody', group=group)
+
+ obj = self.getjson()['body']
+ self.assertEqual(
+ obj['UID'], nobody_uid, 'nobody uid user=nobody group=%s' % group
+ )
+
+ self.assertEqual(
+ obj['GID'], group_id, 'nobody gid user=nobody group=%s' % group
+ )
+
+ self.load('user_group', group=group)
+
+ obj = self.getjson()['body']
+ self.assertEqual(
+ obj['UID'], nobody_uid, 'nobody uid group=%s' % group
+ )
+
+ self.assertEqual(obj['GID'], group_id, 'nobody gid group=%s' % group)
+
+ self.load('user_group', user='root')
+
+ obj = self.getjson()['body']
+ self.assertEqual(obj['UID'], 0, 'root uid user=root')
+ self.assertEqual(obj['GID'], 0, 'root gid user=root')
+
+ group = 'root'
+
+ try:
+ grp.getgrnam(group)
+ group = True
+ except:
+ group = False
+
+ if group:
+ self.load('user_group', user='root', group='root')
+
+ obj = self.getjson()['body']
+ self.assertEqual(obj['UID'], 0, 'root uid user=root group=root')
+ self.assertEqual(obj['GID'], 0, 'root gid user=root group=root')
+
+ self.load('user_group', group='root')
+
+ obj = self.getjson()['body']
+ self.assertEqual(obj['UID'], nobody_uid, 'root uid group=root')
+ self.assertEqual(obj['GID'], 0, 'root gid group=root')
+
if __name__ == '__main__':
TestPythonApplication.main()
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index fe0baa13..f808f795 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -136,27 +136,27 @@ class TestPythonEnvironment(TestApplicationPython):
def test_python_environment_replace_default(self):
self.load('environment')
- pwd_default = self.get(
+ home_default = self.get(
headers={
'Host': 'localhost',
- 'X-Variables': 'PWD',
+ 'X-Variables': 'HOME',
'Connection': 'close',
}
)['body']
- self.assertGreater(len(pwd_default), 1, 'get default')
+ self.assertGreater(len(home_default), 1, 'get default')
- self.conf({"PWD": "new/pwd"}, 'applications/environment/environment')
+ self.conf({"HOME": "/"}, 'applications/environment/environment')
self.assertEqual(
self.get(
headers={
'Host': 'localhost',
- 'X-Variables': 'PWD',
+ 'X-Variables': 'HOME',
'Connection': 'close',
}
)['body'],
- 'new/pwd,',
+ '/,',
'replace default',
)
@@ -166,11 +166,11 @@ class TestPythonEnvironment(TestApplicationPython):
self.get(
headers={
'Host': 'localhost',
- 'X-Variables': 'PWD',
+ 'X-Variables': 'HOME',
'Connection': 'close',
}
)['body'],
- pwd_default,
+ home_default,
'restore default',
)
diff --git a/test/test_routing.py b/test/test_routing.py
index 2960f978..eb7b2fd8 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -45,409 +45,160 @@ class TestRouting(TestApplicationProto):
def route(self, route):
return self.conf([route], 'routes')
- def test_routes_match_method_positive(self):
- self.assertEqual(self.get()['status'], 200, 'method positive GET')
- self.assertEqual(self.post()['status'], 404, 'method positive POST')
-
- def test_routes_match_method_positive_many(self):
+ def route_match(self, match):
self.assertIn(
'success',
self.route(
- {
- "match": {"method": ["GET", "POST"]},
- "action": {"pass": "applications/empty"},
- }
+ {"match": match, "action": {"pass": "applications/empty"}}
),
- 'method positive many configure',
- )
-
- self.assertEqual(self.get()['status'], 200, 'method positive many GET')
- self.assertEqual(
- self.post()['status'], 200, 'method positive many POST'
- )
- self.assertEqual(
- self.delete()['status'], 404, 'method positive many DELETE'
+ 'route match configure',
)
- def test_routes_match_method_negative(self):
+ def route_match_invalid(self, match):
self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "!GET"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method negative configure',
- )
-
- self.assertEqual(self.get()['status'], 404, 'method negative GET')
- self.assertEqual(self.post()['status'], 200, 'method negative POST')
-
- def test_routes_match_method_negative_many(self):
- self.assertIn(
- 'success',
+ 'error',
self.route(
- {
- "match": {"method": ["!GET", "!POST"]},
- "action": {"pass": "applications/empty"},
- }
+ {"match": match, "action": {"pass": "applications/empty"}}
),
- 'method negative many configure',
+ 'route match configure invalid',
)
- self.assertEqual(self.get()['status'], 404, 'method negative many GET')
+ def host(self, host, status):
self.assertEqual(
- self.post()['status'], 404, 'method negative many POST'
+ self.get(headers={'Host': host, 'Connection': 'close'})[
+ 'status'
+ ],
+ status,
+ 'match host',
)
+
+ def cookie(self, cookie, status):
self.assertEqual(
- self.delete()['status'], 200, 'method negative many DELETE'
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': cookie,
+ 'Connection': 'close',
+ },
+ )['status'],
+ status,
+ 'match cookie',
)
- def test_routes_match_method_wildcard_left(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "*ET"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method wildcard left configure',
- )
+ def test_routes_match_method_positive(self):
+ self.assertEqual(self.get()['status'], 200, 'GET')
+ self.assertEqual(self.post()['status'], 404, 'POST')
- self.assertEqual(self.get()['status'], 200, 'method wildcard left GET')
- self.assertEqual(
- self.post()['status'], 404, 'method wildcard left POST'
- )
+ def test_routes_match_method_positive_many(self):
+ self.route_match({"method": ["GET", "POST"]})
- def test_routes_match_method_wildcard_right(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "GE*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method wildcard right configure',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
+ self.assertEqual(self.post()['status'], 200, 'POST')
+ self.assertEqual(self.delete()['status'], 404, 'DELETE')
- self.assertEqual(
- self.get()['status'], 200, 'method wildcard right GET'
- )
- self.assertEqual(
- self.post()['status'], 404, 'method wildcard right POST'
- )
+ def test_routes_match_method_negative(self):
+ self.route_match({"method": "!GET"})
- def test_routes_match_method_wildcard_left_right(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "*GET*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method wildcard left right configure',
- )
+ self.assertEqual(self.get()['status'], 404, 'GET')
+ self.assertEqual(self.post()['status'], 200, 'POST')
- self.assertEqual(
- self.get()['status'], 200, 'method wildcard right GET'
- )
- self.assertEqual(
- self.post()['status'], 404, 'method wildcard right POST'
- )
+ def test_routes_match_method_negative_many(self):
+ self.route_match({"method": ["!GET", "!POST"]})
- def test_routes_match_method_wildcard(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method wildcard configure',
- )
+ self.assertEqual(self.get()['status'], 404, 'GET')
+ self.assertEqual(self.post()['status'], 404, 'POST')
+ self.assertEqual(self.delete()['status'], 200, 'DELETE')
- self.assertEqual(self.get()['status'], 200, 'method wildcard')
+ def test_routes_match_method_wildcard_left(self):
+ self.route_match({"method": "*ET"})
- def test_routes_match_invalid(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"method": "**"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'wildcard invalid',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
+ self.assertEqual(self.post()['status'], 404, 'POST')
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"method": "blah**"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'wildcard invalid 2',
- )
+ def test_routes_match_method_wildcard_right(self):
+ self.route_match({"method": "GE*"})
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"host": "*blah*blah"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'wildcard invalid 3',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
+ self.assertEqual(self.post()['status'], 404, 'POST')
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"host": "blah*blah*blah"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'wildcard invalid 4',
- )
+ def test_routes_match_method_wildcard_left_right(self):
+ self.route_match({"method": "*GET*"})
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"host": "blah*blah*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'wildcard invalid 5',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
+ self.assertEqual(self.post()['status'], 404, 'POST')
- def test_routes_match_wildcard_middle(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "ex*le"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'host wildcard middle configure',
- )
+ def test_routes_match_method_wildcard(self):
+ self.route_match({"method": "*"})
- self.assertEqual(
- self.get(headers={'Host': 'example', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'host wildcard middle',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
- self.assertEqual(
- self.get(headers={'Host': 'www.example', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'host wildcard middle 2',
- )
+ def test_routes_match_invalid(self):
+ self.route_match_invalid({"method": "**"})
+ self.route_match_invalid({"method": "blah**"})
+ self.route_match_invalid({"host": "*blah*blah"})
+ self.route_match_invalid({"host": "blah*blah*blah"})
+ self.route_match_invalid({"host": "blah*blah*"})
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'host wildcard middle 3',
- )
+ def test_routes_match_wildcard_middle(self):
+ self.route_match({"host": "ex*le"})
- self.assertEqual(
- self.get(headers={'Host': 'exampl', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'host wildcard middle 4',
- )
+ self.host('example', 200)
+ self.host('www.example', 404)
+ self.host('example.com', 404)
+ self.host('exampl', 404)
def test_routes_match_method_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "get"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'method case insensitive configure',
- )
+ self.route_match({"method": "get"})
- self.assertEqual(self.get()['status'], 200, 'method case insensitive')
+ self.assertEqual(self.get()['status'], 200, 'GET')
def test_routes_match_wildcard_left_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "*et"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard case insensitive configure',
- )
+ self.route_match({"method": "*get"})
+ self.assertEqual(self.get()['status'], 200, 'GET')
- self.assertEqual(
- self.get()['status'], 200, 'match wildcard case insensitive'
- )
+ self.route_match({"method": "*et"})
+ self.assertEqual(self.get()['status'], 200, 'GET')
def test_routes_match_wildcard_middle_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "g*t"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard case insensitive configure',
- )
+ self.route_match({"method": "g*t"})
- self.assertEqual(
- self.get()['status'], 200, 'match wildcard case insensitive'
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
def test_routes_match_wildcard_right_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "get*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard case insensitive configure',
- )
+ self.route_match({"method": "get*"})
+ self.assertEqual(self.get()['status'], 200, 'GET')
- self.assertEqual(
- self.get()['status'], 200, 'match wildcard case insensitive'
- )
+ self.route_match({"method": "ge*"})
+ self.assertEqual(self.get()['status'], 200, 'GET')
def test_routes_match_wildcard_substring_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "*et*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard substring case insensitive configure',
- )
+ self.route_match({"method": "*et*"})
- self.assertEqual(
- self.get()['status'],
- 200,
- 'match wildcard substring case insensitive',
- )
+ self.assertEqual(self.get()['status'], 200, 'GET')
def test_routes_match_wildcard_left_case_sensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "*blah"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard left case sensitive configure',
- )
+ self.route_match({"uri": "*blah"})
- self.assertEqual(
- self.get(url='/blah')['status'],
- 200,
- 'match wildcard left case sensitive /blah',
- )
-
- self.assertEqual(
- self.get(url='/BLAH')['status'],
- 404,
- 'match wildcard left case sensitive /BLAH',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
+ self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH')
def test_routes_match_wildcard_middle_case_sensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "/b*h"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard middle case sensitive configure',
- )
+ self.route_match({"uri": "/b*h"})
- self.assertEqual(
- self.get(url='/blah')['status'],
- 200,
- 'match wildcard middle case sensitive /blah',
- )
-
- self.assertEqual(
- self.get(url='/BLAH')['status'],
- 404,
- 'match wildcard middle case sensitive /BLAH',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
+ self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH')
def test_routes_match_wildcard_right_case_sensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "/bla*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard right case sensitive configure',
- )
-
- self.assertEqual(
- self.get(url='/blah')['status'],
- 200,
- 'match wildcard right case sensitive /blah',
- )
+ self.route_match({"uri": "/bla*"})
- self.assertEqual(
- self.get(url='/BLAH')['status'],
- 404,
- 'match wildcard right case sensitive /BLAH',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
+ self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH')
def test_routes_match_wildcard_substring_case_sensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "*bla*"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match wildcard substring case sensitive configure',
- )
-
- self.assertEqual(
- self.get(url='/blah')['status'],
- 200,
- 'match wildcard substring case sensitive /blah',
- )
+ self.route_match({"uri": "*bla*"})
- self.assertEqual(
- self.get(url='/BLAH')['status'],
- 404,
- 'match wildcard substring case sensitive /BLAH',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
+ self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH')
def test_routes_absent(self):
self.conf(
@@ -616,65 +367,18 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 200, 'routes two')
def test_routes_match_host_positive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "localhost"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host positive configure',
- )
-
- self.assertEqual(
- self.get()['status'], 200, 'match host positive localhost'
- )
-
- self.assertEqual(
- self.get(headers={'Host': 'localhost.', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host positive trailing dot',
- )
-
- self.assertEqual(
- self.get(headers={'Host': 'www.localhost', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'match host positive www.localhost',
- )
-
- self.assertEqual(
- self.get(headers={'Host': 'localhost1', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'match host positive localhost1',
- )
+ self.route_match({"host": "localhost"})
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 404,
- 'match host positive example.com',
- )
+ self.assertEqual(self.get()['status'], 200, 'localhost')
+ self.host('localhost.', 200)
+ self.host('localhost.', 200)
+ self.host('.localhost', 404)
+ self.host('www.localhost', 404)
+ self.host('localhost1', 404)
@unittest.skip('not yet')
def test_routes_match_host_absent(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "localhost"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host absent configure',
- )
+ self.route_match({"host": "localhost"})
self.assertEqual(
self.get(headers={'Connection': 'close'})['status'],
@@ -683,212 +387,52 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_host_ipv4(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "127.0.0.1"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host ipv4 configure',
- )
+ self.route_match({"host": "127.0.0.1"})
- self.assertEqual(
- self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host ipv4',
- )
+ self.host('127.0.0.1', 200)
+ self.host('127.0.0.1:7080', 200)
def test_routes_match_host_ipv6(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "[::1]"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host ipv6 configure',
- )
+ self.route_match({"host": "[::1]"})
- self.assertEqual(
- self.get(headers={'Host': '[::1]', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host ipv6',
- )
-
- self.assertEqual(
- self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host ipv6 port',
- )
+ self.host('[::1]', 200)
+ self.host('[::1]:7080', 200)
def test_routes_match_host_positive_many(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": ["localhost", "example.com"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host positive many configure',
- )
-
- self.assertEqual(
- self.get()['status'], 200, 'match host positive many localhost'
- )
+ self.route_match({"host": ["localhost", "example.com"]})
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host positive many example.com',
- )
+ self.assertEqual(self.get()['status'], 200, 'localhost')
+ self.host('example.com', 200)
def test_routes_match_host_positive_and_negative(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": ["*example.com", "!www.example.com"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host positive and negative configure',
- )
+ self.route_match({"host": ["*example.com", "!www.example.com"]})
- self.assertEqual(
- self.get()['status'],
- 404,
- 'match host positive and negative localhost',
- )
-
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host positive and negative example.com',
- )
-
- self.assertEqual(
- self.get(
- headers={'Host': 'www.example.com', 'Connection': 'close'}
- )['status'],
- 404,
- 'match host positive and negative www.example.com',
- )
-
- self.assertEqual(
- self.get(
- headers={'Host': '!www.example.com', 'Connection': 'close'}
- )['status'],
- 200,
- 'match host positive and negative !www.example.com',
- )
+ self.assertEqual(self.get()['status'], 404, 'localhost')
+ self.host('example.com', 200)
+ self.host('www.example.com', 404)
+ self.host('!www.example.com', 200)
def test_routes_match_host_positive_and_negative_wildcard(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": ["*example*", "!www.example*"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host positive and negative wildcard configure',
- )
+ self.route_match({"host": ["*example*", "!www.example*"]})
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'match host positive and negative wildcard example.com',
- )
-
- self.assertEqual(
- self.get(
- headers={'Host': 'www.example.com', 'Connection': 'close'}
- )['status'],
- 404,
- 'match host positive and negative wildcard www.example.com',
- )
+ self.host('example.com', 200)
+ self.host('www.example.com', 404)
def test_routes_match_host_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "Example.com"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'host case insensitive configure',
- )
+ self.route_match({"host": "Example.com"})
- self.assertEqual(
- self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'host case insensitive example.com',
- )
-
- self.assertEqual(
- self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'})[
- 'status'
- ],
- 200,
- 'host case insensitive EXAMPLE.COM',
- )
+ self.host('example.com', 200)
+ self.host('EXAMPLE.COM', 200)
def test_routes_match_host_port(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": "example.com"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host port configure',
- )
+ self.route_match({"host": "example.com"})
- self.assertEqual(
- self.get(
- headers={'Host': 'example.com:7080', 'Connection': 'close'}
- )['status'],
- 200,
- 'match host port',
- )
+ self.host('example.com:7080', 200)
def test_routes_match_host_empty(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"host": ""},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match host empty configure',
- )
+ self.route_match({"host": ""})
- self.assertEqual(
- self.get(headers={'Host': '', 'Connection': 'close'})['status'],
- 200,
- 'match host empty',
- )
+ self.host('', 200)
self.assertEqual(
self.get(http_10=True, headers={})['status'],
200,
@@ -897,160 +441,80 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 404, 'match host empty 3')
def test_routes_match_uri_positive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": ["/blah", "/slash/"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match uri positive configure',
- )
+ self.route_match({"uri": ["/blah", "/slash/"]})
- self.assertEqual(self.get()['status'], 404, 'match uri positive')
- self.assertEqual(
- self.get(url='/blah')['status'], 200, 'match uri positive blah'
- )
- self.assertEqual(
- self.get(url='/blah#foo')['status'],
- 200,
- 'match uri positive #foo',
- )
- self.assertEqual(
- self.get(url='/blah?var')['status'], 200, 'match uri args'
- )
- self.assertEqual(
- self.get(url='//blah')['status'], 200, 'match uri adjacent slashes'
- )
- self.assertEqual(
- self.get(url='/slash/foo/../')['status'],
- 200,
- 'match uri relative path',
- )
+ self.assertEqual(self.get()['status'], 404, '/')
+ self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
+ self.assertEqual(self.get(url='/blah#foo')['status'], 200, '/blah#foo')
+ self.assertEqual(self.get(url='/blah?var')['status'], 200, '/blah?var')
+ self.assertEqual(self.get(url='//blah')['status'], 200, '//blah')
self.assertEqual(
- self.get(url='/slash/./')['status'],
- 200,
- 'match uri relative path 2',
- )
- self.assertEqual(
- self.get(url='/slash//.//')['status'],
- 200,
- 'match uri adjacent slashes 2',
- )
- self.assertEqual(
- self.get(url='/%')['status'], 400, 'match uri percent'
+ self.get(url='/slash/foo/../')['status'], 200, 'relative'
)
+ self.assertEqual(self.get(url='/slash/./')['status'], 200, '/slash/./')
self.assertEqual(
- self.get(url='/%1')['status'], 400, 'match uri percent digit'
+ self.get(url='/slash//.//')['status'], 200, 'adjacent slashes'
)
+ self.assertEqual(self.get(url='/%')['status'], 400, 'percent')
+ self.assertEqual(self.get(url='/%1')['status'], 400, 'percent digit')
+ self.assertEqual(self.get(url='/%A')['status'], 400, 'percent letter')
self.assertEqual(
- self.get(url='/%A')['status'], 400, 'match uri percent letter'
+ self.get(url='/slash/.?args')['status'], 200, 'dot args'
)
self.assertEqual(
- self.get(url='/slash/.?args')['status'], 200, 'match uri dot args'
- )
- self.assertEqual(
- self.get(url='/slash/.#frag')['status'], 200, 'match uri dot frag'
+ self.get(url='/slash/.#frag')['status'], 200, 'dot frag'
)
self.assertEqual(
self.get(url='/slash/foo/..?args')['status'],
200,
- 'match uri dot dot args',
+ 'dot dot args',
)
self.assertEqual(
self.get(url='/slash/foo/..#frag')['status'],
200,
- 'match uri dot dot frag',
+ 'dot dot frag',
)
self.assertEqual(
- self.get(url='/slash/.')['status'], 200, 'match uri trailing dot'
+ self.get(url='/slash/.')['status'], 200, 'trailing dot'
)
self.assertEqual(
self.get(url='/slash/foo/..')['status'],
200,
- 'match uri trailing dot dot',
+ 'trailing dot dot',
)
def test_routes_match_uri_case_sensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "/BLAH"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match uri case sensitive configure',
- )
+ self.route_match({"uri": "/BLAH"})
- self.assertEqual(
- self.get(url='/blah')['status'],
- 404,
- 'match uri case sensitive blah',
- )
- self.assertEqual(
- self.get(url='/BlaH')['status'],
- 404,
- 'match uri case sensitive BlaH',
- )
- self.assertEqual(
- self.get(url='/BLAH')['status'],
- 200,
- 'match uri case sensitive BLAH',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 404, '/blah')
+ self.assertEqual(self.get(url='/BlaH')['status'], 404, '/BlaH')
+ self.assertEqual(self.get(url='/BLAH')['status'], 200, '/BLAH')
def test_routes_match_uri_normalize(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": "/blah"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match uri normalize configure',
- )
+ self.route_match({"uri": "/blah"})
self.assertEqual(
- self.get(url='/%62%6c%61%68')['status'], 200, 'match uri normalize'
+ self.get(url='/%62%6c%61%68')['status'], 200, 'normalize'
)
def test_routes_match_empty_array(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"uri": []},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match empty array configure',
- )
+ self.route_match({"uri": []})
- self.assertEqual(
- self.get(url='/blah')['status'],
- 200,
- 'match empty array',
- )
+ self.assertEqual(self.get(url='/blah')['status'], 200, 'empty array')
def test_routes_reconfigure(self):
- self.assertIn('success', self.conf([], 'routes'), 'routes redefine')
- self.assertEqual(self.get()['status'], 404, 'routes redefine request')
+ self.assertIn('success', self.conf([], 'routes'), 'redefine')
+ self.assertEqual(self.get()['status'], 404, 'redefine request')
self.assertIn(
'success',
self.conf([{"action": {"pass": "applications/empty"}}], 'routes'),
- 'routes redefine 2',
- )
- self.assertEqual(
- self.get()['status'], 200, 'routes redefine request 2'
+ 'redefine 2',
)
+ self.assertEqual(self.get()['status'], 200, 'redefine request 2')
- self.assertIn('success', self.conf([], 'routes'), 'routes redefine 3')
- self.assertEqual(
- self.get()['status'], 404, 'routes redefine request 3'
- )
+ self.assertIn('success', self.conf([], 'routes'), 'redefine 3')
+ self.assertEqual(self.get()['status'], 404, 'redefine request 3')
self.assertIn(
'success',
@@ -1072,63 +536,46 @@ class TestRouting(TestApplicationProto):
},
}
),
- 'routes redefine 4',
- )
- self.assertEqual(
- self.get()['status'], 200, 'routes redefine request 4'
+ 'redefine 4',
)
+ self.assertEqual(self.get()['status'], 200, 'redefine request 4')
self.assertIn(
- 'success', self.conf_delete('routes/main/0'), 'routes redefine 5'
- )
- self.assertEqual(
- self.get()['status'], 404, 'routes redefine request 5'
+ 'success', self.conf_delete('routes/main/0'), 'redefine 5'
)
+ self.assertEqual(self.get()['status'], 404, 'redefine request 5')
self.assertIn(
'success',
self.conf_post(
{"action": {"pass": "applications/empty"}}, 'routes/main'
),
- 'routes redefine 6',
- )
- self.assertEqual(
- self.get()['status'], 200, 'routes redefine request 6'
+ 'redefine 6',
)
+ self.assertEqual(self.get()['status'], 200, 'redefine request 6')
self.assertIn(
'error',
self.conf(
{"action": {"pass": "applications/empty"}}, 'routes/main/2'
),
- 'routes redefine 7',
+ 'redefine 7',
)
self.assertIn(
'success',
self.conf(
{"action": {"pass": "applications/empty"}}, 'routes/main/1'
),
- 'routes redefine 8',
+ 'redefine 8',
)
self.assertEqual(
- len(self.conf_get('routes/main')), 2, 'routes redefine conf 8'
- )
- self.assertEqual(
- self.get()['status'], 200, 'routes redefine request 8'
+ len(self.conf_get('routes/main')), 2, 'redefine conf 8'
)
+ self.assertEqual(self.get()['status'], 200, 'redefine request 8')
def test_routes_edit(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": "GET"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'routes edit configure',
- )
+ self.route_match({"method": "GET"})
self.assertEqual(self.get()['status'], 200, 'routes edit GET')
self.assertEqual(self.post()['status'], 404, 'routes edit POST')
@@ -1260,16 +707,7 @@ class TestRouting(TestApplicationProto):
def test_match_edit(self):
self.skip_alerts.append(r'failed to apply new conf')
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"method": ["GET", "POST"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match edit configure',
- )
+ self.route_match({"method": ["GET", "POST"]})
self.assertEqual(self.get()['status'], 200, 'match edit GET')
self.assertEqual(self.post()['status'], 200, 'match edit POST')
@@ -1390,20 +828,7 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 200, 'match edit GET 8')
def test_routes_match_rules(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {
- "method": "GET",
- "host": "localhost",
- "uri": "/",
- },
- "action": {"pass": "applications/empty"},
- }
- ),
- 'routes match rules configure',
- )
+ self.route_match({"method": "GET", "host": "localhost", "uri": "/"})
self.assertEqual(self.get()['status'], 200, 'routes match rules')
@@ -1417,75 +842,18 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 500, 'routes loop')
def test_routes_match_headers(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"host": "localhost"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers configure',
- )
+ self.route_match({"headers": {"host": "localhost"}})
self.assertEqual(self.get()['status'], 200, 'match headers')
- self.assertEqual(
- self.get(
- headers={
- "Host": "Localhost",
- "Connection": "close",
- }
- )['status'],
- 200,
- 'match headers case insensitive',
- )
- self.assertEqual(
- self.get(
- headers={
- "Host": "localhost.com",
- "Connection": "close",
- }
- )['status'],
- 404,
- 'match headers exact',
- )
- self.assertEqual(
- self.get(
- headers={
- "Host": "llocalhost",
- "Connection": "close",
- }
- )['status'],
- 404,
- 'match headers exact 2',
- )
- self.assertEqual(
- self.get(
- headers={
- "Host": "host",
- "Connection": "close",
- }
- )['status'],
- 404,
- 'match headers exact 3',
- )
+ self.host('Localhost', 200)
+ self.host('localhost.com', 404)
+ self.host('llocalhost', 404)
+ self.host('host', 404)
def test_routes_match_headers_multiple(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {
- "headers": {"host": "localhost", "x-blah": "test"}
- },
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers multiple configure',
- )
+ self.route_match({"headers": {"host": "localhost", "x-blah": "test"}})
self.assertEqual(self.get()['status'], 404, 'match headers multiple')
-
self.assertEqual(
self.get(
headers={
@@ -1511,16 +879,7 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_headers_multiple_values(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"x-blah": "test"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers multiple values configure',
- )
+ self.route_match({"headers": {"x-blah": "test"}})
self.assertEqual(
self.get(
@@ -1557,21 +916,11 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_headers_multiple_rules(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"x-blah": ["test", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers multiple rules configure',
- )
+ self.route_match({"headers": {"x-blah": ["test", "blah"]}})
self.assertEqual(
self.get()['status'], 404, 'match headers multiple rules'
)
-
self.assertEqual(
self.get(
headers={
@@ -1583,7 +932,6 @@ class TestRouting(TestApplicationProto):
200,
'match headers multiple rules 2',
)
-
self.assertEqual(
self.get(
headers={
@@ -1595,7 +943,6 @@ class TestRouting(TestApplicationProto):
200,
'match headers multiple rules 3',
)
-
self.assertEqual(
self.get(
headers={
@@ -1621,16 +968,7 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_headers_case_insensitive(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"X-BLAH": "TEST"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers case insensitive configure',
- )
+ self.route_match({"headers": {"X-BLAH": "TEST"}})
self.assertEqual(
self.get(
@@ -1645,104 +983,27 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_headers_invalid(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"headers": ["blah"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers invalid',
- )
-
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"headers": {"foo": ["bar", {}]}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers invalid 2',
- )
+ self.route_match_invalid({"headers": ["blah"]})
+ self.route_match_invalid({"headers": {"foo": ["bar", {}]}})
+ self.route_match_invalid({"headers": {"": "blah"}})
def test_routes_match_headers_empty_rule(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"host": ""}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers empty rule configure',
- )
-
- self.assertEqual(self.get()['status'], 404, 'match headers empty rule')
-
- self.assertEqual(
- self.get(headers={"Host": "", "Connection": "close"})['status'],
- 200,
- 'match headers empty rule 2',
- )
+ self.route_match({"headers": {"host": ""}})
- def test_routes_match_headers_rule_field_empty(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"headers": {"": "blah"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers rule field empty configure',
- )
+ self.assertEqual(self.get()['status'], 404, 'localhost')
+ self.host('', 200)
def test_routes_match_headers_empty(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers empty configure',
- )
-
- self.assertEqual(self.get()['status'], 200, 'match headers empty')
+ self.route_match({"headers": {}})
+ self.assertEqual(self.get()['status'], 200, 'empty')
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": []},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers array empty configure 2',
- )
-
- self.assertEqual(
- self.get()['status'], 200, 'match headers array empty 2'
- )
+ self.route_match({"headers": []})
+ self.assertEqual(self.get()['status'], 200, 'empty 2')
def test_routes_match_headers_rule_array_empty(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"headers": {"blah": []}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers rule array empty configure',
- )
+ self.route_match({"headers": {"blah": []}})
- self.assertEqual(
- self.get()['status'], 404, 'match headers rule array empty'
- )
+ self.assertEqual(self.get()['status'], 404, 'array empty')
self.assertEqual(
self.get(
headers={
@@ -1754,22 +1015,15 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_headers_array(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {
- "headers": [
- {"x-header1": "foo*"},
- {"x-header2": "bar"},
- {"x-header3": ["foo", "bar"]},
- {"x-header1": "bar", "x-header4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match headers array configure',
+ self.route_match(
+ {
+ "headers": [
+ {"x-header1": "foo*"},
+ {"x-header2": "bar"},
+ {"x-header3": ["foo", "bar"]},
+ {"x-header1": "bar", "x-header4": "foo"},
+ ]
+ }
)
self.assertEqual(self.get()['status'], 404, 'match headers array')
@@ -1860,352 +1114,128 @@ class TestRouting(TestApplicationProto):
)
def test_routes_match_arguments(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": "bar"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments configure',
- )
+ self.route_match({"arguments": {"foo": "bar"}})
- self.assertEqual(self.get()['status'], 404, 'match arguments')
- self.assertEqual(
- self.get(url='/?foo=bar')['status'], 200, 'match arguments 2'
- )
-
- self.assertEqual(
- self.get(url='/?Foo=bar')['status'],
- 404,
- 'match arguments case sensitive',
- )
- self.assertEqual(
- self.get(url='/?foo=Bar')['status'],
- 404,
- 'match arguments case sensitive 2',
- )
- self.assertEqual(
- self.get(url='/?foo=bar1')['status'],
- 404,
- 'match arguments exact',
- )
- self.assertEqual(
- self.get(url='/?1foo=bar')['status'],
- 404,
- 'match arguments exact 2',
- )
+ self.assertEqual(self.get()['status'], 404, 'args')
+ self.assertEqual(self.get(url='/?foo=bar')['status'], 200, 'args 2')
+ self.assertEqual(self.get(url='/?foo=bar1')['status'], 404, 'args 3')
+ self.assertEqual(self.get(url='/?1foo=bar')['status'], 404, 'args 4')
+ self.assertEqual(self.get(url='/?Foo=bar')['status'], 404, 'case')
+ self.assertEqual(self.get(url='/?foo=Bar')['status'], 404, 'case 2')
def test_routes_match_arguments_empty(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments empty configure',
- )
+ self.route_match({"arguments": {}})
+ self.assertEqual(self.get()['status'], 200, 'arguments empty')
- self.assertEqual(self.get()['status'], 200, 'match arguments empty')
-
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": []},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments empty configure 2',
- )
-
- self.assertEqual(self.get()['status'], 200, 'match arguments empty 2')
+ self.route_match({"arguments": []})
+ self.assertEqual(self.get()['status'], 200, 'arguments empty 2')
def test_routes_match_arguments_invalid(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"arguments": ["var"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments invalid',
- )
-
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"arguments": [{"var1": {}}]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments invalid 2',
- )
-
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"arguments": {"": "bar"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments invalid 3',
- )
+ self.route_match_invalid({"arguments": ["var"]})
+ self.route_match_invalid({"arguments": [{"var1": {}}]})
+ self.route_match_invalid({"arguments": {"": "bar"}})
@unittest.skip('not yet')
def test_routes_match_arguments_space(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": "bar "}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments space configure',
- )
+ self.route_match({"arguments": {"foo": "bar "}})
- self.assertEqual(
- self.get(url='/?foo=bar &')['status'],
- 200,
- 'match arguments space',
- )
- self.assertEqual(
- self.get(url='/?foo=bar+&')['status'],
- 200,
- 'match arguments space 2',
- ) # FAIL
- self.assertEqual(
- self.get(url='/?foo=bar%20&')['status'],
- 200,
- 'match arguments space 3',
- ) # FAIL
+ self.assertEqual(self.get(url='/?foo=bar &')['status'], 200, 'sp')
+ # FAIL
+ self.assertEqual(self.get(url='/?foo=bar+&')['status'], 200, 'sp 2')
+ # FAIL
+ self.assertEqual(self.get(url='/?foo=bar%20&')['status'], 200, 'sp 3')
@unittest.skip('not yet')
def test_routes_match_arguments_plus(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": [{"foo": "bar+"}]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments plus configure',
- )
+ self.route_match({"arguments": [{"foo": "bar+"}]})
+ self.assertEqual(self.get(url='/?foo=bar+&')['status'], 200, 'plus')
+ # FAIL
self.assertEqual(
- self.get(url='/?foo=bar+&')['status'],
- 200,
- 'match arguments plus',
+ self.get(url='/?foo=bar%2B&')['status'], 200, 'plus 2'
)
- self.assertEqual(
- self.get(url='/?foo=bar%2B&')['status'],
- 200,
- 'match arguments plus 2',
- ) # FAIL
@unittest.skip('not yet')
def test_routes_match_arguments_hex(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": [{"foo": "bar"}]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments hex configure',
- )
+ self.route_match({"arguments": [{"foo": "bar"}]})
self.assertEqual(
- self.get(url='/?%66%6F%6f=%62%61%72&')['status'],
- 200,
- 'match arguments hex',
- ) # FAIL
+ self.get(url='/?%66%6F%6f=%62%61%72&')['status'], 200, 'hex'
+ )
def test_routes_match_arguments_chars(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": "-._()[],;"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments chars configure',
- )
+ self.route_match({"arguments": {"foo": "-._()[],;"}})
- self.assertEqual(
- self.get(url='/?foo=-._()[],;')['status'],
- 200,
- 'match arguments chars',
- )
+ self.assertEqual(self.get(url='/?foo=-._()[],;')['status'], 200, 'chs')
def test_routes_match_arguments_complex(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": ""}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments complex configure',
- )
+ self.route_match({"arguments": {"foo": ""}})
+ self.assertEqual(self.get(url='/?foo')['status'], 200, 'complex')
self.assertEqual(
- self.get(url='/?foo')['status'],
- 200,
- 'match arguments complex',
+ self.get(url='/?blah=blah&foo=')['status'], 200, 'complex 2'
)
self.assertEqual(
- self.get(url='/?blah=blah&foo=')['status'],
- 200,
- 'match arguments complex 2',
+ self.get(url='/?&&&foo&&&')['status'], 200, 'complex 3'
)
self.assertEqual(
- self.get(url='/?&&&foo&&&')['status'],
- 200,
- 'match arguments complex 3',
+ self.get(url='/?foo&foo=bar&foo')['status'], 404, 'complex 4'
)
self.assertEqual(
- self.get(url='/?foo&foo=bar&foo')['status'],
- 404,
- 'match arguments complex 4',
+ self.get(url='/?foo=&foo')['status'], 200, 'complex 5'
)
self.assertEqual(
- self.get(url='/?foo=&foo')['status'],
- 200,
- 'match arguments complex 5',
+ self.get(url='/?&=&foo&==&')['status'], 200, 'complex 6'
)
self.assertEqual(
- self.get(url='/?&=&foo&==&')['status'],
- 200,
- 'match arguments complex 6',
- )
- self.assertEqual(
- self.get(url='/?&=&bar&==&')['status'],
- 404,
- 'match arguments complex 7',
+ self.get(url='/?&=&bar&==&')['status'], 404, 'complex 7'
)
def test_routes_match_arguments_multiple(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": "bar", "blah": "test"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments multiple configure',
- )
-
- self.assertEqual(self.get()['status'], 404, 'match arguments multiple')
+ self.route_match({"arguments": {"foo": "bar", "blah": "test"}})
+ self.assertEqual(self.get()['status'], 404, 'multiple')
self.assertEqual(
- self.get(url='/?foo=bar&blah=test')['status'],
- 200,
- 'match arguments multiple 2',
+ self.get(url='/?foo=bar&blah=test')['status'], 200, 'multiple 2'
)
-
self.assertEqual(
- self.get(url='/?foo=bar&blah')['status'],
- 404,
- 'match arguments multiple 3',
+ self.get(url='/?foo=bar&blah')['status'], 404, 'multiple 3'
)
def test_routes_match_arguments_multiple_rules(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"arguments": {"foo": ["bar", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments multiple rules configure',
- )
-
- self.assertEqual(
- self.get()['status'], 404, 'match arguments multiple rules'
- )
-
- self.assertEqual(
- self.get(url='/?foo=bar')['status'],
- 200,
- 'match arguments multiple rules 2',
- )
-
- self.assertEqual(
- self.get(url='/?foo=blah')['status'],
- 200,
- 'match arguments multiple rules 3',
- )
+ self.route_match({"arguments": {"foo": ["bar", "blah"]}})
+ self.assertEqual(self.get()['status'], 404, 'rules')
+ self.assertEqual(self.get(url='/?foo=bar')['status'], 200, 'rules 2')
+ self.assertEqual(self.get(url='/?foo=blah')['status'], 200, 'rules 3')
self.assertEqual(
self.get(url='/?foo=blah&foo=bar&foo=blah')['status'],
200,
- 'match arguments multiple rules 4',
+ 'rules 4',
)
-
self.assertEqual(
- self.get(url='/?foo=blah&foo=bar&foo=')['status'],
- 404,
- 'match arguments multiple rules 5',
+ self.get(url='/?foo=blah&foo=bar&foo=')['status'], 404, 'rules 5'
)
def test_routes_match_arguments_array(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {
- "arguments": [
- {"var1": "val1*"},
- {"var2": "val2"},
- {"var3": ["foo", "bar"]},
- {"var1": "bar", "var4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match arguments array configure',
+ self.route_match(
+ {
+ "arguments": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ }
)
- self.assertEqual(self.get()['status'], 404, 'match arguments array')
+ self.assertEqual(self.get()['status'], 404, 'arr')
+ self.assertEqual(self.get(url='/?var1=val123')['status'], 200, 'arr 2')
+ self.assertEqual(self.get(url='/?var2=val2')['status'], 200, 'arr 3')
+ self.assertEqual(self.get(url='/?var3=bar')['status'], 200, 'arr 4')
+ self.assertEqual(self.get(url='/?var1=bar')['status'], 404, 'arr 5')
self.assertEqual(
- self.get(url='/?var1=val123')['status'],
- 200,
- 'match arguments array 2',
- )
- self.assertEqual(
- self.get(url='/?var2=val2')['status'],
- 200,
- 'match arguments array 3',
- )
- self.assertEqual(
- self.get(url='/?var3=bar')['status'],
- 200,
- 'match arguments array 4',
- )
- self.assertEqual(
- self.get(url='/?var1=bar')['status'],
- 404,
- 'match arguments array 5',
- )
- self.assertEqual(
- self.get(url='/?var1=bar&var4=foo')['status'],
- 200,
- 'match arguments array 6',
+ self.get(url='/?var1=bar&var4=foo')['status'], 200, 'arr 6'
)
self.assertIn(
@@ -2214,574 +1244,514 @@ class TestRouting(TestApplicationProto):
'match arguments array configure 2',
)
- self.assertEqual(
- self.get(url='/?var2=val2')['status'],
- 404,
- 'match arguments array 7',
- )
- self.assertEqual(
- self.get(url='/?var3=foo')['status'],
- 200,
- 'match arguments array 8',
- )
+ self.assertEqual(self.get(url='/?var2=val2')['status'], 404, 'arr 7')
+ self.assertEqual(self.get(url='/?var3=foo')['status'], 200, 'arr 8')
def test_routes_match_cookies(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"cookies": {"foO": "bar"}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match cookie configure',
- )
+ self.route_match({"cookies": {"foO": "bar"}})
+
+ self.assertEqual(self.get()['status'], 404, 'cookie')
+ self.cookie('foO=bar', 200)
+ self.cookie('foO=bar;1', 200)
+ self.cookie(['foO=bar', 'blah=blah'], 200)
+ self.cookie('foO=bar; blah=blah', 200)
+ self.cookie('Foo=bar', 404)
+ self.cookie('foO=Bar', 404)
+ self.cookie('foO=bar1', 404)
+ self.cookie('1foO=bar;', 404)
- self.assertEqual(self.get()['status'], 404, 'match cookie')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foO=bar',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies 2',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['foO=bar', 'blah=blah'],
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies 3',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foO=bar; blah=blah',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies 4',
- )
+ def test_routes_match_cookies_empty(self):
+ self.route_match({"cookies": {}})
+ self.assertEqual(self.get()['status'], 200, 'cookies empty')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'Foo=bar',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies case sensitive',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foO=Bar',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies case sensitive 2',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foO=bar1',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies exact',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': '1foO=bar;',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies exact 2',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foO=bar;1',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies exact 3',
- )
+ self.route_match({"cookies": []})
+ self.assertEqual(self.get()['status'], 200, 'cookies empty 2')
- def test_routes_match_cookies_empty(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"cookies": {}},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match cookies empty configure',
+ def test_routes_match_cookies_invalid(self):
+ self.route_match_invalid({"cookies": ["var"]})
+ self.route_match_invalid({"cookies": [{"foo": {}}]})
+
+ def test_routes_match_cookies_multiple(self):
+ self.route_match({"cookies": {"foo": "bar", "blah": "blah"}})
+
+ self.assertEqual(self.get()['status'], 404, 'multiple')
+ self.cookie('foo=bar; blah=blah', 200)
+ self.cookie(['foo=bar', 'blah=blah'], 200)
+ self.cookie(['foo=bar; blah', 'blah'], 404)
+ self.cookie(['foo=bar; blah=test', 'blah=blah'], 404)
+
+ def test_routes_match_cookies_multiple_values(self):
+ self.route_match({"cookies": {"blah": "blah"}})
+
+ self.cookie(['blah=blah', 'blah=blah', 'blah=blah'], 200)
+ self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 404)
+ self.cookie(['blah=blah; blah=', 'blah=blah'], 404)
+
+ def test_routes_match_cookies_multiple_rules(self):
+ self.route_match({"cookies": {"blah": ["test", "blah"]}})
+
+ self.assertEqual(self.get()['status'], 404, 'multiple rules')
+ self.cookie('blah=test', 200)
+ self.cookie('blah=blah', 200)
+ self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 200)
+ self.cookie(['blah=blah; blah=test', 'blah=blah'], 200)
+ self.cookie(['blah=blah', 'blah'], 200) # invalid cookie
+
+ def test_routes_match_cookies_array(self):
+ self.route_match(
+ {
+ "cookies": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ }
)
- self.assertEqual(self.get()['status'], 200, 'match cookies empty')
+ self.assertEqual(self.get()['status'], 404, 'cookies array')
+ self.cookie('var1=val123', 200)
+ self.cookie('var2=val2', 200)
+ self.cookie(' var2=val2 ', 200)
+ self.cookie('var3=bar', 200)
+ self.cookie('var3=bar;', 200)
+ self.cookie('var1=bar', 404)
+ self.cookie('var1=bar; var4=foo;', 200)
+ self.cookie(['var1=bar', 'var4=foo'], 200)
self.assertIn(
'success',
- self.route(
- {
- "match": {"cookies": []},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match cookies empty configure 2',
+ self.conf_delete('routes/0/match/cookies/1'),
+ 'match cookies array configure 2',
)
- self.assertEqual(self.get()['status'], 200, 'match cookies empty 2')
+ self.cookie('var2=val2', 404)
+ self.cookie('var3=foo', 200)
- def test_routes_match_cookies_invalid(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"cookies": ["var"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match cookies invalid',
+ def test_routes_match_scheme(self):
+ self.route_match({"scheme": "http"})
+ self.route_match({"scheme": "https"})
+ self.route_match({"scheme": "HtTp"})
+ self.route_match({"scheme": "HtTpS"})
+
+ def test_routes_match_scheme_invalid(self):
+ self.route_match_invalid({"scheme": ["http"]})
+ self.route_match_invalid({"scheme": "ftp"})
+ self.route_match_invalid({"scheme": "ws"})
+ self.route_match_invalid({"scheme": "*"})
+ self.route_match_invalid({"scheme": ""})
+
+ def test_routes_source_port(self):
+ def sock_port():
+ _, sock = self.http(b'', start=True, raw=True, no_recv=True)
+ port = sock.getsockname()[1]
+ return (sock, port)
+
+ sock, port = sock_port()
+ sock2, port2 = sock_port()
+
+ self.route_match({"source": "127.0.0.1:" + str(port)})
+ self.assertEqual(self.get(sock=sock)['status'], 200, 'exact')
+ self.assertEqual(self.get(sock=sock2)['status'], 404, 'exact 2')
+
+ sock, port = sock_port()
+ sock2, port2 = sock_port()
+
+ self.route_match({"source": "!127.0.0.1:" + str(port)})
+ self.assertEqual(self.get(sock=sock)['status'], 404, 'negative')
+ self.assertEqual(self.get(sock=sock2)['status'], 200, 'negative 2')
+
+ sock, port = sock_port()
+ sock2, port2 = sock_port()
+
+ self.route_match(
+ {"source": "127.0.0.1:" + str(port) + "-" + str(port)}
+ )
+ self.assertEqual(self.get(sock=sock)['status'], 200, 'range single')
+ self.assertEqual(self.get(sock=sock2)['status'], 404, 'range single 2')
+
+ socks = [
+ sock_port(),
+ sock_port(),
+ sock_port(),
+ sock_port(),
+ sock_port(),
+ ]
+ socks.sort(key=lambda sock: sock[1])
+
+ self.route_match(
+ {
+ "source": "127.0.0.1:"
+ + str(socks[1][1]) # second port number
+ + "-"
+ + str(socks[3][1]) # fourth port number
+ }
)
+ self.assertEqual(self.get(sock=socks[0][0])['status'], 404, 'range')
+ self.assertEqual(self.get(sock=socks[1][0])['status'], 200, 'range 2')
+ self.assertEqual(self.get(sock=socks[2][0])['status'], 200, 'range 3')
+ self.assertEqual(self.get(sock=socks[3][0])['status'], 200, 'range 4')
+ self.assertEqual(self.get(sock=socks[4][0])['status'], 404, 'range 5')
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"cookies": [{"foo": {}}]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match cookies invalid 2',
+ socks = [
+ sock_port(),
+ sock_port(),
+ sock_port(),
+ ]
+ socks.sort(key=lambda sock: sock[1])
+
+ self.route_match(
+ {
+ "source": [
+ "127.0.0.1:" + str(socks[0][1]),
+ "127.0.0.1:" + str(socks[2][1]),
+ ]
+ }
)
+ self.assertEqual(self.get(sock=socks[0][0])['status'], 200, 'array')
+ self.assertEqual(self.get(sock=socks[1][0])['status'], 404, 'array 2')
+ self.assertEqual(self.get(sock=socks[2][0])['status'], 200, 'array 3')
- def test_routes_match_cookies_multiple(self):
+ def test_routes_source_addr(self):
self.assertIn(
'success',
- self.route(
+ self.conf(
{
- "match": {"cookies": {"foo": "bar", "blah": "blah"}},
- "action": {"pass": "applications/empty"},
- }
+ "*:7080": {"pass": "routes"},
+ "[::1]:7081": {"pass": "routes"},
+ },
+ 'listeners',
),
- 'match cookies multiple configure',
+ 'source listeners configure',
)
- self.assertEqual(self.get()['status'], 404, 'match cookies multiple')
+ def get_ipv6():
+ return self.get(sock_type='ipv6', port=7081)
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'foo=bar; blah=blah',
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple 2',
- )
+ self.route_match({"source": "127.0.0.1"})
+ self.assertEqual(self.get()['status'], 200, 'exact')
+ self.assertEqual(get_ipv6()['status'], 404, 'exact ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['foo=bar', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple 3',
- )
+ self.route_match({"source": ["127.0.0.1"]})
+ self.assertEqual(self.get()['status'], 200, 'exact 2')
+ self.assertEqual(get_ipv6()['status'], 404, 'exact 2 ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['foo=bar; blah', 'blah'],
- 'Connection': 'close',
- }
- )['status'],
- 404,
- 'match cookies multiple 4',
- )
+ self.route_match({"source": "!127.0.0.1"})
+ self.assertEqual(self.get()['status'], 404, 'exact neg')
+ self.assertEqual(get_ipv6()['status'], 200, 'exact neg ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['foo=bar; blah=test', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 404,
- 'match cookies multiple 5',
- )
+ self.route_match({"source": "127.0.0.2"})
+ self.assertEqual(self.get()['status'], 404, 'exact 3')
+ self.assertEqual(get_ipv6()['status'], 404, 'exact 3 ipv6')
- def test_routes_match_cookies_multiple_values(self):
+ self.route_match({"source": "127.0.0.1-127.0.0.1"})
+ self.assertEqual(self.get()['status'], 200, 'range single')
+ self.assertEqual(get_ipv6()['status'], 404, 'range single ipv6')
+
+ self.route_match({"source": "127.0.0.2-127.0.0.2"})
+ self.assertEqual(self.get()['status'], 404, 'range single 2')
+ self.assertEqual(get_ipv6()['status'], 404, 'range single 2 ipv6')
+
+ self.route_match({"source": "127.0.0.2-127.0.0.3"})
+ self.assertEqual(self.get()['status'], 404, 'range')
+ self.assertEqual(get_ipv6()['status'], 404, 'range ipv6')
+
+ self.route_match({"source": "127.0.0.1-127.0.0.2"})
+ self.assertEqual(self.get()['status'], 200, 'range 2')
+ self.assertEqual(get_ipv6()['status'], 404, 'range 2 ipv6')
+
+ self.route_match({"source": "127.0.0.0-127.0.0.2"})
+ self.assertEqual(self.get()['status'], 200, 'range 3')
+ self.assertEqual(get_ipv6()['status'], 404, 'range 3 ipv6')
+
+ self.route_match({"source": "127.0.0.0-127.0.0.1"})
+ self.assertEqual(self.get()['status'], 200, 'range 4')
+ self.assertEqual(get_ipv6()['status'], 404, 'range 4 ipv6')
+
+ self.route_match({"source": "126.0.0.0-127.0.0.0"})
+ self.assertEqual(self.get()['status'], 404, 'range 5')
+ self.assertEqual(get_ipv6()['status'], 404, 'range 5 ipv6')
+
+ self.route_match({"source": "126.126.126.126-127.0.0.2"})
+ self.assertEqual(self.get()['status'], 200, 'range 6')
+ self.assertEqual(get_ipv6()['status'], 404, 'range 6 ipv6')
+
+ def test_routes_source_ipv6(self):
self.assertIn(
'success',
- self.route(
+ self.conf(
{
- "match": {"cookies": {"blah": "blah"}},
- "action": {"pass": "applications/empty"},
- }
+ "[::1]:7080": {"pass": "routes"},
+ "127.0.0.1:7081": {"pass": "routes"},
+ },
+ 'listeners',
),
- 'match cookies multiple values configure',
+ 'source listeners configure',
)
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah', 'blah=blah', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match headers multiple values',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah', 'blah=test', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 404,
- 'match cookies multiple values 2',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah; blah=', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 404,
- 'match cookies multiple values 3',
- )
+ self.route_match({"source": "::1"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'exact')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'exact ipv4')
- def test_routes_match_cookies_multiple_rules(self):
+ self.route_match({"source": ["::1"]})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'exact 2')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'exact 2 ipv4')
+
+ self.route_match({"source": "!::1"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'exact neg')
+ self.assertEqual(self.get(port=7081)['status'], 200, 'exact neg ipv4')
+
+ self.route_match({"source": "::2"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'exact 3')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'exact 3 ipv4')
+
+ self.route_match({"source": "::1-::1"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range ipv4')
+
+ self.route_match({"source": "::2-::2"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'range 2')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range 2 ipv4')
+
+ self.route_match({"source": "::2-::3"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'range 3')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range 3 ipv4')
+
+ self.route_match({"source": "::1-::2"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 4')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range 4 ipv4')
+
+ self.route_match({"source": "::0-::2"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 5')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range 5 ipv4')
+
+ self.route_match({"source": "::0-::1"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 6')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'range 6 ipv4')
+
+ def test_routes_source_cidr(self):
self.assertIn(
'success',
- self.route(
+ self.conf(
{
- "match": {"cookies": {"blah": ["test", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
+ "*:7080": {"pass": "routes"},
+ "[::1]:7081": {"pass": "routes"},
+ },
+ 'listeners',
),
- 'match cookies multiple rules configure',
+ 'source listeners configure',
)
- self.assertEqual(
- self.get()['status'], 404, 'match cookies multiple rules'
- )
+ def get_ipv6():
+ return self.get(sock_type='ipv6', port=7081)
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'blah=test',
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple rules 2',
- )
+ self.route_match({"source": "127.0.0.1/32"})
+ self.assertEqual(self.get()['status'], 200, '32')
+ self.assertEqual(get_ipv6()['status'], 404, '32 ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'blah=blah',
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple rules 3',
- )
+ self.route_match({"source": "127.0.0.0/32"})
+ self.assertEqual(self.get()['status'], 404, '32 2')
+ self.assertEqual(get_ipv6()['status'], 404, '32 2 ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah', 'blah=test', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple rules 4',
- )
+ self.route_match({"source": "127.0.0.0/31"})
+ self.assertEqual(self.get()['status'], 200, '31')
+ self.assertEqual(get_ipv6()['status'], 404, '31 ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah; blah=test', 'blah=blah'],
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple rules 5',
- )
+ self.route_match({"source": "0.0.0.0/1"})
+ self.assertEqual(self.get()['status'], 200, '1')
+ self.assertEqual(get_ipv6()['status'], 404, '1 ipv6')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['blah=blah', 'blah'], # invalid cookie
- 'Connection': 'close',
- }
- )['status'],
- 200,
- 'match cookies multiple rules 6',
- )
+ self.route_match({"source": "0.0.0.0/0"})
+ self.assertEqual(self.get()['status'], 200, '0')
+ self.assertEqual(get_ipv6()['status'], 404, '0 ipv6')
- def test_routes_match_cookies_array(self):
+ def test_routes_source_cidr_ipv6(self):
self.assertIn(
'success',
- self.route(
+ self.conf(
{
- "match": {
- "cookies": [
- {"var1": "val1*"},
- {"var2": "val2"},
- {"var3": ["foo", "bar"]},
- {"var1": "bar", "var4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
+ "[::1]:7080": {"pass": "routes"},
+ "127.0.0.1:7081": {"pass": "routes"},
+ },
+ 'listeners',
),
- 'match cookies array configure',
+ 'source listeners configure',
)
- self.assertEqual(self.get()['status'], 404, 'match cookies array')
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var1=val123',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 2',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var2=val2',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 3',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var3=bar',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 4',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var3=bar;',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 5',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var1=bar',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies array 6',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var1=bar; var4=foo;',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 7',
- )
- self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': ['var1=bar', 'var4=foo'],
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 8',
- )
+ self.route_match({"source": "::1/128"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '128')
+ self.assertEqual(self.get(port=7081)['status'], 404, '128 ipv4')
+
+ self.route_match({"source": "::0/128"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 404, '128 2')
+ self.assertEqual(self.get(port=7081)['status'], 404, '128 ipv4')
+
+ self.route_match({"source": "::0/127"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '127')
+ self.assertEqual(self.get(port=7081)['status'], 404, '127 ipv4')
+
+ self.route_match({"source": "::0/32"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '32')
+ self.assertEqual(self.get(port=7081)['status'], 404, '32 ipv4')
+
+ self.route_match({"source": "::0/1"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '1')
+ self.assertEqual(self.get(port=7081)['status'], 404, '1 ipv4')
+
+ self.route_match({"source": "::/0"})
+ self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '0')
+ self.assertEqual(self.get(port=7081)['status'], 404, '0 ipv4')
+
+ def test_routes_source_unix(self):
+ addr = self.testdir + '/sock'
self.assertIn(
'success',
- self.conf_delete('routes/0/match/cookies/1'),
- 'match cookies array configure 2',
+ self.conf({"unix:" + addr: {"pass": "routes"}}, 'listeners'),
+ 'source listeners configure',
)
+ self.route_match({"source": "!0.0.0.0/0"})
self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var2=val2',
- 'Connection': 'close',
- },
- )['status'],
- 404,
- 'match cookies array 9',
+ self.get(sock_type='unix', addr=addr)['status'], 200, 'unix ipv4'
)
+
+ self.route_match({"source": "!::/0"})
self.assertEqual(
- self.get(
- headers={
- 'Host': 'localhost',
- 'Cookie': 'var3=foo',
- 'Connection': 'close',
- },
- )['status'],
- 200,
- 'match cookies array 10',
+ self.get(sock_type='unix', addr=addr)['status'], 200, 'unix ipv6'
)
- def test_routes_match_scheme(self):
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"scheme": "http"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match scheme http configure',
- )
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"scheme": "https"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match scheme https configure',
+ def test_routes_match_source(self):
+ self.route_match({"source": "::"})
+ self.route_match(
+ {
+ "source": [
+ "127.0.0.1",
+ "192.168.0.10:8080",
+ "192.168.0.11:8080-8090",
+ ]
+ }
)
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"scheme": "HtTp"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match scheme http case insensitive configure',
+ self.route_match(
+ {
+ "source": [
+ "10.0.0.0/8",
+ "10.0.0.0/7:1000",
+ "10.0.0.0/32:8080-8090",
+ ]
+ }
)
- self.assertIn(
- 'success',
- self.route(
- {
- "match": {"scheme": "HtTpS"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'match scheme https case insensitive configure',
+ self.route_match(
+ {
+ "source": [
+ "10.0.0.0-10.0.0.1",
+ "10.0.0.0-11.0.0.0:1000",
+ "127.0.0.0-127.0.0.255:8080-8090",
+ ]
+ }
)
-
- def test_routes_match_scheme_invalid(self):
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"scheme": ["http"]},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'scheme invalid type no arrays allowed',
+ self.route_match(
+ {"source": ["2001::", "[2002::]:8000", "[2003::]:8080-8090"]}
)
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"scheme": "ftp"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'scheme invalid protocol 1',
+ self.route_match(
+ {
+ "source": [
+ "2001::-200f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ "[fe08::-feff::]:8000",
+ "[fff0::-fff0::10]:8080-8090",
+ ]
+ }
)
- self.assertIn(
- 'error',
- self.route(
- {
- "match": {"scheme": "ws"},
- "action": {"pass": "applications/empty"},
- }
- ),
- 'scheme invalid protocol 2',
+ self.route_match(
+ {
+ "source": [
+ "2001::/16",
+ "[0ff::/64]:8000",
+ "[fff0:abcd:ffff:ffff:ffff::/128]:8080-8090",
+ ]
+ }
)
+ self.route_match({"source": "*:0-65535"})
+ self.assertEqual(self.get()['status'], 200, 'source any')
+
+ def test_routes_match_source_invalid(self):
+ self.route_match_invalid({"source": "127"})
+ self.route_match_invalid({"source": "256.0.0.1"})
+ self.route_match_invalid({"source": "127.0.0."})
+ self.route_match_invalid({"source": "127.0.0.1:"})
+ self.route_match_invalid({"source": "127.0.0.1/"})
+ self.route_match_invalid({"source": "11.0.0.0/33"})
+ self.route_match_invalid({"source": "11.0.0.0/65536"})
+ self.route_match_invalid({"source": "11.0.0.0-10.0.0.0"})
+ self.route_match_invalid({"source": "11.0.0.0:3000-2000"})
+ self.route_match_invalid({"source": ["11.0.0.0:3000-2000"]})
+ self.route_match_invalid({"source": "[2001::]:3000-2000"})
+ self.route_match_invalid({"source": "2001::-2000::"})
+ self.route_match_invalid({"source": "2001::/129"})
+ self.route_match_invalid({"source": "::FFFFF"})
+ self.route_match_invalid({"source": "[::1]:"})
+ self.route_match_invalid({"source": "*:"})
+ self.route_match_invalid({"source": "*:1-a"})
+ self.route_match_invalid({"source": "*:65536"})
+
+ def test_routes_match_destination(self):
self.assertIn(
- 'error',
- self.route(
- {
- "match": {"scheme": "*"},
- "action": {"pass": "applications/empty"},
- }
+ 'success',
+ self.conf(
+ {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
+ 'listeners',
),
- 'scheme invalid no wildcard allowed',
+ 'listeners configure',
)
+
+ self.route_match({"destination": "*:7080"})
+ self.assertEqual(self.get()['status'], 200, 'dest')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest 2')
+
+ self.route_match({"destination": ["127.0.0.1:7080"]})
+ self.assertEqual(self.get()['status'], 200, 'dest 3')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest 4')
+
+ self.route_match({"destination": "!*:7080"})
+ self.assertEqual(self.get()['status'], 404, 'dest neg')
+ self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 2')
+
+ def test_routes_match_destination_proxy(self):
self.assertIn(
- 'error',
- self.route(
+ 'success',
+ self.conf(
{
- "match": {"scheme": ""},
- "action": {"pass": "applications/empty"},
+ "listeners": {
+ "*:7080": {"pass": "routes/first"},
+ "*:7081": {"pass": "routes/second"},
+ },
+ "routes": {
+ "first": [
+ {"action": {"proxy": "http://127.0.0.1:7081"}}
+ ],
+ "second": [
+ {
+ "match": {"destination": ["127.0.0.1:7081"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ },
+ "applications": {
+ "empty": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/empty",
+ "working_directory": self.current_dir
+ + "/python/empty",
+ "module": "wsgi",
+ }
+ },
}
),
- 'scheme invalid empty',
+ 'proxy configure',
)
+ self.assertEqual(self.get()['status'], 200, 'proxy')
+
if __name__ == '__main__':
TestRouting.main()
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
index 3df2bc82..c6648095 100644
--- a/test/test_routing_tls.py
+++ b/test/test_routing_tls.py
@@ -48,10 +48,8 @@ class TestRoutingTLS(TestApplicationTLS):
'scheme configure',
)
- self.assertEqual(self.get()['status'], 200, 'scheme http')
- self.assertEqual(
- self.get_ssl(port=7081)['status'], 204, 'scheme https'
- )
+ self.assertEqual(self.get()['status'], 200, 'http')
+ self.assertEqual(self.get_ssl(port=7081)['status'], 204, 'https')
if __name__ == '__main__':
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index bbb252d7..83a71f96 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -285,7 +285,7 @@ class TestRubyApplication(TestApplicationRuby):
def test_ruby_application_body_empty(self):
self.load('body_empty')
- self.assertEqual(self.get()['body'], '0\r\n\r\n', 'body empty')
+ self.assertEqual(self.get()['body'], '', 'body empty')
def test_ruby_application_body_array(self):
self.load('body_array')
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index 18345828..7212a95c 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -23,7 +23,7 @@ class TestApplicationGo(TestApplicationProto):
os.mkdir(self.testdir + '/go')
env = os.environ.copy()
- env['GOPATH'] = self.pardir + '/go'
+ env['GOPATH'] = self.pardir + '/build/go'
try:
process = Popen(
@@ -44,7 +44,7 @@ class TestApplicationGo(TestApplicationProto):
return process
- def load(self, script, name='app'):
+ def load(self, script, name='app', **kwargs):
self.prepare_env(script, name)
self._load_conf(
@@ -60,5 +60,6 @@ class TestApplicationGo(TestApplicationProto):
"executable": self.testdir + "/go/" + name,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index bcf87f59..a370d96b 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -6,7 +6,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationJava(TestApplicationProto):
- def load(self, script, name='app'):
+ def load(self, script, name='app', **kwargs):
app_path = self.testdir + '/java'
web_inf_path = app_path + '/WEB-INF/'
classes_path = web_inf_path + 'classes/'
@@ -82,5 +82,6 @@ class TestApplicationJava(TestApplicationProto):
"webapp": app_path,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py
index 3cc72669..d818298f 100644
--- a/test/unit/applications/lang/node.py
+++ b/test/unit/applications/lang/node.py
@@ -15,20 +15,22 @@ class TestApplicationNode(TestApplicationProto):
return unit if not complete_check else unit.complete()
- def load(self, script, name='app.js'):
+ def load(self, script, name='app.js', **kwargs):
# copy application
shutil.copytree(
self.current_dir + '/node/' + script, self.testdir + '/node'
)
- # link modules
+ # copy modules
- os.symlink(
+ shutil.copytree(
self.pardir + '/node/node_modules',
self.testdir + '/node/node_modules',
)
+ self.public_dir(self.testdir + '/node')
+
self._load_conf(
{
"listeners": {"*:7080": {"pass": "applications/" + script}},
@@ -40,5 +42,6 @@ class TestApplicationNode(TestApplicationProto):
"executable": name,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/perl.py b/test/unit/applications/lang/perl.py
index 79df2cfa..d32aca33 100644
--- a/test/unit/applications/lang/perl.py
+++ b/test/unit/applications/lang/perl.py
@@ -4,7 +4,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPerl(TestApplicationProto):
application_type = "perl"
- def load(self, script, name='psgi.pl'):
+ def load(self, script, name='psgi.pl', **kwargs):
script_path = self.current_dir + '/perl/' + script
self._load_conf(
@@ -18,5 +18,6 @@ class TestApplicationPerl(TestApplicationProto):
"script": script_path + '/' + name,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py
index 9c54368d..6b1677e6 100644
--- a/test/unit/applications/lang/php.py
+++ b/test/unit/applications/lang/php.py
@@ -4,7 +4,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPHP(TestApplicationProto):
application_type = "php"
- def load(self, script, name='index.php'):
+ def load(self, script, name='index.php', **kwargs):
script_path = self.current_dir + '/php/' + script
self._load_conf(
@@ -19,5 +19,6 @@ class TestApplicationPHP(TestApplicationProto):
"index": name,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index ded76cb6..fdda024a 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -4,7 +4,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPython(TestApplicationProto):
application_type = "python"
- def load(self, script, name=None):
+ def load(self, script, name=None, **kwargs):
if name is None:
name = script
@@ -22,5 +22,6 @@ class TestApplicationPython(TestApplicationProto):
"module": "wsgi",
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
index d30735ad..8c8acecc 100644
--- a/test/unit/applications/lang/ruby.py
+++ b/test/unit/applications/lang/ruby.py
@@ -4,7 +4,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationRuby(TestApplicationProto):
application_type = "ruby"
- def load(self, script, name='config.ru'):
+ def load(self, script, name='config.ru', **kwargs):
script_path = self.current_dir + '/ruby/' + script
self._load_conf(
@@ -18,5 +18,6 @@ class TestApplicationRuby(TestApplicationProto):
"script": script_path + '/' + name,
}
},
- }
+ },
+ **kwargs
)
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index 4105473f..ae1af354 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -25,7 +25,19 @@ class TestApplicationProto(TestControl):
return found
- def _load_conf(self, conf):
+ def _load_conf(self, conf, **kwargs):
+ 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']
+
self.assertIn(
'success', self.conf(conf), 'load application configuration'
)
diff --git a/test/unit/feature/isolation.py b/test/unit/feature/isolation.py
index 9b06ab3c..3f474993 100644
--- a/test/unit/feature/isolation.py
+++ b/test/unit/feature/isolation.py
@@ -82,6 +82,3 @@ class TestFeatureIsolation(TestApplicationProto):
data = int(os.readlink(nspath)[len(nstype) + 2 : -1])
return data
-
- def parsejson(self, data):
- return json.loads(data.split('\n')[1])
diff --git a/test/unit/http.py b/test/unit/http.py
index c7e3e36d..839e91a2 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -1,5 +1,6 @@
import re
import time
+import json
import socket
import select
from unit.main import TestUnit
@@ -86,23 +87,24 @@ class TestHTTP(TestUnit):
sock.sendall(req)
+ encoding = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
+
if TestUnit.detailed:
print('>>>')
try:
- print(req.decode('utf-8', 'ignore'))
+ print(req.decode(encoding, 'ignore'))
except UnicodeEncodeError:
print(req)
resp = ''
if 'no_recv' not in kwargs:
- enc = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
read_timeout = (
30 if 'read_timeout' not in kwargs else kwargs['read_timeout']
)
resp = self.recvall(
sock, read_timeout=read_timeout, buff_size=read_buffer_size
- ).decode(enc)
+ ).decode(encoding)
if TestUnit.detailed:
print('<<<')
@@ -114,6 +116,15 @@ class TestHTTP(TestUnit):
if 'raw_resp' not in kwargs:
resp = self._resp_to_dict(resp)
+ headers = resp.get('headers')
+ if headers and headers.get('Transfer-Encoding') == 'chunked':
+ resp['body'] = self._parse_chunked_body(resp['body']).decode(
+ encoding
+ )
+
+ if 'json' in kwargs:
+ resp = self._parse_json(resp)
+
if 'start' not in kwargs:
sock.close()
return resp
@@ -151,7 +162,7 @@ class TestHTTP(TestUnit):
return data
def _resp_to_dict(self, resp):
- m = re.search('(.*?\x0d\x0a?)\x0d\x0a?(.*)', resp, re.M | re.S)
+ m = re.search(r'(.*?\x0d\x0a?)\x0d\x0a?(.*)', resp, re.M | re.S)
if not m:
return {}
@@ -162,12 +173,12 @@ class TestHTTP(TestUnit):
headers_lines = p.findall(headers_text)
status = re.search(
- '^HTTP\/\d\.\d\s(\d+)|$', headers_lines.pop(0)
+ r'^HTTP\/\d\.\d\s(\d+)|$', headers_lines.pop(0)
).group(1)
headers = {}
for line in headers_lines:
- m = re.search('(.*)\:\s(.*)', line)
+ m = re.search(r'(.*)\:\s(.*)', line)
if m.group(1) not in headers:
headers[m.group(1)] = m.group(2)
@@ -180,6 +191,65 @@ class TestHTTP(TestUnit):
return {'status': int(status), 'headers': headers, 'body': body}
+ def _parse_chunked_body(self, raw_body):
+ if isinstance(raw_body, str):
+ raw_body = bytes(raw_body.encode())
+
+ crlf = b'\r\n'
+ chunks = raw_body.split(crlf)
+
+ if len(chunks) < 3:
+ self.fail('Invalid chunked body')
+
+ if chunks.pop() != b'':
+ self.fail('No CRLF at the end of the body')
+
+ try:
+ last_size = int(chunks[-2], 16)
+ except:
+ self.fail('Invalid zero size chunk')
+
+ if last_size != 0 or chunks[-1] != b'':
+ self.fail('Incomplete body')
+
+ body = b''
+ while len(chunks) >= 2:
+ try:
+ size = int(chunks.pop(0), 16)
+ except:
+ self.fail('Invalid chunk size %s' % str(size))
+
+ if size == 0:
+ self.assertEqual(len(chunks), 1, 'last zero size')
+ break
+
+ temp_body = crlf.join(chunks)
+
+ body += temp_body[:size]
+
+ temp_body = temp_body[size + len(crlf) :]
+
+ chunks = temp_body.split(crlf)
+
+ return body
+
+ def _parse_json(self, resp):
+ headers = resp['headers']
+
+ self.assertIn('Content-Type', headers, 'Content-Type header set')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'application/json',
+ 'Content-Type header is application/json',
+ )
+
+ resp['body'] = json.loads(resp['body'])
+
+ return resp
+
+ def getjson(self, **kwargs):
+ return self.get(json=True, **kwargs)
+
def waitforsocket(self, port):
ret = False
diff --git a/test/unit/main.py b/test/unit/main.py
index 094fdb0e..ea6afd7f 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -1,6 +1,7 @@
import os
import re
import sys
+import stat
import time
import fcntl
import shutil
@@ -20,6 +21,9 @@ class TestUnit(unittest.TestCase):
pardir = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
)
+ is_su = os.geteuid() == 0
+ uid = os.geteuid()
+ gid = os.getegid()
architecture = platform.architecture()[0]
system = platform.system()
maxDiff = None
@@ -188,13 +192,19 @@ class TestUnit(unittest.TestCase):
self.stop_processes()
def _run(self):
- self.unitd = self.pardir + '/build/unitd'
+ build_dir = self.pardir + '/build'
+ self.unitd = build_dir + '/unitd'
if not os.path.isfile(self.unitd):
exit("Could not find unit")
self.testdir = tempfile.mkdtemp(prefix='unit-test-')
+ self.public_dir(self.testdir)
+
+ if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777':
+ self.public_dir(build_dir)
+
os.mkdir(self.testdir + '/state')
print()
@@ -328,6 +338,15 @@ class TestUnit(unittest.TestCase):
return ret
+ def public_dir(self, path):
+ os.chmod(path, 0o777)
+
+ for root, dirs, files in os.walk(path):
+ for d in dirs:
+ os.chmod(os.path.join(root, d), 0o777)
+ for f in files:
+ os.chmod(os.path.join(root, f), 0o777)
+
@staticmethod
def _parse_args():
parser = argparse.ArgumentParser(add_help=False)
diff --git a/version b/version
index 2e798f2b..037e31ce 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.13.0
-NXT_VERNUM=11300
+NXT_VERSION=1.14.0
+NXT_VERNUM=11400