summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2020-04-16 18:27:26 +0300
committerAndrei Belov <defan@nginx.com>2020-04-16 18:27:26 +0300
commit74f32d26b91f49d3392605e81c1597b375890b60 (patch)
treeadfc67dfc86461441bde65512f745ce27bd6ea28
parent2ff9df10ef1df43c935c870175e52473dad2c21a (diff)
parent9877087756144d3bdf343d0d4e91e1efbcc62c93 (diff)
downloadunit-74f32d26b91f49d3392605e81c1597b375890b60.tar.gz
unit-74f32d26b91f49d3392605e81c1597b375890b60.tar.bz2
Merged with the default branch.1.17.0-1
-rw-r--r--.hgtags1
-rw-r--r--CHANGES27
-rw-r--r--auto/sendfile56
-rw-r--r--auto/sources1
-rw-r--r--docs/changes.xml86
-rw-r--r--go/port.go10
-rw-r--r--pkg/Makefile4
-rw-r--r--pkg/docker/Dockerfile.full2
-rw-r--r--pkg/docker/Dockerfile.go1.11-dev2
-rw-r--r--pkg/docker/Dockerfile.minimal2
-rw-r--r--pkg/docker/Dockerfile.perl5.282
-rw-r--r--pkg/docker/Dockerfile.php7.32
-rw-r--r--pkg/docker/Dockerfile.python2.72
-rw-r--r--pkg/docker/Dockerfile.python3.72
-rw-r--r--pkg/docker/Dockerfile.ruby2.52
-rw-r--r--pkg/docker/Makefile27
-rw-r--r--pkg/shasum.mak9
-rw-r--r--src/java/nxt_jni_InputStream.c4
-rw-r--r--src/nodejs/unit-http/http_server.js12
-rw-r--r--src/nxt_conf.c265
-rw-r--r--src/nxt_conf.h2
-rw-r--r--src/nxt_conf_validation.c67
-rw-r--r--src/nxt_conn.h2
-rw-r--r--src/nxt_conn_accept.c85
-rw-r--r--src/nxt_conn_connect.c2
-rw-r--r--src/nxt_controller.c19
-rw-r--r--src/nxt_epoll_engine.c7
-rw-r--r--src/nxt_errno.h1
-rw-r--r--src/nxt_h1proto.c88
-rw-r--r--src/nxt_h1proto_websocket.c2
-rw-r--r--src/nxt_http.h18
-rw-r--r--src/nxt_http_parse.c26
-rw-r--r--src/nxt_http_parse.h4
-rw-r--r--src/nxt_http_request.c6
-rw-r--r--src/nxt_http_response.c4
-rw-r--r--src/nxt_http_return.c57
-rw-r--r--src/nxt_http_route.c56
-rw-r--r--src/nxt_listen_socket.c130
-rw-r--r--src/nxt_listen_socket.h4
-rw-r--r--src/nxt_log_moderation.c1
-rw-r--r--src/nxt_port_memory.c17
-rw-r--r--src/nxt_process.c5
-rw-r--r--src/nxt_process.h2
-rw-r--r--src/nxt_router.c77
-rw-r--r--src/nxt_router_request.h1
-rw-r--r--src/nxt_runtime.c2
-rw-r--r--src/nxt_socket.c13
-rw-r--r--src/nxt_socket.h2
-rw-r--r--src/nxt_string.c219
-rw-r--r--src/nxt_string.h8
-rw-r--r--src/nxt_unit.c102
-rw-r--r--src/nxt_unit.h23
-rw-r--r--src/nxt_upstream_round_robin.c48
-rw-r--r--src/perl/nxt_perl_psgi.c2
-rw-r--r--src/test/nxt_clone_test.c6
-rw-r--r--src/test/nxt_http_parse_test.c17
-rw-r--r--test/python/delayed/wsgi.py1
-rw-r--r--test/python/log_body/wsgi.py2
-rw-r--r--test/python/upstreams/0/wsgi.py8
-rw-r--r--test/python/upstreams/1/wsgi.py8
-rw-r--r--test/python/upstreams/2/wsgi.py8
-rw-r--r--test/test_configuration.py80
-rw-r--r--test/test_go_application.py10
-rw-r--r--test/test_java_application.py10
-rw-r--r--test/test_java_websockets.py28
-rw-r--r--test/test_node_application.py8
-rw-r--r--test/test_node_websockets.py28
-rw-r--r--test/test_perl_application.py10
-rw-r--r--test/test_php_application.py10
-rw-r--r--test/test_php_basic.py56
-rw-r--r--test/test_python_application.py48
-rw-r--r--test/test_python_basic.py69
-rw-r--r--test/test_python_procman.py173
-rw-r--r--test/test_return.py198
-rw-r--r--test/test_routing.py161
-rw-r--r--test/test_routing_tls.py26
-rw-r--r--test/test_ruby_application.py10
-rw-r--r--test/test_share_fallback.py165
-rw-r--r--test/test_tls.py5
-rw-r--r--test/test_upstreams_rr.py282
-rw-r--r--test/test_usr1.py3
-rw-r--r--test/unit/applications/websockets.py20
-rw-r--r--test/unit/http.py51
-rw-r--r--test/unit/main.py175
-rw-r--r--version4
85 files changed, 2006 insertions, 1294 deletions
diff --git a/.hgtags b/.hgtags
index 62076955..e3c41446 100644
--- a/.hgtags
+++ b/.hgtags
@@ -39,3 +39,4 @@ c1625c52dd6444ed613348719fbb54c7abcc6619 1.12.0-1
a3ea27be5ebde3d54e06cf6fe613cb4b53ab76d2 1.15.0-1
8bab088952dd9d7caa3d04fd4b3026cef26fcf7d 1.16.0
0361f3eda67a17295ab324c831cb9d8c560286ed 1.16.0-1
+4b13438632bc37ca599113be90af64f6e2f09d83 1.17.0
diff --git a/CHANGES b/CHANGES
index c44466e6..3ffd06b7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,31 @@
+Changes with Unit 1.17.0 16 Apr 2020
+
+ *) Feature: a "return" action with optional "location" for immediate
+ responses and external redirection.
+
+ *) Feature: fractional weights support for upstream servers.
+
+ *) Bugfix: accidental 502 "Bad Gateway" errors might have occurred in
+ applications under high load.
+
+ *) Bugfix: memory leak in the router; the bug had appeared in 1.13.0.
+
+ *) Bugfix: segmentation fault might have occurred in the router process
+ when reaching open connections limit.
+
+ *) Bugfix: "close() failed (9: Bad file descriptor)" alerts might have
+ appeared in the log while processing large request bodies; the bug
+ had appeared in 1.16.0.
+
+ *) Bugfix: existing application processes didn't reopen the log file.
+
+ *) Bugfix: incompatibility with some Node.js applications.
+
+ *) Bugfix: broken build on DragonFly BSD; the bug had appeared in
+ 1.16.0.
+
+
Changes with Unit 1.16.0 12 Mar 2020
*) Feature: basic load-balancing support with round-robin.
diff --git a/auto/sendfile b/auto/sendfile
index e5bf3b79..a065f7b6 100644
--- a/auto/sendfile
+++ b/auto/sendfile
@@ -46,7 +46,7 @@ if [ $nxt_found = no ]; then
int main() {
off_t sent;
- sendfile(-1, -1, 0, 0, NULL, &sent, SF_NODISKIO);
+ sendfile(-1, -1, 0, 0, NULL, &sent, 0);
return 0;
}"
. auto/feature
@@ -57,55 +57,63 @@ if [ $nxt_found = no ]; then
fi
-NXT_LIBSENDFILE=
-
if [ $nxt_found = no ]; then
- # Solaris 8 sendfilev().
+ # MacOSX sendfile().
- nxt_feature="Solaris sendfilev()"
- nxt_feature_name=NXT_HAVE_SOLARIS_SENDFILEV
- nxt_feature_libs="-lsendfile"
- nxt_feature_test="#include <sys/sendfile.h>
+ nxt_feature="MacOSX sendfile()"
+ nxt_feature_name=NXT_HAVE_MACOSX_SENDFILE
+ nxt_feature_libs=
+ nxt_feature_test="#include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/uio.h>
+ #include <stdlib.h>
int main() {
- size_t sent;
- struct sendfilevec vec;
+ off_t sent;
- sendfilev(-1, &vec, 0, &sent);
+ sendfile(-1, -1, 0, &sent, NULL, 0);
return 0;
}"
. auto/feature
if [ $nxt_found = yes ]; then
- NXT_HAVE_SOLARIS_SENDFILEV=YES
- NXT_LIBSENDFILE=$nxt_feature_libs
+ NXT_HAVE_MACOSX_SENDFILE=YES
fi
fi
if [ $nxt_found = no ]; then
+ $echo
+ $echo "$0: error: no supported sendfile() found."
+ $echo
+ exit 1;
+fi
- # MacOSX sendfile().
- nxt_feature="MacOSX sendfile()"
- nxt_feature_name=NXT_HAVE_MACOSX_SENDFILE
- nxt_feature_libs=
- nxt_feature_test="#include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/uio.h>
- #include <stdlib.h>
+NXT_LIBSENDFILE=
+
+if [ $nxt_found = no ]; then
+
+ # Solaris 8 sendfilev().
+
+ nxt_feature="Solaris sendfilev()"
+ nxt_feature_name=NXT_HAVE_SOLARIS_SENDFILEV
+ nxt_feature_libs="-lsendfile"
+ nxt_feature_test="#include <sys/sendfile.h>
int main() {
- off_t sent;
+ size_t sent;
+ struct sendfilevec vec;
- sendfile(-1, -1, 0, &sent, NULL, 0);
+ sendfilev(-1, &vec, 0, &sent);
return 0;
}"
. auto/feature
if [ $nxt_found = yes ]; then
- NXT_HAVE_MACOSX_SENDFILE=YES
+ NXT_HAVE_SOLARIS_SENDFILEV=YES
+ NXT_LIBSENDFILE=$nxt_feature_libs
fi
fi
diff --git a/auto/sources b/auto/sources
index 2283e543..c6b34bbc 100644
--- a/auto/sources
+++ b/auto/sources
@@ -87,6 +87,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_error.c \
src/nxt_http_route.c \
src/nxt_http_route_addr.c \
+ src/nxt_http_return.c \
src/nxt_http_static.c \
src/nxt_http_proxy.c \
src/nxt_application.c \
diff --git a/docs/changes.xml b/docs/changes.xml
index 11f08eca..2ff5fcab 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -13,6 +13,92 @@
unit-perl
unit-ruby
unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
+ ver="1.17.0" rev="1"
+ date="2020-04-16" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.17.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.17.0" rev="1"
+ date="2020-04-16" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="feature">
+<para>
+a "return" action with optional "location" for immediate responses and external
+redirection.
+</para>
+</change>
+
+<change type="feature">
+<para>
+fractional weights support for upstream servers.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+accidental 502 "Bad Gateway" errors might have occurred in applications under
+high load.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+memory leak in the router; the bug had appeared in 1.13.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+segmentation fault might have occurred in the router process when reaching
+open connections limit.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+"close() failed (9: Bad file descriptor)" alerts might have appeared in the log
+while processing large request bodies; the bug had appeared in 1.16.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+existing application processes didn't reopen the log file.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+incompatibility with some Node.js applications.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+broken build on DragonFly BSD; the bug had appeared in 1.16.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7
+ unit-python3.8
+ unit-go
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
ver="1.16.0" rev="1"
date="2020-03-12" time="18:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/go/port.go b/go/port.go
index a68cae74..59a13f8b 100644
--- a/go/port.go
+++ b/go/port.go
@@ -50,7 +50,11 @@ func add_port(p *port) {
port_registry_.m = make(map[port_key]*port)
}
- port_registry_.m[p.key] = p
+ old := port_registry_.m[p.key]
+
+ if old == nil {
+ port_registry_.m[p.key] = p
+ }
port_registry_.Unlock()
}
@@ -138,6 +142,8 @@ func nxt_go_port_send(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int,
if err != nil {
nxt_go_warn("write result %d (%d), %s", n, oobn, err)
+
+ n = -1
}
return C.ssize_t(n)
@@ -164,6 +170,8 @@ func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int,
if err != nil {
nxt_go_warn("read result %d (%d), %s", n, oobn, err)
+
+ n = -1
}
return C.ssize_t(n)
diff --git a/pkg/Makefile b/pkg/Makefile
index 7926606d..15ff075d 100644
--- a/pkg/Makefile
+++ b/pkg/Makefile
@@ -1,6 +1,7 @@
#!/usr/bin/make
include ../version
+include shasum.mak
VERSION ?= $(NXT_VERSION)
RELEASE ?= 1
@@ -14,6 +15,7 @@ dist:
-r $(VERSION) \
-p unit-$(VERSION) \
-X "../.hg*" -X "../pkg/" -X "../docs/"
+ $(SHA512SUM) unit-$(VERSION).tar.gz > unit-$(VERSION).tar.gz.sha512
rpm:
@cd rpm && VERSION=$(VERSION) RELEASE=$(RELEASE) make all
@@ -32,5 +34,7 @@ clean:
@cd deb && make clean
@cd docker && make clean
@cd npm && make clean
+ rm -f unit-$(VERSION).tar.gz
+ rm -f unit-$(VERSION).tar.gz.sha512
.PHONY: default rpm deb docker npm clean
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
index 60da78db..fb9144c2 100644
--- a/pkg/docker/Dockerfile.full
+++ b/pkg/docker/Dockerfile.full
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.go1.11-dev b/pkg/docker/Dockerfile.go1.11-dev
index ab9bb699..2b213836 100644
--- a/pkg/docker/Dockerfile.go1.11-dev
+++ b/pkg/docker/Dockerfile.go1.11-dev
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index 03fab2a2..af97aa4f 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.perl5.28 b/pkg/docker/Dockerfile.perl5.28
index f9b596f2..793b48d1 100644
--- a/pkg/docker/Dockerfile.perl5.28
+++ b/pkg/docker/Dockerfile.perl5.28
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.php7.3 b/pkg/docker/Dockerfile.php7.3
index e3c2bfbd..5e3f0e97 100644
--- a/pkg/docker/Dockerfile.php7.3
+++ b/pkg/docker/Dockerfile.php7.3
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
index 065fc61b..9e3a431c 100644
--- a/pkg/docker/Dockerfile.python2.7
+++ b/pkg/docker/Dockerfile.python2.7
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python3.7 b/pkg/docker/Dockerfile.python3.7
index d80d5533..2517896b 100644
--- a/pkg/docker/Dockerfile.python3.7
+++ b/pkg/docker/Dockerfile.python3.7
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.ruby2.5 b/pkg/docker/Dockerfile.ruby2.5
index 3d141335..7258bd28 100644
--- a/pkg/docker/Dockerfile.ruby2.5
+++ b/pkg/docker/Dockerfile.ruby2.5
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.16.0-1~buster
+ENV UNIT_VERSION 1.17.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile
index d80b8763..7647e51b 100644
--- a/pkg/docker/Makefile
+++ b/pkg/docker/Makefile
@@ -1,6 +1,7 @@
#!/usr/bin/make
include ../../version
+include ../shasum.mak
DEFAULT_RELEASE := 1
@@ -29,12 +30,16 @@ MODULE_full="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION} unit-python2.7=$${U
MODULE_minimal="unit=$${UNIT_VERSION}"
+EXPORT_DIR := $(VERSION)
+
default:
- @echo "valid targets: all build dockerfiles push clean"
+ @echo "valid targets: all build dockerfiles push tag export clean"
dockerfiles: $(addprefix Dockerfile., $(MODULES))
-build: dockerfiles $(addprefix build-,$(MODULES))
-push: build $(addprefix push-,$(MODULES)) latest
+build: $(addprefix build-,$(MODULES))
+tag: $(addprefix tag-,$(MODULES))
+push: $(addprefix push-,$(MODULES)) latest
+export: $(addsuffix .tar.gz,$(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODULES))) $(addsuffix .tar.gz.sha512, $(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODULES)))
Dockerfile.%: ../../version
@echo "===> Building $@"
@@ -46,17 +51,29 @@ Dockerfile.%: ../../version
build-%: Dockerfile.%
docker build -t unit:$(VERSION)-$* -f Dockerfile.$* .
-push-%:
+tag-%: build-%
docker tag unit:$(VERSION)-$* nginx/unit:$(VERSION)-$*
+
+push-%: tag-%
docker push nginx/unit:$(VERSION)-$*
latest:
docker tag nginx/unit:$(VERSION)-full nginx/unit:latest
docker push nginx/unit:latest
+$(EXPORT_DIR):
+ mkdir -p $@
+
+$(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz: $(EXPORT_DIR) tag-%
+ docker save nginx/unit:$(VERSION)-$* | gzip > $@
+
+$(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz.sha512: $(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz
+ $(SHA512SUM) $< | sed 's,$(EXPORT_DIR)/,,' > $@
+
all: $(addprefix Dockerfile., $(MODULES))
clean:
rm -f $(addprefix Dockerfile., $(MODULES))
+ rm -rf $(EXPORT_DIR)
-.PHONY: default all build dockerfiles latest push clean
+.PHONY: default all build dockerfiles latest push tag export clean
diff --git a/pkg/shasum.mak b/pkg/shasum.mak
new file mode 100644
index 00000000..39ec09e6
--- /dev/null
+++ b/pkg/shasum.mak
@@ -0,0 +1,9 @@
+ifeq ($(shell sha512sum --version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = sha512sum
+else ifeq ($(shell shasum --version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = shasum -a 512
+else ifeq ($(shell openssl version >/dev/null 2>&1 || echo FAIL),)
+SHA512SUM = openssl sha512
+else
+SHA512SUM = $(error no SHA-512 tool found!)
+endif
diff --git a/src/java/nxt_jni_InputStream.c b/src/java/nxt_jni_InputStream.c
index 3b74b0c1..fabff685 100644
--- a/src/java/nxt_jni_InputStream.c
+++ b/src/java/nxt_jni_InputStream.c
@@ -104,7 +104,7 @@ nxt_java_InputStream_readLine(JNIEnv *env, jclass cls,
res = nxt_unit_request_read(req, data + off, res);
}
- nxt_unit_req_debug(req, "readLine '%.*s'", res, (char *) data + off);
+ nxt_unit_req_debug(req, "readLine '%.*s'", (int) res, (char *) data + off);
(*env)->ReleasePrimitiveArrayCritical(env, out, data, 0);
@@ -152,7 +152,7 @@ nxt_java_InputStream_read(JNIEnv *env, jclass cls, jlong req_info_ptr,
res = nxt_unit_request_read(req, data + off, len);
- nxt_unit_req_debug(req, "read '%.*s'", res, (char *) data + off);
+ nxt_unit_req_debug(req, "read '%.*s'", (int) res, (char *) data + off);
(*env)->ReleasePrimitiveArrayCritical(env, b, data, 0);
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index 2f324329..d378e410 100644
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -451,8 +451,18 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) {
return this;
};
-Server.prototype.listen = function () {
+Server.prototype.listen = function (...args) {
this.unit.listen();
+
+ const cb = args.pop();
+
+ if (typeof cb === 'function') {
+ this.once('listening', cb);
+ }
+
+ this.emit('listening');
+
+ return this;
};
Server.prototype.emit_request = function (req, res) {
diff --git a/src/nxt_conf.c b/src/nxt_conf.c
index 2a952c35..1aca0a7e 100644
--- a/src/nxt_conf.c
+++ b/src/nxt_conf.c
@@ -7,13 +7,13 @@
#include <nxt_main.h>
#include <nxt_conf.h>
-#if 0
-#include <math.h>
+
#include <float.h>
-#endif
+#include <math.h>
#define NXT_CONF_MAX_SHORT_STRING 14
+#define NXT_CONF_MAX_NUMBER_LEN 14
#define NXT_CONF_MAX_STRING NXT_INT32_T_MAX
#define NXT_CONF_MAX_TOKEN_LEN 256
@@ -46,8 +46,7 @@ typedef struct nxt_conf_object_s nxt_conf_object_t;
struct nxt_conf_value_s {
union {
uint8_t boolean; /* 1 bit. */
- int64_t integer;
- double number;
+ u_char number[NXT_CONF_MAX_NUMBER_LEN + 1];;
struct {
u_char start[NXT_CONF_MAX_SHORT_STRING];
@@ -130,8 +129,6 @@ static nxt_int_t nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op,
static nxt_int_t nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op,
nxt_conf_value_t *dst, nxt_conf_value_t *src);
-static size_t nxt_conf_json_integer_length(nxt_conf_value_t *value);
-static u_char *nxt_conf_json_print_integer(u_char *p, nxt_conf_value_t *value);
static size_t nxt_conf_json_string_length(nxt_conf_value_t *value);
static u_char *nxt_conf_json_print_string(u_char *p, nxt_conf_value_t *value);
static size_t nxt_conf_json_array_length(nxt_conf_value_t *value,
@@ -221,10 +218,10 @@ nxt_conf_set_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, nxt_str_t *str)
}
-int64_t
-nxt_conf_get_integer(nxt_conf_value_t *value)
+double
+nxt_conf_get_number(nxt_conf_value_t *value)
{
- return value->u.integer;
+ return nxt_strtod(value->u.number, NULL);
}
@@ -312,13 +309,19 @@ void
nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name,
int64_t value, uint32_t index)
{
+ u_char *p, *end;
nxt_conf_object_member_t *member;
member = &object->u.object->members[index];
nxt_conf_set_string(&member->name, name);
- member->value.u.integer = value;
+ p = member->value.u.number;
+ end = p + NXT_CONF_MAX_NUMBER_LEN;
+
+ end = nxt_sprintf(p, end, "%L", value);
+ *end = '\0';
+
member->value.type = NXT_CONF_VALUE_INTEGER;
}
@@ -551,6 +554,7 @@ nxt_int_t
nxt_conf_map_object(nxt_mp_t *mp, nxt_conf_value_t *value, nxt_conf_map_t *map,
nxt_uint_t n, void *data)
{
+ double num;
nxt_str_t str, *s;
nxt_uint_t i;
nxt_conf_value_t *v;
@@ -600,30 +604,32 @@ nxt_conf_map_object(nxt_mp_t *mp, nxt_conf_value_t *value, nxt_conf_map_t *map,
break;
}
+ num = nxt_strtod(v->u.number, NULL);
+
switch (map[i].type) {
case NXT_CONF_MAP_INT32:
- ptr->i32 = v->u.integer;
+ ptr->i32 = num;
break;
case NXT_CONF_MAP_INT64:
- ptr->i64 = v->u.integer;
+ ptr->i64 = num;
break;
case NXT_CONF_MAP_INT:
- ptr->i = v->u.integer;
+ ptr->i = num;
break;
case NXT_CONF_MAP_SIZE:
- ptr->size = v->u.integer;
+ ptr->size = num;
break;
case NXT_CONF_MAP_OFF:
- ptr->off = v->u.integer;
+ ptr->off = num;
break;
case NXT_CONF_MAP_MSEC:
- ptr->msec = v->u.integer * 1000;
+ ptr->msec = (nxt_msec_t) num * 1000;
break;
default:
@@ -635,11 +641,7 @@ nxt_conf_map_object(nxt_mp_t *mp, nxt_conf_value_t *value, nxt_conf_map_t *map,
case NXT_CONF_MAP_DOUBLE:
if (v->type == NXT_CONF_VALUE_NUMBER) {
- ptr->dbl = v->u.number;
-
- } else if (v->type == NXT_CONF_VALUE_INTEGER) {
- ptr->dbl = v->u.integer;
-
+ ptr->dbl = nxt_strtod(v->u.number, NULL);
}
break;
@@ -1269,6 +1271,7 @@ nxt_conf_json_skip_space(u_char *start, u_char *end)
case '\r':
continue;
case '/':
+ start = p;
state = sw_after_slash;
continue;
}
@@ -1285,7 +1288,6 @@ nxt_conf_json_skip_space(u_char *start, u_char *end)
continue;
}
- p--;
break;
case sw_single_comment:
@@ -1318,6 +1320,10 @@ nxt_conf_json_skip_space(u_char *start, u_char *end)
break;
}
+ if (nxt_slow_path(state != sw_normal)) {
+ return start;
+ }
+
return p;
}
@@ -2032,56 +2038,51 @@ static u_char *
nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
u_char *end, nxt_conf_json_error_t *error)
{
- u_char *p, ch;
- uint64_t integer;
- nxt_int_t sign;
-#if 0
- uint64_t frac, power
- nxt_int_t e, negative;
-#endif
-
- static const uint64_t cutoff = NXT_INT64_T_MAX / 10;
- static const uint64_t cutlim = NXT_INT64_T_MAX % 10;
+ u_char *p, *s, ch, c, *dot_pos;
+ size_t size;
+ double num;
- ch = *start;
+ s = start;
+ ch = *s;
if (ch == '-') {
- sign = -1;
- start++;
-
- } else {
- sign = 1;
+ s++;
}
- integer = 0;
+ dot_pos = NULL;
- for (p = start; nxt_fast_path(p != end); p++) {
+ for (p = s; nxt_fast_path(p != end); p++) {
ch = *p;
/* Values below '0' become >= 208. */
- ch = ch - '0';
+ c = ch - '0';
+
+ if (c > 9) {
+ if (ch == '.' && nxt_fast_path(dot_pos == NULL)) {
+ dot_pos = p;
+ continue;
+ }
- if (ch > 9) {
break;
}
+ }
- if (nxt_slow_path(integer >= cutoff
- && (integer > cutoff || ch > cutlim)))
- {
- nxt_conf_json_parse_error(error, start,
- "The integer is too large. Such a large JSON integer value "
- "is not supported."
+ if (dot_pos != NULL) {
+ if (nxt_slow_path(p - dot_pos <= 1)) {
+ nxt_conf_json_parse_error(error, s,
+ "The number is invalid. A fraction part in JSON numbers "
+ "must contain at least one digit."
);
return NULL;
}
- integer = integer * 10 + ch;
+ } else {
+ dot_pos = p;
}
- if (nxt_slow_path(p - start > 1 && *start == '0')) {
-
- nxt_conf_json_parse_error(error, start,
+ if (nxt_slow_path(dot_pos - s > 1 && *start == '0')) {
+ nxt_conf_json_parse_error(error, s,
"The number is invalid. Leading zeros are not allowed in JSON "
"numbers."
);
@@ -2089,101 +2090,79 @@ nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
return NULL;
}
- if (ch != '.') {
- value->type = NXT_CONF_VALUE_INTEGER;
- value->u.integer = sign * integer;
- return p;
- }
+ if (ch == 'e' || ch == 'E') {
+ p++;
+ s = p;
-#if 0
- start = p + 1;
+ if (nxt_fast_path(s != end)) {
+ ch = *s;
- frac = 0;
- power = 1;
+ if (ch == '-' || ch == '+') {
+ s++;
+ }
- for (p = start; nxt_fast_path(p != end); p++) {
- ch = *p;
+ for (p = s; nxt_fast_path(p != end); p++) {
+ ch = *p;
- /* Values below '0' become >= 208. */
- ch = ch - '0';
+ /* Values below '0' become >= 208. */
+ c = ch - '0';
- if (ch > 9) {
- break;
+ if (c > 9) {
+ break;
+ }
+ }
}
- if (nxt_slow_path((frac >= cutoff && (frac > cutoff || ch > cutlim))
- || power > cutoff))
- {
+ if (nxt_slow_path(p == s)) {
+ nxt_conf_json_parse_error(error, start,
+ "The number is invalid. An exponent part in JSON numbers "
+ "must contain at least one digit."
+ );
+
return NULL;
}
-
- frac = frac * 10 + ch;
- power *= 10;
}
- if (nxt_slow_path(p == start)) {
- return NULL;
- }
-
- value->type = NXT_CONF_VALUE_NUMBER;
- value->u.number = integer + (double) frac / power;
-
- value->u.number = copysign(value->u.number, sign);
-
- if (ch == 'e' || ch == 'E') {
- start = p + 1;
-
- ch = *start;
-
- if (ch == '-' || ch == '+') {
- start++;
- }
-
- negative = (ch == '-') ? 1 : 0;
- e = 0;
-
- for (p = start; nxt_fast_path(p != end); p++) {
- ch = *p;
+ size = p - start;
- /* Values below '0' become >= 208. */
- ch = ch - '0';
+ if (size > NXT_CONF_MAX_NUMBER_LEN) {
+ nxt_conf_json_parse_error(error, start,
+ "The number is too long. Such a long JSON number value "
+ "is not supported."
+ );
- if (ch > 9) {
- break;
- }
+ return NULL;
+ }
- e = e * 10 + ch;
+ nxt_memcpy(value->u.number, start, size);
+ value->u.number[size] = '\0';
- if (nxt_slow_path(e > DBL_MAX_10_EXP)) {
- return NULL;
- }
- }
+ nxt_errno = 0;
+ end = NULL;
- if (nxt_slow_path(p == start)) {
- return NULL;
- }
+ num = nxt_strtod(value->u.number, &end);
- if (negative) {
- value->u.number /= exp10(e);
+ if (nxt_slow_path(nxt_errno == NXT_ERANGE
+ || fabs(num) > (double) NXT_INT64_T_MAX))
+ {
+ nxt_conf_json_parse_error(error, start,
+ "The number is out of representable range. Such JSON number "
+ "value is not supported."
+ );
- } else {
- value->u.number *= exp10(e);
- }
+ return NULL;
}
- if (nxt_fast_path(isfinite(value->u.number))) {
- return p;
+ if (nxt_slow_path(end == NULL || *end != '\0')) {
+ nxt_thread_log_alert("strtod(\"%s\", %s) failed %E", value->u.number,
+ end == NULL ? (u_char *) "NULL" : end, nxt_errno);
+ return NULL;
}
-#else
- nxt_conf_json_parse_error(error, start,
- "The number is not an integer. JSON numbers with decimals and "
- "exponents are not supported."
- );
-
-#endif
+ value->type = (num == trunc(num)) ? NXT_CONF_VALUE_INTEGER
+ : NXT_CONF_VALUE_NUMBER;
- return NULL;
+ return p;
}
@@ -2212,11 +2191,8 @@ nxt_conf_json_length(nxt_conf_value_t *value, nxt_conf_json_pretty_t *pretty)
return value->u.boolean ? nxt_length("true") : nxt_length("false");
case NXT_CONF_VALUE_INTEGER:
- return nxt_conf_json_integer_length(value);
-
case NXT_CONF_VALUE_NUMBER:
- /* TODO */
- return 0;
+ return nxt_strlen(value->u.number);
case NXT_CONF_VALUE_SHORT_STRING:
case NXT_CONF_VALUE_STRING:
@@ -2249,11 +2225,8 @@ nxt_conf_json_print(u_char *p, nxt_conf_value_t *value,
: nxt_cpymem(p, "false", 5);
case NXT_CONF_VALUE_INTEGER:
- return nxt_conf_json_print_integer(p, value);
-
case NXT_CONF_VALUE_NUMBER:
- /* TODO */
- return p;
+ return nxt_cpystr(p, value->u.number);
case NXT_CONF_VALUE_SHORT_STRING:
case NXT_CONF_VALUE_STRING:
@@ -2273,32 +2246,6 @@ nxt_conf_json_print(u_char *p, nxt_conf_value_t *value,
static size_t
-nxt_conf_json_integer_length(nxt_conf_value_t *value)
-{
- int64_t num;
-
- num = llabs(value->u.integer);
-
- if (num <= 9999) {
- return nxt_length("-9999");
- }
-
- if (num <= 99999999999LL) {
- return nxt_length("-99999999999");
- }
-
- return NXT_INT64_T_LEN;
-}
-
-
-static u_char *
-nxt_conf_json_print_integer(u_char *p, nxt_conf_value_t *value)
-{
- return nxt_sprintf(p, p + NXT_INT64_T_LEN, "%L", value->u.integer);
-}
-
-
-static size_t
nxt_conf_json_string_length(nxt_conf_value_t *value)
{
nxt_str_t str;
diff --git a/src/nxt_conf.h b/src/nxt_conf.h
index 66201fee..201a3a14 100644
--- a/src/nxt_conf.h
+++ b/src/nxt_conf.h
@@ -114,7 +114,7 @@ NXT_EXPORT void nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str);
NXT_EXPORT void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str);
NXT_EXPORT nxt_int_t nxt_conf_set_string_dup(nxt_conf_value_t *value,
nxt_mp_t *mp, nxt_str_t *str);
-NXT_EXPORT int64_t nxt_conf_get_integer(nxt_conf_value_t *value);
+NXT_EXPORT double nxt_conf_get_number(nxt_conf_value_t *value);
NXT_EXPORT uint8_t nxt_conf_get_boolean(nxt_conf_value_t *value);
// FIXME reimplement and reorder functions below
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 3a3654bd..bc03bdfb 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -17,7 +17,7 @@ typedef enum {
NXT_CONF_VLDT_NULL = 1 << NXT_CONF_NULL,
NXT_CONF_VLDT_BOOLEAN = 1 << NXT_CONF_BOOLEAN,
NXT_CONF_VLDT_INTEGER = 1 << NXT_CONF_INTEGER,
- NXT_CONF_VLDT_NUMBER = 1 << NXT_CONF_NUMBER,
+ NXT_CONF_VLDT_NUMBER = (1 << NXT_CONF_NUMBER) | NXT_CONF_VLDT_INTEGER,
NXT_CONF_VLDT_STRING = 1 << NXT_CONF_STRING,
NXT_CONF_VLDT_ARRAY = 1 << NXT_CONF_ARRAY,
NXT_CONF_VLDT_OBJECT = 1 << NXT_CONF_OBJECT,
@@ -64,6 +64,8 @@ static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_pass(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_routes(nxt_conf_validation_t *vldt,
@@ -354,6 +356,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = {
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = {
+ { nxt_string("return"),
+ NXT_CONF_VLDT_INTEGER,
+ &nxt_conf_vldt_return,
+ NULL },
+
+ { nxt_string("location"),
+ NXT_CONF_VLDT_STRING,
+ NULL,
+ NULL },
+
+ NXT_CONF_VLDT_END
+};
+
+
static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = {
{ nxt_string("share"),
NXT_CONF_VLDT_STRING,
@@ -715,7 +732,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_members[] = {
static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_server_members[] = {
{ nxt_string("weight"),
- NXT_CONF_VLDT_INTEGER,
+ NXT_CONF_VLDT_NUMBER,
&nxt_conf_vldt_server_weight,
NULL },
@@ -756,8 +773,8 @@ nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
static nxt_str_t type_name[] = {
nxt_string("a null"),
nxt_string("a boolean"),
- nxt_string("an integer"),
- nxt_string("a number"),
+ nxt_string("an integer number"),
+ nxt_string("a fractional number"),
nxt_string("a string"),
nxt_string("an array"),
nxt_string("an object"),
@@ -978,6 +995,7 @@ nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
} actions[] = {
{ nxt_string("pass"), nxt_conf_vldt_pass_action_members },
+ { nxt_string("return"), nxt_conf_vldt_return_action_members },
{ nxt_string("share"), nxt_conf_vldt_share_action_members },
{ nxt_string("proxy"), nxt_conf_vldt_proxy_action_members },
};
@@ -993,8 +1011,8 @@ nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
if (members != NULL) {
return nxt_conf_vldt_error(vldt, "The \"action\" object must have "
- "just one of \"pass\", \"share\" or "
- "\"proxy\" options set.");
+ "just one of \"pass\", \"return\", "
+ "\"share\", or \"proxy\" options set.");
}
members = actions[i].members;
@@ -1002,8 +1020,8 @@ nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
if (members == NULL) {
return nxt_conf_vldt_error(vldt, "The \"action\" object must have "
- "either \"pass\", \"share\", or "
- "\"proxy\" option set.");
+ "either \"pass\", \"return\", \"share\", "
+ "or \"proxy\" option set.");
}
return nxt_conf_vldt_object(vldt, value, members);
@@ -1115,6 +1133,23 @@ error:
static nxt_int_t
+nxt_conf_vldt_return(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ int64_t status;
+
+ status = nxt_conf_get_number(value);
+
+ if (status < NXT_HTTP_INVALID || status > NXT_HTTP_STATUS_MAX) {
+ return nxt_conf_vldt_error(vldt, "The \"return\" value is out of "
+ "allowed HTTP status code range 0-999.");
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
@@ -1591,8 +1626,8 @@ nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
nxt_int_t ret;
nxt_conf_vldt_processes_conf_t proc;
- if (nxt_conf_type(value) == NXT_CONF_INTEGER) {
- int_value = nxt_conf_get_integer(value);
+ if (nxt_conf_type(value) == NXT_CONF_NUMBER) {
+ int_value = nxt_conf_get_number(value);
if (int_value < 1) {
return nxt_conf_vldt_error(vldt, "The \"processes\" number must be "
@@ -2025,18 +2060,18 @@ static nxt_int_t
nxt_conf_vldt_server_weight(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
- int64_t int_value;
+ double num_value;
- int_value = nxt_conf_get_integer(value);
+ num_value = nxt_conf_get_number(value);
- if (int_value <= 0) {
+ if (num_value < 0) {
return nxt_conf_vldt_error(vldt, "The \"weight\" number must be "
- "greater than 0.");
+ "positive.");
}
- if (int_value > NXT_INT32_T_MAX) {
+ if (num_value > 1000000) {
return nxt_conf_vldt_error(vldt, "The \"weight\" number must "
- "not exceed %d.", NXT_INT32_T_MAX);
+ "not exceed 1,000,000");
}
return NXT_OK;
diff --git a/src/nxt_conn.h b/src/nxt_conn.h
index 2c1d49a0..a443601f 100644
--- a/src/nxt_conn.h
+++ b/src/nxt_conn.h
@@ -106,7 +106,7 @@ typedef struct {
nxt_work_handler_t accept;
nxt_listen_socket_t *listen;
- nxt_conn_t *next; /* STUB */
+ nxt_conn_t *next;
nxt_work_queue_t *work_queue;
nxt_timer_t timer;
diff --git a/src/nxt_conn_accept.c b/src/nxt_conn_accept.c
index 4ad2d02f..6a89840c 100644
--- a/src/nxt_conn_accept.c
+++ b/src/nxt_conn_accept.c
@@ -24,8 +24,10 @@ static void nxt_conn_listen_handler(nxt_task_t *task, void *obj,
void *data);
static nxt_conn_t *nxt_conn_accept_next(nxt_task_t *task,
nxt_listen_event_t *lev);
-static nxt_int_t nxt_conn_accept_close_idle(nxt_task_t *task,
+static void nxt_conn_accept_close_idle(nxt_task_t *task,
nxt_listen_event_t *lev);
+static void nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj,
+ void *data);
static void nxt_conn_listen_event_error(nxt_task_t *task, void *obj,
void *data);
static void nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj,
@@ -99,12 +101,12 @@ nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev)
goto fail;
}
- lev->next = c;
c->socket.read_work_queue = lev->socket.read_work_queue;
c->socket.write_ready = 1;
c->remote = nxt_sockaddr_cache_alloc(engine, lev->listen);
if (nxt_fast_path(c->remote != NULL)) {
+ lev->next = c;
return c;
}
}
@@ -197,6 +199,7 @@ nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c)
c->listen = lev;
lev->count++;
+ lev->next = NULL;
c->socket.data = NULL;
c->read_work_queue = lev->work_queue;
@@ -228,62 +231,80 @@ nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev)
{
nxt_conn_t *c;
- lev->next = NULL;
+ c = lev->next;
- do {
+ if (c == NULL) {
c = nxt_conn_accept_alloc(task, lev);
- if (nxt_fast_path(c != NULL)) {
- return c;
+ if (nxt_slow_path(c == NULL)) {
+ nxt_conn_accept_close_idle(task, lev);
}
+ }
- } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK);
+ return c;
+}
- nxt_alert(task, "no available connections, "
- "new connections are not accepted within 1s");
- return NULL;
+static void
+nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev)
+{
+ nxt_event_engine_t *engine;
+
+ engine = task->thread->engine;
+
+ nxt_work_queue_add(&engine->close_work_queue,
+ nxt_conn_accept_close_idle_handler, task, NULL, NULL);
+
+ nxt_timer_add(engine, &lev->timer, 100);
+
+ nxt_fd_event_disable_read(engine, &lev->socket);
+
+ nxt_alert(task, "new connections are not accepted within 100ms");
}
-static nxt_int_t
-nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev)
+static void
+nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj, void *data)
{
+ nxt_uint_t times;
nxt_conn_t *c;
nxt_queue_t *idle;
- nxt_queue_link_t *link;
+ nxt_queue_link_t *link, *next;
nxt_event_engine_t *engine;
static nxt_log_moderation_t nxt_idle_close_log_moderation = {
NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION
};
+ times = 10;
engine = task->thread->engine;
-
idle = &engine->idle_connections;
for (link = nxt_queue_last(idle);
link != nxt_queue_head(idle);
- link = nxt_queue_next(link))
+ link = next)
{
+ next = nxt_queue_next(link);
+
c = nxt_queue_link_data(link, nxt_conn_t, link);
+ nxt_debug(c->socket.task, "idle connection: %d rdy:%d",
+ c->socket.fd, c->socket.read_ready);
+
if (!c->socket.read_ready) {
nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO,
task->log, "no available connections, "
"close idle connection");
- nxt_queue_remove(link);
- nxt_conn_close(engine, c);
- return NXT_OK;
- }
- }
+ c->read_state->close_handler(c->socket.task, c, c->socket.data);
- nxt_timer_add(engine, &lev->timer, 1000);
+ times--;
- nxt_fd_event_disable_read(engine, &lev->socket);
-
- return NXT_DECLINED;
+ if (times == 0) {
+ break;
+ }
+ }
+ }
}
@@ -313,12 +334,10 @@ nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev,
case ENFILE:
case ENOBUFS:
case ENOMEM:
- if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) {
- nxt_alert(task, "%s(%d) failed %E, "
- "new connections are not accepted within 1s",
- accept_syscall, lev->socket.fd, err);
- }
+ nxt_alert(task, "%s(%d) failed %E",
+ accept_syscall, lev->socket.fd, err);
+ nxt_conn_accept_close_idle(task, lev);
return;
default:
@@ -339,14 +358,10 @@ nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data)
timer = obj;
lev = nxt_timer_data(timer, nxt_listen_event_t, timer);
- c = lev->next;
+ c = nxt_conn_accept_next(task, lev);
if (c == NULL) {
- c = nxt_conn_accept_next(task, lev);
-
- if (c == NULL) {
- return;
- }
+ return;
}
nxt_fd_event_enable_accept(task->thread->engine, &lev->socket);
diff --git a/src/nxt_conn_connect.c b/src/nxt_conn_connect.c
index d045853f..220fb5f9 100644
--- a/src/nxt_conn_connect.c
+++ b/src/nxt_conn_connect.c
@@ -108,7 +108,7 @@ nxt_conn_socket(nxt_task_t *task, nxt_conn_t *c)
c->write_timer.task = task;
if (c->local != NULL) {
- if (nxt_slow_path(nxt_socket_bind(task, s, c->local, 0) != NXT_OK)) {
+ if (nxt_slow_path(nxt_socket_bind(task, s, c->local) != NXT_OK)) {
nxt_socket_close(task, s);
return NXT_ERROR;
}
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index cc1ed534..f9b2cf26 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -130,14 +130,11 @@ nxt_controller_start(nxt_task_t *task, void *data)
nxt_mp_t *mp;
nxt_int_t ret;
nxt_str_t *json;
- nxt_runtime_t *rt;
nxt_conf_value_t *conf;
nxt_conf_validation_t vldt;
nxt_controller_init_t *init;
- rt = task->thread->runtime;
-
- ret = nxt_http_fields_hash(&nxt_controller_fields_hash, rt->mem_pool,
+ ret = nxt_http_fields_hash(&nxt_controller_fields_hash,
nxt_controller_request_fields,
nxt_nitems(nxt_controller_request_fields));
@@ -402,24 +399,14 @@ nxt_controller_conf_send(nxt_task_t *task, nxt_conf_value_t *conf,
nxt_int_t
nxt_runtime_controller_socket(nxt_task_t *task, nxt_runtime_t *rt)
{
- nxt_sockaddr_t *sa;
nxt_listen_socket_t *ls;
- sa = rt->controller_listen;
-
ls = nxt_mp_alloc(rt->mem_pool, sizeof(nxt_listen_socket_t));
if (ls == NULL) {
return NXT_ERROR;
}
- ls->sockaddr = nxt_sockaddr_create(rt->mem_pool, &sa->u.sockaddr,
- sa->socklen, sa->length);
- if (ls->sockaddr == NULL) {
- return NXT_ERROR;
- }
-
- ls->sockaddr->type = sa->type;
- nxt_sockaddr_text(ls->sockaddr);
+ ls->sockaddr = rt->controller_listen;
nxt_listen_socket_remote_size(ls);
@@ -441,7 +428,7 @@ nxt_runtime_controller_socket(nxt_task_t *task, nxt_runtime_t *rt)
#endif
ls->handler = nxt_controller_conn_init;
- if (nxt_listen_socket_create(task, ls, 0) != NXT_OK) {
+ if (nxt_listen_socket_create(task, rt->mem_pool, ls) != NXT_OK) {
return NXT_ERROR;
}
diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c
index a944834e..d53df1bc 100644
--- a/src/nxt_epoll_engine.c
+++ b/src/nxt_epoll_engine.c
@@ -926,6 +926,13 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
error = ((events & (EPOLLERR | EPOLLHUP)) != 0);
ev->epoll_error = error;
+ if (error
+ && ev->read <= NXT_EVENT_BLOCKED
+ && ev->write <= NXT_EVENT_BLOCKED)
+ {
+ error = 0;
+ }
+
#if (NXT_HAVE_EPOLL_EDGE)
ev->epoll_eof = ((events & EPOLLRDHUP) != 0);
diff --git a/src/nxt_errno.h b/src/nxt_errno.h
index 1b29ef2f..40bcfa3f 100644
--- a/src/nxt_errno.h
+++ b/src/nxt_errno.h
@@ -47,6 +47,7 @@ typedef int nxt_err_t;
#define NXT_ETIME ETIME
#define NXT_ENOMOREFILES 0
#define NXT_ENOBUFS ENOBUFS
+#define NXT_ERANGE ERANGE
#if (NXT_HPUX)
/* HP-UX uses EWOULDBLOCK instead of EAGAIN. */
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index 35918bd8..a139f611 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -74,6 +74,8 @@ static void nxt_h1p_idle_close(nxt_task_t *task, void *obj, void *data);
static void 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);
static void nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data);
+static void nxt_h1p_idle_response_error(nxt_task_t *task, void *obj,
+ void *data);
static void nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj,
void *data);
static nxt_msec_t nxt_h1p_idle_response_timer_value(nxt_conn_t *c,
@@ -184,16 +186,16 @@ static nxt_http_field_proc_t nxt_h1p_peer_fields[] = {
nxt_int_t
-nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt)
+nxt_h1p_init(nxt_task_t *task)
{
nxt_int_t ret;
- ret = nxt_http_fields_hash(&nxt_h1p_fields_hash, rt->mem_pool,
+ ret = nxt_http_fields_hash(&nxt_h1p_fields_hash,
nxt_h1p_fields, nxt_nitems(nxt_h1p_fields));
if (nxt_fast_path(ret == NXT_OK)) {
ret = nxt_http_fields_hash(&nxt_h1p_peer_fields_hash,
- rt->mem_pool, nxt_h1p_peer_fields,
+ nxt_h1p_peer_fields,
nxt_nitems(nxt_h1p_peer_fields));
}
@@ -470,6 +472,8 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "h1p conn request init");
+ nxt_queue_remove(&c->link);
+
r = nxt_http_request_create(task);
if (nxt_fast_path(r != NULL)) {
@@ -1103,6 +1107,8 @@ static const nxt_str_t nxt_http_redirection[] = {
nxt_string("HTTP/1.1 302 Found\r\n"),
nxt_string("HTTP/1.1 303 See Other\r\n"),
nxt_string("HTTP/1.1 304 Not Modified\r\n"),
+ nxt_string("HTTP/1.1 307 Temporary Redirect\r\n"),
+ nxt_string("HTTP/1.1 308 Permanent Redirect\r\n"),
};
@@ -1364,11 +1370,11 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
void
-nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p)
+nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_bool_t all)
{
- size_t size;
- nxt_buf_t *b, *in, *next;
- nxt_conn_t *c;
+ size_t size;
+ nxt_buf_t *b, *in, *next;
+ nxt_conn_t *c;
nxt_debug(task, "h1p complete buffers");
@@ -1390,8 +1396,7 @@ nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p)
next = b->next;
b->next = NULL;
- nxt_work_queue_add(&task->thread->engine->fast_work_queue,
- b->completion_handler, task, b, b->parent);
+ b->completion_handler(task, b, b->parent);
b = next;
}
@@ -1403,9 +1408,8 @@ nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p)
if (in != NULL) {
size = nxt_buf_mem_used_size(&in->mem);
- if (size == 0) {
- nxt_work_queue_add(&task->thread->engine->fast_work_queue,
- in->completion_handler, task, in, in->parent);
+ if (size == 0 || all) {
+ in->completion_handler(task, in, in->parent);
c->read = NULL;
}
@@ -1714,6 +1718,8 @@ nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "h1p conn close");
+ nxt_queue_remove(&c->link);
+
nxt_h1p_shutdown(task, c);
}
@@ -1727,6 +1733,8 @@ nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "h1p conn error");
+ nxt_queue_remove(&c->link);
+
nxt_h1p_shutdown(task, c);
}
@@ -1745,8 +1753,9 @@ nxt_h1p_conn_timer_value(nxt_conn_t *c, uintptr_t data)
static void
nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
{
- size_t size;
- nxt_buf_t *in;
+ size_t size;
+ nxt_buf_t *in;
+ nxt_event_engine_t *engine;
nxt_debug(task, "h1p keepalive");
@@ -1754,7 +1763,7 @@ nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
nxt_conn_tcp_nodelay_on(task, c);
}
- nxt_h1p_complete_buffers(task, h1p);
+ nxt_h1p_complete_buffers(task, h1p, 0);
in = c->read;
@@ -1762,10 +1771,13 @@ nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
c->sent = 0;
+ engine = task->thread->engine;
+ nxt_queue_insert_head(&engine->idle_connections, &c->link);
+
if (in == NULL) {
c->read_state = &nxt_h1p_keepalive_state;
- nxt_conn_read(task->thread->engine, c);
+ nxt_conn_read(engine, c);
} else {
size = nxt_buf_mem_used_size(&in->mem);
@@ -1831,6 +1843,8 @@ nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data)
c = nxt_read_timer_conn(timer);
c->block_read = 1;
+ nxt_queue_remove(&c->link);
+
nxt_h1p_idle_response(task, c);
}
@@ -1898,7 +1912,7 @@ static const nxt_conn_state_t nxt_h1p_timeout_response_state
nxt_aligned(64) =
{
.ready_handler = nxt_h1p_conn_sent,
- .error_handler = nxt_h1p_conn_error,
+ .error_handler = nxt_h1p_idle_response_error,
.timer_handler = nxt_h1p_idle_response_timeout,
.timer_value = nxt_h1p_idle_response_timer_value,
@@ -1919,6 +1933,19 @@ nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data)
static void
+nxt_h1p_idle_response_error(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_conn_t *c;
+
+ c = obj;
+
+ nxt_debug(task, "h1p response error");
+
+ nxt_h1p_shutdown(task, c);
+}
+
+
+static void
nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj, void *data)
{
nxt_conn_t *c;
@@ -1952,20 +1979,25 @@ nxt_h1p_shutdown(nxt_task_t *task, nxt_conn_t *c)
h1p = c->socket.data;
- if (nxt_slow_path(h1p != NULL && h1p->websocket_timer != NULL)) {
- timer = &h1p->websocket_timer->timer;
+ if (h1p != NULL) {
+ nxt_h1p_complete_buffers(task, h1p, 1);
- if (timer->handler != nxt_h1p_conn_ws_shutdown) {
- timer->handler = nxt_h1p_conn_ws_shutdown;
- nxt_timer_add(task->thread->engine, timer, 0);
+ if (nxt_slow_path(h1p->websocket_timer != NULL)) {
+ timer = &h1p->websocket_timer->timer;
- } else {
- nxt_debug(task, "h1p already scheduled ws shutdown");
- }
+ if (timer->handler != nxt_h1p_conn_ws_shutdown) {
+ timer->handler = nxt_h1p_conn_ws_shutdown;
+ nxt_timer_add(task->thread->engine, timer, 0);
- } else {
- nxt_h1p_closing(task, c);
+ } else {
+ nxt_debug(task, "h1p already scheduled ws shutdown");
+ }
+
+ return;
+ }
}
+
+ nxt_h1p_closing(task, c);
}
@@ -2052,8 +2084,6 @@ nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "h1p conn free");
- nxt_queue_remove(&c->link);
-
engine = task->thread->engine;
nxt_sockaddr_cache_free(engine, c);
diff --git a/src/nxt_h1proto_websocket.c b/src/nxt_h1proto_websocket.c
index c9ff899c..42a50a34 100644
--- a/src/nxt_h1proto_websocket.c
+++ b/src/nxt_h1proto_websocket.c
@@ -135,7 +135,7 @@ nxt_h1p_websocket_frame_start(nxt_task_t *task, nxt_http_request_t *r,
c = h1p->conn;
c->read = ws_frame;
- nxt_h1p_complete_buffers(task, h1p);
+ nxt_h1p_complete_buffers(task, h1p, 0);
in = c->read;
c->read_state = &nxt_h1p_read_ws_frame_header_state;
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 0e0694e5..841f5b40 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -23,6 +23,8 @@ typedef enum {
NXT_HTTP_FOUND = 302,
NXT_HTTP_SEE_OTHER = 303,
NXT_HTTP_NOT_MODIFIED = 304,
+ NXT_HTTP_TEMPORARY_REDIRECT = 307,
+ NXT_HTTP_PERMANENT_REDIRECT = 308,
NXT_HTTP_BAD_REQUEST = 400,
NXT_HTTP_FORBIDDEN = 403,
@@ -43,6 +45,9 @@ typedef enum {
NXT_HTTP_SERVICE_UNAVAILABLE = 503,
NXT_HTTP_GATEWAY_TIMEOUT = 504,
NXT_HTTP_VERSION_NOT_SUPPORTED = 505,
+ NXT_HTTP_SERVER_ERROR_MAX = 599,
+
+ NXT_HTTP_STATUS_MAX = 999,
} nxt_http_status_t;
@@ -192,6 +197,7 @@ struct nxt_http_action_s {
nxt_http_action_t *fallback;
nxt_upstream_t *upstream;
uint32_t upstream_number;
+ nxt_http_status_t return_code;
} u;
nxt_str_t name;
@@ -239,9 +245,9 @@ nxt_http_date(u_char *buf, struct tm *tm)
}
-nxt_int_t nxt_http_init(nxt_task_t *task, nxt_runtime_t *rt);
-nxt_int_t nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt);
-nxt_int_t nxt_http_response_hash_init(nxt_task_t *task, nxt_runtime_t *rt);
+nxt_int_t nxt_http_init(nxt_task_t *task);
+nxt_int_t nxt_h1p_init(nxt_task_t *task);
+nxt_int_t nxt_http_response_hash_init(nxt_task_t *task);
void nxt_http_conn_init(nxt_task_t *task, void *obj, void *data);
nxt_http_request_t *nxt_http_request_create(nxt_task_t *task);
@@ -282,6 +288,9 @@ nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf,
nxt_upstream_t ***upstream_joint);
+nxt_http_action_t *nxt_http_return_handler(nxt_task_t *task,
+ nxt_http_request_t *r, nxt_http_action_t *action);
+
nxt_http_action_t *nxt_http_static_handler(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_action_t *action);
nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash);
@@ -320,7 +329,8 @@ void nxt_h1p_websocket_first_frame_start(nxt_task_t *task,
nxt_http_request_t *r, nxt_buf_t *ws_frame);
void nxt_h1p_websocket_frame_start(nxt_task_t *task, nxt_http_request_t *r,
nxt_buf_t *ws_frame);
-void nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p);
+void nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p,
+ nxt_bool_t all);
nxt_msec_t nxt_h1p_conn_request_timer_value(nxt_conn_t *c, uintptr_t data);
extern const nxt_conn_state_t nxt_h1p_idle_close_state;
diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c
index 4c5d4936..22004cc1 100644
--- a/src/nxt_http_parse.c
+++ b/src/nxt_http_parse.c
@@ -22,8 +22,6 @@ static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
-static void *nxt_http_field_hash_alloc(void *pool, size_t size);
-static void nxt_http_field_hash_free(void *pool, void *p);
static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
void *data);
@@ -1133,8 +1131,8 @@ const nxt_lvlhsh_proto_t nxt_http_fields_hash_proto nxt_aligned(64) = {
NXT_LVLHSH_BUCKET_SIZE(64),
{ NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 },
nxt_http_field_hash_test,
- nxt_http_field_hash_alloc,
- nxt_http_field_hash_free,
+ nxt_lvlhsh_alloc,
+ nxt_lvlhsh_free,
};
@@ -1153,20 +1151,6 @@ nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
}
-static void *
-nxt_http_field_hash_alloc(void *pool, size_t size)
-{
- return nxt_mp_align(pool, size, size);
-}
-
-
-static void
-nxt_http_field_hash_free(void *pool, void *p)
-{
- nxt_mp_free(pool, p);
-}
-
-
static nxt_int_t
nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data)
{
@@ -1175,7 +1159,7 @@ nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data)
nxt_int_t
-nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
+nxt_http_fields_hash(nxt_lvlhsh_t *hash,
nxt_http_field_proc_t items[], nxt_uint_t count)
{
u_char ch;
@@ -1187,7 +1171,6 @@ nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
lhq.replace = 0;
lhq.proto = &nxt_http_fields_hash_proto;
- lhq.pool = mp;
for (i = 0; i < count; i++) {
key = NXT_HTTP_FIELD_HASH_INIT;
@@ -1214,7 +1197,7 @@ nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
nxt_uint_t
-nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
+nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash,
nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level)
{
u_char ch;
@@ -1229,7 +1212,6 @@ nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
lhq.replace = 0;
lhq.proto = &proto;
- lhq.pool = mp;
mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xFFFF;
diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h
index d319c71d..0f888949 100644
--- a/src/nxt_http_parse.h
+++ b/src/nxt_http_parse.h
@@ -102,9 +102,9 @@ nxt_int_t nxt_http_parse_request(nxt_http_request_parse_t *rp,
nxt_int_t nxt_http_parse_fields(nxt_http_request_parse_t *rp,
nxt_buf_mem_t *b);
-nxt_int_t nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
+nxt_int_t nxt_http_fields_hash(nxt_lvlhsh_t *hash,
nxt_http_field_proc_t items[], nxt_uint_t count);
-nxt_uint_t nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
+nxt_uint_t nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash,
nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level);
nxt_int_t nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash,
void *ctx);
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 72aaa290..050587f7 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -36,17 +36,17 @@ nxt_time_string_t nxt_http_date_cache = {
nxt_int_t
-nxt_http_init(nxt_task_t *task, nxt_runtime_t *rt)
+nxt_http_init(nxt_task_t *task)
{
nxt_int_t ret;
- ret = nxt_h1p_init(task, rt);
+ ret = nxt_h1p_init(task);
if (ret != NXT_OK) {
return ret;
}
- return nxt_http_response_hash_init(task, rt);
+ return nxt_http_response_hash_init(task);
}
diff --git a/src/nxt_http_response.c b/src/nxt_http_response.c
index 00ecff00..55a4686c 100644
--- a/src/nxt_http_response.c
+++ b/src/nxt_http_response.c
@@ -34,9 +34,9 @@ static nxt_http_field_proc_t nxt_response_fields[] = {
nxt_int_t
-nxt_http_response_hash_init(nxt_task_t *task, nxt_runtime_t *rt)
+nxt_http_response_hash_init(nxt_task_t *task)
{
- return nxt_http_fields_hash(&nxt_response_fields_hash, rt->mem_pool,
+ return nxt_http_fields_hash(&nxt_response_fields_hash,
nxt_response_fields, nxt_nitems(nxt_response_fields));
}
diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c
new file mode 100644
index 00000000..c466cc25
--- /dev/null
+++ b/src/nxt_http_return.c
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_router.h>
+#include <nxt_http.h>
+
+
+static const nxt_http_request_state_t nxt_http_return_send_state;
+
+
+nxt_http_action_t *
+nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r,
+ nxt_http_action_t *action)
+{
+ nxt_http_field_t *field;
+ nxt_http_status_t status;
+
+ status = action->u.return_code;
+
+ if (status >= NXT_HTTP_BAD_REQUEST
+ && status <= NXT_HTTP_SERVER_ERROR_MAX)
+ {
+ nxt_http_request_error(task, r, status);
+ return NULL;
+ }
+
+ r->status = status;
+ r->resp.content_length_n = 0;
+
+ if (action->name.length > 0) {
+ field = nxt_list_zero_add(r->resp.fields);
+ if (nxt_slow_path(field == NULL)) {
+ nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ nxt_http_field_name_set(field, "Location");
+
+ field->value = action->name.start;
+ field->value_length = action->name.length;
+ }
+
+ r->state = &nxt_http_return_send_state;
+
+ nxt_http_request_header_send(task, r, NULL, NULL);
+
+ return NULL;
+}
+
+
+static const nxt_http_request_state_t nxt_http_return_send_state
+ nxt_aligned(64) =
+{
+ .error_handler = nxt_http_request_error_handler,
+};
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index d7f20bcb..ca43c060 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -41,6 +41,8 @@ typedef enum {
typedef struct {
nxt_conf_value_t *pass;
+ nxt_conf_value_t *ret;
+ nxt_str_t location;
nxt_conf_value_t *share;
nxt_conf_value_t *proxy;
nxt_conf_value_t *fallback;
@@ -432,8 +434,6 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NULL;
}
- match->action.u.route = NULL;
- match->action.handler = NULL;
match->items = n;
action_conf = nxt_conf_get_path(cv, &action_path);
@@ -578,6 +578,16 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = {
offsetof(nxt_http_route_action_conf_t, pass)
},
{
+ nxt_string("return"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_action_conf_t, ret)
+ },
+ {
+ nxt_string("location"),
+ NXT_CONF_MAP_STR,
+ offsetof(nxt_http_route_action_conf_t, location)
+ },
+ {
nxt_string("share"),
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_action_conf_t, share)
@@ -602,6 +612,7 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
nxt_mp_t *mp;
nxt_int_t ret;
nxt_str_t name, *string;
+ nxt_uint_t encode;
nxt_conf_value_t *conf;
nxt_http_route_action_conf_t accf;
@@ -613,6 +624,43 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
return ret;
}
+ nxt_memzero(action, sizeof(nxt_http_action_t));
+
+ mp = tmcf->router_conf->mem_pool;
+
+ if (accf.ret != NULL) {
+ action->handler = nxt_http_return_handler;
+ action->u.return_code = nxt_conf_get_number(accf.ret);
+
+ if (accf.location.length > 0) {
+ if (nxt_is_complex_uri_encoded(accf.location.start,
+ accf.location.length))
+ {
+ string = nxt_str_dup(mp, &action->name, &accf.location);
+ if (nxt_slow_path(string == NULL)) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ string = &action->name;
+
+ encode = nxt_encode_complex_uri(NULL, accf.location.start,
+ accf.location.length);
+ string->length = accf.location.length + encode * 2;
+
+ string->start = nxt_mp_nget(mp, string->length);
+ if (nxt_slow_path(string->start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_encode_complex_uri(string->start, accf.location.start,
+ accf.location.length);
+ }
+ }
+
+ return NXT_OK;
+ }
+
conf = accf.pass;
if (accf.share != NULL) {
@@ -625,15 +673,13 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
nxt_conf_get_string(conf, &name);
- mp = tmcf->router_conf->mem_pool;
-
string = nxt_str_dup(mp, &action->name, &name);
if (nxt_slow_path(string == NULL)) {
return NXT_ERROR;
}
if (accf.fallback != NULL) {
- action->u.fallback = nxt_mp_zalloc(mp, sizeof(nxt_http_action_t));
+ action->u.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
if (nxt_slow_path(action->u.fallback == NULL)) {
return NXT_ERROR;
}
diff --git a/src/nxt_listen_socket.c b/src/nxt_listen_socket.c
index 9eeca690..f10abdef 100644
--- a/src/nxt_listen_socket.c
+++ b/src/nxt_listen_socket.c
@@ -27,14 +27,23 @@ nxt_listen_socket(nxt_task_t *task, nxt_socket_t s, int backlog)
nxt_int_t
-nxt_listen_socket_create(nxt_task_t *task, nxt_listen_socket_t *ls,
- nxt_bool_t bind_test)
+nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_listen_socket_t *ls)
{
- nxt_log_t log, *old;
- nxt_uint_t family;
- nxt_socket_t s;
- nxt_thread_t *thr;
- nxt_sockaddr_t *sa;
+ nxt_log_t log, *old;
+ nxt_uint_t family;
+ nxt_socket_t s;
+ nxt_thread_t *thr;
+ nxt_sockaddr_t *sa;
+#if (NXT_HAVE_UNIX_DOMAIN)
+ int ret;
+ u_char *p;
+ nxt_err_t err;
+ nxt_socket_t ts;
+ nxt_sockaddr_t *orig_sa;
+ nxt_file_name_t *name, *tmp;
+ nxt_file_access_t access;
+#endif
sa = ls->sockaddr;
@@ -49,7 +58,7 @@ nxt_listen_socket_create(nxt_task_t *task, nxt_listen_socket_t *ls,
s = nxt_socket_create(task, family, sa->type, 0, ls->flags);
if (s == -1) {
- goto socket_fail;
+ goto fail;
}
if (nxt_socket_setsockopt(task, s, SOL_SOCKET, SO_REUSEADDR, 1) != NXT_OK) {
@@ -81,34 +90,49 @@ nxt_listen_socket_create(nxt_task_t *task, nxt_listen_socket_t *ls,
nxt_socket_defer_accept(task, s, sa);
}
- switch (nxt_socket_bind(task, s, sa, bind_test)) {
+#if (NXT_HAVE_UNIX_DOMAIN)
- case NXT_OK:
- break;
+ if (family == AF_UNIX
+ && sa->type == SOCK_STREAM
+ && sa->u.sockaddr_un.sun_path[0] != '\0')
+ {
+ orig_sa = sa;
- case NXT_ERROR:
- goto fail;
+ sa = nxt_sockaddr_alloc(mp, sa->socklen + 4, sa->length + 4);
+ if (sa == NULL) {
+ goto fail;
+ }
- default: /* NXT_DECLINED: EADDRINUSE on bind() test */
- return NXT_OK;
+ sa->type = SOCK_STREAM;
+ sa->u.sockaddr_un.sun_family = AF_UNIX;
+
+ p = nxt_cpystr((u_char *) sa->u.sockaddr_un.sun_path,
+ (u_char *) orig_sa->u.sockaddr_un.sun_path);
+ nxt_memcpy(p, ".tmp", 4);
+
+ nxt_sockaddr_text(sa);
+
+ (void) unlink(sa->u.sockaddr_un.sun_path);
+
+ } else {
+ orig_sa = NULL;
+ }
+
+#endif
+
+ if (nxt_socket_bind(task, s, sa) != NXT_OK) {
+ goto fail;
}
#if (NXT_HAVE_UNIX_DOMAIN)
if (family == AF_UNIX) {
- nxt_file_name_t *name;
- nxt_file_access_t access;
-
name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
access = (S_IRUSR | S_IWUSR);
if (nxt_file_set_access(name, access) != NXT_OK) {
- goto fail;
- }
-
- if (bind_test && nxt_file_delete(name) != NXT_OK) {
- goto fail;
+ goto listen_fail;
}
}
@@ -119,19 +143,71 @@ nxt_listen_socket_create(nxt_task_t *task, nxt_listen_socket_t *ls,
if (listen(s, ls->backlog) != 0) {
nxt_alert(task, "listen(%d, %d) failed %E",
s, ls->backlog, nxt_socket_errno);
- goto fail;
+ goto listen_fail;
+ }
+
+#if (NXT_HAVE_UNIX_DOMAIN)
+
+ if (orig_sa != NULL) {
+ ts = nxt_socket_create(task, AF_UNIX, SOCK_STREAM, 0, 0);
+ if (ts == -1) {
+ goto listen_fail;
+ }
+
+ ret = connect(ts, &orig_sa->u.sockaddr, orig_sa->socklen);
+
+ err = nxt_socket_errno;
+
+ nxt_socket_close(task, ts);
+
+ if (ret == 0) {
+ nxt_alert(task, "connect(%d, %*s) succeed, address already in use",
+ ts, (size_t) orig_sa->length,
+ nxt_sockaddr_start(orig_sa));
+
+ goto listen_fail;
+ }
+
+ if (err != NXT_ENOENT && err != NXT_ECONNREFUSED) {
+ nxt_alert(task, "connect(%d, %*s) failed %E",
+ ts, (size_t) orig_sa->length,
+ nxt_sockaddr_start(orig_sa), err);
+
+ goto listen_fail;
+ }
+
+ tmp = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
+ name = (nxt_file_name_t *) orig_sa->u.sockaddr_un.sun_path;
+
+ if (nxt_file_rename(tmp, name) != NXT_OK) {
+ goto listen_fail;
+ }
}
+#endif
+
ls->socket = s;
thr->log = old;
return NXT_OK;
-fail:
+listen_fail:
+
+#if (NXT_HAVE_UNIX_DOMAIN)
+
+ if (family == AF_UNIX) {
+ name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
+
+ (void) nxt_file_delete(name);
+ }
- nxt_socket_close(task, s);
+#endif
-socket_fail:
+fail:
+
+ if (s != -1) {
+ nxt_socket_close(task, s);
+ }
thr->log = old;
diff --git a/src/nxt_listen_socket.h b/src/nxt_listen_socket.h
index 80b95425..e2435b76 100644
--- a/src/nxt_listen_socket.h
+++ b/src/nxt_listen_socket.h
@@ -54,8 +54,8 @@ typedef struct {
NXT_EXPORT nxt_int_t nxt_listen_socket(nxt_task_t *task, nxt_socket_t s,
int backlog);
-NXT_EXPORT nxt_int_t nxt_listen_socket_create(nxt_task_t *task,
- nxt_listen_socket_t *ls, nxt_bool_t bind_test);
+NXT_EXPORT nxt_int_t nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_listen_socket_t *ls);
NXT_EXPORT nxt_int_t nxt_listen_socket_update(nxt_task_t *task,
nxt_listen_socket_t *ls, nxt_listen_socket_t *prev);
NXT_EXPORT void nxt_listen_socket_remote_size(nxt_listen_socket_t *ls);
diff --git a/src/nxt_log_moderation.c b/src/nxt_log_moderation.c
index 7c2d7a50..95f9cbfe 100644
--- a/src/nxt_log_moderation.c
+++ b/src/nxt_log_moderation.c
@@ -61,6 +61,7 @@ nxt_log_moderate_allow(nxt_log_moderation_t *mod)
mod->timer.work_queue = &thr->engine->fast_work_queue;
mod->timer.handler = nxt_log_moderate_timer_handler;
mod->timer.log = &nxt_main_log;
+ mod->timer.task = &nxt_main_task;
nxt_timer_add(thr->engine, &mod->timer, 1000);
}
diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c
index 24a40406..33d3777e 100644
--- a/src/nxt_port_memory.c
+++ b/src/nxt_port_memory.c
@@ -111,7 +111,7 @@ 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_buf_t *b, *next;
nxt_port_t *port;
nxt_process_t *process;
nxt_chunk_id_t c;
@@ -124,11 +124,12 @@ nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
b = obj;
- mp = b->data;
-
nxt_assert(data == b->parent);
mmap_handler = data;
+
+complete_buf:
+
hdr = mmap_handler->hdr;
if (nxt_slow_path(hdr->src_pid != nxt_pid && hdr->dst_pid != nxt_pid)) {
@@ -184,8 +185,18 @@ release_buf:
nxt_port_mmap_handler_use(mmap_handler, -1);
+ next = b->next;
+ mp = b->data;
+
nxt_mp_free(mp, b);
nxt_mp_release(mp);
+
+ if (next != NULL) {
+ b = next;
+ mmap_handler = b->parent;
+
+ goto complete_buf;
+ }
}
diff --git a/src/nxt_process.c b/src/nxt_process.c
index 035f747f..f5959edf 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -613,14 +613,13 @@ nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
nxt_port_t *
-nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
- nxt_port_id_t port_id)
+nxt_process_connected_port_find(nxt_process_t *process, nxt_port_t *port)
{
nxt_port_t *res;
nxt_thread_mutex_lock(&process->cp_mutex);
- res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
+ res = nxt_port_hash_find(&process->connected_ports, port->pid, port->id);
nxt_thread_mutex_unlock(&process->cp_mutex);
diff --git a/src/nxt_process.h b/src/nxt_process.h
index 343fffb8..3f7155c8 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -111,7 +111,7 @@ void nxt_process_connected_port_remove(nxt_process_t *process,
nxt_port_t *port);
nxt_port_t *nxt_process_connected_port_find(nxt_process_t *process,
- nxt_pid_t pid, nxt_port_id_t port_id);
+ nxt_port_t *port);
void nxt_worker_process_quit_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
diff --git a/src/nxt_router.c b/src/nxt_router.c
index a913284c..93b750a0 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -303,7 +303,7 @@ nxt_router_start(nxt_task_t *task, void *data)
}
#endif
- ret = nxt_http_init(task, rt);
+ ret = nxt_http_init(task);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
@@ -462,6 +462,7 @@ nxt_inline void
nxt_request_app_link_init(nxt_task_t *task,
nxt_request_app_link_t *req_app_link, nxt_request_rpc_data_t *req_rpc_data)
{
+ nxt_buf_t *body;
nxt_event_engine_t *engine;
engine = task->thread->engine;
@@ -480,6 +481,17 @@ nxt_request_app_link_init(nxt_task_t *task,
req_app_link->work.task = &engine->task;
req_app_link->work.obj = req_app_link;
req_app_link->work.data = engine;
+
+ body = req_rpc_data->request->body;
+
+ if (body != NULL && nxt_buf_is_file(body)) {
+ req_app_link->body_fd = body->file->fd;
+
+ body->file->fd = -1;
+
+ } else {
+ req_app_link->body_fd = -1;
+ }
}
@@ -513,6 +525,10 @@ nxt_request_app_link_alloc(nxt_task_t *task,
nxt_request_app_link_init(task, req_app_link, req_rpc_data);
+ if (ra_src != NULL) {
+ req_app_link->body_fd = ra_src->body_fd;
+ }
+
req_app_link->mem_pool = mp;
return req_app_link;
@@ -654,6 +670,12 @@ nxt_request_app_link_release(nxt_task_t *task,
req_app_link->app_port = NULL;
}
+ if (req_app_link->body_fd != -1) {
+ nxt_fd_close(req_app_link->body_fd);
+
+ req_app_link->body_fd = -1;
+ }
+
nxt_router_msg_cancel(task, &req_app_link->msg_info, req_app_link->stream);
mp = req_app_link->mem_pool;
@@ -713,12 +735,15 @@ nxt_request_app_link_use(nxt_task_t *task, nxt_request_app_link_t *req_app_link,
nxt_inline void
-nxt_request_app_link_error(nxt_request_app_link_t *req_app_link, int code,
- const char *str)
+nxt_request_app_link_error(nxt_task_t *task, nxt_app_t *app,
+ nxt_request_app_link_t *req_app_link, const char *str)
{
req_app_link->app_port = NULL;
- req_app_link->err_code = code;
+ req_app_link->err_code = 500;
req_app_link->err_str = str;
+
+ nxt_alert(task, "app \"%V\" internal error: %s on #%uD",
+ &app->name, str, req_app_link->stream);
}
@@ -3887,7 +3912,7 @@ nxt_router_app_port_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_debug(task, "app '%V' %p abort next stream #%uD",
&app->name, app, req_app_link->stream);
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, app, req_app_link,
"Failed to start application process");
nxt_request_app_link_use(task, req_app_link, -1);
}
@@ -4643,7 +4668,7 @@ nxt_router_port_post_select(nxt_task_t *task, nxt_port_select_state_t *state)
nxt_port_use(task, state->port, -1);
}
- nxt_request_app_link_error(state->req_app_link, 500,
+ nxt_request_app_link_error(task, app, state->req_app_link,
"Failed to allocate shared req<->app link");
return NXT_ERROR;
@@ -4671,7 +4696,7 @@ nxt_router_port_post_select(nxt_task_t *task, nxt_port_select_state_t *state)
res = nxt_router_start_app_process(task, app);
if (nxt_slow_path(res != NXT_OK)) {
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, app, req_app_link,
"Failed to start app process");
return NXT_ERROR;
@@ -4774,8 +4799,7 @@ static void
nxt_router_app_prepare_request(nxt_task_t *task,
nxt_request_app_link_t *req_app_link)
{
- nxt_fd_t fd;
- nxt_buf_t *buf, *body;
+ nxt_buf_t *buf;
nxt_int_t res;
nxt_port_t *port, *c_port, *reply_port;
nxt_apr_action_t apr_action;
@@ -4787,14 +4811,15 @@ nxt_router_app_prepare_request(nxt_task_t *task,
apr_action = NXT_APR_REQUEST_FAILED;
- c_port = nxt_process_connected_port_find(port->process, reply_port->pid,
- reply_port->id);
+ c_port = nxt_process_connected_port_find(port->process, reply_port);
+
if (nxt_slow_path(c_port != reply_port)) {
res = nxt_port_send_port(task, port, reply_port, 0);
if (nxt_slow_path(res != NXT_OK)) {
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, port->app, req_app_link,
"Failed to send reply port to application");
+
goto release_port;
}
@@ -4805,7 +4830,7 @@ nxt_router_app_prepare_request(nxt_task_t *task,
nxt_app_msg_prefix[port->app->type]);
if (nxt_slow_path(buf == NULL)) {
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, port->app, req_app_link,
"Failed to prepare message for application");
goto release_port;
}
@@ -4829,31 +4854,29 @@ nxt_router_app_prepare_request(nxt_task_t *task,
&req_app_link->msg_info.tracking,
req_app_link->stream);
if (nxt_slow_path(res != NXT_OK)) {
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, port->app, req_app_link,
"Failed to get tracking area");
goto release_port;
}
- body = req_app_link->request->body;
- fd = (body != NULL && nxt_buf_is_file(body)) ? body->file->fd : -1;
+ if (req_app_link->body_fd != -1) {
+ nxt_debug(task, "stream #%uD: send body fd %d", req_app_link->stream,
+ req_app_link->body_fd);
+
+ lseek(req_app_link->body_fd, 0, SEEK_SET);
+ }
- res = nxt_port_socket_twrite(task, port,
- NXT_PORT_MSG_REQ_HEADERS
- | NXT_PORT_MSG_CLOSE_FD,
- fd,
+ res = nxt_port_socket_twrite(task, port, NXT_PORT_MSG_REQ_HEADERS,
+ req_app_link->body_fd,
req_app_link->stream, reply_port->id, buf,
&req_app_link->msg_info.tracking);
if (nxt_slow_path(res != NXT_OK)) {
- nxt_request_app_link_error(req_app_link, 500,
+ nxt_request_app_link_error(task, port->app, req_app_link,
"Failed to send message to application");
goto release_port;
}
- if (fd != -1) {
- body->file->fd = -1;
- }
-
release_port:
nxt_router_app_port_release(task, port, apr_action);
@@ -5178,10 +5201,6 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
}
}
- if (r->body != NULL && nxt_buf_is_file(r->body)) {
- lseek(r->body->file->fd, 0, SEEK_SET);
- }
-
return out;
}
diff --git a/src/nxt_router_request.h b/src/nxt_router_request.h
index c3d5767e..a38980ee 100644
--- a/src/nxt_router_request.h
+++ b/src/nxt_router_request.h
@@ -50,6 +50,7 @@ struct nxt_request_app_link_s {
nxt_http_request_t *request;
nxt_msg_info_t msg_info;
nxt_request_rpc_data_t *req_rpc_data;
+ nxt_fd_t body_fd;
nxt_nsec_t res_time;
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index f6d80ccb..bcd156ee 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -1205,7 +1205,7 @@ nxt_runtime_listen_sockets_create(nxt_task_t *task, nxt_runtime_t *rt)
}
}
- if (nxt_listen_socket_create(task, &curr[c], 0) != NXT_OK) {
+ if (nxt_listen_socket_create(task, rt->mem_pool, &curr[c]) != NXT_OK) {
return NXT_ERROR;
}
diff --git a/src/nxt_socket.c b/src/nxt_socket.c
index 2a809184..cc3d7378 100644
--- a/src/nxt_socket.c
+++ b/src/nxt_socket.c
@@ -184,11 +184,8 @@ nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
nxt_int_t
-nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa,
- nxt_bool_t test)
+nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
{
- nxt_err_t err;
-
nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
nxt_sockaddr_start(sa));
@@ -196,14 +193,8 @@ nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa,
return NXT_OK;
}
- err = nxt_socket_errno;
-
- if (err == NXT_EADDRINUSE && test) {
- return NXT_DECLINED;
- }
-
nxt_alert(task, "bind(%d, %*s) failed %E",
- s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
+ s, (size_t) sa->length, nxt_sockaddr_start(sa), nxt_socket_errno);
return NXT_ERROR;
}
diff --git a/src/nxt_socket.h b/src/nxt_socket.h
index 6a450f83..e39d8e4d 100644
--- a/src/nxt_socket.h
+++ b/src/nxt_socket.h
@@ -101,7 +101,7 @@ NXT_EXPORT nxt_int_t nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s,
NXT_EXPORT nxt_int_t nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s,
nxt_uint_t level, nxt_uint_t sockopt, int val);
NXT_EXPORT nxt_int_t nxt_socket_bind(nxt_task_t *task, nxt_socket_t s,
- nxt_sockaddr_t *sa, nxt_bool_t test);
+ nxt_sockaddr_t *sa);
NXT_EXPORT nxt_int_t nxt_socket_connect(nxt_task_t *task, nxt_socket_t s,
nxt_sockaddr_t *sa);
NXT_EXPORT void nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s,
diff --git a/src/nxt_string.c b/src/nxt_string.c
index d567883f..54f96abc 100644
--- a/src/nxt_string.c
+++ b/src/nxt_string.c
@@ -110,6 +110,24 @@ nxt_memcpy_upcase(u_char *dst, const u_char *src, size_t length)
u_char *
+nxt_cpystr(u_char *dst, const u_char *src)
+{
+ for ( ;; ) {
+ *dst = *src;
+
+ if (*dst == '\0') {
+ break;
+ }
+
+ dst++;
+ src++;
+ }
+
+ return dst;
+}
+
+
+u_char *
nxt_cpystrn(u_char *dst, const u_char *src, size_t length)
{
if (length == 0) {
@@ -457,34 +475,54 @@ nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
}
+static const uint8_t nxt_hex2int[256]
+ nxt_aligned(32) =
+{
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 16, 16, 16, 16, 16,
+ 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+};
+
+
+static const uint32_t nxt_uri_escape[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+};
+
+
u_char *
nxt_decode_uri(u_char *dst, u_char *src, size_t length)
{
u_char *end, ch;
uint8_t d0, d1;
- static const uint8_t hex[256]
- nxt_aligned(32) =
- {
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 16, 16, 16, 16, 16,
- 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- };
-
- nxt_prefetch(&hex['0']);
+ nxt_prefetch(&nxt_hex2int['0']);
end = src + length;
@@ -496,8 +534,8 @@ nxt_decode_uri(u_char *dst, u_char *src, size_t length)
return NULL;
}
- d0 = hex[*src++];
- d1 = hex[*src++];
+ d0 = nxt_hex2int[*src++];
+ d1 = nxt_hex2int[*src++];
if (nxt_slow_path((d0 | d1) >= 16)) {
return NULL;
@@ -521,26 +559,6 @@ nxt_encode_uri(u_char *dst, u_char *src, size_t length)
static const u_char hex[16] = "0123456789ABCDEF";
- /* " ", "#", "%", "?", %00-%1F, %7F-%FF */
-
- static const uint32_t escape[] = {
- 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
-
- /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
- 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
-
- /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
-
- /* ~}| {zyx wvut srqp onml kjih gfed cba` */
- 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
-
- 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
- 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
- 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
- 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
- };
-
end = src + length;
if (dst == NULL) {
@@ -551,7 +569,7 @@ nxt_encode_uri(u_char *dst, u_char *src, size_t length)
while (src < end) {
- if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
+ if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
n++;
}
@@ -563,7 +581,7 @@ nxt_encode_uri(u_char *dst, u_char *src, size_t length)
while (src < end) {
- if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
+ if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '%';
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
@@ -577,3 +595,112 @@ nxt_encode_uri(u_char *dst, u_char *src, size_t length)
return (uintptr_t) dst;
}
+
+
+uintptr_t
+nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length)
+{
+ u_char *reserved, *end, ch;
+ nxt_uint_t n;
+
+ static const u_char hex[16] = "0123456789ABCDEF";
+
+ reserved = (u_char *) "?#\0";
+
+ end = src + length;
+
+ if (dst == NULL) {
+
+ /* Find the number of the characters to be escaped. */
+
+ n = 0;
+
+ while (src < end) {
+ ch = *src++;
+
+ if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
+ if (ch == reserved[0]) {
+ reserved++;
+ continue;
+ }
+
+ if (ch == reserved[1]) {
+ reserved += 2;
+ continue;
+ }
+
+ n++;
+ }
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (src < end) {
+ ch = *src++;
+
+ if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
+ if (ch == reserved[0]) {
+ reserved++;
+
+ } else if (ch == reserved[1]) {
+ reserved += 2;
+
+ } else {
+ *dst++ = '%';
+ *dst++ = hex[ch >> 4];
+ *dst++ = hex[ch & 0xf];
+ continue;
+ }
+ }
+
+ *dst++ = ch;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
+nxt_bool_t
+nxt_is_complex_uri_encoded(u_char *src, size_t length)
+{
+ u_char *reserved, *end, ch;
+ uint8_t d0, d1;
+
+ reserved = (u_char *) "?#\0";
+
+ for (end = src + length; src < end; src++) {
+ ch = *src;
+
+ if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
+ if (ch == '%') {
+ if (end - src < 2) {
+ return 0;
+ }
+
+ d0 = nxt_hex2int[*++src];
+ d1 = nxt_hex2int[*++src];
+
+ if ((d0 | d1) >= 16) {
+ return 0;
+ }
+
+ continue;
+ }
+
+ if (ch == reserved[0]) {
+ reserved++;
+ continue;
+ }
+
+ if (ch == reserved[1]) {
+ reserved += 2;
+ continue;
+ }
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
diff --git a/src/nxt_string.h b/src/nxt_string.h
index de498048..7863c60e 100644
--- a/src/nxt_string.h
+++ b/src/nxt_string.h
@@ -20,6 +20,10 @@ nxt_upcase(c) \
nxt_isdigit(c) \
((u_char) ((c) - '0') <= 9)
+#define \
+nxt_strtod(s, endptr) \
+ strtod((char *) s, (char **) endptr)
+
#define \
nxt_strlen(s) \
@@ -83,6 +87,7 @@ nxt_strncmp(s1, s2, length) \
strncmp((char *) s1, (char *) s2, length)
+NXT_EXPORT u_char *nxt_cpystr(u_char *dst, const u_char *src);
NXT_EXPORT u_char *nxt_cpystrn(u_char *dst, const u_char *src, size_t length);
NXT_EXPORT nxt_int_t nxt_strcasecmp(const u_char *s1, const u_char *s2);
NXT_EXPORT nxt_int_t nxt_strncasecmp(const u_char *s1, const u_char *s2,
@@ -170,6 +175,9 @@ NXT_EXPORT nxt_bool_t nxt_strvers_match(u_char *version, u_char *prefix,
NXT_EXPORT u_char *nxt_decode_uri(u_char *dst, u_char *src, size_t length);
NXT_EXPORT uintptr_t nxt_encode_uri(u_char *dst, u_char *src, size_t length);
+NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src,
+ size_t length);
+NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length);
#endif /* _NXT_STRING_H_INCLUDED_ */
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index 7a4124fb..67244420 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -767,6 +767,16 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id,
case _NXT_PORT_MSG_CHANGE_FILE:
nxt_unit_debug(ctx, "#%"PRIu32": change_file: fd %d",
port_msg->stream, recv_msg.fd);
+
+ if (dup2(recv_msg.fd, lib->log_fd) == -1) {
+ nxt_unit_alert(ctx, "#%"PRIu32": dup2(%d, %d) failed: %s (%d)",
+ port_msg->stream, recv_msg.fd, lib->log_fd,
+ strerror(errno), errno);
+
+ goto fail;
+ }
+
+ rc = NXT_UNIT_OK;
break;
case _NXT_PORT_MSG_MMAP:
@@ -971,8 +981,10 @@ nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
req_impl->websocket = 0;
nxt_unit_debug(ctx, "#%"PRIu32": %.*s %.*s (%d)", recv_msg->stream,
- (int) r->method_length, nxt_unit_sptr_get(&r->method),
- (int) r->target_length, nxt_unit_sptr_get(&r->target),
+ (int) r->method_length,
+ (char *) nxt_unit_sptr_get(&r->method),
+ (int) r->target_length,
+ (char *) nxt_unit_sptr_get(&r->target),
(int) r->content_length);
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
@@ -2084,7 +2096,7 @@ nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream,
nxt_unit_debug(ctx, "process %d allocated_chunks %d",
mmap_buf->process->pid,
- mmap_buf->process->outgoing.allocated_chunks);
+ (int) mmap_buf->process->outgoing.allocated_chunks);
} else {
if (nxt_slow_path(mmap_buf->plain_ptr == NULL
@@ -2623,7 +2635,6 @@ nxt_unit_buf_read(nxt_unit_buf_t **b, uint64_t *len, void *dst, size_t size)
void
nxt_unit_request_done(nxt_unit_request_info_t *req, int rc)
{
- ssize_t res;
uint32_t size;
nxt_port_msg_t msg;
nxt_unit_impl_t *lib;
@@ -2678,12 +2689,8 @@ skip_response_send:
msg.mf = 0;
msg.tracking = 0;
- res = lib->callbacks.port_send(req->ctx, &req->response_port,
- &msg, sizeof(msg), NULL, 0);
- if (nxt_slow_path(res != sizeof(msg))) {
- nxt_unit_req_alert(req, "last message send failed: %s (%d)",
- strerror(errno), errno);
- }
+ (void) lib->callbacks.port_send(req->ctx, &req->response_port,
+ &msg, sizeof(msg), NULL, 0);
nxt_unit_request_info_release(req);
}
@@ -2972,7 +2979,7 @@ unlock:
nxt_unit_debug(ctx, "process %d allocated_chunks %d",
process->pid,
- process->outgoing.allocated_chunks);
+ (int) process->outgoing.allocated_chunks);
pthread_mutex_unlock(&process->outgoing.mutex);
@@ -3001,9 +3008,6 @@ nxt_unit_send_oosm(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
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;
}
@@ -3027,6 +3031,7 @@ nxt_unit_wait_shm_ack(nxt_unit_ctx_t *ctx)
}
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);
@@ -3282,9 +3287,6 @@ nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, int fd)
res = lib->callbacks.port_send(ctx, port_id, &msg, sizeof(msg),
&cmsg, sizeof(cmsg));
if (nxt_slow_path(res != sizeof(msg))) {
- nxt_unit_warn(ctx, "failed to send shm to %d: %s (%d)",
- (int) port_id->pid, strerror(errno), errno);
-
return NXT_UNIT_ERROR;
}
@@ -3691,7 +3693,7 @@ nxt_unit_mmap_release(nxt_unit_ctx_t *ctx,
nxt_unit_debug(ctx, "process %d allocated_chunks %d",
process->pid,
- process->outgoing.allocated_chunks);
+ (int) process->outgoing.allocated_chunks);
}
if (hdr->dst_pid == lib->pid
@@ -3727,9 +3729,6 @@ nxt_unit_send_shm_ack(nxt_unit_ctx_t *ctx, pid_t pid)
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;
}
@@ -3882,6 +3881,10 @@ nxt_unit_run(nxt_unit_ctx_t *ctx)
while (nxt_fast_path(lib->online)) {
rc = nxt_unit_run_once(ctx);
+
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ break;
+ }
}
return rc;
@@ -4279,16 +4282,38 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
int rc;
nxt_unit_impl_t *lib;
nxt_unit_process_t *process;
- nxt_unit_port_impl_t *new_port;
+ nxt_unit_port_impl_t *new_port, *old_port;
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ pthread_mutex_lock(&lib->mutex);
+
+ old_port = nxt_unit_port_hash_find(&lib->ports, &port->id, 0);
+
+ if (nxt_slow_path(old_port != NULL)) {
+ nxt_unit_debug(ctx, "add_port: duplicate %d,%d in_fd %d out_fd %d",
+ port->id.pid, port->id.id,
+ port->in_fd, port->out_fd);
+
+ if (port->in_fd != -1) {
+ close(port->in_fd);
+ port->in_fd = -1;
+ }
+
+ if (port->out_fd != -1) {
+ close(port->out_fd);
+ port->out_fd = -1;
+ }
+
+ pthread_mutex_unlock(&lib->mutex);
+
+ return NXT_UNIT_OK;
+ }
+
nxt_unit_debug(ctx, "add_port: %d,%d in_fd %d out_fd %d",
port->id.pid, port->id.id,
port->in_fd, port->out_fd);
- pthread_mutex_lock(&lib->mutex);
-
process = nxt_unit_process_get(ctx, port->id.pid);
if (nxt_slow_path(process == NULL)) {
rc = NXT_UNIT_ERROR;
@@ -4309,6 +4334,9 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
rc = nxt_unit_port_hash_add(&lib->ports, &new_port->port);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ nxt_unit_alert(ctx, "add_port: %d,%d hash_add failed",
+ port->id.pid, port->id.id);
+
goto unlock;
}
@@ -4540,14 +4568,24 @@ nxt_unit_port_send(nxt_unit_ctx_t *ctx, int fd,
msg.msg_control = (void *) oob;
msg.msg_controllen = oob_size;
+retry:
+
res = sendmsg(fd, &msg, 0);
if (nxt_slow_path(res == -1)) {
- nxt_unit_warn(ctx, "port_send(%d, %d) failed: %s (%d)",
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ /*
+ * FIXME: This should be "alert" after router graceful shutdown
+ * implementation.
+ */
+ nxt_unit_warn(ctx, "sendmsg(%d, %d) failed: %s (%d)",
fd, (int) buf_size, strerror(errno), errno);
} else {
- nxt_unit_debug(ctx, "port_send(%d, %d): %d", fd, (int) buf_size,
+ nxt_unit_debug(ctx, "sendmsg(%d, %d): %d", fd, (int) buf_size,
(int) res);
}
@@ -4617,14 +4655,20 @@ nxt_unit_port_recv(nxt_unit_ctx_t *ctx, int fd, void *buf, size_t buf_size,
msg.msg_control = oob;
msg.msg_controllen = oob_size;
+retry:
+
res = recvmsg(fd, &msg, 0);
if (nxt_slow_path(res == -1)) {
- nxt_unit_warn(ctx, "port_recv(%d) failed: %s (%d)",
- fd, strerror(errno), errno);
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ nxt_unit_alert(ctx, "recvmsg(%d) failed: %s (%d)",
+ fd, strerror(errno), errno);
} else {
- nxt_unit_debug(ctx, "port_recv(%d): %d", fd, (int) res);
+ nxt_unit_debug(ctx, "recvmsg(%d): %d", fd, (int) res);
}
return res;
diff --git a/src/nxt_unit.h b/src/nxt_unit.h
index 900f3ac2..596dd8b6 100644
--- a/src/nxt_unit.h
+++ b/src/nxt_unit.h
@@ -356,10 +356,29 @@ int nxt_unit_websocket_retain(nxt_unit_websocket_frame_t *ws);
void nxt_unit_websocket_done(nxt_unit_websocket_frame_t *ws);
-void nxt_unit_log(nxt_unit_ctx_t *ctx, int level, const char* fmt, ...);
+#if defined __has_attribute
+
+#if __has_attribute(format)
+
+#define NXT_ATTR_FORMAT __attribute__((format(printf, 3, 4)))
+
+#endif
+
+#endif
+
+
+#if !defined(NXT_ATTR_FORMAT)
+
+#define NXT_ATTR_FORMAT
+
+#endif
+
+
+void nxt_unit_log(nxt_unit_ctx_t *ctx, int level, const char* fmt, ...)
+ NXT_ATTR_FORMAT;
void nxt_unit_req_log(nxt_unit_request_info_t *req, int level,
- const char* fmt, ...);
+ const char* fmt, ...) NXT_ATTR_FORMAT;
#if (NXT_DEBUG)
diff --git a/src/nxt_upstream_round_robin.c b/src/nxt_upstream_round_robin.c
index fd76ecb5..31e2f48a 100644
--- a/src/nxt_upstream_round_robin.c
+++ b/src/nxt_upstream_round_robin.c
@@ -4,6 +4,7 @@
* Copyright (C) NGINX, Inc.
*/
+#include <math.h>
#include <nxt_router.h>
#include <nxt_http.h>
#include <nxt_upstream.h>
@@ -38,34 +39,47 @@ static const nxt_upstream_server_proto_t nxt_upstream_round_robin_proto = {
};
-static nxt_conf_map_t nxt_upstream_round_robin_server_conf[] = {
- {
- nxt_string("weight"),
- NXT_CONF_MAP_INT32,
- offsetof(nxt_upstream_round_robin_server_t, weight),
- },
-};
-
-
nxt_int_t
nxt_upstream_round_robin_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *upstream_conf, nxt_upstream_t *upstream)
{
+ double total, k, w;
size_t size;
- uint32_t i, n, next;
+ uint32_t i, n, next, wt;
nxt_mp_t *mp;
nxt_str_t name;
nxt_sockaddr_t *sa;
- nxt_conf_value_t *servers_conf, *srvcf;
+ nxt_conf_value_t *servers_conf, *srvcf, *wtcf;
nxt_upstream_round_robin_t *urr;
static nxt_str_t servers = nxt_string("servers");
+ static nxt_str_t weight = nxt_string("weight");
mp = tmcf->router_conf->mem_pool;
servers_conf = nxt_conf_get_object_member(upstream_conf, &servers, NULL);
n = nxt_conf_object_members_count(servers_conf);
+ total = 0.0;
+ next = 0;
+
+ for (i = 0; i < n; i++) {
+ srvcf = nxt_conf_next_object_member(servers_conf, &name, &next);
+ wtcf = nxt_conf_get_object_member(srvcf, &weight, NULL);
+ w = (wtcf != NULL) ? nxt_conf_get_number(wtcf) : 1;
+ total += w;
+ }
+
+ /*
+ * This prevents overflow of int32_t
+ * in nxt_upstream_round_robin_server_get().
+ */
+ k = (total == 0) ? 0 : (NXT_INT32_T_MAX / 2) / total;
+
+ if (isinf(k)) {
+ k = 1;
+ }
+
size = sizeof(nxt_upstream_round_robin_t)
+ n * sizeof(nxt_upstream_round_robin_server_t);
@@ -88,14 +102,14 @@ nxt_upstream_round_robin_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
sa->type = SOCK_STREAM;
urr->server[i].sockaddr = sa;
- urr->server[i].weight = 1;
urr->server[i].protocol = NXT_HTTP_PROTO_H1;
- nxt_conf_map_object(mp, srvcf, nxt_upstream_round_robin_server_conf,
- nxt_nitems(nxt_upstream_round_robin_server_conf),
- &urr->server[i]);
+ wtcf = nxt_conf_get_object_member(srvcf, &weight, NULL);
+ w = (wtcf != NULL) ? k * nxt_conf_get_number(wtcf) : k;
+ wt = (w > 1 || w == 0) ? round(w) : 1;
- urr->server[i].effective_weight = urr->server[i].weight;
+ urr->server[i].weight = wt;
+ urr->server[i].effective_weight = wt;
}
upstream->proto = &nxt_upstream_round_robin_proto;
@@ -177,7 +191,7 @@ nxt_upstream_round_robin_server_get(nxt_task_t *task, nxt_upstream_server_t *us)
}
}
- if (best == NULL) {
+ if (best == NULL || total == 0) {
us->state->error(task, us);
return;
}
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index 16159b5b..548e6daa 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -166,7 +166,7 @@ nxt_perl_psgi_io_error_write(PerlInterpreter *my_perl,
nxt_perl_psgi_input_t *input;
input = (nxt_perl_psgi_input_t *) arg->ctx;
- nxt_unit_req_error(input->req, "Perl: %s", vbuf);
+ nxt_unit_req_error(input->req, "Perl: %s", (const char*) vbuf);
return (long) length;
}
diff --git a/src/test/nxt_clone_test.c b/src/test/nxt_clone_test.c
index 15d36557..64b9ddea 100644
--- a/src/test/nxt_clone_test.c
+++ b/src/test/nxt_clone_test.c
@@ -588,13 +588,13 @@ nxt_clone_test_parse_map(nxt_task_t *task, nxt_str_t *map_str,
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);
+ map->map[i].host = nxt_conf_get_number(value);
value = nxt_conf_get_object_member(obj, &cont_name, NULL);
- map->map[i].container = nxt_conf_get_integer(value);
+ map->map[i].container = nxt_conf_get_number(value);
value = nxt_conf_get_object_member(obj, &size_name, NULL);
- map->map[i].size = nxt_conf_get_integer(value);
+ map->map[i].size = nxt_conf_get_number(value);
}
return NXT_OK;
diff --git a/src/test/nxt_http_parse_test.c b/src/test/nxt_http_parse_test.c
index 8dcbc061..9630b21c 100644
--- a/src/test/nxt_http_parse_test.c
+++ b/src/test/nxt_http_parse_test.c
@@ -510,7 +510,7 @@ static nxt_str_t nxt_http_test_big_request = nxt_string(
nxt_int_t
nxt_http_parse_test(nxt_thread_t *thr)
{
- nxt_mp_t *mp, *mp_temp;
+ nxt_mp_t *mp_temp;
nxt_int_t rc;
nxt_uint_t i, colls, lvl_colls;
nxt_lvlhsh_t hash;
@@ -519,12 +519,7 @@ nxt_http_parse_test(nxt_thread_t *thr)
nxt_thread_time_update(thr);
- mp = nxt_mp_create(1024, 128, 256, 32);
- if (mp == NULL) {
- return NXT_ERROR;
- }
-
- rc = nxt_http_fields_hash(&nxt_http_test_fields_hash, mp,
+ rc = nxt_http_fields_hash(&nxt_http_test_fields_hash,
nxt_http_test_fields,
nxt_nitems(nxt_http_test_fields));
if (rc != NXT_OK) {
@@ -569,14 +564,14 @@ nxt_http_parse_test(nxt_thread_t *thr)
nxt_memzero(&hash, sizeof(nxt_lvlhsh_t));
- colls = nxt_http_fields_hash_collisions(&hash, mp,
+ colls = nxt_http_fields_hash_collisions(&hash,
nxt_http_test_bench_fields,
nxt_nitems(nxt_http_test_bench_fields),
0);
nxt_memzero(&hash, sizeof(nxt_lvlhsh_t));
- lvl_colls = nxt_http_fields_hash_collisions(&hash, mp,
+ lvl_colls = nxt_http_fields_hash_collisions(&hash,
nxt_http_test_bench_fields,
nxt_nitems(nxt_http_test_bench_fields),
1);
@@ -587,7 +582,7 @@ nxt_http_parse_test(nxt_thread_t *thr)
nxt_memzero(&hash, sizeof(nxt_lvlhsh_t));
- rc = nxt_http_fields_hash(&hash, mp, nxt_http_test_bench_fields,
+ rc = nxt_http_fields_hash(&hash, nxt_http_test_bench_fields,
nxt_nitems(nxt_http_test_bench_fields));
if (rc != NXT_OK) {
return NXT_ERROR;
@@ -607,8 +602,6 @@ nxt_http_parse_test(nxt_thread_t *thr)
return NXT_ERROR;
}
- nxt_mp_destroy(mp);
-
return NXT_OK;
}
diff --git a/test/python/delayed/wsgi.py b/test/python/delayed/wsgi.py
index d25e2765..3eb5a6f8 100644
--- a/test/python/delayed/wsgi.py
+++ b/test/python/delayed/wsgi.py
@@ -11,6 +11,7 @@ def application(environ, start_response):
write = start_response('200', [('Content-Length', str(len(body)))])
if not body:
+ time.sleep(delay)
return []
step = int(len(body) / parts)
diff --git a/test/python/log_body/wsgi.py b/test/python/log_body/wsgi.py
index 9dcb1b0c..0ec07a68 100644
--- a/test/python/log_body/wsgi.py
+++ b/test/python/log_body/wsgi.py
@@ -2,7 +2,7 @@ def application(environ, start_response):
content_length = int(environ.get('CONTENT_LENGTH', 0))
body = bytes(environ['wsgi.input'].read(content_length))
- environ['wsgi.errors'].write(body)
+ environ['wsgi.errors'].write(body.decode())
environ['wsgi.errors'].flush()
start_response('200', [('Content-Length', '0')])
diff --git a/test/python/upstreams/0/wsgi.py b/test/python/upstreams/0/wsgi.py
deleted file mode 100644
index 2c88979b..00000000
--- a/test/python/upstreams/0/wsgi.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import time
-
-def application(env, start_response):
- delay = int(env.get('HTTP_X_DELAY', 0))
-
- start_response('200', [('Content-Length', '0'), ('X-Upstream', '0')])
- time.sleep(delay)
- return []
diff --git a/test/python/upstreams/1/wsgi.py b/test/python/upstreams/1/wsgi.py
deleted file mode 100644
index 5077bdb1..00000000
--- a/test/python/upstreams/1/wsgi.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import time
-
-def application(env, start_response):
- delay = int(env.get('HTTP_X_DELAY', 0))
-
- start_response('200', [('Content-Length', '0'), ('X-Upstream', '1')])
- time.sleep(delay)
- return []
diff --git a/test/python/upstreams/2/wsgi.py b/test/python/upstreams/2/wsgi.py
deleted file mode 100644
index bb0ce797..00000000
--- a/test/python/upstreams/2/wsgi.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import time
-
-def application(env, start_response):
- delay = int(env.get('HTTP_X_DELAY', 0))
-
- start_response('200', [('Content-Length', '0'), ('X-Upstream', '2')])
- time.sleep(delay)
- return []
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 186e037d..daba874b 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -83,6 +83,86 @@ class TestConfiguration(TestControl):
'unicode number',
)
+ def test_json_utf8_bom(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ b"""\xEF\xBB\xBF
+ {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi"
+ }
+ }
+ """,
+ 'applications',
+ ),
+ 'UTF-8 BOM',
+ )
+
+ def test_json_comment_single_line(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ b"""
+ // this is bridge
+ {
+ "//app": {
+ "type": "python", // end line
+ "processes": {"spare": 0},
+ // inside of block
+ "path": "/app",
+ "module": "wsgi"
+ }
+ // double //
+ }
+ // end of json \xEF\t
+ """,
+ 'applications',
+ ),
+ 'single line comments',
+ )
+
+ def test_json_comment_multi_line(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ b"""
+ /* this is bridge */
+ {
+ "/*app": {
+ /**
+ * multiple lines
+ **/
+ "type": "python",
+ "processes": /* inline */ {"spare": 0},
+ "path": "/app",
+ "module": "wsgi"
+ /*
+ // end of block */
+ }
+ /* blah * / blah /* blah */
+ }
+ /* end of json \xEF\t\b */
+ """,
+ 'applications',
+ ),
+ 'multi line comments',
+ )
+
+ def test_json_comment_invalid(self):
+ self.assertIn('error', self.conf(b'/{}', 'applications'), 'slash')
+ self.assertIn('error', self.conf(b'//{}', 'applications'), 'comment')
+ self.assertIn('error', self.conf(b'{} /', 'applications'), 'slash end')
+ self.assertIn(
+ 'error', self.conf(b'/*{}', 'applications'), 'slash star'
+ )
+ self.assertIn(
+ 'error', self.conf(b'{} /*', 'applications'), 'slash star end'
+ )
+
def test_applications_open_brace(self):
self.assertIn('error', self.conf('{', 'applications'), 'open brace')
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 42429be7..c9d4ba77 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -89,6 +89,7 @@ class TestGoApplication(TestApplicationGo):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -96,12 +97,13 @@ class TestGoApplication(TestApplicationGo):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -109,10 +111,10 @@ class TestGoApplication(TestApplicationGo):
'Connection': 'close',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_go_application_cookies(self):
self.load('cookies')
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 9d873d6b..7bd351a4 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -1085,6 +1085,7 @@ class TestJavaApplication(TestApplicationJava):
self.assertEqual(self.post()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Connection': 'keep-alive',
@@ -1092,12 +1093,13 @@ class TestJavaApplication(TestApplicationJava):
'Host': 'localhost',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Connection': 'close',
@@ -1105,10 +1107,10 @@ class TestJavaApplication(TestApplicationJava):
'Host': 'localhost',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_java_application_http_10(self):
self.load('empty')
diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py
index d75ee3a6..7ea04620 100644
--- a/test/test_java_websockets.py
+++ b/test/test_java_websockets.py
@@ -22,11 +22,11 @@ class TestJavaWebsockets(TestApplicationJava):
)
self.skip_alerts.extend(
- [r'last message send failed', r'socket close\(\d+\) failed']
+ [r'socket close\(\d+\) failed']
)
def close_connection(self, sock):
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
@@ -441,12 +441,12 @@ class TestJavaWebsockets(TestApplicationJava):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(sock, self.ws.OP_PONG, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_7')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_7')
# 2_8
self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_8')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_8')
# 2_9
@@ -512,7 +512,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_2')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_2')
sock.close()
# 3_3
@@ -530,7 +530,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_3')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_3')
sock.close()
# 3_4
@@ -548,7 +548,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_4')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_4')
sock.close()
# 3_5
@@ -734,7 +734,7 @@ class TestJavaWebsockets(TestApplicationJava):
# 5_4
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_4')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_4')
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
frame = self.ws.frame_read(sock)
@@ -771,7 +771,7 @@ class TestJavaWebsockets(TestApplicationJava):
ping_payload = 'ping payload'
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_7')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_7')
self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
@@ -955,7 +955,7 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock)
self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_20')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_20')
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
self.check_frame(
@@ -1088,7 +1088,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_PING, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1100,7 +1100,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1113,7 +1113,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1128,7 +1128,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.recvall(sock, read_timeout=1)
self.ws.frame_write(sock, self.ws.OP_PING, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
diff --git a/test/test_node_application.py b/test/test_node_application.py
index b80d17d3..174af15d 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -112,6 +112,7 @@ class TestNodeApplication(TestApplicationNode):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -119,12 +120,13 @@ class TestNodeApplication(TestApplicationNode):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -132,10 +134,10 @@ class TestNodeApplication(TestApplicationNode):
'Content-Type': 'text/html',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_node_application_write_buffer(self):
self.load('write_buffer')
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
index bb189552..4ce727db 100644
--- a/test/test_node_websockets.py
+++ b/test/test_node_websockets.py
@@ -22,11 +22,11 @@ class TestNodeWebsockets(TestApplicationNode):
)
self.skip_alerts.extend(
- [r'last message send failed', r'socket close\(\d+\) failed']
+ [r'socket close\(\d+\) failed']
)
def close_connection(self, sock):
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
@@ -460,12 +460,12 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(sock, self.ws.OP_PONG, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_7')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_7')
# 2_8
self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_8')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_8')
# 2_9
@@ -531,7 +531,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_2')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_2')
sock.close()
# 3_3
@@ -549,7 +549,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_3')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_3')
sock.close()
# 3_4
@@ -567,7 +567,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_4')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_4')
sock.close()
# 3_5
@@ -753,7 +753,7 @@ class TestNodeWebsockets(TestApplicationNode):
# 5_4
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_4')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_4')
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
frame = self.ws.frame_read(sock)
@@ -790,7 +790,7 @@ class TestNodeWebsockets(TestApplicationNode):
ping_payload = 'ping payload'
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_7')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_7')
self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
@@ -974,7 +974,7 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock)
self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_20')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_20')
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
self.check_frame(
@@ -1107,7 +1107,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_PING, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1119,7 +1119,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1132,7 +1132,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
@@ -1147,7 +1147,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.recvall(sock, read_timeout=1)
self.ws.frame_write(sock, self.ws.OP_PING, '')
- self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+ self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc')
sock.close()
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index a4bac623..cc4eb915 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -197,6 +197,7 @@ class TestPerlApplication(TestApplicationPerl):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -204,12 +205,13 @@ class TestPerlApplication(TestApplicationPerl):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -217,10 +219,10 @@ class TestPerlApplication(TestApplicationPerl):
'Content-Type': 'text/html',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_perl_body_io_fake(self):
self.load('body_io_fake')
diff --git a/test/test_php_application.py b/test/test_php_application.py
index c3645a99..48e1e815 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -183,6 +183,7 @@ class TestPHPApplication(TestApplicationPHP):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -190,12 +191,13 @@ class TestPHPApplication(TestApplicationPHP):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -203,10 +205,10 @@ class TestPHPApplication(TestApplicationPHP):
'Content-Type': 'text/html',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_php_application_conditional(self):
self.load('conditional')
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 7ecff1b2..5fde3e00 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -37,9 +37,6 @@ class TestPHPBasic(TestControl):
'applications',
)
- def test_php_get_applications_prefix(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(
self.conf_get('applications'),
{
@@ -53,9 +50,6 @@ class TestPHPBasic(TestControl):
'applications prefix',
)
- def test_php_get_applications_prefix_2(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(
self.conf_get('applications/app'),
{
@@ -67,9 +61,6 @@ class TestPHPBasic(TestControl):
'applications prefix 2',
)
- def test_php_get_applications_prefix_3(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(self.conf_get('applications/app/type'), 'php', 'type')
self.assertEqual(
self.conf_get('applications/app/processes/spare'),
@@ -86,18 +77,12 @@ class TestPHPBasic(TestControl):
'listeners',
)
- def test_php_get_listeners_prefix(self):
- self.conf(self.conf_basic)
-
self.assertEqual(
self.conf_get('listeners'),
{"*:7080": {"pass": "applications/app"}},
'listeners prefix',
)
- def test_php_get_listeners_prefix_2(self):
- self.conf(self.conf_basic)
-
self.assertEqual(
self.conf_get('listeners/*:7080'),
{"pass": "applications/app"},
@@ -147,49 +132,24 @@ class TestPHPBasic(TestControl):
def test_php_delete(self):
self.conf(self.conf_basic)
- self.assertIn(
- 'error',
- self.conf_delete('applications/app'),
- 'delete app before listener',
- )
- self.assertIn(
- 'success', self.conf_delete('listeners/*:7080'), 'delete listener'
- )
- self.assertIn(
- 'success',
- self.conf_delete('applications/app'),
- 'delete app after listener',
- )
- self.assertIn(
- 'error', self.conf_delete('applications/app'), 'delete app again'
- )
+ self.assertIn('error', self.conf_delete('applications/app'))
+ self.assertIn('success', self.conf_delete('listeners/*:7080'))
+ self.assertIn('success', self.conf_delete('applications/app'))
+ self.assertIn('error', self.conf_delete('applications/app'))
def test_php_delete_blocks(self):
self.conf(self.conf_basic)
- self.assertIn(
- 'success',
- self.conf_delete('listeners'),
- 'listeners delete',
- )
-
- self.assertIn(
- 'success',
- self.conf_delete('applications'),
- 'applications delete',
- )
-
- self.assertIn(
- 'success',
- self.conf(self.conf_app, 'applications'),
- 'listeners restore',
- )
+ self.assertIn('success', self.conf_delete('listeners'))
+ self.assertIn('success', self.conf_delete('applications'))
+ self.assertIn('success', self.conf(self.conf_app, 'applications'))
self.assertIn(
'success',
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'),
'applications restore',
)
+
if __name__ == '__main__':
TestPHPBasic.main()
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 460cc804..8d435b48 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -187,6 +187,7 @@ class TestPythonApplication(TestApplicationPython):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -194,12 +195,13 @@ class TestPythonApplication(TestApplicationPython):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -207,10 +209,10 @@ class TestPythonApplication(TestApplicationPython):
'Content-Type': 'text/html',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_python_keepalive_reconfigure(self):
self.skip_alerts.extend(
@@ -340,14 +342,16 @@ class TestPythonApplication(TestApplicationPython):
self.assertEqual(self.get()['status'], 200, 'init')
- (resp, sock) = self.http(
+ (_, sock) = self.http(
b"""GET / HTTP/1.1
""",
start=True,
raw=True,
- read_timeout=5,
+ no_recv=True,
)
+ self.assertEqual(self.get()['status'], 200)
+
self.assertIn(
'success',
self.conf({"listeners": {}, "applications": {}}),
@@ -378,6 +382,38 @@ Connection: close
self.wait_for_record(r'At exit called\.'), 'atexit'
)
+ def test_python_process_switch(self):
+ self.load('delayed')
+
+ self.assertIn(
+ 'success',
+ self.conf('2', 'applications/delayed/processes'),
+ 'configure 2 processes',
+ )
+
+ self.get(headers={
+ 'Host': 'localhost',
+ 'Content-Length': '0',
+ 'X-Delay': '5',
+ 'Connection': 'close',
+ }, no_recv=True)
+
+ headers_delay_1 = {
+ 'Connection': 'close',
+ 'Host': 'localhost',
+ 'Content-Length': '0',
+ 'X-Delay': '1',
+ }
+
+ self.get(headers=headers_delay_1, no_recv=True)
+
+ time.sleep(0.5)
+
+ for _ in range(10):
+ self.get(headers=headers_delay_1, no_recv=True)
+
+ self.get(headers=headers_delay_1)
+
@unittest.skip('not yet')
def test_python_application_start_response_exit(self):
self.load('start_response_exit')
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index 67a5f548..3233fca2 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -19,17 +19,9 @@ class TestPythonBasic(TestControl):
}
def test_python_get_empty(self):
- self.assertEqual(
- self.conf_get(), {'listeners': {}, 'applications': {}}, 'empty'
- )
-
- def test_python_get_prefix_listeners(self):
- self.assertEqual(self.conf_get('listeners'), {}, 'listeners prefix')
-
- def test_python_get_prefix_applications(self):
- self.assertEqual(
- self.conf_get('applications'), {}, 'applications prefix'
- )
+ self.assertEqual(self.conf_get(), {'listeners': {}, 'applications': {}})
+ self.assertEqual(self.conf_get('listeners'), {})
+ self.assertEqual(self.conf_get('applications'), {})
def test_python_get_applications(self):
self.conf(self.conf_app, 'applications')
@@ -50,9 +42,6 @@ class TestPythonBasic(TestControl):
'applications',
)
- def test_python_get_applications_prefix(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(
self.conf_get('applications'),
{
@@ -66,9 +55,6 @@ class TestPythonBasic(TestControl):
'applications prefix',
)
- def test_python_get_applications_prefix_2(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(
self.conf_get('applications/app'),
{
@@ -80,9 +66,6 @@ class TestPythonBasic(TestControl):
'applications prefix 2',
)
- def test_python_get_applications_prefix_3(self):
- self.conf(self.conf_app, 'applications')
-
self.assertEqual(
self.conf_get('applications/app/type'), 'python', 'type'
)
@@ -99,18 +82,12 @@ class TestPythonBasic(TestControl):
'listeners',
)
- def test_python_get_listeners_prefix(self):
- self.conf(self.conf_basic)
-
self.assertEqual(
self.conf_get('listeners'),
{"*:7080": {"pass": "applications/app"}},
'listeners prefix',
)
- def test_python_get_listeners_prefix_2(self):
- self.conf(self.conf_basic)
-
self.assertEqual(
self.conf_get('listeners/*:7080'),
{"pass": "applications/app"},
@@ -160,44 +137,18 @@ class TestPythonBasic(TestControl):
def test_python_delete(self):
self.conf(self.conf_basic)
- self.assertIn(
- 'error',
- self.conf_delete('applications/app'),
- 'delete app before listener',
- )
- self.assertIn(
- 'success', self.conf_delete('listeners/*:7080'), 'delete listener'
- )
- self.assertIn(
- 'success',
- self.conf_delete('applications/app'),
- 'delete app after listener',
- )
- self.assertIn(
- 'error', self.conf_delete('applications/app'), 'delete app again'
- )
+ self.assertIn('error', self.conf_delete('applications/app'))
+ self.assertIn('success', self.conf_delete('listeners/*:7080'))
+ self.assertIn('success', self.conf_delete('applications/app'))
+ self.assertIn('error', self.conf_delete('applications/app'))
def test_python_delete_blocks(self):
self.conf(self.conf_basic)
- self.assertIn(
- 'success',
- self.conf_delete('listeners'),
- 'listeners delete',
- )
-
- self.assertIn(
- 'success',
- self.conf_delete('applications'),
- 'applications delete',
- )
-
- self.assertIn(
- 'success',
- self.conf(self.conf_app, 'applications'),
- 'listeners restore',
- )
+ self.assertIn('success', self.conf_delete('listeners'))
+ self.assertIn('success', self.conf_delete('applications'))
+ self.assertIn('success', self.conf(self.conf_app, 'applications'))
self.assertIn(
'success',
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'),
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index 52d8cacb..a2e6126c 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -8,6 +8,13 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonProcman(TestApplicationPython):
prerequisites = {'modules': ['python']}
+ def setUp(self):
+ super().setUp()
+
+ self.app_name = "app-" + self.testdir.split('/')[-1]
+ self.app_proc = 'applications/' + self.app_name + '/processes'
+ self.load('empty', self.app_name)
+
def pids_for_process(self):
time.sleep(0.2)
@@ -19,103 +26,20 @@ class TestPythonProcman(TestApplicationPython):
return pids
- def setUp(self):
- super().setUp()
-
- self.app_name = "app-" + self.testdir.split('/')[-1]
- self.load('empty', self.app_name)
-
- def test_python_processes_access(self):
- self.conf('1', 'applications/' + self.app_name + '/processes')
-
- self.assertIn(
- 'error',
- self.conf_get('/applications/' + self.app_name + '/processes/max'),
- 'max no access',
- )
- self.assertIn(
- 'error',
- self.conf_get(
- '/applications/' + self.app_name + '/processes/spare'
- ),
- 'spare no access',
- )
- self.assertIn(
- 'error',
- self.conf_get(
- '/applications/' + self.app_name + '/processes/idle_timeout'
- ),
- 'idle_timeout no access',
- )
-
- def test_python_processes_spare_negative(self):
- self.assertIn(
- 'error',
- self.conf(
- {"spare": -1}, 'applications/' + self.app_name + '/processes'
- ),
- 'negative spare',
- )
-
- def test_python_processes_max_negative(self):
- self.assertIn(
- 'error',
- self.conf(
- {"max": -1}, 'applications/' + self.app_name + '/processes'
- ),
- 'negative max',
- )
-
- def test_python_processes_idle_timeout_negative(self):
- self.assertIn(
- 'error',
- self.conf(
- {"idle_timeout": -1},
- 'applications/' + self.app_name + '/processes',
- ),
- 'negative idle_timeout',
- )
-
- def test_python_processes_spare_gt_max_default(self):
- self.assertIn(
- 'error',
- self.conf(
- {"spare": 2}, 'applications/' + self.app_name + '/processes'
- ),
- 'spare greater than max default',
- )
-
- def test_python_processes_spare_gt_max(self):
- self.assertIn(
- 'error',
- self.conf(
- {"spare": 2, "max": 1, "idle_timeout": 1},
- '/applications/' + self.app_name + '/processes',
- ),
- 'spare greater than max',
- )
+ def conf_proc(self, conf, path=None):
+ if path is None:
+ path = self.app_proc
- def test_python_processes_max_zero(self):
- self.assertIn(
- 'error',
- self.conf(
- {"spare": 0, "max": 0, "idle_timeout": 1},
- 'applications/' + self.app_name + '/processes',
- ),
- 'max 0',
- )
+ self.assertIn('success', self.conf(conf, path), 'configure processes')
def test_python_processes_idle_timeout_zero(self):
- self.conf(
- {"spare": 0, "max": 2, "idle_timeout": 0},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0})
self.get()
self.assertEqual(len(self.pids_for_process()), 0, 'idle timeout 0')
def test_python_prefork(self):
- self.conf('2', 'applications/' + self.app_name + '/processes')
+ self.conf_proc('2')
pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'prefork 2')
@@ -123,7 +47,7 @@ class TestPythonProcman(TestApplicationPython):
self.get()
self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 2')
- self.conf('4', 'applications/' + self.app_name + '/processes')
+ self.conf_proc('4')
pids = self.pids_for_process()
self.assertEqual(len(pids), 4, 'prefork 4')
@@ -135,21 +59,16 @@ class TestPythonProcman(TestApplicationPython):
@unittest.skip('not yet')
def test_python_prefork_same_processes(self):
- self.conf('2', 'applications/' + self.app_name + '/processes')
-
+ self.conf_proc('2')
pids = self.pids_for_process()
- self.conf('4', 'applications/' + self.app_name + '/processes')
-
+ self.conf_proc('4')
pids_new = self.pids_for_process()
self.assertTrue(pids.issubset(pids_new), 'prefork same processes')
def test_python_ondemand(self):
- self.conf(
- {"spare": 0, "max": 8, "idle_timeout": 1},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1})
self.assertEqual(len(self.pids_for_process()), 0, 'on-demand 0')
@@ -169,10 +88,7 @@ class TestPythonProcman(TestApplicationPython):
self.stop_all()
def test_python_scale_updown(self):
- self.conf(
- {"spare": 2, "max": 8, "idle_timeout": 1},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1})
pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'updown 2')
@@ -200,10 +116,7 @@ class TestPythonProcman(TestApplicationPython):
self.stop_all()
def test_python_reconfigure(self):
- self.conf(
- {"spare": 2, "max": 6, "idle_timeout": 1},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1})
pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'reconf 2')
@@ -213,7 +126,7 @@ class TestPythonProcman(TestApplicationPython):
self.assertEqual(len(pids_new), 3, 'reconf 3')
self.assertTrue(pids.issubset(pids_new), 'reconf 3 only 1 new')
- self.conf('6', 'applications/' + self.app_name + '/processes/spare')
+ self.conf_proc('6', self.app_proc + '/spare')
pids = self.pids_for_process()
self.assertEqual(len(pids), 6, 'reconf 6')
@@ -224,10 +137,7 @@ class TestPythonProcman(TestApplicationPython):
self.stop_all()
def test_python_idle_timeout(self):
- self.conf(
- {"spare": 0, "max": 6, "idle_timeout": 2},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
self.get()
pids = self.pids_for_process()
@@ -250,10 +160,7 @@ class TestPythonProcman(TestApplicationPython):
self.assertEqual(len(self.pids_for_process()), 0, 'idle timed out')
def test_python_processes_connection_keepalive(self):
- self.conf(
- {"spare": 0, "max": 6, "idle_timeout": 2},
- 'applications/' + self.app_name + '/processes',
- )
+ self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
(resp, sock) = self.get(
headers={'Host': 'localhost', 'Connection': 'keep-alive'},
@@ -272,6 +179,42 @@ class TestPythonProcman(TestApplicationPython):
sock.close()
+ def test_python_processes_access(self):
+ self.conf_proc('1')
+
+ path = '/' + self.app_proc
+ self.assertIn('error', self.conf_get(path + '/max'))
+ self.assertIn('error', self.conf_get(path + '/spare'))
+ self.assertIn('error', self.conf_get(path + '/idle_timeout'))
+
+ def test_python_processes_invalid(self):
+ self.assertIn(
+ 'error', self.conf({"spare": -1}, self.app_proc), 'negative spare',
+ )
+ self.assertIn(
+ 'error', self.conf({"max": -1}, self.app_proc), 'negative max',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"idle_timeout": -1}, self.app_proc),
+ 'negative idle_timeout',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"spare": 2}, self.app_proc),
+ 'spare gt max default',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"spare": 2, "max": 1}, self.app_proc),
+ 'spare gt max',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"spare": 0, "max": 0}, self.app_proc),
+ 'max zero',
+ )
+
def stop_all(self):
self.conf({"listeners": {}, "applications": {}})
diff --git a/test/test_return.py b/test/test_return.py
new file mode 100644
index 00000000..fcb51745
--- /dev/null
+++ b/test/test_return.py
@@ -0,0 +1,198 @@
+import re
+import unittest
+from unit.applications.proto import TestApplicationProto
+
+
+class TestReturn(TestApplicationProto):
+ prerequisites = {}
+
+ def setUp(self):
+ super().setUp()
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [{"action": {"return": 200}}],
+ "applications": {},
+ }
+ )
+
+ def get_resps_sc(self, req=10):
+ to_send = b"""GET / HTTP/1.1
+Host: localhost
+
+""" * (
+ req - 1
+ )
+
+ to_send += b"""GET / HTTP/1.1
+Host: localhost
+Connection: close
+
+"""
+
+ return self.http(to_send, raw_resp=True, raw=True)
+
+ def test_return(self):
+ resp = self.get()
+ self.assertEqual(resp['status'], 200)
+ self.assertIn('Server', resp['headers'])
+ self.assertIn('Date', resp['headers'])
+ self.assertEqual(resp['headers']['Content-Length'], '0')
+ self.assertEqual(resp['headers']['Connection'], 'close')
+ self.assertEqual(resp['body'], '', 'body')
+
+ resp = self.post(body='blah')
+ self.assertEqual(resp['status'], 200)
+ self.assertEqual(resp['body'], '', 'body')
+
+ resp = self.get_resps_sc()
+ self.assertEqual(len(re.findall('200 OK', resp)), 10)
+ self.assertEqual(len(re.findall('Connection:', resp)), 1)
+ self.assertEqual(len(re.findall('Connection: close', resp)), 1)
+
+ resp = self.get(http_10=True)
+ self.assertEqual(resp['status'], 200)
+ self.assertIn('Server', resp['headers'])
+ self.assertIn('Date', resp['headers'])
+ self.assertEqual(resp['headers']['Content-Length'], '0')
+ self.assertNotIn('Connection', resp['headers'])
+ self.assertEqual(resp['body'], '', 'body')
+
+ def test_return_update(self):
+ self.assertIn('success', self.conf('0', 'routes/0/action/return'))
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 0)
+ self.assertEqual(resp['body'], '')
+
+ self.assertIn('success', self.conf('404', 'routes/0/action/return'))
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 404)
+ self.assertNotEqual(resp['body'], '')
+
+ self.assertIn('success', self.conf('598', 'routes/0/action/return'))
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 598)
+ self.assertNotEqual(resp['body'], '')
+
+ self.assertIn('success', self.conf('999', 'routes/0/action/return'))
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 999)
+ self.assertEqual(resp['body'], '')
+
+ def test_return_location(self):
+ reserved = ":/?#[]@!$&'()*+,;="
+ unreserved = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789-._~")
+ unsafe = " \"%<>\\^`{|}"
+ unsafe_enc = "%20%22%25%3C%3E%5C%5E%60%7B%7C%7D"
+
+ def check_location(location, expect=None):
+ if expect is None:
+ expect = location
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"return": 301, "location": location}, 'routes/0/action'
+ ),
+ 'configure location'
+ )
+
+ self.assertEqual(self.get()['headers']['Location'], expect)
+
+ # FAIL: can't specify empty header value.
+ # check_location("")
+
+ check_location(reserved)
+
+ # After first "?" all other "?" encoded.
+ check_location("/?" + reserved, "/?:/%3F#[]@!$&'()*+,;=")
+ check_location("???", "?%3F%3F")
+
+ # After first "#" all other "?" or "#" encoded.
+ check_location("/#" + reserved, "/#:/%3F%23[]@!$&'()*+,;=")
+ check_location("##?#?", "#%23%3F%23%3F")
+
+ # After first "?" next "#" not encoded.
+ check_location("/?#" + reserved, "/?#:/%3F%23[]@!$&'()*+,;=")
+ check_location("??##", "?%3F#%23")
+ check_location("/?##?", "/?#%23%3F")
+
+ # Unreserved never encoded.
+ check_location(unreserved)
+ check_location("/" + unreserved + "?" + unreserved + "#" + unreserved)
+
+ # Unsafe always encoded.
+ check_location(unsafe, unsafe_enc)
+ check_location("?" + unsafe, "?" + unsafe_enc)
+ check_location("#" + unsafe, "#" + unsafe_enc)
+
+ # %00-%20 and %7F-%FF always encoded.
+ check_location(u"\u0000\u0018\u001F\u0020\u0021", "%00%18%1F%20!")
+ check_location(u"\u007F\u0080н\u20BD", "%7F%C2%80%D0%BD%E2%82%BD")
+
+ # Encoded string detection. If at least one char need to be encoded
+ # then whole string will be encoded.
+ check_location("%20")
+ check_location("/%20?%20#%20")
+ check_location(" %20", "%20%2520")
+ check_location("%20 ", "%2520%20")
+ check_location("/%20?%20#%20 ", "/%2520?%2520#%2520%20")
+
+ def test_return_location_edit(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"return": 302, "location": "blah"}, 'routes/0/action'
+ ),
+ 'configure init location'
+ )
+ self.assertEqual(self.get()['headers']['Location'], 'blah')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/action/location'),
+ 'location delete'
+ )
+ self.assertNotIn('Location', self.get()['headers'])
+
+ self.assertIn(
+ 'success',
+ self.conf('"blah"', 'routes/0/action/location'),
+ 'location restore'
+ )
+ self.assertEqual(self.get()['headers']['Location'], 'blah')
+
+ self.assertIn(
+ 'error',
+ self.conf_post('"blah"', 'routes/0/action/location'),
+ 'location method not allowed'
+ )
+ self.assertEqual(self.get()['headers']['Location'], 'blah')
+
+ def test_return_invalid(self):
+ def check_error(conf):
+ self.assertIn('error', self.conf(conf, 'routes/0/action'))
+
+ check_error({"return": "200"})
+ check_error({"return": []})
+ check_error({"return": 80.1})
+ check_error({"return": 1000})
+ check_error({"return": -1})
+ check_error({"return": 200, "share": "/blah"})
+
+ self.assertIn(
+ 'error', self.conf('001', 'routes/0/action/return'), 'leading zero'
+ )
+
+ check_error({"return": 301, "location": 0})
+ check_error({"return": 301, "location": []})
+
+
+if __name__ == '__main__':
+ TestReturn.main()
diff --git a/test/test_routing.py b/test/test_routing.py
index 950923d6..ad793662 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -16,27 +16,10 @@ class TestRouting(TestApplicationProto):
"routes": [
{
"match": {"method": "GET"},
- "action": {"pass": "applications/empty"},
+ "action": {"return": 200},
}
],
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + '/python/empty',
- "working_directory": self.current_dir
- + '/python/empty',
- "module": "wsgi",
- },
- "mirror": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + '/python/mirror',
- "working_directory": self.current_dir
- + '/python/mirror',
- "module": "wsgi",
- },
- },
+ "applications": {},
}
),
'routing configure',
@@ -48,18 +31,14 @@ class TestRouting(TestApplicationProto):
def route_match(self, match):
self.assertIn(
'success',
- self.route(
- {"match": match, "action": {"pass": "applications/empty"}}
- ),
+ self.route({"match": match, "action": {"return": 200}}),
'route match configure',
)
def route_match_invalid(self, match):
self.assertIn(
'error',
- self.route(
- {"match": match, "action": {"pass": "applications/empty"}}
- ),
+ self.route({"match": match, "action": {"return": 200}}),
'route match configure invalid',
)
@@ -233,24 +212,7 @@ class TestRouting(TestApplicationProto):
{
"listeners": {"*:7080": {"pass": "routes/main"}},
"routes": {"main": []},
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + '/python/empty',
- "working_directory": self.current_dir
- + '/python/empty',
- "module": "wsgi",
- },
- "mirror": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + '/python/mirror',
- "working_directory": self.current_dir
- + '/python/mirror',
- "module": "wsgi",
- },
- },
+ "applications": {},
}
),
'route empty configure',
@@ -272,7 +234,7 @@ class TestRouting(TestApplicationProto):
def test_routes_route_match_absent(self):
self.assertIn(
'success',
- self.conf([{"action": {"pass": "applications/empty"}}], 'routes'),
+ self.conf([{"action": {"return": 200}}], 'routes'),
'route match absent configure',
)
@@ -349,14 +311,8 @@ class TestRouting(TestApplicationProto):
'success',
self.conf(
[
- {
- "match": {"method": "GET"},
- "action": {"pass": "applications/empty"},
- },
- {
- "match": {"method": "POST"},
- "action": {"pass": "applications/mirror"},
- },
+ {"match": {"method": "GET"}, "action": {"return": 200}},
+ {"match": {"method": "POST"}, "action": {"return": 201}},
],
'routes',
),
@@ -364,18 +320,7 @@ class TestRouting(TestApplicationProto):
)
self.assertEqual(self.get()['status'], 200, 'rules two match first')
- self.assertEqual(
- self.post(
- headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close',
- },
- body='X',
- )['status'],
- 200,
- 'rules two match second',
- )
+ self.assertEqual(self.post()['status'], 201, 'rules two match second')
def test_routes_two(self):
self.assertIn(
@@ -393,20 +338,11 @@ class TestRouting(TestApplicationProto):
"second": [
{
"match": {"host": "localhost"},
- "action": {"pass": "applications/empty"},
+ "action": {"return": 200},
}
],
},
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + '/python/empty',
- "working_directory": self.current_dir
- + '/python/empty',
- "module": "wsgi",
- }
- },
+ "applications": {},
}
),
'routes two configure',
@@ -556,7 +492,7 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf([{"action": {"pass": "applications/empty"}}], 'routes'),
+ self.conf([{"action": {"return": 200}}], 'routes'),
'redefine 2',
)
self.assertEqual(self.get()['status'], 200, 'redefine request 2')
@@ -569,19 +505,8 @@ class TestRouting(TestApplicationProto):
self.conf(
{
"listeners": {"*:7080": {"pass": "routes/main"}},
- "routes": {
- "main": [{"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",
- }
- },
+ "routes": {"main": [{"action": {"return": 200}}]},
+ "applications": {},
}
),
'redefine 4',
@@ -595,25 +520,19 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf_post(
- {"action": {"pass": "applications/empty"}}, 'routes/main'
- ),
+ self.conf_post({"action": {"return": 200}}, 'routes/main'),
'redefine 6',
)
self.assertEqual(self.get()['status'], 200, 'redefine request 6')
self.assertIn(
'error',
- self.conf(
- {"action": {"pass": "applications/empty"}}, 'routes/main/2'
- ),
+ self.conf({"action": {"return": 200}}, 'routes/main/2'),
'redefine 7',
)
self.assertIn(
'success',
- self.conf(
- {"action": {"pass": "applications/empty"}}, 'routes/main/1'
- ),
+ self.conf({"action": {"return": 201}}, 'routes/main/1'),
'redefine 8',
)
@@ -631,10 +550,7 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
self.conf_post(
- {
- "match": {"method": "POST"},
- "action": {"pass": "applications/empty"},
- },
+ {"match": {"method": "POST"}, "action": {"return": 200}},
'routes',
),
'routes edit configure 2',
@@ -654,9 +570,7 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.post()['status'], 200, 'routes edit POST 2')
self.assertIn(
- 'success',
- self.conf_delete('routes/0'),
- 'routes edit configure 3',
+ 'success', self.conf_delete('routes/0'), 'routes edit configure 3',
)
self.assertEqual(self.get()['status'], 404, 'routes edit GET 3')
@@ -682,9 +596,7 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.post()['status'], 200, 'routes edit POST 4')
self.assertIn(
- 'success',
- self.conf_delete('routes/0'),
- 'routes edit configure 5',
+ 'success', self.conf_delete('routes/0'), 'routes edit configure 5',
)
self.assertEqual(self.get()['status'], 404, 'routes edit GET 5')
@@ -693,10 +605,7 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
self.conf_post(
- {
- "match": {"method": "POST"},
- "action": {"pass": "applications/empty"},
- },
+ {"match": {"method": "POST"}, "action": {"return": 200}},
'routes',
),
'routes edit configure 6',
@@ -710,19 +619,8 @@ class TestRouting(TestApplicationProto):
self.conf(
{
"listeners": {"*:7080": {"pass": "routes/main"}},
- "routes": {
- "main": [{"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",
- }
- },
+ "routes": {"main": [{"action": {"return": 200}}]},
+ "applications": {},
}
),
'route edit configure 7',
@@ -1838,20 +1736,11 @@ class TestRouting(TestApplicationProto):
"second": [
{
"match": {"destination": ["127.0.0.1:7081"]},
- "action": {"pass": "applications/empty"},
+ "action": {"return": 200},
}
],
},
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/empty",
- "working_directory": self.current_dir
- + "/python/empty",
- "module": "wsgi",
- }
- },
+ "applications": {},
}
),
'proxy configure',
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
index c6648095..36bd9057 100644
--- a/test/test_routing_tls.py
+++ b/test/test_routing_tls.py
@@ -2,9 +2,9 @@ from unit.applications.tls import TestApplicationTLS
class TestRoutingTLS(TestApplicationTLS):
- prerequisites = {'modules': ['python', 'openssl']}
+ prerequisites = {'modules': ['openssl']}
- def test_routes_match_scheme(self):
+ def test_routes_match_scheme_tls(self):
self.certificate()
self.assertIn(
@@ -21,35 +21,21 @@ class TestRoutingTLS(TestApplicationTLS):
"routes": [
{
"match": {"scheme": "http"},
- "action": {"pass": "applications/empty"},
+ "action": {"return": 200},
},
{
"match": {"scheme": "https"},
- "action": {"pass": "applications/204_no_content"},
+ "action": {"return": 201},
},
],
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/empty",
- "module": "wsgi",
- },
- "204_no_content": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir
- + "/python/204_no_content",
- "module": "wsgi",
- },
- },
+ "applications": {},
}
),
'scheme configure',
)
self.assertEqual(self.get()['status'], 200, 'http')
- self.assertEqual(self.get_ssl(port=7081)['status'], 204, 'https')
+ self.assertEqual(self.get_ssl(port=7081)['status'], 201, 'https')
if __name__ == '__main__':
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 83a71f96..bdaabe51 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -322,6 +322,7 @@ class TestRubyApplication(TestApplicationRuby):
self.assertEqual(self.get()['status'], 200, 'init')
+ body = '0123456789' * 500
(resp, sock) = self.post(
headers={
'Host': 'localhost',
@@ -329,12 +330,13 @@ class TestRubyApplication(TestApplicationRuby):
'Content-Type': 'text/html',
},
start=True,
- body='0123456789' * 500,
+ body=body,
read_timeout=1,
)
- self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
+ self.assertEqual(resp['body'], body, 'keep-alive 1')
+ body = '0123456789'
resp = self.post(
headers={
'Host': 'localhost',
@@ -342,10 +344,10 @@ class TestRubyApplication(TestApplicationRuby):
'Content-Type': 'text/html',
},
sock=sock,
- body='0123456789',
+ body=body,
)
- self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+ self.assertEqual(resp['body'], body, 'keep-alive 2')
def test_ruby_application_constants(self):
self.load('constants')
diff --git a/test/test_share_fallback.py b/test/test_share_fallback.py
index 8c45793e..c51e43ee 100644
--- a/test/test_share_fallback.py
+++ b/test/test_share_fallback.py
@@ -20,19 +20,10 @@ class TestStatic(TestApplicationProto):
{
"listeners": {
"*:7080": {"pass": "routes"},
- "*:7081": {"pass": "applications/empty"},
+ "*:7081": {"pass": "routes"},
},
"routes": [{"action": {"share": self.testdir + "/assets"}}],
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/empty",
- "working_directory": self.current_dir
- + "/python/empty",
- "module": "wsgi",
- }
- },
+ "applications": {},
}
)
@@ -41,37 +32,22 @@ class TestStatic(TestApplicationProto):
super().tearDown()
+ def action_update(self, conf):
+ self.assertIn('success', self.conf(conf, 'routes/0/action'))
+
def test_fallback(self):
- self.assertIn(
- 'success',
- self.conf({"share": "/blah"}, 'routes/0/action'),
- 'configure bad path no fallback',
- )
+ self.action_update({"share": "/blah"})
self.assertEqual(self.get()['status'], 404, 'bad path no fallback')
- self.assertIn(
- 'success',
- self.conf(
- {"share": "/blah", "fallback": {"pass": "applications/empty"}},
- 'routes/0/action',
- ),
- 'configure bad path fallback',
- )
+ self.action_update({"share": "/blah", "fallback": {"return": 200}})
+
resp = self.get()
self.assertEqual(resp['status'], 200, 'bad path fallback status')
self.assertEqual(resp['body'], '', 'bad path fallback')
def test_fallback_valid_path(self):
- self.assertIn(
- 'success',
- self.conf(
- {
- "share": self.testdir + "/assets",
- "fallback": {"pass": "applications/empty"},
- },
- 'routes/0/action',
- ),
- 'configure fallback',
+ self.action_update(
+ {"share": self.testdir + "/assets", "fallback": {"return": 200}}
)
resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback status')
@@ -90,36 +66,28 @@ class TestStatic(TestApplicationProto):
)
def test_fallback_nested(self):
- self.assertIn(
- 'success',
- self.conf(
- {
- "share": "/blah",
- "fallback": {
- "share": "/blah/blah",
- "fallback": {"pass": "applications/empty"},
- },
+ self.action_update(
+ {
+ "share": "/blah",
+ "fallback": {
+ "share": "/blah/blah",
+ "fallback": {"return": 200},
},
- 'routes/0/action',
- ),
- 'configure fallback nested',
+ }
)
+
resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback nested status')
self.assertEqual(resp['body'], '', 'fallback nested')
def test_fallback_share(self):
- self.assertIn(
- 'success',
- self.conf(
- {
- "share": "/blah",
- "fallback": {"share": self.testdir + "/assets"},
- },
- 'routes/0/action',
- ),
- 'configure fallback share',
+ self.action_update(
+ {
+ "share": "/blah",
+ "fallback": {"share": self.testdir + "/assets"},
+ }
)
+
resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback share status')
self.assertEqual(resp['body'], '0123456789', 'fallback share')
@@ -136,76 +104,51 @@ class TestStatic(TestApplicationProto):
self.assertIn(
'success',
self.conf(
- {
- "share": "/blah",
- "fallback": {"proxy": "http://127.0.0.1:7081"},
- },
- 'routes/0/action',
+ [
+ {
+ "match": {"destination": "*:7081"},
+ "action": {"return": 200},
+ },
+ {
+ "action": {
+ "share": "/blah",
+ "fallback": {"proxy": "http://127.0.0.1:7081"},
+ }
+ },
+ ],
+ 'routes',
),
- 'configure fallback proxy',
+ 'configure fallback proxy route',
)
+
resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback proxy status')
self.assertEqual(resp['body'], '', 'fallback proxy')
@unittest.skip('not yet')
def test_fallback_proxy_cycle(self):
- self.assertIn(
- 'success',
- self.conf(
- {
- "share": "/blah",
- "fallback": {"proxy": "http://127.0.0.1:7080"},
- },
- 'routes/0/action',
- ),
- 'configure fallback cycle',
+ self.action_update(
+ {
+ "share": "/blah",
+ "fallback": {"proxy": "http://127.0.0.1:7080"},
+ }
)
self.assertNotEqual(self.get()['status'], 200, 'fallback cycle')
- self.assertIn(
- 'success', self.conf_delete('listeners/*:7081'), 'delete listener'
- )
+ self.assertIn('success', self.conf_delete('listeners/*:7081'))
self.assertNotEqual(self.get()['status'], 200, 'fallback cycle 2')
def test_fallback_invalid(self):
- self.assertIn(
- 'error',
- self.conf({"share": "/blah", "fallback": {}}, 'routes/0/action'),
- 'configure fallback empty',
- )
- self.assertIn(
- 'error',
- self.conf({"share": "/blah", "fallback": ""}, 'routes/0/action'),
- 'configure fallback not object',
- )
- self.assertIn(
- 'error',
- self.conf(
- {
- "proxy": "http://127.0.0.1:7081",
- "fallback": {"share": "/blah"},
- },
- 'routes/0/action',
- ),
- 'configure fallback proxy invalid',
- )
- self.assertIn(
- 'error',
- self.conf(
- {
- "pass": "applications/empty",
- "fallback": {"share": "/blah"},
- },
- 'routes/0/action',
- ),
- 'configure fallback pass invalid',
- )
- self.assertIn(
- 'error',
- self.conf({"fallback": {"share": "/blah"}}, 'routes/0/action'),
- 'configure fallback only',
+ def check_error(conf):
+ self.assertIn('error', self.conf(conf, 'routes/0/action'))
+
+ check_error({"share": "/blah", "fallback": {}})
+ check_error({"share": "/blah", "fallback": ""})
+ check_error({"return": 200, "fallback": {"share": "/blah"}})
+ check_error(
+ {"proxy": "http://127.0.0.1:7081", "fallback": {"share": "/blah"}}
)
+ check_error({"fallback": {"share": "/blah"}})
if __name__ == '__main__':
diff --git a/test/test_tls.py b/test/test_tls.py
index 475e9919..d9dcf237 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -521,7 +521,6 @@ basicConstraints = critical,CA:TRUE"""
)
def test_tls_application_respawn(self):
- self.skip_alerts.append(r'process \d+ exited on signal 9')
self.load('mirror')
self.certificate()
@@ -530,7 +529,7 @@ basicConstraints = critical,CA:TRUE"""
self.add_tls(application='mirror')
- (resp, sock) = self.post_ssl(
+ (_, sock) = self.post_ssl(
headers={
'Host': 'localhost',
'Connection': 'keep-alive',
@@ -545,6 +544,8 @@ basicConstraints = critical,CA:TRUE"""
subprocess.call(['kill', '-9', app_id])
+ self.skip_alerts.append(r'process %s exited on signal 9' % app_id)
+
self.wait_for_record(
re.compile(
' (?!' + app_id + '#)(\d+)#\d+ "mirror" application started'
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
index 2bc2d90a..7045318a 100644
--- a/test/test_upstreams_rr.py
+++ b/test/test_upstreams_rr.py
@@ -16,10 +16,10 @@ class TestUpstreamsRR(TestApplicationPython):
{
"listeners": {
"*:7080": {"pass": "upstreams/one"},
- "*:7081": {"pass": "applications/ups_0"},
- "*:7082": {"pass": "applications/ups_1"},
- "*:7083": {"pass": "applications/ups_2"},
"*:7090": {"pass": "upstreams/two"},
+ "*:7081": {"pass": "routes/one"},
+ "*:7082": {"pass": "routes/two"},
+ "*:7083": {"pass": "routes/three"},
},
"upstreams": {
"one": {
@@ -35,32 +35,12 @@ class TestUpstreamsRR(TestApplicationPython):
},
},
},
- "applications": {
- "ups_0": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/upstreams/0",
- "working_directory": self.current_dir
- + "/python/upstreams/0",
- "module": "wsgi",
- },
- "ups_1": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/upstreams/1",
- "working_directory": self.current_dir
- + "/python/upstreams/1",
- "module": "wsgi",
- },
- "ups_2": {
- "type": "python",
- "processes": {"spare": 0},
- "path": self.current_dir + "/python/upstreams/2",
- "working_directory": self.current_dir
- + "/python/upstreams/2",
- "module": "wsgi",
- },
+ "routes": {
+ "one": [{"action": {"return": 200}}],
+ "two": [{"action": {"return": 201}}],
+ "three": [{"action": {"return": 202}}],
},
+ "applications": {},
},
),
'upstreams initial configuration',
@@ -70,15 +50,17 @@ class TestUpstreamsRR(TestApplicationPython):
def get_resps(self, req=100, port=7080):
resps = [0]
+
for _ in range(req):
- headers = self.get(port=port)['headers']
- if 'X-Upstream' in headers:
- ups = int(headers['X-Upstream'])
+ status = self.get(port=port)['status']
+ if 200 > status or status > 209:
+ continue
- if ups > len(resps) - 1:
- resps.extend([0] * (ups - len(resps) + 1))
+ ups = status % 10
+ if ups > len(resps) - 1:
+ resps.extend([0] * (ups - len(resps) + 1))
- resps[ups] += 1
+ resps[ups] += 1
return resps
@@ -97,16 +79,19 @@ Connection: close
"""
resp = self.http(to_send, raw_resp=True, raw=True, port=port)
- ups = re.findall('X-Upstream: (\d+)', resp)
- resps = [0] * (int(max(ups)) + 1)
+ status = re.findall(r'HTTP\/\d\.\d\s(\d\d\d)', resp)
+ status = list(filter(lambda x: x[:2] == '20', status))
+ ups = list(map(lambda x: int(x[-1]), status))
+ resps = [0] * (max(ups) + 1)
for i in range(len(ups)):
- resps[int(ups[i])] += 1
+ resps[ups[i]] += 1
return resps
def test_upstreams_rr_no_weight(self):
resps = self.get_resps()
+ self.assertEqual(sum(resps), 100, 'no weight sum')
self.assertLessEqual(
abs(resps[0] - resps[1]), self.cpu_count, 'no weight'
)
@@ -127,6 +112,7 @@ Connection: close
)
resps = self.get_resps()
+ self.assertEqual(sum(resps), 100, 'no weight 3 sum')
self.assertLessEqual(
abs(resps[0] - resps[1]), self.cpu_count, 'no weight 3'
)
@@ -138,6 +124,7 @@ Connection: close
)
resps = self.get_resps()
+ self.assertEqual(sum(resps), 100, 'no weight 4 sum')
self.assertLessEqual(
max(resps) - min(resps), self.cpu_count, 'no weight 4'
)
@@ -193,6 +180,67 @@ Connection: close
self.assertEqual(resps[0], 60, 'weight 2 0')
self.assertEqual(resps[2], 40, 'weight 2 1')
+ def test_upstreams_rr_weight_rational(self):
+ def set_weights(w1, w2):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "127.0.0.1:7081": {"weight": w1},
+ "127.0.0.1:7082": {"weight": w2},
+ },
+ 'upstreams/one/servers',
+ ),
+ 'configure weights',
+ )
+
+ def check_reqs(w1, w2, reqs=10):
+ resps = self.get_resps_sc(req=reqs)
+ self.assertEqual(resps[0], reqs * w1 / (w1 + w2), 'weight 1')
+ self.assertEqual(resps[1], reqs * w2 / (w1 + w2), 'weight 2')
+
+ def check_weights(w1, w2):
+ set_weights(w1, w2)
+ check_reqs(w1, w2)
+
+ check_weights(0, 1)
+ check_weights(0, 999999.0123456)
+ check_weights(1, 9)
+ check_weights(100000, 900000)
+ check_weights(1, .25)
+ check_weights(1, 0.25)
+ check_weights(0.2, .8)
+ check_weights(1, 1.5)
+ check_weights(1e-3, 1E-3)
+ check_weights(1e-20, 1e-20)
+ check_weights(1e4, 1e4)
+ check_weights(1000000, 1000000)
+
+ set_weights(0.25, 0.25)
+ self.assertIn(
+ 'success',
+ self.conf_delete('upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'delete weight',
+ )
+ check_reqs(1, 0.25)
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "127.0.0.1:7081": {"weight": 0.1},
+ "127.0.0.1:7082": {"weight": 1},
+ "127.0.0.1:7083": {"weight": 0.9},
+ },
+ 'upstreams/one/servers',
+ ),
+ 'configure weights',
+ )
+ resps = self.get_resps_sc(req=20)
+ self.assertEqual(resps[0], 1, 'weight 3 1')
+ self.assertEqual(resps[1], 10, 'weight 3 2')
+ self.assertEqual(resps[2], 9, 'weight 3 3')
+
def test_upstreams_rr_independent(self):
def sum_resps(*args):
sum = [0] * len(args[0])
@@ -234,33 +282,71 @@ Connection: close
r_one = sum_resps(r_one, self.get_resps(req=10))
r_two = sum_resps(r_two, self.get_resps(req=10, port=7090))
+
+ self.assertEqual(sum(r_one), 100, 'dep one mix sum')
self.assertLessEqual(
abs(r_one[0] - r_one[1]), self.cpu_count, 'dep one mix'
)
+ self.assertEqual(sum(r_two), 100, 'dep two mix sum')
self.assertLessEqual(
abs(r_two[0] - r_two[1]), self.cpu_count, 'dep two mix'
)
def test_upstreams_rr_delay(self):
- headers_delay_1 = {
- 'Connection': 'close',
- 'Host': 'localhost',
- 'Content-Length': '0',
- 'X-Delay': '1',
- }
- headers_no_delay = {
- 'Connection': 'close',
- 'Host': 'localhost',
- 'Content-Length': '0',
- }
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "upstreams/one"},
+ "*:7081": {"pass": "routes"},
+ "*:7082": {"pass": "routes"},
+ },
+ "upstreams": {
+ "one": {
+ "servers": {
+ "127.0.0.1:7081": {},
+ "127.0.0.1:7082": {},
+ },
+ },
+ },
+ "routes": [
+ {
+ "match": {"destination": "*:7081"},
+ "action": {"pass": "applications/delayed"},
+ },
+ {
+ "match": {"destination": "*:7082"},
+ "action": {"return": 201},
+ },
+ ],
+ "applications": {
+ "delayed": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/delayed",
+ "working_directory": self.current_dir
+ + "/python/delayed",
+ "module": "wsgi",
+ }
+ },
+ },
+ ),
+ 'upstreams initial configuration',
+ )
req = 50
socks = []
for i in range(req):
- headers = headers_delay_1 if i % 5 == 0 else headers_no_delay
+ delay = 1 if i % 5 == 0 else 0
_, sock = self.get(
- headers=headers,
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': '0',
+ 'X-Delay': str(delay),
+ 'Connection': 'close',
+ },
start=True,
no_recv=True,
)
@@ -271,12 +357,12 @@ Connection: close
resp = self.recvall(socks[i]).decode()
socks[i].close()
- m = re.search('X-Upstream: (\d+)', resp)
+ m = re.search('HTTP/1.1 20(\d)', resp)
+ self.assertIsNotNone(m, 'status')
resps[int(m.group(1))] += 1
- self.assertLessEqual(
- abs(resps[0] - resps[1]), self.cpu_count, 'dep two mix'
- )
+ self.assertEqual(sum(resps), req, 'delay sum')
+ self.assertLessEqual(abs(resps[0] - resps[1]), self.cpu_count, 'delay')
def test_upstreams_rr_active_req(self):
conns = 5
@@ -303,7 +389,7 @@ Connection: close
# Send one more request and read response to make sure that previous
# requests had enough time to reach server.
- self.assertEqual(self.get()['status'], 200)
+ self.assertEqual(self.get()['body'], '')
self.assertIn(
'success',
@@ -327,13 +413,17 @@ Connection: close
)
for i in range(conns):
- resp = self.recvall(socks[i]).decode()
- socks[i].close()
-
- self.assertRegex(resp, r'X-Upstream', 'active req GET')
+ self.assertEqual(
+ self.http(b'', sock=socks[i], raw=True)['body'],
+ '',
+ 'active req GET',
+ )
- resp = self.http(b"""0123456789""", sock=socks2[i], raw=True)
- self.assertEqual(resp['status'], 200, 'active req POST')
+ self.assertEqual(
+ self.http(b"""0123456789""", sock=socks2[i], raw=True)['body'],
+ '',
+ 'active req POST',
+ )
def test_upstreams_rr_bad_server(self):
self.assertIn(
@@ -356,14 +446,11 @@ Connection: close
def test_upstreams_rr_post(self):
resps = [0, 0]
for _ in range(50):
- resps[
- int(self.post(body='0123456789')['headers']['X-Upstream'])
- ] += 1
- resps[int(self.get()['headers']['X-Upstream'])] += 1
+ resps[self.get()['status'] % 10] += 1
+ resps[self.post(body='0123456789')['status'] % 10] += 1
- self.assertLessEqual(
- abs(resps[0] - resps[1]), self.cpu_count, 'post'
- )
+ self.assertEqual(sum(resps), 100, 'post sum')
+ self.assertLessEqual(abs(resps[0] - resps[1]), self.cpu_count, 'post')
def test_upstreams_rr_unix(self):
addr_0 = self.testdir + '/sock_0'
@@ -374,8 +461,8 @@ Connection: close
self.conf(
{
"*:7080": {"pass": "upstreams/one"},
- "unix:" + addr_0: {"pass": "applications/ups_0"},
- "unix:" + addr_1: {"pass": "applications/ups_1"},
+ "unix:" + addr_0: {"pass": "routes/one"},
+ "unix:" + addr_1: {"pass": "routes/two"},
},
'listeners',
),
@@ -385,7 +472,7 @@ Connection: close
self.assertIn(
'success',
self.conf(
- {"unix:" + addr_0: {}, "unix:" + addr_1: {},},
+ {"unix:" + addr_0: {}, "unix:" + addr_1: {}},
'upstreams/one/servers',
),
'configure servers unix',
@@ -402,8 +489,8 @@ Connection: close
self.conf(
{
"*:7080": {"pass": "upstreams/one"},
- "[::1]:7081": {"pass": "applications/ups_0"},
- "[::1]:7082": {"pass": "applications/ups_1"},
+ "[::1]:7081": {"pass": "routes/one"},
+ "[::1]:7082": {"pass": "routes/two"},
},
'listeners',
),
@@ -413,7 +500,7 @@ Connection: close
self.assertIn(
'success',
self.conf(
- {"[::1]:7081": {}, "[::1]:7082": {},}, 'upstreams/one/servers'
+ {"[::1]:7081": {}, "[::1]:7082": {}}, 'upstreams/one/servers'
),
'configure servers ipv6',
)
@@ -429,9 +516,29 @@ Connection: close
self.conf({}, 'upstreams/one/servers'),
'configure servers empty',
)
-
self.assertEqual(self.get()['status'], 502, 'servers empty')
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"127.0.0.1:7081": {"weight": 0}}, 'upstreams/one/servers'
+ ),
+ 'configure servers empty one',
+ )
+ self.assertEqual(self.get()['status'], 502, 'servers empty one')
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "127.0.0.1:7081": {"weight": 0},
+ "127.0.0.1:7082": {"weight": 0},
+ },
+ 'upstreams/one/servers',
+ ),
+ 'configure servers empty two',
+ )
+ self.assertEqual(self.get()['status'], 502, 'servers empty two')
+
def test_upstreams_rr_invalid(self):
self.assertIn(
'error', self.conf({}, 'upstreams'), 'upstreams empty',
@@ -449,16 +556,21 @@ Connection: close
self.conf({}, 'upstreams/one/servers/127.0.0.1:7081/blah'),
'invalid server option',
)
- self.assertIn(
- 'error',
- self.conf({}, 'upstreams/one/servers/127.0.0.1:7081/weight'),
- 'invalid weight option',
- )
- self.assertIn(
- 'error',
- self.conf('-1', 'upstreams/one/servers/127.0.0.1:7081/weight'),
- 'invalid negative weight',
- )
+
+ def check_weight(w):
+ self.assertIn(
+ 'error',
+ self.conf(w, 'upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'invalid weight option',
+ )
+ check_weight({})
+ check_weight('-1')
+ check_weight('1.')
+ check_weight('1.1.')
+ check_weight('.')
+ check_weight('.01234567890123')
+ check_weight('1000001')
+ check_weight('2e6')
if __name__ == '__main__':
diff --git a/test/test_usr1.py b/test/test_usr1.py
index 2b4f394b..155303ea 100644
--- a/test/test_usr1.py
+++ b/test/test_usr1.py
@@ -51,12 +51,11 @@ class TestUSR1(TestApplicationPython):
self.search_in_log(r'/usr1', log_new), 'rename new 2'
)
- @unittest.skip('not yet')
def test_usr1_unit_log(self):
self.load('log_body')
log_new = 'new.log'
- log_path = self.testdir + '/' + 'unit.log'
+ log_path = self.testdir + '/unit.log'
log_path_new = self.testdir + '/' + log_new
os.rename(log_path, log_path_new)
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
index ef16f433..fc15e8e4 100644
--- a/test/unit/applications/websockets.py
+++ b/test/unit/applications/websockets.py
@@ -52,7 +52,11 @@ class TestApplicationWebsocket(TestApplicationProto):
)
resp = ''
- while select.select([sock], [], [], 30)[0]:
+ while True:
+ rlist = select.select([sock], [], [], 60)[0]
+ if not rlist:
+ self.fail('Can\'t read response from server.')
+
resp += sock.recv(4096).decode()
if (
@@ -70,10 +74,18 @@ class TestApplicationWebsocket(TestApplicationProto):
def serialize_close(self, code=1000, reason=''):
return struct.pack('!H', code) + reason.encode('utf-8')
- def frame_read(self, sock, read_timeout=30):
+ def frame_read(self, sock, read_timeout=60):
def recv_bytes(sock, bytes):
data = b''
- while select.select([sock], [], [], read_timeout)[0]:
+ while True:
+ rlist = select.select([sock], [], [], read_timeout)[0]
+ if not rlist:
+ # For all current cases if the "read_timeout" was changed
+ # than test do not expect to get a response from server.
+ if read_timeout == 60:
+ self.fail('Can\'t read response from server.')
+ break
+
data += sock.recv(bytes - len(data))
if len(data) == bytes:
@@ -221,7 +233,7 @@ class TestApplicationWebsocket(TestApplicationProto):
op_code = self.OP_CONT
pos = end
- def message_read(self, sock, read_timeout=10):
+ def message_read(self, sock, read_timeout=60):
frame = self.frame_read(sock, read_timeout=read_timeout)
while not frame['fin']:
diff --git a/test/unit/http.py b/test/unit/http.py
index 47fb48f1..13384dc8 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -17,11 +17,6 @@ class TestHTTP(TestUnit):
port = 7080 if 'port' not in kwargs else kwargs['port']
url = '/' if 'url' not in kwargs else kwargs['url']
http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1'
- read_buffer_size = (
- 4096
- if 'read_buffer_size' not in kwargs
- else kwargs['read_buffer_size']
- )
headers = (
{'Host': 'localhost', 'Connection': 'close'}
@@ -60,7 +55,7 @@ class TestHTTP(TestUnit):
sock.connect(connect_args)
except ConnectionRefusedError:
sock.close()
- return None
+ self.fail('Client can\'t connect to the server.')
else:
sock = kwargs['sock']
@@ -101,12 +96,15 @@ class TestHTTP(TestUnit):
resp = ''
if 'no_recv' not in kwargs:
- 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(encoding)
+ recvall_kwargs = {}
+
+ if 'read_timeout' in kwargs:
+ recvall_kwargs['read_timeout'] = kwargs['read_timeout']
+
+ if 'read_buffer_size' in kwargs:
+ recvall_kwargs['buff_size'] = kwargs['read_buffer_size']
+
+ resp = self.recvall(sock, **recvall_kwargs).decode(encoding)
self.log_in(resp)
@@ -174,9 +172,26 @@ class TestHTTP(TestUnit):
def put(self, **kwargs):
return self.http('PUT', **kwargs)
- def recvall(self, sock, read_timeout=30, buff_size=4096):
+ def recvall(self, sock, **kwargs):
+ timeout_default = 60
+
+ timeout = (
+ timeout_default
+ if 'read_timeout' not in kwargs
+ else kwargs['read_timeout']
+ )
+ buff_size = 4096 if 'buff_size' not in kwargs else kwargs['buff_size']
+
data = b''
- while select.select([sock], [], [], read_timeout)[0]:
+ while True:
+ rlist = select.select([sock], [], [], timeout)[0]
+ if not rlist:
+ # For all current cases if the "read_timeout" was changed
+ # than test do not expect to get a response from server.
+ if timeout == timeout_default:
+ self.fail('Can\'t read response from server.')
+ break
+
try:
part = sock.recv(buff_size)
except:
@@ -264,12 +279,8 @@ class TestHTTP(TestUnit):
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',
- )
+ self.assertIn('Content-Type', headers)
+ self.assertEqual(headers['Content-Type'], 'application/json')
resp['body'] = json.loads(resp['body'])
diff --git a/test/unit/main.py b/test/unit/main.py
index 69234dcc..4507f71a 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -4,6 +4,7 @@ import sys
import stat
import time
import fcntl
+import atexit
import shutil
import signal
import argparse
@@ -151,48 +152,6 @@ class TestUnit(unittest.TestCase):
def setUp(self):
self._run()
- def tearDown(self):
- self.stop()
-
- # detect errors and failures for current test
-
- def list2reason(exc_list):
- if exc_list and exc_list[-1][0] is self:
- return exc_list[-1][1]
-
- if hasattr(self, '_outcome'):
- result = self.defaultTestResult()
- self._feedErrorsToResult(result, self._outcome.errors)
- else:
- result = getattr(
- self, '_outcomeForDoCleanups', self._resultForDoCleanups
- )
-
- success = not list2reason(result.errors) and not list2reason(
- result.failures
- )
-
- # check unit.log for alerts
-
- unit_log = self.testdir + '/unit.log'
-
- with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f:
- self._check_alerts(f.read())
-
- # remove unit.log
-
- if not TestUnit.save_log and success:
- shutil.rmtree(self.testdir)
-
- else:
- self._print_log()
-
- def stop(self):
- if self._started:
- self._stop()
-
- self.stop_processes()
-
def _run(self):
build_dir = self.pardir + '/build'
self.unitd = build_dir + '/unitd'
@@ -224,62 +183,81 @@ class TestUnit(unittest.TestCase):
stderr=log,
)
+ atexit.register(self.stop)
+
if not self.waitforfiles(self.testdir + '/control.unit.sock'):
exit("Could not start unit")
- self._started = True
-
self.skip_alerts = [
r'read signalfd\(4\) failed',
- r'last message send failed',
r'sendmsg.+failed',
r'recvmsg.+failed',
]
self.skip_sanitizer = False
- def _stop(self):
- with self._p as p:
- p.send_signal(signal.SIGQUIT)
+ def tearDown(self):
+ stop_errs = self.stop()
- try:
- retcode = p.wait(15)
- if retcode:
- self.fail(
- "Child process terminated with code " + str(retcode)
- )
- except:
- self.fail("Could not terminate unit")
- p.kill()
+ # detect errors and failures for current test
- self._started = False
+ def list2reason(exc_list):
+ if exc_list and exc_list[-1][0] is self:
+ return exc_list[-1][1]
- def _check_alerts(self, log):
- found = False
+ if hasattr(self, '_outcome'):
+ result = self.defaultTestResult()
+ self._feedErrorsToResult(result, self._outcome.errors)
+ else:
+ result = getattr(
+ self, '_outcomeForDoCleanups', self._resultForDoCleanups
+ )
- alerts = re.findall('.+\[alert\].+', log)
+ success = not list2reason(result.errors) and not list2reason(
+ result.failures
+ )
- if alerts:
- print('All alerts/sanitizer errors found in log:')
- [print(alert) for alert in alerts]
- found = True
+ # check unit.log for alerts
- if self.skip_alerts:
- for skip in self.skip_alerts:
- alerts = [al for al in alerts if re.search(skip, al) is None]
+ unit_log = self.testdir + '/unit.log'
- if alerts:
- self._print_log(log)
- self.assertFalse(alerts, 'alert(s)')
+ with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f:
+ self._check_alerts(f.read())
- if not self.skip_sanitizer:
- sanitizer_errors = re.findall('.+Sanitizer.+', log)
+ # remove unit.log
- if sanitizer_errors:
- self._print_log(log)
- self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
+ if not TestUnit.save_log and success:
+ shutil.rmtree(self.testdir)
- if found:
- print('skipped.')
+ else:
+ self._print_log()
+
+ self.assertListEqual(stop_errs, [None, None], 'stop errors')
+
+ def stop(self):
+ errors = []
+
+ errors.append(self._stop())
+
+ errors.append(self.stop_processes())
+
+ atexit.unregister(self.stop)
+
+ return errors
+
+ def _stop(self):
+ if self._p.poll() is not None:
+ return
+
+ with self._p as p:
+ p.send_signal(signal.SIGQUIT)
+
+ try:
+ retcode = p.wait(15)
+ if retcode:
+ return 'Child process terminated with code ' + str(retcode)
+ except:
+ p.kill()
+ return 'Could not terminate unit'
def run_process(self, target, *args):
if not hasattr(self, '_processes'):
@@ -294,12 +272,17 @@ class TestUnit(unittest.TestCase):
if not hasattr(self, '_processes'):
return
+ fail = False
for process in self._processes:
- process.terminate()
- process.join(timeout=5)
-
if process.is_alive():
- self.fail('Fail to stop process')
+ process.terminate()
+ process.join(timeout=15)
+
+ if process.is_alive():
+ fail = True
+
+ if fail:
+ return 'Fail to stop process'
def waitforfiles(self, *files):
for i in range(50):
@@ -329,6 +312,34 @@ class TestUnit(unittest.TestCase):
for f in files:
os.chmod(os.path.join(root, f), 0o777)
+ def _check_alerts(self, log):
+ found = False
+
+ alerts = re.findall('.+\[alert\].+', log)
+
+ if alerts:
+ print('All alerts/sanitizer errors found in log:')
+ [print(alert) for alert in alerts]
+ found = True
+
+ if self.skip_alerts:
+ for skip in self.skip_alerts:
+ alerts = [al for al in alerts if re.search(skip, al) is None]
+
+ if alerts:
+ self._print_log(log)
+ self.assertFalse(alerts, 'alert(s)')
+
+ if not self.skip_sanitizer:
+ sanitizer_errors = re.findall('.+Sanitizer.+', log)
+
+ if sanitizer_errors:
+ self._print_log(log)
+ self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
+
+ if found:
+ print('skipped.')
+
@staticmethod
def _parse_args():
parser = argparse.ArgumentParser(add_help=False)
diff --git a/version b/version
index a0f9ae94..b5283235 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.16.0
-NXT_VERNUM=11600
+NXT_VERSION=1.17.0
+NXT_VERNUM=11700