summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2019-05-30 17:44:29 +0300
committerAndrei Belov <defan@nginx.com>2019-05-30 17:44:29 +0300
commit4921df052be8437d912f3c60faa9a667890e4498 (patch)
tree3678c551f148a0d177721597de978c090237f205
parent3b7a7ff2aa5840d4238584410ee1ebc6860fb9c5 (diff)
parent7da320a93af07765e79c929287704936c431f3cd (diff)
downloadunit-1.9.0-1.tar.gz
unit-1.9.0-1.tar.bz2
Merged with the default branch.1.9.0-1
Diffstat (limited to '')
-rw-r--r--.hgtags1
-rw-r--r--CHANGES31
-rw-r--r--auto/help3
-rw-r--r--docs/changes.xml103
-rw-r--r--pkg/deb/Makefile13
-rw-r--r--pkg/deb/Makefile.go11148
-rw-r--r--pkg/deb/Makefile.jsc-common10
-rw-r--r--pkg/deb/debian.module/unit.example-go-config2
-rw-r--r--pkg/deb/debian.module/unit.example-go1.10-config2
-rw-r--r--pkg/deb/debian.module/unit.example-go1.11-config15
-rw-r--r--pkg/deb/debian.module/unit.example-go1.7-config2
-rw-r--r--pkg/deb/debian.module/unit.example-go1.8-config2
-rw-r--r--pkg/deb/debian.module/unit.example-go1.9-config2
-rw-r--r--pkg/deb/debian.module/unit.example-jsc10-config2
-rw-r--r--pkg/deb/debian.module/unit.example-jsc11-config2
-rw-r--r--pkg/deb/debian.module/unit.example-jsc8-config2
-rw-r--r--pkg/deb/debian.module/unit.example-jsc9-config2
-rw-r--r--pkg/deb/debian.module/unit.example-perl-config2
-rw-r--r--pkg/deb/debian.module/unit.example-php-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python2.7-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python3.4-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python3.5-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python3.6-config2
-rw-r--r--pkg/deb/debian.module/unit.example-python3.7-config2
-rw-r--r--pkg/deb/debian.module/unit.example-ruby-config2
-rw-r--r--pkg/deb/debian/unit.example.config8
-rw-r--r--pkg/docker/Dockerfile.full2
-rw-r--r--pkg/docker/Dockerfile.go1.7-dev2
-rw-r--r--pkg/docker/Dockerfile.go1.8-dev2
-rw-r--r--pkg/docker/Dockerfile.minimal2
-rw-r--r--pkg/docker/Dockerfile.perl5.242
-rw-r--r--pkg/docker/Dockerfile.php7.02
-rw-r--r--pkg/docker/Dockerfile.python2.72
-rw-r--r--pkg/docker/Dockerfile.python3.52
-rw-r--r--pkg/docker/Dockerfile.ruby2.32
-rw-r--r--pkg/rpm/Makefile13
-rw-r--r--pkg/rpm/Makefile.python272
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-go-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-jsc11-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-jsc8-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-php-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config2
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example.config8
-rw-r--r--pkg/rpm/unit.module.spec.in2
-rw-r--r--pkg/rpm/unit.spec.in2
-rw-r--r--src/go/unit/nxt_cgo_lib.c4
-rw-r--r--src/go/unit/request.go7
-rw-r--r--src/java/nginx/unit/Context.java6
-rw-r--r--src/java/nginx/unit/Request.java8
-rw-r--r--src/java/nxt_jni_Context.c2
-rw-r--r--src/java/nxt_jni_Request.c23
-rw-r--r--src/nodejs/unit-http/binding.gyp9
-rw-r--r--src/nodejs/unit-http/binding_pub.gyp9
-rwxr-xr-xsrc/nodejs/unit-http/http_server.js12
-rw-r--r--src/nodejs/unit-http/nxt_napi.h656
-rw-r--r--src/nodejs/unit-http/unit.cpp865
-rw-r--r--src/nodejs/unit-http/unit.h35
-rw-r--r--src/nxt_application.c28
-rw-r--r--src/nxt_application.h56
-rw-r--r--src/nxt_buf.h2
-rw-r--r--src/nxt_conf.c225
-rw-r--r--src/nxt_conf.h13
-rw-r--r--src/nxt_conf_validation.c101
-rw-r--r--src/nxt_controller.c35
-rw-r--r--src/nxt_epoll_engine.c2
-rw-r--r--src/nxt_errno.h1
-rw-r--r--src/nxt_h1proto.c17
-rw-r--r--src/nxt_hpux_sendfile.c4
-rw-r--r--src/nxt_http.h10
-rw-r--r--src/nxt_http_parse.c6
-rw-r--r--src/nxt_http_parse.h5
-rw-r--r--src/nxt_http_request.c126
-rw-r--r--src/nxt_http_route.c1021
-rw-r--r--src/nxt_job_resolve.c16
-rw-r--r--src/nxt_kqueue_engine.c2
-rw-r--r--src/nxt_main_process.c2
-rw-r--r--src/nxt_php_sapi.c6
-rw-r--r--src/nxt_poll_engine.c2
-rw-r--r--src/nxt_port.c2
-rw-r--r--src/nxt_port_memory.c4
-rw-r--r--src/nxt_port_socket.c89
-rw-r--r--src/nxt_process.c49
-rw-r--r--src/nxt_python_wsgi.c27
-rw-r--r--src/nxt_router.c224
-rw-r--r--src/nxt_router.h5
-rw-r--r--src/nxt_runtime.c3
-rw-r--r--src/nxt_sockaddr.c16
-rw-r--r--src/nxt_socket.h4
-rw-r--r--src/nxt_socketpair.c17
-rw-r--r--src/nxt_sprintf.c12
-rw-r--r--src/nxt_timer.h2
-rw-r--r--src/nxt_unicode_lowcase.pl6
-rw-r--r--src/nxt_unit.c4
-rw-r--r--src/nxt_unit_request.h1
-rw-r--r--src/perl/nxt_perl_psgi.c5
-rw-r--r--src/ruby/nxt_ruby.c4
-rwxr-xr-xtest/node/has_header/app.js2
-rwxr-xr-xtest/node/promise_handler/app.js2
-rwxr-xr-xtest/node/remove_header/app.js2
-rwxr-xr-xtest/node/variables/app.js6
-rw-r--r--test/php/404/index.php8
-rw-r--r--test/php/ini_precision/ini/php.ini (renamed from test/php/ini_precision/php.ini)0
-rw-r--r--test/python/mirror/wsgi.py1
-rw-r--r--test/test_access_log.py243
-rw-r--r--test/test_configuration.py383
-rw-r--r--test/test_go_application.py155
-rw-r--r--test/test_http_header.py485
-rw-r--r--test/test_java_application.py1192
-rw-r--r--test/test_node_application.py356
-rw-r--r--test/test_perl_application.py180
-rw-r--r--test/test_php_application.py346
-rw-r--r--test/test_php_basic.py149
-rw-r--r--test/test_python_application.py404
-rw-r--r--test/test_python_basic.py156
-rw-r--r--test/test_python_environment.py223
-rw-r--r--test/test_python_procman.py198
-rw-r--r--test/test_routing.py3121
-rw-r--r--test/test_ruby_application.py233
-rw-r--r--test/test_settings.py152
-rw-r--r--test/test_tls.py508
-rw-r--r--test/unit.py763
-rw-r--r--test/unit/__init__.py0
-rw-r--r--test/unit/applications/__init__.py0
-rw-r--r--test/unit/applications/lang/__init__.py0
-rw-r--r--test/unit/applications/lang/go.py40
-rw-r--r--test/unit/applications/lang/java.py74
-rw-r--r--test/unit/applications/lang/node.py34
-rw-r--r--test/unit/applications/lang/perl.py20
-rw-r--r--test/unit/applications/lang/php.py21
-rw-r--r--test/unit/applications/lang/python.py24
-rw-r--r--test/unit/applications/lang/ruby.py20
-rw-r--r--test/unit/applications/proto.py31
-rw-r--r--test/unit/applications/tls.py92
-rw-r--r--test/unit/control.py61
-rw-r--r--test/unit/http.py162
-rw-r--r--test/unit/main.py324
-rw-r--r--version4
145 files changed, 9896 insertions, 4406 deletions
diff --git a/.hgtags b/.hgtags
index 3a0712b8..8b51754d 100644
--- a/.hgtags
+++ b/.hgtags
@@ -21,3 +21,4 @@ abb8cfb421f608df1c23f5c333c5f049a79a681a 1.7-1
fe0d5eb09b66e77a2b66455faa51d3fa04146d3d 1.7.1-1
0a18a14d169f156f8e2daca35aa86d5a6dd9b1ae 1.8.0
f47fc64d3d9e3dedb95042e93c7f73b31f458338 1.8.0-1
+dda6319de785dc2d225d818349aba56fc48d47f6 1.9.0
diff --git a/CHANGES b/CHANGES
index 5398910e..79761847 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,35 @@
+Changes with Unit 1.9.0 30 May 2019
+
+ *) Feature: request routing by arguments, headers, and cookies.
+
+ *) Feature: route matching patterns allow a wildcard in the middle.
+
+ *) Feature: POST operation for appending elements to arrays in
+ configuration.
+
+ *) Feature: support for changing credentials using CAP_SETUID and
+ CAP_SETGID capabilities on Linux without running main process as
+ privileged user.
+
+ *) Bugfix: memory leak in the router process might have happened when a
+ client prematurely closed the connection.
+
+ *) Bugfix: applying a large configuration might have failed.
+
+ *) Bugfix: PUT and DELETE operations on array elements in configuration
+ did not work.
+
+ *) Bugfix: request schema in applications did not reflect TLS
+ connections.
+
+ *) Bugfix: restored compatibility with Node.js applications that use
+ ServerResponse._implicitHeader() function; the bug had appeared in
+ 1.7.
+
+ *) Bugfix: various compatibility issues with Node.js applications.
+
+
Changes with Unit 1.8.0 01 Mar 2019
*) Change: now three numbers are always used for versioning: major,
diff --git a/auto/help b/auto/help
index f22362d5..fe0c7056 100644
--- a/auto/help
+++ b/auto/help
@@ -58,4 +58,7 @@ cat << END
nodejs OPTIONS configure Node.js module
run "./configure nodejs --help" to see available options
+ java OPTIONS configure Java module
+ run "./configure java --help" to see available options
+
END
diff --git a/docs/changes.xml b/docs/changes.xml
index e4b8158d..e1a8734c 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -5,6 +5,109 @@
<change_log title="unit">
+<changes apply="unit-go1.11" ver="1.9.0" rev="1"
+ date="2019-05-30" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+Initial release of Go 1.11 module for NGINX Unit.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7
+ unit-go unit-go1.7 unit-go1.8 unit-go1.9 unit-go1.10
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
+ ver="1.9.0" rev="1"
+ date="2019-05-30" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.9.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.9.0" rev="1"
+ date="2019-05-30" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="feature">
+<para>
+request routing by arguments, headers, and cookies.
+</para>
+</change>
+
+<change type="feature">
+<para>
+route matching patterns allow a wildcard in the middle.
+</para>
+</change>
+
+<change type="feature">
+<para>
+POST operation for appending elements to arrays in configuration.
+</para>
+</change>
+
+<change type="feature">
+<para>
+support for changing credentials using CAP_SETUID and CAP_SETGID capabilities
+on Linux without running main process as privileged user.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+memory leak in the router process might have happened when a client
+prematurely closed the connection.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+applying a large configuration might have failed.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+PUT and DELETE operations on array elements in configuration did not work.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+request schema in applications did not reflect TLS connections.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+restored compatibility with Node.js applications that use
+ServerResponse._implicitHeader() function; the bug had appeared in 1.7.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+various compatibility issues with Node.js applications.
+</para>
+</change>
+
+</changes>
+
+
<changes apply="unit-jsc-common" ver="1.8.0" rev="1"
date="2019-03-01" time="18:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index f481ff02..421c4169 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -16,6 +16,19 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit)
MODULES=
+# Ubuntu 19.04
+ifeq ($(CODENAME),disco)
+include Makefile.php
+include Makefile.python27
+include Makefile.python37
+include Makefile.go110
+include Makefile.go111
+include Makefile.perl
+include Makefile.ruby
+include Makefile.jsc-common
+include Makefile.jsc11
+endif
+
# Ubuntu 18.10
ifeq ($(CODENAME),cosmic)
include Makefile.php
diff --git a/pkg/deb/Makefile.go111 b/pkg/deb/Makefile.go111
new file mode 100644
index 00000000..f8ed5ae8
--- /dev/null
+++ b/pkg/deb/Makefile.go111
@@ -0,0 +1,48 @@
+MODULES+= go111
+MODULE_SUFFIX_go111= go1.11
+
+MODULE_SUMMARY_go111= Go 1.11 module for NGINX Unit
+
+MODULE_VERSION_go111= $(VERSION)
+MODULE_RELEASE_go111= 1
+
+MODULE_CONFARGS_go111= go --go=/usr/lib/go-1.11/bin/go --go-path=/usr/share/gocode
+MODULE_MAKEARGS_go111= /usr/lib/go-1.11/bin/go
+MODULE_INSTARGS_go111= /usr/lib/go-1.11/bin/go-install
+
+MODULE_SOURCES_go111= unit.example-go-app \
+ unit.example-go1.11-config
+
+BUILD_DEPENDS_go111= golang-1.11
+BUILD_DEPENDS+= $(BUILD_DEPENDS_go111)
+
+MODULE_BUILD_DEPENDS_go111=,golang-1.11
+MODULE_DEPENDS_go111=,golang-1.11
+
+define MODULE_PREINSTALL_go111
+ mkdir -p debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/go-app
+ install -m 644 -p debian/unit.example-go-app debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/go-app/let-my-people.go
+ install -m 644 -p debian/unit.example-go1.11-config debian/unit-go1.11/usr/share/doc/unit-go1.11/examples/unit.config
+endef
+export MODULE_PREINSTALL_go111
+
+define MODULE_POST_go111
+cat <<BANNER
+----------------------------------------------------------------------
+
+The $(MODULE_SUMMARY_go111) has been installed.
+
+To check out the sample app, run these commands:
+
+ GOPATH=/usr/share/gocode /usr/lib/go-1.11/bin/go build -o /tmp/go1.11-app /usr/share/doc/unit-$(MODULE_SUFFIX_go111)/examples/go-app/let-my-people.go
+ sudo service unit restart
+ cd /usr/share/doc/unit-$(MODULE_SUFFIX_go111)/examples
+ sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config
+ curl http://localhost:8500/
+
+Online documentation is available at https://unit.nginx.org
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_go111
diff --git a/pkg/deb/Makefile.jsc-common b/pkg/deb/Makefile.jsc-common
index 42fdb12f..080e248f 100644
--- a/pkg/deb/Makefile.jsc-common
+++ b/pkg/deb/Makefile.jsc-common
@@ -6,11 +6,17 @@ MODULE_SUMMARY_jsc_common= Java shared packages for NGINX Unit
MODULE_VERSION_jsc_common= $(VERSION)
MODULE_RELEASE_jsc_common= 1
-MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-8-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/
+ifeq ($(CODENAME),disco)
+JAVA_MINVERSION= 11
+else
+JAVA_MINVERSION= 8
+endif
+
+MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-$(JAVA_MINVERSION)-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/
MODULE_MAKEARGS_jsc_common= java
MODULE_INSTARGS_jsc_common= java-shared-install
-BUILD_DEPENDS_jsc_common= openjdk-8-jdk-headless openjdk-8-jre-headless
+BUILD_DEPENDS_jsc_common= openjdk-$(JAVA_MINVERSION)-jdk-headless openjdk-$(JAVA_MINVERSION)-jre-headless
BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc_common)
MODULE_NOARCH_jsc_common= true
diff --git a/pkg/deb/debian.module/unit.example-go-config b/pkg/deb/debian.module/unit.example-go-config
index 079ce0b8..a2c91e80 100644
--- a/pkg/deb/debian.module/unit.example-go-config
+++ b/pkg/deb/debian.module/unit.example-go-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-go1.10-config b/pkg/deb/debian.module/unit.example-go1.10-config
index 29bb9059..61790b73 100644
--- a/pkg/deb/debian.module/unit.example-go1.10-config
+++ b/pkg/deb/debian.module/unit.example-go1.10-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-go1.11-config b/pkg/deb/debian.module/unit.example-go1.11-config
new file mode 100644
index 00000000..5e063a4c
--- /dev/null
+++ b/pkg/deb/debian.module/unit.example-go1.11-config
@@ -0,0 +1,15 @@
+{
+ "applications": {
+ "example_go": {
+ "type": "external",
+ "user": "nobody",
+ "executable": "/tmp/go1.11-app"
+ }
+ },
+
+ "listeners": {
+ "*:8500": {
+ "pass": "applications/example_go"
+ }
+ }
+}
diff --git a/pkg/deb/debian.module/unit.example-go1.7-config b/pkg/deb/debian.module/unit.example-go1.7-config
index bd98fe3a..e1a8e1a4 100644
--- a/pkg/deb/debian.module/unit.example-go1.7-config
+++ b/pkg/deb/debian.module/unit.example-go1.7-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-go1.8-config b/pkg/deb/debian.module/unit.example-go1.8-config
index 1c14ca25..e570f38c 100644
--- a/pkg/deb/debian.module/unit.example-go1.8-config
+++ b/pkg/deb/debian.module/unit.example-go1.8-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-go1.9-config b/pkg/deb/debian.module/unit.example-go1.9-config
index 341894b7..90ef7d5f 100644
--- a/pkg/deb/debian.module/unit.example-go1.9-config
+++ b/pkg/deb/debian.module/unit.example-go1.9-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-jsc10-config b/pkg/deb/debian.module/unit.example-jsc10-config
index 6929356d..969491e5 100644
--- a/pkg/deb/debian.module/unit.example-jsc10-config
+++ b/pkg/deb/debian.module/unit.example-jsc10-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java10"
+ "pass": "applications/example_java10"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-jsc11-config b/pkg/deb/debian.module/unit.example-jsc11-config
index 6c1d9549..3f7dd518 100644
--- a/pkg/deb/debian.module/unit.example-jsc11-config
+++ b/pkg/deb/debian.module/unit.example-jsc11-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java11"
+ "pass": "applications/example_java11"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-jsc8-config b/pkg/deb/debian.module/unit.example-jsc8-config
index 0254677b..4d79112f 100644
--- a/pkg/deb/debian.module/unit.example-jsc8-config
+++ b/pkg/deb/debian.module/unit.example-jsc8-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java8"
+ "pass": "applications/example_java8"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-jsc9-config b/pkg/deb/debian.module/unit.example-jsc9-config
index c64a1aff..a8faa268 100644
--- a/pkg/deb/debian.module/unit.example-jsc9-config
+++ b/pkg/deb/debian.module/unit.example-jsc9-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java9"
+ "pass": "applications/example_java9"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-perl-config b/pkg/deb/debian.module/unit.example-perl-config
index 0329b78a..031928ce 100644
--- a/pkg/deb/debian.module/unit.example-perl-config
+++ b/pkg/deb/debian.module/unit.example-perl-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8600": {
- "application": "example_perl"
+ "pass": "applications/example_perl"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-php-config b/pkg/deb/debian.module/unit.example-php-config
index 71564df3..8f23c984 100644
--- a/pkg/deb/debian.module/unit.example-php-config
+++ b/pkg/deb/debian.module/unit.example-php-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8300": {
- "application": "example_php"
+ "pass": "applications/example_php"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python-config b/pkg/deb/debian.module/unit.example-python-config
index 2866cc16..d612c89d 100644
--- a/pkg/deb/debian.module/unit.example-python-config
+++ b/pkg/deb/debian.module/unit.example-python-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python2.7-config b/pkg/deb/debian.module/unit.example-python2.7-config
index 70df9d6d..bede8899 100644
--- a/pkg/deb/debian.module/unit.example-python2.7-config
+++ b/pkg/deb/debian.module/unit.example-python2.7-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python3.4-config b/pkg/deb/debian.module/unit.example-python3.4-config
index fbda2886..dd496bd8 100644
--- a/pkg/deb/debian.module/unit.example-python3.4-config
+++ b/pkg/deb/debian.module/unit.example-python3.4-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python3.5-config b/pkg/deb/debian.module/unit.example-python3.5-config
index 495995ef..2be6de4a 100644
--- a/pkg/deb/debian.module/unit.example-python3.5-config
+++ b/pkg/deb/debian.module/unit.example-python3.5-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python3.6-config b/pkg/deb/debian.module/unit.example-python3.6-config
index 1a75fdbf..a77e8e07 100644
--- a/pkg/deb/debian.module/unit.example-python3.6-config
+++ b/pkg/deb/debian.module/unit.example-python3.6-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-python3.7-config b/pkg/deb/debian.module/unit.example-python3.7-config
index 00c24f23..9b13c058 100644
--- a/pkg/deb/debian.module/unit.example-python3.7-config
+++ b/pkg/deb/debian.module/unit.example-python3.7-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/deb/debian.module/unit.example-ruby-config b/pkg/deb/debian.module/unit.example-ruby-config
index 53eee534..15a92735 100644
--- a/pkg/deb/debian.module/unit.example-ruby-config
+++ b/pkg/deb/debian.module/unit.example-ruby-config
@@ -10,7 +10,7 @@
"listeners": {
"*:8700": {
- "application": "example_ruby"
+ "pass": "applications/example_ruby"
}
}
}
diff --git a/pkg/deb/debian/unit.example.config b/pkg/deb/debian/unit.example.config
index 8d86acbe..5610cb3a 100644
--- a/pkg/deb/debian/unit.example.config
+++ b/pkg/deb/debian/unit.example.config
@@ -33,19 +33,19 @@
"listeners": {
"*:8300": {
- "application": "example_php"
+ "pass": "applications/example_php"
},
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
},
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
},
"*:8600": {
- "application": "example_perl"
+ "pass": "applications/example_perl"
}
}
}
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
index 7e688710..14afc75b 100644
--- a/pkg/docker/Dockerfile.full
+++ b/pkg/docker/Dockerfile.full
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.go1.7-dev b/pkg/docker/Dockerfile.go1.7-dev
index c0245ea7..ad3d888d 100644
--- a/pkg/docker/Dockerfile.go1.7-dev
+++ b/pkg/docker/Dockerfile.go1.7-dev
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.go1.8-dev b/pkg/docker/Dockerfile.go1.8-dev
index 3c13c018..915d859a 100644
--- a/pkg/docker/Dockerfile.go1.8-dev
+++ b/pkg/docker/Dockerfile.go1.8-dev
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index b48f8410..5214c24a 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.perl5.24 b/pkg/docker/Dockerfile.perl5.24
index 4d5f502e..c2c91866 100644
--- a/pkg/docker/Dockerfile.perl5.24
+++ b/pkg/docker/Dockerfile.perl5.24
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.php7.0 b/pkg/docker/Dockerfile.php7.0
index 0afcf28a..b14a38b4 100644
--- a/pkg/docker/Dockerfile.php7.0
+++ b/pkg/docker/Dockerfile.php7.0
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
index 57619e26..82de3318 100644
--- a/pkg/docker/Dockerfile.python2.7
+++ b/pkg/docker/Dockerfile.python2.7
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python3.5 b/pkg/docker/Dockerfile.python3.5
index 410f395c..b103d8f9 100644
--- a/pkg/docker/Dockerfile.python3.5
+++ b/pkg/docker/Dockerfile.python3.5
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.ruby2.3 b/pkg/docker/Dockerfile.ruby2.3
index 7b674178..50ec7ed6 100644
--- a/pkg/docker/Dockerfile.ruby2.3
+++ b/pkg/docker/Dockerfile.ruby2.3
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.8.0-1~stretch
+ENV UNIT_VERSION 1.9.0-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile
index d94890f2..9e343aa2 100644
--- a/pkg/rpm/Makefile
+++ b/pkg/rpm/Makefile
@@ -11,6 +11,8 @@ ifeq ($(shell test `rpm --eval '0%{?rhel} -eq 6 -a 0%{?amzn} -eq 0'`; echo $$?),
OSVER = centos6
else ifeq ($(shell test `rpm --eval '0%{?rhel} -eq 7 -a 0%{?amzn} -eq 0'`; echo $$?), 0)
OSVER = centos7
+else ifeq ($(shell rpm --eval "%{?rhel}"), 8)
+OSVER = centos8
else ifeq ($(shell rpm --eval "%{?amzn}"), 1)
OSVER = amazonlinux1
else ifeq ($(shell rpm --eval "%{?amzn}"), 2)
@@ -63,6 +65,17 @@ include Makefile.jsc8
include Makefile.jsc11
endif
+ifeq ($(OSVER), centos8)
+include Makefile.php
+include Makefile.python27
+include Makefile.python36
+include Makefile.go
+include Makefile.perl
+include Makefile.jsc-common
+include Makefile.jsc8
+include Makefile.jsc11
+endif
+
ifeq ($(OSVER), amazonlinux1)
include Makefile.php
include Makefile.python27
diff --git a/pkg/rpm/Makefile.python27 b/pkg/rpm/Makefile.python27
index 005eff17..95b392a9 100644
--- a/pkg/rpm/Makefile.python27
+++ b/pkg/rpm/Makefile.python27
@@ -15,7 +15,7 @@ MODULE_SOURCES_python27= unit.example-python-app \
ifneq (,$(findstring $(OSVER),opensuse-leap opensuse-tumbleweed sles))
BUILD_DEPENDS_python27= python-devel
-else ifeq ($(OSVER), fedora)
+else ifneq (,$(findstring $(OSVER),fedora centos8))
BUILD_DEPENDS_python27= python2-devel
else
BUILD_DEPENDS_python27= python27-devel
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
index 079ce0b8..a2c91e80 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc11-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc11-config
index 6c1d9549..3f7dd518 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc11-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc11-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java11"
+ "pass": "applications/example_java11"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc8-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc8-config
index 0254677b..4d79112f 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc8-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-jsc8-config
@@ -9,7 +9,7 @@
"listeners": {
"*:8800": {
- "application": "example_java8"
+ "pass": "applications/example_java8"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
index 0329b78a..031928ce 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8600": {
- "application": "example_perl"
+ "pass": "applications/example_perl"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
index 71564df3..8f23c984 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8300": {
- "application": "example_php"
+ "pass": "applications/example_php"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
index 2866cc16..d612c89d 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
index c8a876f5..7541fcb3 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
index cab381e2..b64e570c 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
index 23e0ea53..025f3428 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
index 758dab25..825cabc4 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
index ada7ae5b..7f5e52f1 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
@@ -11,7 +11,7 @@
"listeners": {
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config
index 53eee534..15a92735 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config
@@ -10,7 +10,7 @@
"listeners": {
"*:8700": {
- "application": "example_ruby"
+ "pass": "applications/example_ruby"
}
}
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example.config b/pkg/rpm/rpmbuild/SOURCES/unit.example.config
index 1715c971..6fe35e2f 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example.config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example.config
@@ -33,19 +33,19 @@
"listeners": {
"*:8300": {
- "application": "example_php"
+ "pass": "applications/example_php"
},
"*:8400": {
- "application": "example_python"
+ "pass": "applications/example_python"
},
"*:8500": {
- "application": "example_go"
+ "pass": "applications/example_go"
},
"*:8600": {
- "application": "example_perl"
+ "pass": "applications/example_perl"
}
}
}
diff --git a/pkg/rpm/unit.module.spec.in b/pkg/rpm/unit.module.spec.in
index ab55d2a4..023ebfab 100644
--- a/pkg/rpm/unit.module.spec.in
+++ b/pkg/rpm/unit.module.spec.in
@@ -48,7 +48,7 @@ This package contains %%SUMMARY%%.
%debug_package
%endif
-%if 0%{?fedora}
+%if (0%{?fedora}) || (0%{?rhel} >= 8)
%define _debugsource_template %{nil}
%endif
diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in
index 2d5c1bd1..afd2f1ff 100644
--- a/pkg/rpm/unit.spec.in
+++ b/pkg/rpm/unit.spec.in
@@ -72,7 +72,7 @@ dynamically via an API.
%debug_package
%endif
-%if 0%{?fedora}
+%if (0%{?fedora}) || (0%{?rhel} >= 8)
%define _debugsource_template %{nil}
%endif
diff --git a/src/go/unit/nxt_cgo_lib.c b/src/go/unit/nxt_cgo_lib.c
index 98a23482..cc1228f5 100644
--- a/src/go/unit/nxt_cgo_lib.c
+++ b/src/go/unit/nxt_cgo_lib.c
@@ -83,6 +83,10 @@ nxt_cgo_request_handler(nxt_unit_request_info_t *req)
nxt_go_request_set_remote_addr(go_req,
nxt_cgo_str_init(&remote_addr, &r->remote, r->remote_length));
+ if (r->tls) {
+ nxt_go_request_set_tls(go_req);
+ }
+
nxt_go_request_handler(go_req, (uintptr_t) req->unit->data);
}
diff --git a/src/go/unit/request.go b/src/go/unit/request.go
index 829a2c64..ad56cabb 100644
--- a/src/go/unit/request.go
+++ b/src/go/unit/request.go
@@ -14,6 +14,7 @@ import (
"io"
"net/http"
"net/url"
+ "crypto/tls"
"unsafe"
)
@@ -125,6 +126,12 @@ func nxt_go_request_set_remote_addr(go_req uintptr, addr *C.nxt_cgo_str_t) {
get_request(go_req).req.RemoteAddr = C.GoStringN(addr.start, addr.length)
}
+//export nxt_go_request_set_tls
+func nxt_go_request_set_tls(go_req uintptr) {
+
+ get_request(go_req).req.TLS = &tls.ConnectionState{ }
+}
+
//export nxt_go_request_handler
func nxt_go_request_handler(go_req uintptr, h uintptr) {
r := get_request(go_req)
diff --git a/src/java/nginx/unit/Context.java b/src/java/nginx/unit/Context.java
index 643a336b..f6d5e339 100644
--- a/src/java/nginx/unit/Context.java
+++ b/src/java/nginx/unit/Context.java
@@ -306,7 +306,7 @@ public class Context implements ServletContext, InitParams
PrintWriter writer = response.getWriter();
for (String n : ls) {
- writer.println("<a href=\"" + n + "\">" + n + "</a><br>");
+ writer.println("<a href=\"" + n + "\">" + n + "</a><br>");
}
writer.close();
@@ -547,7 +547,7 @@ public class Context implements ServletContext, InitParams
j = j.getParent();
}
}
- system_loader = j;
+ system_loader = j;
}
private boolean isSystemPath(String path)
@@ -1733,7 +1733,7 @@ public class Context implements ServletContext, InitParams
@Override
public FileVisitResult visitFile(
- Path file, BasicFileAttributes attrs)
+ Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
diff --git a/src/java/nginx/unit/Request.java b/src/java/nginx/unit/Request.java
index dc73c656..3ba46f6c 100644
--- a/src/java/nginx/unit/Request.java
+++ b/src/java/nginx/unit/Request.java
@@ -920,7 +920,7 @@ public class Request implements HttpServletRequest, DynamicPathRequest
@Override
public String getScheme()
{
- log("getScheme");
+ trace("getScheme");
return getScheme(req_ptr);
}
@@ -980,11 +980,13 @@ public class Request implements HttpServletRequest, DynamicPathRequest
@Override
public boolean isSecure()
{
- log("isSecure");
+ trace("isSecure");
- return false;
+ return isSecure(req_ptr);
}
+ private static native boolean isSecure(long req_ptr);
+
@Override
public void removeAttribute(String name)
{
diff --git a/src/java/nxt_jni_Context.c b/src/java/nxt_jni_Context.c
index 8f7adddf..589e1c5b 100644
--- a/src/java/nxt_jni_Context.c
+++ b/src/java/nxt_jni_Context.c
@@ -55,7 +55,7 @@ nxt_java_initContext(JNIEnv *env, jobject cl)
}
nxt_java_Context_stop = (*env)->GetMethodID(env, cls, "stop", "()V");
- if (nxt_java_Context_service == NULL) {
+ if (nxt_java_Context_stop == NULL) {
nxt_unit_warn(NULL, "nginx.unit.Context.stop() not found");
goto failed;
}
diff --git a/src/java/nxt_jni_Request.c b/src/java/nxt_jni_Request.c
index 6fb9cb44..733290dd 100644
--- a/src/java/nxt_jni_Request.c
+++ b/src/java/nxt_jni_Request.c
@@ -56,6 +56,8 @@ static jstring JNICALL nxt_java_Request_getServerName(JNIEnv *env, jclass cls,
jlong req_ptr);
static jint JNICALL nxt_java_Request_getServerPort(JNIEnv *env, jclass cls,
jlong req_ptr);
+static jboolean JNICALL nxt_java_Request_isSecure(JNIEnv *env, jclass cls,
+ jlong req_ptr);
static void JNICALL nxt_java_Request_log(JNIEnv *env, jclass cls,
jlong req_info_ptr, jstring msg, jint msg_len);
static void JNICALL nxt_java_Request_trace(JNIEnv *env, jclass cls,
@@ -166,6 +168,10 @@ nxt_java_initRequest(JNIEnv *env, jobject cl)
(char *) "(J)I",
nxt_java_Request_getServerPort },
+ { (char *) "isSecure",
+ (char *) "(J)Z",
+ nxt_java_Request_isSecure },
+
{ (char *) "log",
(char *) "(JLjava/lang/String;I)V",
nxt_java_Request_log },
@@ -536,7 +542,11 @@ nxt_java_Request_getRemotePort(JNIEnv *env, jclass cls, jlong req_ptr)
static jstring JNICALL
nxt_java_Request_getScheme(JNIEnv *env, jclass cls, jlong req_ptr)
{
- return (*env)->NewStringUTF(env, "http");
+ nxt_unit_request_t *r;
+
+ r = nxt_jlong2ptr(req_ptr);
+
+ return (*env)->NewStringUTF(env, r->tls ? "https" : "http");
}
@@ -603,6 +613,17 @@ nxt_java_Request_getServerPort(JNIEnv *env, jclass cls, jlong req_ptr)
}
+static jboolean JNICALL
+nxt_java_Request_isSecure(JNIEnv *env, jclass cls, jlong req_ptr)
+{
+ nxt_unit_request_t *r;
+
+ r = nxt_jlong2ptr(req_ptr);
+
+ return r->tls != 0;
+}
+
+
static void JNICALL
nxt_java_Request_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jstring msg,
jint msg_len)
diff --git a/src/nodejs/unit-http/binding.gyp b/src/nodejs/unit-http/binding.gyp
index ee09bfed..55d965bd 100644
--- a/src/nodejs/unit-http/binding.gyp
+++ b/src/nodejs/unit-http/binding.gyp
@@ -1,6 +1,15 @@
{
'targets': [{
'target_name': "unit-http",
+ 'cflags!': [ '-fno-exceptions' ],
+ 'cflags_cc!': [ '-fno-exceptions' ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
+ }
+ }]
+ ],
'sources': ["unit.cpp", "addon.cpp"],
'include_dirs': [
"<!(echo $UNIT_SRC_PATH)", "<!(echo $UNIT_BUILD_PATH)"
diff --git a/src/nodejs/unit-http/binding_pub.gyp b/src/nodejs/unit-http/binding_pub.gyp
index 6fe3d9bc..3c39933a 100644
--- a/src/nodejs/unit-http/binding_pub.gyp
+++ b/src/nodejs/unit-http/binding_pub.gyp
@@ -1,6 +1,15 @@
{
'targets': [{
'target_name': "unit-http",
+ 'cflags!': [ '-fno-exceptions' ],
+ 'cflags_cc!': [ '-fno-exceptions' ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
+ }
+ }]
+ ],
'sources': ["unit.cpp", "addon.cpp"],
'libraries': ["-lunit"]
}]
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index 057a1f26..ae8e204a 100755
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -197,6 +197,14 @@ function writeHead(statusCode, reason, obj) {
}
};
+/*
+ * Some Node.js packages are known to be using this undocumented function,
+ * notably "compression" middleware.
+ */
+ServerResponse.prototype._implicitHeader = function _implicitHeader() {
+ this.writeHead(this.statusCode);
+};
+
ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
var contentLength = 0;
@@ -387,6 +395,10 @@ Server.prototype.emit_events = function (server, req, res) {
});
};
+Server.prototype.emit_close = function () {
+ this.emit('close');
+};
+
function connectionListener(socket) {
}
diff --git a/src/nodejs/unit-http/nxt_napi.h b/src/nodejs/unit-http/nxt_napi.h
new file mode 100644
index 00000000..9bcf3a21
--- /dev/null
+++ b/src/nodejs/unit-http/nxt_napi.h
@@ -0,0 +1,656 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_NODEJS_NAPI_H_INCLUDED_
+#define _NXT_NODEJS_NAPI_H_INCLUDED_
+
+#include <node_api.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "version.h"
+#include <nxt_unit.h>
+
+#if NXT_VERNUM != NXT_NODE_VERNUM
+#error "libunit version mismatch."
+#endif
+
+#include <nxt_unit_response.h>
+#include <nxt_unit_request.h>
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+struct nxt_napi {
+
+ struct exception {
+ exception(const char *s) : str(s) { }
+
+ const char *str;
+ };
+
+
+ nxt_napi(napi_env env) : env_(env) { }
+
+
+ inline napi_value
+ coerce_to_string(napi_value val)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_coerce_to_string(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to coerce to string");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ create_buffer(size_t size, void **data)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_create_buffer(env_, size, data, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create buffer");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ create_function(const char *name, size_t len, napi_callback cb, void *data)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_create_function(env_, name, len, cb, data, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create function");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ create_function(napi_callback cb)
+ {
+ return create_function(NULL, 0, cb, NULL);
+ }
+
+
+ inline napi_value
+ create_object()
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_create_object(env_, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create object");
+ }
+
+ return res;
+ }
+
+
+ inline napi_ref
+ create_reference(napi_value val, int ref_count = 1)
+ {
+ napi_ref res;
+ napi_status status;
+
+ status = napi_create_reference(env_, val, ref_count, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create reference");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ create_string_latin1(const char *str, size_t len)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_create_string_latin1(env_, str, len, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create latin1 string");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ create_string_latin1(nxt_unit_sptr_t &str, size_t len)
+ {
+ const char *p;
+
+ p = (const char *) nxt_unit_sptr_get(&str);
+
+ return create_string_latin1(p, len);
+ }
+
+
+ inline napi_value
+ define_class(const char *name, napi_callback ctor, size_t prop_count,
+ const napi_property_descriptor* props)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_define_class(env_, name, NAPI_AUTO_LENGTH, ctor, nullptr,
+ prop_count, props, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to define class");
+ }
+
+ return res;
+ }
+
+
+ inline void
+ delete_reference(napi_ref ref)
+ {
+ napi_delete_reference(env_, ref);
+ }
+
+
+ inline uint32_t
+ get_array_length(napi_value val)
+ {
+ uint32_t res;
+ napi_status status;
+
+ status = napi_get_array_length(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get array length");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_cb_info(napi_callback_info info, size_t &argc, napi_value *argv)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_cb_info(env_, info, &argc, argv, &res, nullptr);
+ if (status != napi_ok) {
+ throw exception("Failed to get arguments from js");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_cb_info(napi_callback_info info)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_cb_info(env_, info, nullptr, nullptr, &res, nullptr);
+ if (status != napi_ok) {
+ throw exception("Failed to get arguments from js");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_element(napi_value obj, uint32_t i)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_element(env_, obj, i, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get element");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_named_property(napi_value obj, const char *name)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_named_property(env_, obj, name, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get named property");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_new_target(napi_callback_info info)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_new_target(env_, info, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get new target");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_property(napi_value val, napi_value key)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_property(env_, val, key, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get property");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_property_names(napi_value val)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_property_names(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get property names");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ get_reference_value(napi_ref ref)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_get_reference_value(env_, ref, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get reference value");
+ }
+
+ return res;
+ }
+
+
+ inline nxt_unit_request_info_t *
+ get_request_info(napi_value obj)
+ {
+ int64_t n;
+ napi_status status;
+
+ status = napi_get_value_int64(env_, obj, &n);
+ if (status != napi_ok) {
+ throw exception("Failed to get request pointer");
+ }
+
+ return (nxt_unit_request_info_t *) (intptr_t) n;
+ }
+
+
+ inline size_t
+ get_value_string_latin1(napi_value val, char *buf, size_t bufsize)
+ {
+ size_t res;
+ napi_status status;
+
+ status = napi_get_value_string_latin1(env_, val, buf, bufsize, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get string latin1");
+ }
+
+ return res;
+ }
+
+
+ inline uint32_t
+ get_value_uint32(napi_value obj)
+ {
+ uint32_t res;
+ napi_status status;
+
+ status = napi_get_value_uint32(env_, obj, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get uint32_t");
+ }
+
+ return res;
+ }
+
+
+ inline bool
+ is_array(napi_value val)
+ {
+ bool res;
+ napi_status status;
+
+ status = napi_is_array(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to confirm value is array");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ make_callback(napi_async_context ctx, napi_value val, napi_value func,
+ int argc, const napi_value *argv)
+ {
+ napi_value res, ex;
+ napi_status status;
+
+ status = napi_make_callback(env_, ctx, val, func, argc, argv, &res);
+ if (status != napi_ok) {
+ if (status != napi_pending_exception) {
+ throw exception("Failed to make callback");
+ }
+
+ status = napi_get_and_clear_last_exception(env_, &ex);
+ if (status != napi_ok) {
+ throw exception("Failed to get and clear last exception");
+ }
+
+ /* Logging a description of the error and call stack. */
+ status = napi_fatal_exception(env_, ex);
+ if (status != napi_ok) {
+ throw exception("Failed napi_fatal_exception()");
+ }
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ new_instance(napi_value ctor)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_new_instance(env_, ctor, 0, NULL, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create instance");
+ }
+
+ return res;
+ }
+
+
+ inline napi_value
+ new_instance(napi_value ctor, napi_value param)
+ {
+ napi_value res;
+ napi_status status;
+
+ status = napi_new_instance(env_, ctor, 1, &param, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to create instance");
+ }
+
+ return res;
+ }
+
+
+ inline void
+ set_element(napi_value obj, uint32_t i, napi_value val)
+ {
+ napi_status status;
+
+ status = napi_set_element(env_, obj, i, val);
+ if (status != napi_ok) {
+ throw exception("Failed to set element");
+ }
+ }
+
+
+ inline void
+ set_named_property(napi_value obj, const char *name, napi_value val)
+ {
+ napi_status status;
+
+ status = napi_set_named_property(env_, obj, name, val);
+ if (status != napi_ok) {
+ throw exception("Failed to set named property");
+ }
+ }
+
+
+ inline void
+ set_named_property(napi_value obj, const char *name, napi_callback cb)
+ {
+ set_named_property(obj, name, create_function(cb));
+ }
+
+
+ inline napi_value
+ set_named_property(napi_value obj, const char *name, nxt_unit_sptr_t &val,
+ size_t len)
+ {
+ napi_value str;
+
+ str = create_string_latin1(val, len);
+
+ set_named_property(obj, name, str);
+
+ return str;
+ }
+
+
+ inline void
+ set_named_property(napi_value obj, const char *name, intptr_t val)
+ {
+ napi_value ptr;
+ napi_status status;
+
+ status = napi_create_int64(env_, val, &ptr);
+ if (status != napi_ok) {
+ throw exception("Failed to create int64");
+ }
+
+ set_named_property(obj, name, ptr);
+ }
+
+
+ inline void
+ throw_error(const char *str)
+ {
+ napi_throw_error(env_, NULL, str);
+ }
+
+
+ inline void
+ throw_error(const exception &e)
+ {
+ napi_throw_error(env_, NULL, e.str);
+ }
+
+
+ inline napi_valuetype
+ type_of(napi_value val)
+ {
+ napi_status status;
+ napi_valuetype res;
+
+ status = napi_typeof(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to get typeof");
+ }
+
+ return res;
+ }
+
+
+ inline void *
+ unwrap(napi_value val)
+ {
+ void *res;
+ napi_status status;
+
+ status = napi_unwrap(env_, val, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to unwrap");
+ }
+
+ return res;
+ }
+
+
+ inline napi_ref
+ wrap(napi_value val, void *obj, napi_finalize fin_cb, void *hint = nullptr)
+ {
+ napi_ref res;
+ napi_status status;
+
+ status = napi_wrap(env_, val, obj, fin_cb, hint, &res);
+ if (status != napi_ok) {
+ throw exception("Failed to wrap");
+ }
+
+ return res;
+ }
+
+
+ inline
+ operator napi_env()
+ {
+ return env_;
+ }
+
+
+ napi_env env()
+ {
+ return env_;
+ }
+
+private:
+ napi_env env_;
+};
+
+
+struct nxt_handle_scope : public nxt_napi {
+ nxt_handle_scope(napi_env env) : nxt_napi(env)
+ {
+ napi_status status;
+
+ status = napi_open_handle_scope(env, &scope_);
+ if (status != napi_ok) {
+ throw exception("Failed to open handle scope");
+ }
+ }
+
+ ~nxt_handle_scope()
+ {
+ napi_status status;
+
+ status = napi_close_handle_scope(env(), scope_);
+ if (status != napi_ok) {
+ throw_error("Failed to close handle scope");
+ }
+ }
+
+private:
+ napi_handle_scope scope_;
+};
+
+
+struct nxt_async_context : public nxt_napi {
+ nxt_async_context(napi_env env, const char *name) :
+ nxt_napi(env)
+ {
+ napi_value name_val;
+ napi_status status;
+
+ name_val = create_string_latin1(name, NAPI_AUTO_LENGTH);
+
+ status = napi_async_init(env, NULL, name_val, &context_);
+ if (status != napi_ok) {
+ throw exception("Failed to init async object");
+ }
+ }
+
+ operator napi_async_context() {
+ return context_;
+ }
+
+ ~nxt_async_context()
+ {
+ napi_status status;
+
+ status = napi_async_destroy(env(), context_);
+ if (status != napi_ok) {
+ throw_error("Failed to destroy async object");
+ }
+ }
+
+private:
+ napi_async_context context_;
+};
+
+
+struct nxt_callback_scope : public nxt_napi {
+ nxt_callback_scope(nxt_async_context& ctx) :
+ nxt_napi(ctx.env())
+ {
+ napi_value resource;
+ napi_status status;
+
+ resource = create_object();
+
+ status = napi_open_callback_scope(env(), resource, ctx, &scope_);
+ if (status != napi_ok) {
+ throw exception("Failed to open callback scope");
+ }
+ }
+
+ ~nxt_callback_scope()
+ {
+ napi_status status;
+
+ status = napi_close_callback_scope(env(), scope_);
+ if (status != napi_ok) {
+ throw_error("Failed to close callback scope");
+ }
+ }
+
+private:
+ napi_callback_scope scope_;
+};
+
+
+#endif /* _NXT_NODEJS_NAPI_H_INCLUDED_ */
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index 60b0412a..3f66189a 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -20,9 +20,9 @@ struct nxt_nodejs_ctx_t {
};
-Unit::Unit(napi_env env):
- env_(env),
- wrapper_(nullptr),
+Unit::Unit(napi_env env, napi_value jsthis):
+ nxt_napi(env),
+ wrapper_(wrap(jsthis, this, destroy)),
unit_ctx_(nullptr)
{
}
@@ -30,15 +30,15 @@ Unit::Unit(napi_env env):
Unit::~Unit()
{
- napi_delete_reference(env_, wrapper_);
+ delete_reference(wrapper_);
}
napi_value
Unit::init(napi_env env, napi_value exports)
{
- napi_value cons, fn;
- napi_status status;
+ nxt_napi napi(env);
+ napi_value cons;
napi_property_descriptor properties[] = {
{ "createServer", 0, create_server, 0, 0, 0, napi_default, 0 },
@@ -46,61 +46,22 @@ Unit::init(napi_env env, napi_value exports)
{ "_read", 0, _read, 0, 0, 0, napi_default, 0 }
};
- status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr,
- 3, properties, &cons);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_create_reference(env, cons, 1, &constructor_);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_set_named_property(env, exports, "Unit", cons);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_create_function(env, NULL, 0, response_send_headers, NULL,
- &fn);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_set_named_property(env, exports,
- "unit_response_headers", fn);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_create_function(env, NULL, 0, response_write, NULL, &fn);
- if (status != napi_ok) {
- goto failed;
- }
+ try {
+ cons = napi.define_class("Unit", create, 3, properties);
+ constructor_ = napi.create_reference(cons);
- status = napi_set_named_property(env, exports, "unit_response_write", fn);
- if (status != napi_ok) {
- goto failed;
- }
+ napi.set_named_property(exports, "Unit", cons);
+ napi.set_named_property(exports, "unit_response_headers",
+ response_send_headers);
+ napi.set_named_property(exports, "unit_response_write", response_write);
+ napi.set_named_property(exports, "unit_response_end", response_end);
- status = napi_create_function(env, NULL, 0, response_end, NULL, &fn);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_set_named_property(env, exports, "unit_response_end", fn);
- if (status != napi_ok) {
- goto failed;
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
}
return exports;
-
-failed:
-
- napi_throw_error(env, NULL, "Failed to define Unit class");
-
- return nullptr;
}
@@ -116,63 +77,33 @@ Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
napi_value
Unit::create(napi_env env, napi_callback_info info)
{
- Unit *obj;
- napi_ref ref;
- napi_value target, cons, instance, jsthis;
- napi_status status;
-
- status = napi_get_new_target(env, info, &target);
- if (status != napi_ok) {
- goto failed;
- }
+ nxt_napi napi(env);
+ napi_value target, cons, instance, jsthis;
- if (target != nullptr) {
- /* Invoked as constructor: `new Unit(...)` */
- status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis,
- nullptr);
- if (status != napi_ok) {
- goto failed;
- }
+ try {
+ target = napi.get_new_target(info);
- obj = new Unit(env);
+ if (target != nullptr) {
+ /* Invoked as constructor: `new Unit(...)`. */
+ jsthis = napi.get_cb_info(info);
- status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
- destroy, nullptr, &obj->wrapper_);
- if (status != napi_ok) {
- goto failed;
- }
+ new Unit(env, jsthis);
+ napi.create_reference(jsthis);
- status = napi_create_reference(env, jsthis, 1, &ref);
- if (status != napi_ok) {
- goto failed;
+ return jsthis;
}
- return jsthis;
- }
-
- /* Invoked as plain function `Unit(...)`, turn into construct call. */
- status = napi_get_reference_value(env, constructor_, &cons);
- if (status != napi_ok) {
- goto failed;
- }
+ /* Invoked as plain function `Unit(...)`, turn into construct call. */
+ cons = napi.get_reference_value(constructor_);
+ instance = napi.new_instance(cons);
+ napi.create_reference(instance);
- status = napi_new_instance(env, cons, 0, nullptr, &instance);
- if (status != napi_ok) {
- goto failed;
- }
-
- status = napi_create_reference(env, instance, 1, &ref);
- if (status != napi_ok) {
- goto failed;
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
}
return instance;
-
-failed:
-
- napi_throw_error(env, NULL, "Failed to create Unit object");
-
- return nullptr;
}
@@ -181,20 +112,19 @@ Unit::create_server(napi_env env, napi_callback_info info)
{
Unit *obj;
size_t argc;
+ nxt_napi napi(env);
napi_value jsthis, argv;
- napi_status status;
nxt_unit_init_t unit_init;
argc = 1;
- status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
- if (status != napi_ok) {
- goto failed;
- }
+ try {
+ jsthis = napi.get_cb_info(info, argc, &argv);
+ obj = (Unit *) napi.unwrap(jsthis);
- status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
- if (status != napi_ok) {
- goto failed;
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
}
memset(&unit_init, 0, sizeof(nxt_unit_init_t));
@@ -230,40 +160,22 @@ Unit::listen(napi_env env, napi_callback_info info)
napi_value
Unit::_read(napi_env env, napi_callback_info info)
{
- Unit *obj;
void *data;
size_t argc;
- int64_t req_pointer;
- napi_value jsthis, buffer, argv;
- napi_status status;
+ nxt_napi napi(env);
+ napi_value buffer, argv;
nxt_unit_request_info_t *req;
argc = 1;
- status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get arguments from js");
- return nullptr;
- }
+ try {
+ napi.get_cb_info(info, argc, &argv);
- status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get Unit object form js");
- return nullptr;
- }
+ req = napi.get_request_info(argv);
+ buffer = napi.create_buffer((size_t) req->content_length, &data);
- status = napi_get_value_int64(env, argv, &req_pointer);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
-
- req = (nxt_unit_request_info_t *) (uintptr_t) req_pointer;
-
- status = napi_create_buffer(env, (size_t) req->content_length,
- &data, &buffer);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to create request buffer");
+ } catch (exception &e) {
+ napi.throw_error(e);
return nullptr;
}
@@ -276,138 +188,38 @@ Unit::_read(napi_env env, napi_callback_info info)
void
Unit::request_handler(nxt_unit_request_info_t *req)
{
- Unit *obj;
- napi_value socket, request, response, global, server_obj, except;
- napi_value emit_events, events_res, async_name, resource_object;
- napi_status status;
- napi_async_context async_context;
- napi_callback_scope async_scope;
- napi_value events_args[3];
+ Unit *obj;
+ napi_value socket, request, response, server_obj;
+ napi_value emit_events;
+ napi_value events_args[3];
obj = reinterpret_cast<Unit *>(req->unit->data);
- napi_handle_scope scope;
- status = napi_open_handle_scope(obj->env_, &scope);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to create handle scope");
- return;
- }
-
- server_obj = obj->get_server_object();
- if (server_obj == nullptr) {
- napi_throw_error(obj->env_, NULL, "Failed to get server object");
- return;
- }
-
- status = napi_get_global(obj->env_, &global);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to get global variable");
- return;
- }
-
- socket = obj->create_socket(server_obj, req);
- if (socket == nullptr) {
- napi_throw_error(obj->env_, NULL, "Failed to create socket object");
- return;
- }
-
- request = obj->create_request(server_obj, socket);
- if (request == nullptr) {
- napi_throw_error(obj->env_, NULL, "Failed to create request object");
- return;
- }
-
- response = obj->create_response(server_obj, socket, request, req, obj);
- if (response == nullptr) {
- napi_throw_error(obj->env_, NULL, "Failed to create response object");
- return;
- }
-
- status = obj->create_headers(req, request);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to create headers");
- return;
- }
-
- status = napi_get_named_property(obj->env_, server_obj, "emit_events",
- &emit_events);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to get "
- "'emit_events' function");
- return;
- }
-
- events_args[0] = server_obj;
- events_args[1] = request;
- events_args[2] = response;
+ try {
+ nxt_handle_scope scope(obj->env());
- status = napi_create_string_utf8(obj->env_, "unit_request_handler",
- sizeof("unit_request_handler") - 1,
- &async_name);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to create utf-8 string");
- return;
- }
+ server_obj = obj->get_server_object();
- status = napi_async_init(obj->env_, NULL, async_name, &async_context);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to init async object");
- return;
- }
+ socket = obj->create_socket(server_obj, req);
+ request = obj->create_request(server_obj, socket);
+ response = obj->create_response(server_obj, socket, request, req);
- status = napi_create_object(obj->env_, &resource_object);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to create object for "
- "callback scope");
- return;
- }
+ obj->create_headers(req, request);
- status = napi_open_callback_scope(obj->env_, resource_object, async_context,
- &async_scope);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to open callback scope");
- return;
- }
+ emit_events = obj->get_named_property(server_obj, "emit_events");
- status = napi_make_callback(obj->env_, async_context, server_obj,
- emit_events, 3, events_args, &events_res);
- if (status != napi_ok) {
- if (status != napi_pending_exception) {
- napi_throw_error(obj->env_, NULL, "Failed to make callback");
- return;
- }
+ events_args[0] = server_obj;
+ events_args[1] = request;
+ events_args[2] = response;
- status = napi_get_and_clear_last_exception(obj->env_, &except);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL,
- "Failed to get and clear last exception");
- return;
- }
-
- /* Logging a description of the error and call stack. */
- status = napi_fatal_exception(obj->env_, except);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to call "
- "napi_fatal_exception() function");
- return;
- }
- }
+ nxt_async_context async_context(obj->env(), "unit_request_handler");
+ nxt_callback_scope async_scope(async_context);
- status = napi_close_callback_scope(obj->env_, async_scope);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to close callback scope");
- return;
- }
+ obj->make_callback(async_context, server_obj, emit_events,
+ 3, events_args);
- status = napi_async_destroy(obj->env_, async_context);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to destroy async object");
- return;
- }
-
- status = napi_close_handle_scope(obj->env_, scope);
- if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to close handle scope");
+ } catch (exception &e) {
+ obj->throw_error(e);
}
}
@@ -432,14 +244,14 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
obj = reinterpret_cast<Unit *>(ctx->unit->data);
if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
- napi_throw_error(obj->env_, NULL, "Failed to upgrade read"
+ obj->throw_error("Failed to upgrade read"
" file descriptor to O_NONBLOCK");
return -1;
}
- status = napi_get_uv_event_loop(obj->env_, &loop);
+ status = napi_get_uv_event_loop(obj->env(), &loop);
if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to get uv.loop");
+ obj->throw_error("Failed to get uv.loop");
return NXT_UNIT_ERROR;
}
@@ -447,13 +259,13 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
err = uv_poll_init(loop, &node_ctx->poll, port->in_fd);
if (err < 0) {
- napi_throw_error(obj->env_, NULL, "Failed to init uv.poll");
+ obj->throw_error("Failed to init uv.poll");
return NXT_UNIT_ERROR;
}
err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback);
if (err < 0) {
- napi_throw_error(obj->env_, NULL, "Failed to start uv.poll");
+ obj->throw_error("Failed to start uv.poll");
return NXT_UNIT_ERROR;
}
@@ -467,7 +279,7 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
}
-inline bool
+inline bool
operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2)
{
return p1.pid == p2.pid && p1.id == p2.id;
@@ -498,6 +310,27 @@ Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
void
Unit::quit(nxt_unit_ctx_t *ctx)
{
+ Unit *obj;
+ napi_value server_obj, emit_close;
+
+ obj = reinterpret_cast<Unit *>(ctx->unit->data);
+
+ try {
+ nxt_handle_scope scope(obj->env());
+
+ server_obj = obj->get_server_object();
+
+ emit_close = obj->get_named_property(server_obj, "emit_close");
+
+ nxt_async_context async_context(obj->env(), "unit_quit");
+ nxt_callback_scope async_scope(async_context);
+
+ obj->make_callback(async_context, server_obj, emit_close, 0, NULL);
+
+ } catch (exception &e) {
+ obj->throw_error(e);
+ }
+
nxt_unit_done(ctx);
}
@@ -505,200 +338,105 @@ Unit::quit(nxt_unit_ctx_t *ctx)
napi_value
Unit::get_server_object()
{
- napi_value unit_obj, server_obj;
- napi_status status;
+ napi_value unit_obj;
- status = napi_get_reference_value(env_, wrapper_, &unit_obj);
- if (status != napi_ok) {
- return nullptr;
- }
-
- status = napi_get_named_property(env_, unit_obj, "server", &server_obj);
- if (status != napi_ok) {
- return nullptr;
- }
+ unit_obj = get_reference_value(wrapper_);
- return server_obj;
+ return get_named_property(unit_obj, "server");
}
-napi_status
+void
Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
{
uint32_t i;
- const char *p;
- napi_value headers, raw_headers, str;
+ napi_value headers, raw_headers;
napi_status status;
- nxt_unit_field_t *f;
nxt_unit_request_t *r;
r = req->request;
- status = napi_create_object(env_, &headers);
- if (status != napi_ok) {
- return status;
- }
+ headers = create_object();
- status = napi_create_array_with_length(env_, r->fields_count * 2,
+ status = napi_create_array_with_length(env(), r->fields_count * 2,
&raw_headers);
if (status != napi_ok) {
- return status;
+ throw exception("Failed to create array");
}
for (i = 0; i < r->fields_count; i++) {
- f = r->fields + i;
-
- status = this->append_header(f, headers, raw_headers, i);
- if (status != napi_ok) {
- return status;
- }
- }
-
- status = napi_set_named_property(env_, request, "headers", headers);
- if (status != napi_ok) {
- return status;
- }
-
- status = napi_set_named_property(env_, request, "rawHeaders", raw_headers);
- if (status != napi_ok) {
- return status;
- }
-
- p = (const char *) nxt_unit_sptr_get(&r->version);
-
- status = napi_create_string_latin1(env_, p, r->version_length, &str);
- if (status != napi_ok) {
- return status;
- }
-
- status = napi_set_named_property(env_, request, "httpVersion", str);
- if (status != napi_ok) {
- return status;
- }
-
- p = (const char *) nxt_unit_sptr_get(&r->method);
-
- status = napi_create_string_latin1(env_, p, r->method_length, &str);
- if (status != napi_ok) {
- return status;
+ append_header(r->fields + i, headers, raw_headers, i);
}
- status = napi_set_named_property(env_, request, "method", str);
- if (status != napi_ok) {
- return status;
- }
-
- p = (const char *) nxt_unit_sptr_get(&r->target);
-
- status = napi_create_string_latin1(env_, p, r->target_length, &str);
- if (status != napi_ok) {
- return status;
- }
+ set_named_property(request, "headers", headers);
+ set_named_property(request, "rawHeaders", raw_headers);
+ set_named_property(request, "httpVersion", r->version, r->version_length);
+ set_named_property(request, "method", r->method, r->method_length);
+ set_named_property(request, "url", r->target, r->target_length);
+}
- status = napi_set_named_property(env_, request, "url", str);
- if (status != napi_ok) {
- return status;
- }
- return napi_ok;
+inline char
+lowcase(char c)
+{
+ return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
}
-inline napi_status
+inline void
Unit::append_header(nxt_unit_field_t *f, napi_value headers,
- napi_value raw_headers, uint32_t idx)
+ napi_value raw_headers, uint32_t idx)
{
- const char *name, *value;
- napi_value str, vstr;
- napi_status status;
+ char *name;
+ uint8_t i;
+ napi_value str, vstr;
- value = (const char *) nxt_unit_sptr_get(&f->value);
+ name = (char *) nxt_unit_sptr_get(&f->name);
- status = napi_create_string_latin1(env_, value, f->value_length, &vstr);
- if (status != napi_ok) {
- return status;
- }
-
- name = (const char *) nxt_unit_sptr_get(&f->name);
-
- status = napi_set_named_property(env_, headers, name, vstr);
- if (status != napi_ok) {
- return status;
- }
+ str = create_string_latin1(name, f->name_length);
- status = napi_create_string_latin1(env_, name, f->name_length, &str);
- if (status != napi_ok) {
- return status;
+ for (i = 0; i < f->name_length; i++) {
+ name[i] = lowcase(name[i]);
}
- status = napi_set_element(env_, raw_headers, idx * 2, str);
- if (status != napi_ok) {
- return status;
- }
-
- status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr);
- if (status != napi_ok) {
- return status;
- }
+ vstr = set_named_property(headers, name, f->value, f->value_length);
- return napi_ok;
+ set_element(raw_headers, idx * 2, str);
+ set_element(raw_headers, idx * 2 + 1, vstr);
}
napi_value
Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
{
- napi_value constructor, return_val, req_pointer;
- napi_status status;
+ napi_value constructor, res;
+ nxt_unit_request_t *r;
- status = napi_get_named_property(env_, server_obj, "socket",
- &constructor);
- if (status != napi_ok) {
- return nullptr;
- }
+ r = req->request;
- status = napi_new_instance(env_, constructor, 0, NULL, &return_val);
- if (status != napi_ok) {
- return nullptr;
- }
+ constructor = get_named_property(server_obj, "socket");
- status = napi_create_int64(env_, (uintptr_t) req, &req_pointer);
- if (status != napi_ok) {
- return nullptr;
- }
+ res = new_instance(constructor);
- status = napi_set_named_property(env_, return_val, "req_pointer",
- req_pointer);
- if (status != napi_ok) {
- return nullptr;
- }
+ set_named_property(res, "req_pointer", (intptr_t) req);
+ set_named_property(res, "remoteAddress", r->remote, r->remote_length);
+ set_named_property(res, "localAddress", r->local, r->local_length);
- return return_val;
+ return res;
}
napi_value
Unit::create_request(napi_value server_obj, napi_value socket)
{
- napi_value constructor, return_val;
- napi_status status;
+ napi_value constructor, return_val;
- status = napi_get_named_property(env_, server_obj, "request",
- &constructor);
- if (status != napi_ok) {
- return nullptr;
- }
+ constructor = get_named_property(server_obj, "request");
- status = napi_new_instance(env_, constructor, 1, &server_obj,
- &return_val);
- if (status != napi_ok) {
- return nullptr;
- }
+ return_val = new_instance(constructor, server_obj);
- status = napi_set_named_property(env_, return_val, "socket", socket);
- if (status != napi_ok) {
- return nullptr;
- }
+ set_named_property(return_val, "socket", socket);
+ set_named_property(return_val, "connection", socket);
return return_val;
}
@@ -706,37 +444,17 @@ Unit::create_request(napi_value server_obj, napi_value socket)
napi_value
Unit::create_response(napi_value server_obj, napi_value socket,
- napi_value request, nxt_unit_request_info_t *req,
- Unit *obj)
+ napi_value request, nxt_unit_request_info_t *req)
{
- napi_value constructor, return_val, req_num;
- napi_status status;
+ napi_value constructor, return_val;
- status = napi_get_named_property(env_, server_obj, "response",
- &constructor);
- if (status != napi_ok) {
- return nullptr;
- }
+ constructor = get_named_property(server_obj, "response");
- status = napi_new_instance(env_, constructor, 1, &request, &return_val);
- if (status != napi_ok) {
- return nullptr;
- }
+ return_val = new_instance(constructor, request);
- status = napi_set_named_property(env_, return_val, "socket", socket);
- if (status != napi_ok) {
- return nullptr;
- }
-
- status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num);
- if (status != napi_ok) {
- return nullptr;
- }
-
- status = napi_set_named_property(env_, return_val, "_req_point", req_num);
- if (status != napi_ok) {
- return nullptr;
- }
+ set_named_property(return_val, "socket", socket);
+ set_named_property(return_val, "connection", socket);
+ set_named_property(return_val, "_req_point", (intptr_t) req);
return return_val;
}
@@ -749,13 +467,12 @@ Unit::response_send_headers(napi_env env, napi_callback_info info)
char *ptr, *name_ptr;
bool is_array;
size_t argc, name_len, value_len;
- int64_t req_p;
uint32_t status_code, header_len, keys_len, array_len;
uint32_t keys_count, i, j;
uint16_t hash;
+ nxt_napi napi(env);
napi_value this_arg, headers, keys, name, value, array_val;
napi_value req_num, array_entry;
- napi_status status;
napi_valuetype val_type;
nxt_unit_field_t *f;
nxt_unit_request_info_t *req;
@@ -763,137 +480,97 @@ Unit::response_send_headers(napi_env env, napi_callback_info info)
argc = 5;
- status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
- if (status != napi_ok) {
- return nullptr;
- }
+ try {
+ this_arg = napi.get_cb_info(info, argc, argv);
+ if (argc != 5) {
+ napi.throw_error("Wrong args count. Expected: "
+ "statusCode, headers, headers count, "
+ "headers length");
+ return nullptr;
+ }
- if (argc != 5) {
- napi_throw_error(env, NULL, "Wrong args count. Need three: "
- "statusCode, headers, headers count, headers length");
- return nullptr;
- }
+ req_num = napi.get_named_property(argv[0], "_req_point");
- status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
+ req = napi.get_request_info(req_num);
- status = napi_get_value_int64(env, req_num, &req_p);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
+ status_code = napi.get_value_uint32(argv[1]);
+ keys_count = napi.get_value_uint32(argv[3]);
+ header_len = napi.get_value_uint32(argv[4]);
- req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
+ /* Need to reserve extra byte for C-string 0-termination. */
+ header_len++;
- status = napi_get_value_uint32(env, argv[1], &status_code);
- if (status != napi_ok) {
- goto failed;
- }
+ headers = argv[2];
- status = napi_get_value_uint32(env, argv[3], &keys_count);
- if (status != napi_ok) {
- goto failed;
- }
+ ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
+ if (ret != NXT_UNIT_OK) {
+ napi.throw_error("Failed to create response");
+ return nullptr;
+ }
- status = napi_get_value_uint32(env, argv[4], &header_len);
- if (status != napi_ok) {
- goto failed;
- }
+ keys = napi.get_property_names(headers);
+ keys_len = napi.get_array_length(keys);
- /* Need to reserve extra byte for C-string 0-termination. */
- header_len++;
+ ptr = req->response_buf->free;
- headers = argv[2];
+ for (i = 0; i < keys_len; i++) {
+ name = napi.get_element(keys, i);
- ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
- if (ret != NXT_UNIT_OK) {
- goto failed;
- }
+ array_entry = napi.get_property(headers, name);
- status = napi_get_property_names(env, headers, &keys);
- if (status != napi_ok) {
- goto failed;
- }
+ name = napi.get_element(array_entry, 0);
+ value = napi.get_element(array_entry, 1);
- status = napi_get_array_length(env, keys, &keys_len);
- if (status != napi_ok) {
- goto failed;
- }
+ name_len = napi.get_value_string_latin1(name, ptr, header_len);
+ name_ptr = ptr;
- ptr = req->response_buf->free;
+ ptr += name_len;
+ header_len -= name_len;
- for (i = 0; i < keys_len; i++) {
- status = napi_get_element(env, keys, i, &name);
- if (status != napi_ok) {
- goto failed;
- }
+ hash = nxt_unit_field_hash(name_ptr, name_len);
- status = napi_get_property(env, headers, name, &array_entry);
- if (status != napi_ok) {
- goto failed;
- }
+ is_array = napi.is_array(value);
- status = napi_get_element(env, array_entry, 0, &name);
- if (status != napi_ok) {
- goto failed;
- }
+ if (is_array) {
+ array_len = napi.get_array_length(value);
- status = napi_get_element(env, array_entry, 1, &value);
- if (status != napi_ok) {
- goto failed;
- }
+ for (j = 0; j < array_len; j++) {
+ array_val = napi.get_element(value, j);
- status = napi_get_value_string_latin1(env, name, ptr, header_len,
- &name_len);
- if (status != napi_ok) {
- goto failed;
- }
+ val_type = napi.type_of(array_val);
- name_ptr = ptr;
+ if (val_type != napi_string) {
+ array_val = napi.coerce_to_string(array_val);
+ }
- ptr += name_len;
- header_len -= name_len;
+ value_len = napi.get_value_string_latin1(array_val, ptr,
+ header_len);
- hash = nxt_unit_field_hash(name_ptr, name_len);
+ f = req->response->fields + req->response->fields_count;
+ f->skip = 0;
- status = napi_is_array(env, value, &is_array);
- if (status != napi_ok) {
- goto failed;
- }
+ nxt_unit_sptr_set(&f->name, name_ptr);
- if (is_array) {
- status = napi_get_array_length(env, value, &array_len);
- if (status != napi_ok) {
- goto failed;
- }
+ f->name_length = name_len;
+ f->hash = hash;
- for (j = 0; j < array_len; j++) {
- status = napi_get_element(env, value, j, &array_val);
- if (status != napi_ok) {
- goto failed;
- }
+ nxt_unit_sptr_set(&f->value, ptr);
+ f->value_length = (uint32_t) value_len;
- napi_typeof(env, array_val, &val_type);
- if (status != napi_ok) {
- goto failed;
+ ptr += value_len;
+ header_len -= value_len;
+
+ req->response->fields_count++;
}
+ } else {
+ val_type = napi.type_of(value);
+
if (val_type != napi_string) {
- status = napi_coerce_to_string(env, array_val, &array_val);
- if (status != napi_ok) {
- goto failed;
- }
+ value = napi.coerce_to_string(value);
}
- status = napi_get_value_string_latin1(env, array_val, ptr,
- header_len,
- &value_len);
- if (status != napi_ok) {
- goto failed;
- }
+ value_len = napi.get_value_string_latin1(value, ptr, header_len);
f = req->response->fields + req->response->fields_count;
f->skip = 0;
@@ -911,60 +588,22 @@ Unit::response_send_headers(napi_env env, napi_callback_info info)
req->response->fields_count++;
}
-
- } else {
- napi_typeof(env, value, &val_type);
- if (status != napi_ok) {
- goto failed;
- }
-
- if (val_type != napi_string) {
- status = napi_coerce_to_string(env, value, &value);
- if (status != napi_ok) {
- goto failed;
- }
- }
-
- status = napi_get_value_string_latin1(env, value, ptr, header_len,
- &value_len);
- if (status != napi_ok) {
- goto failed;
- }
-
- f = req->response->fields + req->response->fields_count;
- f->skip = 0;
-
- nxt_unit_sptr_set(&f->name, name_ptr);
-
- f->name_length = name_len;
- f->hash = hash;
-
- nxt_unit_sptr_set(&f->value, ptr);
- f->value_length = (uint32_t) value_len;
-
- ptr += value_len;
- header_len -= value_len;
-
- req->response->fields_count++;
}
+
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
}
req->response_buf->free = ptr;
ret = nxt_unit_response_send(req);
if (ret != NXT_UNIT_OK) {
- goto failed;
+ napi.throw_error("Failed to send response");
+ return nullptr;
}
return this_arg;
-
-failed:
-
- req->response->fields_count = 0;
-
- napi_throw_error(env, NULL, "Failed to write headers");
-
- return nullptr;
}
@@ -974,8 +613,8 @@ Unit::response_write(napi_env env, napi_callback_info info)
int ret;
char *ptr;
size_t argc, have_buf_len;
- int64_t req_p;
uint32_t buf_len;
+ nxt_napi napi(env);
napi_value this_arg, req_num;
napi_status status;
nxt_unit_buf_t *buf;
@@ -985,39 +624,23 @@ Unit::response_write(napi_env env, napi_callback_info info)
argc = 3;
- status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
- if (status != napi_ok) {
- goto failed;
- }
-
- if (argc != 3) {
- napi_throw_error(env, NULL, "Wrong args count. Need two: "
- "chunk, chunk length");
- return nullptr;
- }
-
- status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
+ try {
+ this_arg = napi.get_cb_info(info, argc, argv);
+ if (argc != 3) {
+ throw exception("Wrong args count. Expected: "
+ "chunk, chunk length");
+ }
- status = napi_get_value_int64(env, req_num, &req_p);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
+ req_num = napi.get_named_property(argv[0], "_req_point");
+ req = napi.get_request_info(req_num);
- req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
+ buf_len = napi.get_value_uint32(argv[2]);
- status = napi_get_value_uint32(env, argv[2], &buf_len);
- if (status != napi_ok) {
- goto failed;
- }
+ buf_type = napi.type_of(argv[1]);
- status = napi_typeof(env, argv[1], &buf_type);
- if (status != napi_ok) {
- goto failed;
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
}
buf_len++;
@@ -1055,7 +678,7 @@ Unit::response_write(napi_env env, napi_callback_info info)
failed:
- napi_throw_error(env, NULL, "Failed to write body");
+ napi.throw_error("Failed to write body");
return nullptr;
}
@@ -1065,33 +688,23 @@ napi_value
Unit::response_end(napi_env env, napi_callback_info info)
{
size_t argc;
- int64_t req_p;
+ nxt_napi napi(env);
napi_value resp, this_arg, req_num;
- napi_status status;
nxt_unit_request_info_t *req;
argc = 1;
- status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to finalize sending body");
- return nullptr;
- }
+ try {
+ this_arg = napi.get_cb_info(info, argc, &resp);
- status = napi_get_named_property(env, resp, "_req_point", &req_num);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
- return nullptr;
- }
+ req_num = napi.get_named_property(resp, "_req_point");
+ req = napi.get_request_info(req_num);
- status = napi_get_value_int64(env, req_num, &req_p);
- if (status != napi_ok) {
- napi_throw_error(env, NULL, "Failed to get request pointer");
+ } catch (exception &e) {
+ napi.throw_error(e);
return nullptr;
}
- req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
-
nxt_unit_request_done(req, NXT_UNIT_OK);
return this_arg;
diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h
index db85e85c..e76d805a 100644
--- a/src/nodejs/unit-http/unit.h
+++ b/src/nodejs/unit-http/unit.h
@@ -6,34 +6,15 @@
#ifndef _NXT_NODEJS_UNIT_H_INCLUDED_
#define _NXT_NODEJS_UNIT_H_INCLUDED_
-#include <node_api.h>
+#include "nxt_napi.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-#include "version.h"
-#include <nxt_unit.h>
-
-#if NXT_VERNUM != NXT_NODE_VERNUM
-#error "libunit version mismatch."
-#endif
-
-#include <nxt_unit_response.h>
-#include <nxt_unit_request.h>
-
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-
-class Unit {
+class Unit : public nxt_napi {
public:
static napi_value init(napi_env env, napi_value exports);
private:
- Unit(napi_env env);
+ Unit(napi_env env, napi_value jsthis);
~Unit();
static napi_value create(napi_env env, napi_callback_info info);
@@ -56,7 +37,7 @@ private:
napi_value create_response(napi_value server_obj, napi_value socket,
napi_value request,
- nxt_unit_request_info_t *req, Unit *obj);
+ nxt_unit_request_info_t *req);
static napi_value response_send_headers(napi_env env,
napi_callback_info info);
@@ -64,18 +45,16 @@ private:
static napi_value response_write(napi_env env, napi_callback_info info);
static napi_value response_end(napi_env env, napi_callback_info info);
- napi_status create_headers(nxt_unit_request_info_t *req,
- napi_value request);
+ void create_headers(nxt_unit_request_info_t *req, napi_value request);
- inline napi_status append_header(nxt_unit_field_t *f, napi_value headers,
+ void append_header(nxt_unit_field_t *f, napi_value headers,
napi_value raw_headers, uint32_t idx);
static napi_ref constructor_;
- napi_env env_;
napi_ref wrapper_;
nxt_unit_ctx_t *unit_ctx_;
};
-#endif /* _NXT_NODEJS_H_INCLUDED_ */
+#endif /* _NXT_NODEJS_UNIT_H_INCLUDED_ */
diff --git a/src/nxt_application.c b/src/nxt_application.c
index a2827b75..f63b90fb 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -36,8 +36,6 @@ static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task,
const char *name);
static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment);
-static void nxt_app_http_release(nxt_task_t *task, void *obj, void *data);
-
static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG,
@@ -431,32 +429,6 @@ nxt_app_set_environment(nxt_conf_value_t *environment)
}
-nxt_int_t
-nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ar)
-{
- ar->timer.handler = nxt_app_http_release;
- nxt_timer_add(task->thread->engine, &ar->timer, 0);
-
- return NXT_OK;
-}
-
-
-static void
-nxt_app_http_release(nxt_task_t *task, void *obj, void *data)
-{
- nxt_timer_t *timer;
- nxt_app_parse_ctx_t *ar;
-
- timer = obj;
-
- nxt_debug(task, "http app release");
-
- ar = nxt_timer_data(timer, nxt_app_parse_ctx_t, timer);
-
- nxt_mp_release(ar->request->mem_pool);
-}
-
-
nxt_app_lang_module_t *
nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name)
{
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 781f05e0..7ff4bb11 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -99,62 +99,6 @@ struct nxt_common_app_conf_s {
};
-typedef struct {
- nxt_str_t method;
- nxt_str_t target;
- nxt_str_t version;
- nxt_str_t path;
- nxt_str_t query;
- nxt_str_t server_name;
-
- nxt_list_t *fields;
-
- nxt_str_t cookie;
- nxt_str_t content_length;
- nxt_str_t content_type;
-
- off_t parsed_content_length;
- nxt_bool_t done;
-
- size_t bufs;
- nxt_buf_t *buf;
-} nxt_app_request_header_t;
-
-
-typedef struct {
- size_t preread_size;
- nxt_bool_t done;
-
- nxt_buf_t *buf;
-} nxt_app_request_body_t;
-
-
-typedef struct {
- nxt_app_request_header_t header;
- nxt_app_request_body_t body;
-
- nxt_str_t remote;
- nxt_str_t local;
-} nxt_app_request_t;
-
-
-typedef struct nxt_app_parse_ctx_s nxt_app_parse_ctx_t;
-
-
-struct nxt_app_parse_ctx_s {
- nxt_app_request_t r;
- nxt_http_request_t *request;
- nxt_timer_t timer;
- void *timer_data;
- nxt_http_request_parse_t parser;
- nxt_http_request_parse_t resp_parser;
- nxt_mp_t *mem_pool;
-};
-
-
-nxt_int_t nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ctx);
-
-
struct nxt_app_module_s {
size_t compat_length;
uint32_t *compat;
diff --git a/src/nxt_buf.h b/src/nxt_buf.h
index d9d4ee1b..9c22d650 100644
--- a/src/nxt_buf.h
+++ b/src/nxt_buf.h
@@ -206,7 +206,7 @@ nxt_buf_set_last(b) \
#define \
nxt_buf_clear_last(b) \
- (b)->is_last = 0
+ (b)->is_last = 0
#define \
diff --git a/src/nxt_conf.c b/src/nxt_conf.c
index 4c6d8839..57870838 100644
--- a/src/nxt_conf.c
+++ b/src/nxt_conf.c
@@ -87,7 +87,6 @@ struct nxt_conf_op_s {
uint32_t index;
uint32_t action; /* nxt_conf_op_action_t */
void *ctx;
- nxt_conf_op_t *next;
};
@@ -113,6 +112,8 @@ static void nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos,
static nxt_int_t nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op,
nxt_conf_value_t *dst, nxt_conf_value_t *src);
+static nxt_int_t nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op,
+ nxt_conf_value_t *dst, nxt_conf_value_t *src);
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);
@@ -736,12 +737,14 @@ nxt_conf_array_qsort(nxt_conf_value_t *value,
}
-nxt_int_t
+nxt_conf_op_ret_t
nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root,
- nxt_str_t *path, nxt_conf_value_t *value)
+ nxt_str_t *path, nxt_conf_value_t *value, nxt_bool_t add)
{
nxt_str_t token;
+ nxt_int_t index;
nxt_conf_op_t *op, **parent;
+ nxt_conf_value_t *node;
nxt_conf_path_parse_t parse;
nxt_conf_object_member_t *member;
@@ -754,7 +757,7 @@ nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root,
for ( ;; ) {
op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t));
if (nxt_slow_path(op == NULL)) {
- return NXT_ERROR;
+ return NXT_CONF_OP_ERROR;
}
*parent = op;
@@ -762,50 +765,107 @@ nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root,
nxt_conf_path_next_token(&parse, &token);
- root = nxt_conf_get_object_member(root, &token, &op->index);
+ switch (root->type) {
+
+ case NXT_CONF_VALUE_OBJECT:
+ node = nxt_conf_get_object_member(root, &token, &op->index);
+ break;
+
+ case NXT_CONF_VALUE_ARRAY:
+ index = nxt_int_parse(token.start, token.length);
+
+ if (index < 0 || index > NXT_INT32_T_MAX) {
+ return NXT_CONF_OP_NOT_FOUND;
+ }
+
+ op->index = index;
+
+ node = nxt_conf_get_array_element(root, index);
+ break;
+
+ default:
+ node = NULL;
+ }
if (parse.last) {
break;
}
- if (root == NULL) {
- return NXT_DECLINED;
+ if (node == NULL) {
+ return NXT_CONF_OP_NOT_FOUND;
}
op->action = NXT_CONF_OP_PASS;
+ root = node;
}
if (value == NULL) {
- if (root == NULL) {
- return NXT_DECLINED;
+ if (node == NULL) {
+ return NXT_CONF_OP_NOT_FOUND;
}
op->action = NXT_CONF_OP_DELETE;
- return NXT_OK;
+ return NXT_CONF_OP_OK;
+ }
+
+ if (add) {
+ if (node == NULL) {
+ return NXT_CONF_OP_NOT_FOUND;
+ }
+
+ if (node->type != NXT_CONF_VALUE_ARRAY) {
+ return NXT_CONF_OP_NOT_ALLOWED;
+ }
+
+ op->action = NXT_CONF_OP_PASS;
+
+ op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t));
+ if (nxt_slow_path(op == NULL)) {
+ return NXT_CONF_OP_ERROR;
+ }
+
+ *parent = op;
+
+ op->index = node->u.array->count;
+ op->action = NXT_CONF_OP_CREATE;
+ op->ctx = value;
+
+ return NXT_CONF_OP_OK;
+ }
+
+ if (node != NULL) {
+ op->action = NXT_CONF_OP_REPLACE;
+ op->ctx = value;
+
+ return NXT_CONF_OP_OK;
}
- if (root == NULL) {
+ op->action = NXT_CONF_OP_CREATE;
+ if (root->type == NXT_CONF_VALUE_ARRAY) {
+ if (op->index > root->u.array->count) {
+ return NXT_CONF_OP_NOT_FOUND;
+ }
+
+ op->ctx = value;
+
+ } else {
member = nxt_mp_zget(mp, sizeof(nxt_conf_object_member_t));
if (nxt_slow_path(member == NULL)) {
- return NXT_ERROR;
+ return NXT_CONF_OP_ERROR;
}
nxt_conf_set_string(&member->name, &token);
member->value = *value;
- op->action = NXT_CONF_OP_CREATE;
+ op->index = root->u.object->count;
op->ctx = member;
-
- } else {
- op->action = NXT_CONF_OP_REPLACE;
- op->ctx = value;
}
- return NXT_OK;
+ return NXT_CONF_OP_OK;
}
@@ -834,16 +894,13 @@ static nxt_int_t
nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
nxt_conf_value_t *src)
{
- size_t size;
- nxt_int_t rc;
- nxt_uint_t n;
-
- if (op != NULL && src->type != NXT_CONF_VALUE_OBJECT) {
+ if (op != NULL
+ && src->type != NXT_CONF_VALUE_ARRAY
+ && src->type != NXT_CONF_VALUE_OBJECT)
+ {
return NXT_ERROR;
}
- dst->type = src->type;
-
switch (src->type) {
case NXT_CONF_VALUE_STRING:
@@ -861,34 +918,116 @@ nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
break;
case NXT_CONF_VALUE_ARRAY:
+ return nxt_conf_copy_array(mp, op, dst, src);
- size = sizeof(nxt_conf_array_t)
- + src->u.array->count * sizeof(nxt_conf_value_t);
+ case NXT_CONF_VALUE_OBJECT:
+ return nxt_conf_copy_object(mp, op, dst, src);
- dst->u.array = nxt_mp_get(mp, size);
- if (nxt_slow_path(dst->u.array == NULL)) {
- return NXT_ERROR;
+ default:
+ dst->u = src->u;
+ }
+
+ dst->type = src->type;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
+ nxt_conf_value_t *src)
+{
+ size_t size;
+ nxt_int_t rc;
+ nxt_uint_t s, d, count, index;
+ nxt_conf_op_t *pass_op;
+ nxt_conf_value_t *value;
+
+ count = src->u.array->count;
+
+ if (op != NULL) {
+ if (op->action == NXT_CONF_OP_CREATE) {
+ count++;
+
+ } else if (op->action == NXT_CONF_OP_DELETE) {
+ count--;
}
+ }
+
+ size = sizeof(nxt_conf_array_t) + count * sizeof(nxt_conf_value_t);
+
+ dst->u.array = nxt_mp_get(mp, size);
+ if (nxt_slow_path(dst->u.array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ dst->u.array->count = count;
+
+ s = 0;
+ d = 0;
- dst->u.array->count = src->u.array->count;
+ pass_op = NULL;
- for (n = 0; n < src->u.array->count; n++) {
- rc = nxt_conf_copy_value(mp, NULL, &dst->u.array->elements[n],
- &src->u.array->elements[n]);
+ /*
+ * This initialization is needed only to
+ * suppress a warning on GCC 4.8 and older.
+ */
+ index = 0;
+
+ do {
+ if (pass_op == NULL) {
+ index = (op == NULL) ? src->u.array->count : op->index;
+ }
+ while (s != index) {
+ rc = nxt_conf_copy_value(mp, pass_op, &dst->u.array->elements[d],
+ &src->u.array->elements[s]);
if (nxt_slow_path(rc != NXT_OK)) {
return NXT_ERROR;
}
+
+ s++;
+ d++;
}
- break;
+ if (pass_op != NULL) {
+ pass_op = NULL;
+ continue;
+ }
- case NXT_CONF_VALUE_OBJECT:
- return nxt_conf_copy_object(mp, op, dst, src);
+ if (op != NULL) {
+ switch (op->action) {
+ case NXT_CONF_OP_PASS:
+ pass_op = op->ctx;
+ index++;
+ break;
- default:
- dst->u = src->u;
- }
+ case NXT_CONF_OP_CREATE:
+ value = op->ctx;
+ dst->u.array->elements[d] = *value;
+
+ d++;
+ break;
+
+ case NXT_CONF_OP_REPLACE:
+ value = op->ctx;
+ dst->u.array->elements[d] = *value;
+
+ s++;
+ d++;
+ break;
+
+ case NXT_CONF_OP_DELETE:
+ s++;
+ break;
+ }
+
+ op = NULL;
+ }
+
+ } while (d != count);
+
+ dst->type = src->type;
return NXT_OK;
}
@@ -939,9 +1078,7 @@ nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
do {
if (pass_op == NULL) {
- index = (op == NULL || op->action == NXT_CONF_OP_CREATE)
- ? src->u.object->count
- : op->index;
+ index = (op == NULL) ? src->u.object->count : op->index;
}
while (s != index) {
@@ -1015,7 +1152,7 @@ nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
break;
}
- op = op->next;
+ op = NULL;
}
} while (d != count);
diff --git a/src/nxt_conf.h b/src/nxt_conf.h
index 20ff3b1e..2435b0e2 100644
--- a/src/nxt_conf.h
+++ b/src/nxt_conf.h
@@ -20,6 +20,14 @@ typedef enum {
} nxt_conf_type_t;
+typedef enum {
+ NXT_CONF_OP_OK = 0,
+ NXT_CONF_OP_NOT_FOUND,
+ NXT_CONF_OP_NOT_ALLOWED,
+ NXT_CONF_OP_ERROR,
+} nxt_conf_op_ret_t;
+
+
typedef struct nxt_conf_value_s nxt_conf_value_t;
typedef struct nxt_conf_op_s nxt_conf_op_t;
@@ -80,8 +88,9 @@ NXT_EXPORT nxt_conf_value_t *nxt_conf_get_array_element(nxt_conf_value_t *value,
NXT_EXPORT 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);
-nxt_int_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops,
- nxt_conf_value_t *root, nxt_str_t *path, nxt_conf_value_t *value);
+nxt_conf_op_ret_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops,
+ nxt_conf_value_t *root, nxt_str_t *path, nxt_conf_value_t *value,
+ nxt_bool_t add);
nxt_conf_value_t *nxt_conf_clone(nxt_mp_t *mp, nxt_conf_op_t *op,
nxt_conf_value_t *value);
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 5653b9eb..bee82dd4 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -66,6 +66,12 @@ static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_match_patterns_set_member(
+ nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
@@ -218,6 +224,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
&nxt_conf_vldt_match_patterns,
NULL },
+ { nxt_string("arguments"),
+ NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_patterns_sets,
+ NULL },
+
+ { nxt_string("headers"),
+ NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_patterns_sets,
+ NULL },
+
+ { nxt_string("cookies"),
+ NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_patterns_sets,
+ NULL },
+
NXT_CONF_VLDT_END
};
@@ -741,9 +762,15 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
nxt_str_t pattern;
nxt_uint_t i, first, last;
+ enum {
+ sw_none,
+ sw_side,
+ sw_middle
+ } state;
+
if (nxt_conf_type(value) != NXT_CONF_STRING) {
- return nxt_conf_vldt_error(vldt,
- "The \"match\" patterns must be strings.");
+ return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", "
+ "\"uri\", and \"method\" must be strings.");
}
nxt_conf_get_string(value, &pattern);
@@ -754,17 +781,37 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
first = (pattern.start[0] == '!');
last = pattern.length - 1;
+ state = sw_none;
for (i = first; i != pattern.length; i++) {
+
ch = pattern.start[i];
if (ch != '*') {
continue;
}
- if (i != first && i != last) {
- return nxt_conf_vldt_error(vldt, "The \"match\" patterns can only "
- "contain \"*\" markers at the sides.");
+ switch (state) {
+ case sw_none:
+ state = (i == first) ? sw_side : sw_middle;
+ break;
+
+ case sw_side:
+ if (i == last) {
+ if (last - first != 1) {
+ break;
+ }
+
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern must "
+ "not contain double \"*\" markers.");
+ }
+
+ /* Fall through. */
+
+ case sw_middle:
+ return nxt_conf_vldt_error(vldt, "The \"match\" patterns can "
+ "either contain \"*\" markers at "
+ "the sides or only one in the middle.");
}
}
@@ -772,6 +819,49 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
}
+static nxt_int_t
+nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_match_patterns_set);
+ }
+
+ /* NXT_CONF_OBJECT */
+
+ return nxt_conf_vldt_match_patterns_set(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" patterns for "
+ "\"arguments\", \"cookies\", and "
+ "\"headers\" must be objects.");
+ }
+
+ return nxt_conf_vldt_object_iterator(vldt, value,
+ &nxt_conf_vldt_match_patterns_set_member);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt,
+ nxt_str_t *name, nxt_conf_value_t *value)
+{
+ if (name->length == 0) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must "
+ "not contain empty member names.");
+ }
+
+ return nxt_conf_vldt_match_patterns(vldt, value, NULL);
+}
+
+
#if (NXT_TLS)
static nxt_int_t
@@ -1268,6 +1358,7 @@ nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, nxt_conf_value_t *valu
return NXT_OK;
}
+
static nxt_int_t
nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
{
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index 29838bd9..49afbe46 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -184,6 +184,7 @@ nxt_controller_start(nxt_task_t *task, void *data)
vldt.pool = nxt_mp_create(1024, 128, 256, 32);
if (nxt_slow_path(vldt.pool == NULL)) {
+ nxt_mp_destroy(mp);
return NXT_ERROR;
}
@@ -929,6 +930,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
nxt_mp_t *mp;
nxt_int_t rc;
nxt_conn_t *c;
+ nxt_bool_t post;
nxt_buf_mem_t *mbuf;
nxt_conf_op_t *ops;
nxt_conf_value_t *value;
@@ -957,7 +959,18 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
return;
}
- if (nxt_str_eq(&req->parser.method, "PUT", 3)) {
+ if (nxt_str_eq(&req->parser.method, "POST", 4)) {
+ if (path->length == 1) {
+ goto not_allowed;
+ }
+
+ post = 1;
+
+ } else {
+ post = 0;
+ }
+
+ if (post || nxt_str_eq(&req->parser.method, "PUT", 3)) {
if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) {
nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link);
@@ -999,15 +1012,20 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
if (path->length != 1) {
rc = nxt_conf_op_compile(c->mem_pool, &ops,
nxt_controller_conf.root,
- path, value);
+ path, value, post);
- if (rc != NXT_OK) {
+ if (rc != NXT_CONF_OP_OK) {
nxt_mp_destroy(mp);
- if (rc == NXT_DECLINED) {
+ switch (rc) {
+ case NXT_CONF_OP_NOT_FOUND:
goto not_found;
+
+ case NXT_CONF_OP_NOT_ALLOWED:
+ goto not_allowed;
}
+ /* rc == NXT_CONF_OP_ERROR */
goto alloc_fail;
}
@@ -1079,13 +1097,14 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
} else {
rc = nxt_conf_op_compile(c->mem_pool, &ops,
nxt_controller_conf.root,
- path, NULL);
+ path, NULL, 0);
if (rc != NXT_OK) {
- if (rc == NXT_DECLINED) {
+ if (rc == NXT_CONF_OP_NOT_FOUND) {
goto not_found;
}
+ /* rc == NXT_CONF_OP_ERROR */
goto alloc_fail;
}
@@ -1144,8 +1163,10 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
return;
}
+not_allowed:
+
resp.status = 405;
- resp.title = (u_char *) "Invalid method.";
+ resp.title = (u_char *) "Method isn't allowed.";
resp.offset = -1;
nxt_controller_response(task, req, &resp);
diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c
index 9f9c8f62..9cdaab9b 100644
--- a/src/nxt_epoll_engine.c
+++ b/src/nxt_epoll_engine.c
@@ -1059,7 +1059,7 @@ nxt_epoll_edge_conn_io_connect(nxt_task_t *task, void *obj, void *data)
state = c->write_state;
- switch (nxt_socket_connect(task, c->socket.fd, c->remote) ){
+ switch (nxt_socket_connect(task, c->socket.fd, c->remote)) {
case NXT_OK:
c->socket.write_ready = 1;
diff --git a/src/nxt_errno.h b/src/nxt_errno.h
index b3d7105a..e3ce8349 100644
--- a/src/nxt_errno.h
+++ b/src/nxt_errno.h
@@ -45,6 +45,7 @@ typedef int nxt_err_t;
#define NXT_EILSEQ EILSEQ
#define NXT_ETIME ETIME
#define NXT_ENOMOREFILES 0
+#define NXT_ENOBUFS ENOBUFS
#if (NXT_HPUX)
/* HP-UX uses EWOULDBLOCK instead of EAGAIN. */
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index 07e3c7bc..3a822042 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -35,6 +35,7 @@ static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj,
void *data);
static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
+static void nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r);
static void nxt_h1p_request_header_send(nxt_task_t *task,
nxt_http_request_t *r);
static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
@@ -103,6 +104,13 @@ const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[3] = {
};
+const nxt_http_proto_tls_t nxt_http_proto_tls[3] = {
+ nxt_h1p_request_tls,
+ NULL,
+ NULL,
+};
+
+
const nxt_http_proto_header_send_t nxt_http_proto_header_send[3] = {
nxt_h1p_request_header_send,
NULL,
@@ -813,6 +821,15 @@ nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
}
+static void
+nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r)
+{
+#if (NXT_TLS)
+ r->tls = r->proto.h1->conn->u.tls;
+#endif
+}
+
+
#define NXT_HTTP_LAST_SUCCESS \
(NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1)
diff --git a/src/nxt_hpux_sendfile.c b/src/nxt_hpux_sendfile.c
index 3c42c559..df200b64 100644
--- a/src/nxt_hpux_sendfile.c
+++ b/src/nxt_hpux_sendfile.c
@@ -13,7 +13,7 @@ ssize_t nxt_hpux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
size_t limit);
static ssize_t nxt_sys_sendfile(int s, int fd, off_t offset, size_t nbytes,
- const struct iovec *hdtrl, int flags)
+ const struct iovec *hdtrl, int flags)
{
return -1;
}
@@ -23,7 +23,7 @@ static ssize_t nxt_sys_sendfile(int s, int fd, off_t offset, size_t nbytes,
/* sendfile() is not declared if _XOPEN_SOURCE_EXTENDED is defined. */
sbsize_t sendfile(int s, int fd, off_t offset, bsize_t nbytes,
- const struct iovec *hdtrl, int flags);
+ const struct iovec *hdtrl, int flags);
#define nxt_sys_sendfile sendfile
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 23c406d3..835cf66d 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -114,12 +114,15 @@ struct nxt_http_request_s {
const nxt_http_request_state_t *state;
nxt_str_t host;
+ nxt_str_t server_name;
nxt_str_t target;
nxt_str_t version;
nxt_str_t *method;
nxt_str_t *path;
nxt_str_t *args;
+ nxt_array_t *arguments; /* of nxt_http_name_value_t */
+ nxt_array_t *cookies; /* of nxt_http_name_value_t */
nxt_list_t *fields;
nxt_http_field_t *content_type;
nxt_http_field_t *content_length;
@@ -130,6 +133,10 @@ struct nxt_http_request_s {
nxt_sockaddr_t *remote;
nxt_sockaddr_t *local;
+ void *tls;
+
+ nxt_timer_t timer;
+ void *timer_data;
nxt_buf_t *last;
@@ -165,6 +172,7 @@ typedef void (*nxt_http_proto_body_read_t)(nxt_task_t *task,
nxt_http_request_t *r);
typedef void (*nxt_http_proto_local_addr_t)(nxt_task_t *task,
nxt_http_request_t *r);
+typedef void (*nxt_http_proto_tls_t)(nxt_task_t *task, nxt_http_request_t *r);
typedef void (*nxt_http_proto_header_send_t)(nxt_task_t *task,
nxt_http_request_t *r);
typedef void (*nxt_http_proto_send_t)(nxt_task_t *task, nxt_http_request_t *r,
@@ -186,7 +194,6 @@ nxt_http_request_t *nxt_http_request_create(nxt_task_t *task);
void nxt_http_request_error(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_status_t status);
void nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r);
-void nxt_http_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
void nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r);
void nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r,
nxt_buf_t *out);
@@ -221,6 +228,7 @@ extern nxt_lvlhsh_t nxt_response_fields_hash;
extern const nxt_http_proto_body_read_t nxt_http_proto_body_read[];
extern const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[];
+extern const nxt_http_proto_tls_t nxt_http_proto_tls[];
extern const nxt_http_proto_header_send_t nxt_http_proto_header_send[];
extern const nxt_http_proto_send_t nxt_http_proto_send[];
extern const nxt_http_proto_body_bytes_sent_t nxt_http_proto_body_bytes_sent[];
diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c
index 34eaaaf9..05df245e 100644
--- a/src/nxt_http_parse.c
+++ b/src/nxt_http_parse.c
@@ -34,10 +34,6 @@ static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
#define NXT_HTTP_FIELD_LVLHSH_SHIFT 5
-#define NXT_HTTP_FIELD_HASH_INIT 159406
-#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c))
-#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h))
-
typedef enum {
NXT_HTTP_TARGET_SPACE = 1, /* \s */
@@ -119,7 +115,7 @@ nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp)
rp->mem_pool = mp;
rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
- if (nxt_slow_path(rp->fields == NULL)){
+ if (nxt_slow_path(rp->fields == NULL)) {
return NXT_ERROR;
}
diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h
index 0326d45c..6c629936 100644
--- a/src/nxt_http_parse.h
+++ b/src/nxt_http_parse.h
@@ -93,6 +93,11 @@ struct nxt_http_field_s {
};
+#define NXT_HTTP_FIELD_HASH_INIT 159406U
+#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c))
+#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h))
+
+
nxt_int_t nxt_http_parse_request_init(nxt_http_request_parse_t *rp,
nxt_mp_t *mp);
nxt_int_t nxt_http_parse_request(nxt_http_request_parse_t *rp,
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 724b0808..1265c186 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -11,6 +11,8 @@
static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp);
static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data);
static void nxt_http_request_pass(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_request_proto_info(nxt_task_t *task,
+ nxt_http_request_t *r);
static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
void *data);
static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
@@ -293,26 +295,23 @@ nxt_http_request_pass(nxt_task_t *task, void *obj, void *data)
pass = r->conf->socket_conf->pass;
- if (nxt_slow_path(pass == NULL)) {
- goto fail;
- }
+ if (nxt_fast_path(pass != NULL)) {
- for ( ;; ) {
- nxt_debug(task, "http request route: %V", &pass->name);
+ do {
+ nxt_debug(task, "http request route: %V", &pass->name);
- pass = pass->handler(task, r, pass);
- if (pass == NULL) {
- break;
- }
+ pass = pass->handler(task, r, pass);
- if (nxt_slow_path(r->pass_count++ == 255)) {
- goto fail;
- }
- }
+ if (pass == NULL) {
+ return;
+ }
- return;
+ if (pass == NXT_HTTP_PASS_ERROR) {
+ break;
+ }
-fail:
+ } while (r->pass_count++ < 255);
+ }
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
}
@@ -322,117 +321,52 @@ nxt_http_pass_t *
nxt_http_request_application(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_pass_t *pass)
{
- nxt_int_t ret;
- nxt_event_engine_t *engine;
- nxt_app_parse_ctx_t *ar;
+ nxt_event_engine_t *engine;
nxt_debug(task, "http request application");
- ar = nxt_mp_zget(r->mem_pool, sizeof(nxt_app_parse_ctx_t));
- if (nxt_slow_path(ar == NULL)) {
- nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
- return NULL;
- }
-
- ar->request = r;
- ar->mem_pool = r->mem_pool;
nxt_mp_retain(r->mem_pool);
- // STUB
engine = task->thread->engine;
- ar->timer.task = &engine->task;
- ar->timer.work_queue = &engine->fast_work_queue;
- ar->timer.log = engine->task.log;
- ar->timer.bias = NXT_TIMER_DEFAULT_BIAS;
-
- ar->r.remote.start = nxt_sockaddr_address(r->remote);
- ar->r.remote.length = r->remote->address_length;
+ r->timer.task = &engine->task;
+ r->timer.work_queue = &engine->fast_work_queue;
+ r->timer.log = engine->task.log;
+ r->timer.bias = NXT_TIMER_DEFAULT_BIAS;
/*
* TODO: need an application flag to get local address
* required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go.
*/
- nxt_http_request_local_addr(task, r);
-
- if (nxt_fast_path(r->local != NULL)) {
- ar->r.local.start = nxt_sockaddr_address(r->local);
- ar->r.local.length = r->local->address_length;
- }
-
- ar->r.header.fields = r->fields;
- ar->r.header.done = 1;
- ar->r.header.version = r->version;
-
- if (r->method != NULL) {
- ar->r.header.method = *r->method;
- }
+ nxt_http_request_proto_info(task, r);
if (r->host.length != 0) {
- ar->r.header.server_name = r->host;
+ r->server_name = r->host;
} else {
- nxt_str_set(&ar->r.header.server_name, "localhost");
+ nxt_str_set(&r->server_name, "localhost");
}
- ar->r.header.target = r->target;
-
- if (r->path != NULL) {
- ar->r.header.path = *r->path;
- }
-
- if (r->args != NULL) {
- ar->r.header.query = *r->args;
- }
-
- if (r->content_type != NULL) {
- ar->r.header.content_type.length = r->content_type->value_length;
- ar->r.header.content_type.start = r->content_type->value;
- }
-
- if (r->content_length != NULL) {
- ar->r.header.content_length.length = r->content_length->value_length;
- ar->r.header.content_length.start = r->content_length->value;
- }
-
- if (r->cookie != NULL) {
- ar->r.header.cookie.length = r->cookie->value_length;
- ar->r.header.cookie.start = r->cookie->value;
- }
-
- if (r->body != NULL) {
- ar->r.body.buf = r->body;
- ar->r.body.preread_size = r->content_length_n;
- ar->r.header.parsed_content_length = r->content_length_n;
- }
-
- ar->r.body.done = 1;
-
- ret = nxt_http_parse_request_init(&ar->resp_parser, r->mem_pool);
- if (nxt_slow_path(ret != NXT_OK)) {
- nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
- return NULL;
- }
-
- nxt_router_process_http_request(task, ar, pass->u.application);
+ nxt_router_process_http_request(task, r, pass->u.application);
return NULL;
}
-void
-nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r)
+static void
+nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r)
{
if (r->proto.any != NULL) {
- nxt_http_proto_body_read[r->protocol](task, r);
+ nxt_http_proto_local_addr[r->protocol](task, r);
+ nxt_http_proto_tls[r->protocol](task, r);
}
}
void
-nxt_http_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
+nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r)
{
if (r->proto.any != NULL) {
- nxt_http_proto_local_addr[r->protocol](task, r);
+ nxt_http_proto_body_read[r->protocol](task, r);
}
}
@@ -589,6 +523,8 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "http request error handler");
+ r->error = 1;
+
if (proto.any != NULL) {
nxt_http_proto_discard[r->protocol](task, r, nxt_http_buf_last(r));
}
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 133c39ab..d6749acb 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -9,9 +9,9 @@
typedef enum {
- NXT_HTTP_ROUTE_STRING = 0,
+ NXT_HTTP_ROUTE_TABLE = 0,
+ NXT_HTTP_ROUTE_STRING,
NXT_HTTP_ROUTE_STRING_PTR,
- NXT_HTTP_ROUTE_FIELD,
NXT_HTTP_ROUTE_HEADER,
NXT_HTTP_ROUTE_ARGUMENT,
NXT_HTTP_ROUTE_COOKIE,
@@ -21,6 +21,7 @@ typedef enum {
typedef enum {
NXT_HTTP_ROUTE_PATTERN_EXACT = 0,
NXT_HTTP_ROUTE_PATTERN_BEGIN,
+ NXT_HTTP_ROUTE_PATTERN_MIDDLE,
NXT_HTTP_ROUTE_PATTERN_END,
NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
} nxt_http_route_pattern_type_t;
@@ -37,11 +38,17 @@ typedef struct {
nxt_conf_value_t *host;
nxt_conf_value_t *uri;
nxt_conf_value_t *method;
+ nxt_conf_value_t *headers;
+ nxt_conf_value_t *arguments;
+ nxt_conf_value_t *cookies;
} nxt_http_route_match_conf_t;
typedef struct {
- nxt_str_t test;
+ u_char *start1;
+ u_char *start2;
+ uint32_t length1;
+ uint32_t length2;
uint32_t min_length;
nxt_http_route_pattern_type_t type:8;
@@ -52,17 +59,66 @@ typedef struct {
typedef struct {
- uintptr_t offset;
- uint32_t items;
+ uint16_t hash;
+ uint16_t name_length;
+ uint32_t value_length;
+ u_char *name;
+ u_char *value;
+} nxt_http_name_value_t;
+
+
+typedef struct {
+ uint16_t hash;
+ uint16_t name_length;
+ uint32_t value_length;
+ u_char *name;
+ u_char *value;
+} nxt_http_cookie_t;
+
+
+typedef struct {
+ /* The object must be the first field. */
nxt_http_route_object_t object:8;
+ uint32_t items;
+
+ union {
+ uintptr_t offset;
+
+ struct {
+ u_char *start;
+ uint16_t hash;
+ uint16_t length;
+ } name;
+ } u;
+
nxt_http_route_pattern_t pattern[0];
} nxt_http_route_rule_t;
typedef struct {
uint32_t items;
- nxt_http_pass_t pass;
nxt_http_route_rule_t *rule[0];
+} nxt_http_route_ruleset_t;
+
+
+typedef struct {
+ /* The object must be the first field. */
+ nxt_http_route_object_t object:8;
+ uint32_t items;
+ nxt_http_route_ruleset_t *ruleset[0];
+} nxt_http_route_table_t;
+
+
+typedef union {
+ nxt_http_route_rule_t *rule;
+ nxt_http_route_table_t *table;
+} nxt_http_route_test_t;
+
+
+typedef struct {
+ uint32_t items;
+ nxt_http_pass_t pass;
+ nxt_http_route_test_t test[0];
} nxt_http_route_match_t;
@@ -79,17 +135,39 @@ struct nxt_http_routes_s {
};
+#define NJS_COOKIE_HASH \
+ (nxt_http_field_hash_end( \
+ nxt_http_field_hash_char( \
+ nxt_http_field_hash_char( \
+ nxt_http_field_hash_char( \
+ nxt_http_field_hash_char( \
+ nxt_http_field_hash_char( \
+ nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT, \
+ 'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF)
+
+
static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
+static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task,
+ nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
+ nxt_bool_t case_sensitive);
+static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task,
+ nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
+ nxt_bool_t case_sensitive);
+static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task,
+ nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name,
+ nxt_bool_t case_sensitive);
static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task,
- nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
- nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case);
+ nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
+ nxt_http_route_pattern_case_t pattern_case);
static int nxt_http_pattern_compare(const void *one, const void *two);
static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
nxt_http_route_pattern_case_t pattern_case);
+static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test,
+ nxt_http_route_pattern_case_t pattern_case);
static void nxt_http_route_resolve(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route);
@@ -103,10 +181,37 @@ static nxt_http_pass_t *nxt_http_route_pass(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_pass_t *start);
static nxt_http_pass_t *nxt_http_route_match(nxt_http_request_t *r,
nxt_http_route_match_t *match);
-static nxt_bool_t nxt_http_route_rule(nxt_http_request_t *r,
+static nxt_int_t nxt_http_route_table(nxt_http_request_t *r,
+ nxt_http_route_table_t *table);
+static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r,
+ nxt_http_route_ruleset_t *ruleset);
+static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule);
+static nxt_int_t nxt_http_route_header(nxt_http_request_t *r,
nxt_http_route_rule_t *rule);
-static nxt_bool_t nxt_http_route_pattern(nxt_http_request_t *r,
+static nxt_int_t nxt_http_route_arguments(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule);
+static nxt_array_t *nxt_http_route_arguments_parse(nxt_http_request_t *r);
+static nxt_http_name_value_t *nxt_http_route_argument(nxt_array_t *array,
+ u_char *name, size_t name_length, uint32_t hash, u_char *start,
+ u_char *end);
+static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, nxt_array_t *array);
+static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule);
+static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r);
+static nxt_int_t nxt_http_route_cookie_parse(nxt_array_t *cookies,
+ u_char *start, u_char *end);
+static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array,
+ u_char *name, size_t name_length, u_char *start, u_char *end);
+static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, nxt_array_t *array);
+static nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, u_char *start, size_t length);
+static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r,
nxt_http_route_pattern_t *pattern, u_char *start, size_t length);
+static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test,
+ size_t length, nxt_bool_t case_sensitive);
nxt_http_routes_t *
@@ -188,6 +293,24 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = {
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_match_conf_t, method),
},
+
+ {
+ nxt_string("headers"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, headers),
+ },
+
+ {
+ nxt_string("arguments"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, arguments),
+ },
+
+ {
+ nxt_string("cookies"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, cookies),
+ },
};
@@ -233,10 +356,13 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
{
size_t size;
uint32_t n;
+ nxt_mp_t *mp;
nxt_int_t ret;
nxt_str_t pass, *string;
nxt_conf_value_t *match_conf, *pass_conf;
- nxt_http_route_rule_t *rule, **p;
+ nxt_http_route_test_t *test;
+ nxt_http_route_rule_t *rule;
+ nxt_http_route_table_t *table;
nxt_http_route_match_t *match;
nxt_http_route_match_conf_t mtcf;
@@ -255,7 +381,9 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0;
size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_rule_t *);
- match = nxt_mp_alloc(tmcf->router_conf->mem_pool, size);
+ mp = tmcf->router_conf->mem_pool;
+
+ match = nxt_mp_alloc(mp, size);
if (nxt_slow_path(match == NULL)) {
return NULL;
}
@@ -264,7 +392,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
match->pass.handler = NULL;
match->items = n;
- string = nxt_str_dup(tmcf->router_conf->mem_pool, &match->pass.name, &pass);
+ string = nxt_str_dup(mp, &match->pass.name, &pass);
if (nxt_slow_path(string == NULL)) {
return NULL;
}
@@ -282,64 +410,232 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NULL;
}
- p = &match->rule[0];
+ test = &match->test[0];
if (mtcf.host != NULL) {
- rule = nxt_http_route_rule_create(task, tmcf, mtcf.host, 1,
+ rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1,
NXT_HTTP_ROUTE_PATTERN_LOWCASE);
if (rule == NULL) {
return NULL;
}
- rule->offset = offsetof(nxt_http_request_t, host);
+ rule->u.offset = offsetof(nxt_http_request_t, host);
rule->object = NXT_HTTP_ROUTE_STRING;
- *p++ = rule;
+ test->rule = rule;
+ test++;
}
if (mtcf.uri != NULL) {
- rule = nxt_http_route_rule_create(task, tmcf, mtcf.uri, 1,
+ rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1,
NXT_HTTP_ROUTE_PATTERN_NOCASE);
if (rule == NULL) {
return NULL;
}
- rule->offset = offsetof(nxt_http_request_t, path);
+ rule->u.offset = offsetof(nxt_http_request_t, path);
rule->object = NXT_HTTP_ROUTE_STRING_PTR;
- *p++ = rule;
+ test->rule = rule;
+ test++;
}
if (mtcf.method != NULL) {
- rule = nxt_http_route_rule_create(task, tmcf, mtcf.method, 1,
+ rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1,
NXT_HTTP_ROUTE_PATTERN_UPCASE);
if (rule == NULL) {
return NULL;
}
- rule->offset = offsetof(nxt_http_request_t, method);
+ rule->u.offset = offsetof(nxt_http_request_t, method);
rule->object = NXT_HTTP_ROUTE_STRING_PTR;
- *p++ = rule;
+ test->rule = rule;
+ test++;
+ }
+
+ if (mtcf.headers != NULL) {
+ table = nxt_http_route_table_create(task, mp, mtcf.headers,
+ NXT_HTTP_ROUTE_HEADER, 0);
+ if (table == NULL) {
+ return NULL;
+ }
+
+ test->table = table;
+ test++;
+ }
+
+ if (mtcf.arguments != NULL) {
+ table = nxt_http_route_table_create(task, mp, mtcf.arguments,
+ NXT_HTTP_ROUTE_ARGUMENT, 1);
+ if (table == NULL) {
+ return NULL;
+ }
+
+ test->table = table;
+ test++;
+ }
+
+ if (mtcf.cookies != NULL) {
+ table = nxt_http_route_table_create(task, mp, mtcf.cookies,
+ NXT_HTTP_ROUTE_COOKIE, 0);
+ if (table == NULL) {
+ return NULL;
+ }
+
+ test->table = table;
+ test++;
}
return match;
}
+static nxt_http_route_table_t *
+nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
+ nxt_bool_t case_sensitive)
+{
+ size_t size;
+ uint32_t i, n;
+ nxt_bool_t array;
+ nxt_conf_value_t *ruleset_cv;
+ nxt_http_route_table_t *table;
+ nxt_http_route_ruleset_t *ruleset;
+
+ array = (nxt_conf_type(table_cv) == NXT_CONF_ARRAY);
+ n = array ? nxt_conf_array_elements_count(table_cv) : 1;
+ size = sizeof(nxt_http_route_table_t)
+ + n * sizeof(nxt_http_route_ruleset_t *);
+
+ table = nxt_mp_alloc(mp, size);
+ if (nxt_slow_path(table == NULL)) {
+ return NULL;
+ }
+
+ table->items = n;
+ table->object = NXT_HTTP_ROUTE_TABLE;
+
+ if (!array) {
+ ruleset = nxt_http_route_ruleset_create(task, mp, table_cv,
+ object, case_sensitive);
+ if (nxt_slow_path(ruleset == NULL)) {
+ return NULL;
+ }
+
+ table->ruleset[0] = ruleset;
+
+ return table;
+ }
+
+ for (i = 0; i < n; i++) {
+ ruleset_cv = nxt_conf_get_array_element(table_cv, i);
+
+ ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv,
+ object, case_sensitive);
+ if (nxt_slow_path(ruleset == NULL)) {
+ return NULL;
+ }
+
+ table->ruleset[i] = ruleset;
+ }
+
+ return table;
+}
+
+
+static nxt_http_route_ruleset_t *
+nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
+ nxt_bool_t case_sensitive)
+{
+ size_t size;
+ uint32_t i, n, next;
+ nxt_str_t name;
+ nxt_conf_value_t *rule_cv;
+ nxt_http_route_rule_t *rule;
+ nxt_http_route_ruleset_t *ruleset;
+
+ n = nxt_conf_object_members_count(ruleset_cv);
+ size = sizeof(nxt_http_route_ruleset_t)
+ + n * sizeof(nxt_http_route_rule_t *);
+
+ ruleset = nxt_mp_alloc(mp, size);
+ if (nxt_slow_path(ruleset == NULL)) {
+ return NULL;
+ }
+
+ ruleset->items = n;
+
+ next = 0;
+
+ for (i = 0; i < n; i++) {
+ rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
+
+ rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
+ case_sensitive);
+ if (nxt_slow_path(rule == NULL)) {
+ return NULL;
+ }
+
+ rule->object = object;
+ ruleset->rule[i] = rule;
+ }
+
+ return ruleset;
+}
+
+
static nxt_http_route_rule_t *
-nxt_http_route_rule_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive)
+{
+ u_char c, *p;
+ uint32_t hash;
+ nxt_uint_t i;
+ nxt_http_route_rule_t *rule;
+
+ rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
+ NXT_HTTP_ROUTE_PATTERN_NOCASE);
+ if (nxt_slow_path(rule == NULL)) {
+ return NULL;
+ }
+
+ rule->u.name.length = name->length;
+
+ p = nxt_mp_nget(mp, name->length);
+ if (nxt_slow_path(p == NULL)) {
+ return NULL;
+ }
+
+ rule->u.name.start = p;
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+
+ for (i = 0; i < name->length; i++) {
+ c = name->start[i];
+ *p++ = c;
+
+ c = nxt_lowcase(c);
+ hash = nxt_http_field_hash_char(hash, c);
+ }
+
+ rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF;
+
+ return rule;
+}
+
+
+static nxt_http_route_rule_t *
+nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
nxt_http_route_pattern_case_t pattern_case)
{
size_t size;
uint32_t i, n;
- nxt_mp_t *mp;
nxt_int_t ret;
nxt_bool_t string;
nxt_conf_value_t *value;
nxt_http_route_rule_t *rule;
nxt_http_route_pattern_t *pattern;
- mp = tmcf->router_conf->mem_pool;
-
string = (nxt_conf_type(cv) != NXT_CONF_ARRAY);
n = string ? 1 : nxt_conf_array_elements_count(cv);
size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t);
@@ -407,8 +703,12 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
{
u_char *start;
nxt_str_t test;
+ nxt_uint_t n, length;
nxt_http_route_pattern_type_t type;
+ /* Suppress warning about uninitialized variable. */
+ length = 0;
+
type = NXT_HTTP_ROUTE_PATTERN_EXACT;
nxt_conf_get_string(cv, &test);
@@ -448,57 +748,98 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
} else if (test.start[test.length - 1] == '*') {
test.length--;
type = NXT_HTTP_ROUTE_PATTERN_BEGIN;
+
+ } else {
+ length = test.length - 1;
+
+ for (n = 1; n < length; n++) {
+ if (test.start[n] == '*') {
+ test.length = n;
+ type = NXT_HTTP_ROUTE_PATTERN_MIDDLE;
+ break;
+ }
+ }
}
}
}
pattern->type = type;
pattern->min_length = test.length;
- pattern->test.length = test.length;
+ pattern->length1 = test.length;
- start = nxt_mp_nget(mp, test.length);
+ start = nxt_http_route_pattern_copy(mp, &test, pattern_case);
if (nxt_slow_path(start == NULL)) {
return NXT_ERROR;
}
- pattern->test.start = start;
+ pattern->start1 = start;
+
+ if (type == NXT_HTTP_ROUTE_PATTERN_MIDDLE) {
+ length -= test.length;
+ pattern->length2 = length;
+ pattern->min_length += length;
+
+ test.start = &test.start[test.length + 1];
+ test.length = length;
+
+ start = nxt_http_route_pattern_copy(mp, &test, pattern_case);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ pattern->start2 = start;
+ }
+
+ return NXT_OK;
+}
+
+
+static u_char *
+nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test,
+ nxt_http_route_pattern_case_t pattern_case)
+{
+ u_char *start;
+
+ start = nxt_mp_nget(mp, test->length);
+ if (nxt_slow_path(start == NULL)) {
+ return start;
+ }
switch (pattern_case) {
case NXT_HTTP_ROUTE_PATTERN_UPCASE:
- nxt_memcpy_upcase(start, test.start, test.length);
+ nxt_memcpy_upcase(start, test->start, test->length);
break;
case NXT_HTTP_ROUTE_PATTERN_LOWCASE:
- nxt_memcpy_lowcase(start, test.start, test.length);
+ nxt_memcpy_lowcase(start, test->start, test->length);
break;
case NXT_HTTP_ROUTE_PATTERN_NOCASE:
- nxt_memcpy(start, test.start, test.length);
+ nxt_memcpy(start, test->start, test->length);
break;
}
- return NXT_OK;
+ return start;
}
void
nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
{
- nxt_uint_t items;
- nxt_http_route_t **route;
+ nxt_http_route_t **route, **end;
nxt_http_routes_t *routes;
routes = tmcf->router_conf->routes;
+
if (routes != NULL) {
- items = routes->items;
route = &routes->route[0];
+ end = route + routes->items;
- while (items != 0) {
+ while (route < end) {
nxt_http_route_resolve(task, tmcf, *route);
route++;
- items--;
}
}
}
@@ -508,17 +849,15 @@ static void
nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_t *route)
{
- nxt_uint_t items;
- nxt_http_route_match_t **match;
+ nxt_http_route_match_t **match, **end;
- items = route->items;
match = &route->match[0];
+ end = match + route->items;
- while (items != 0) {
+ while (match < end) {
nxt_http_pass_resolve(task, tmcf, &(*match)->pass);
match++;
- items--;
}
}
@@ -561,21 +900,18 @@ nxt_http_pass_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
static nxt_http_route_t *
nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name)
{
- nxt_uint_t items;
- nxt_http_route_t **route;
+ nxt_http_route_t **route, **end;
- items = routes->items;
route = &routes->route[0];
+ end = route + routes->items;
- do {
+ while (route < end) {
if (nxt_strstr_eq(&(*route)->name, name)) {
return *route;
}
route++;
- items--;
-
- } while (items != 0);
+ }
return NULL;
}
@@ -627,20 +963,17 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
void
nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes)
{
- nxt_uint_t items;
- nxt_http_route_t **route;
+ nxt_http_route_t **route, **end;
if (routes != NULL) {
- items = routes->items;
route = &routes->route[0];
+ end = route + routes->items;
- do {
+ while (route < end) {
nxt_http_route_cleanup(task, *route);
route++;
- items--;
-
- } while (items != 0);
+ }
}
}
@@ -648,19 +981,16 @@ nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes)
static void
nxt_http_route_cleanup(nxt_task_t *task, nxt_http_route_t *route)
{
- nxt_uint_t items;
- nxt_http_route_match_t **match;
+ nxt_http_route_match_t **match, **end;
- items = route->items;
match = &route->match[0];
+ end = match + route->items;
- do {
+ while (match < end) {
nxt_http_pass_cleanup(task, &(*match)->pass);
match++;
- items--;
-
- } while (items != 0);
+ }
}
@@ -677,23 +1007,21 @@ static nxt_http_pass_t *
nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_pass_t *start)
{
- nxt_uint_t items;
nxt_http_pass_t *pass;
nxt_http_route_t *route;
- nxt_http_route_match_t **match;
+ nxt_http_route_match_t **match, **end;
route = start->u.route;
- items = route->items;
match = &route->match[0];
+ end = match + route->items;
- while (items != 0) {
+ while (match < end) {
pass = nxt_http_route_match(r, *match);
if (pass != NULL) {
return pass;
}
match++;
- items--;
}
nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
@@ -705,87 +1033,488 @@ nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r,
static nxt_http_pass_t *
nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match)
{
- nxt_uint_t items;
- nxt_http_route_rule_t **rule;
+ nxt_int_t ret;
+ nxt_http_route_test_t *test, *end;
- rule = &match->rule[0];
- items = match->items;
+ test = &match->test[0];
+ end = test + match->items;
- while (items != 0) {
- if (!nxt_http_route_rule(r, *rule)) {
- return NULL;
+ while (test < end) {
+ if (test->rule->object != NXT_HTTP_ROUTE_TABLE) {
+ ret = nxt_http_route_rule(r, test->rule);
+
+ } else {
+ ret = nxt_http_route_table(r, test->table);
}
- rule++;
- items--;
+ if (ret <= 0) {
+ /* 0 => NULL, -1 => NXT_HTTP_PASS_ERROR. */
+ return (nxt_http_pass_t *) (intptr_t) ret;
+ }
+
+ test++;
}
return &match->pass;
}
-static nxt_bool_t
+static nxt_int_t
+nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
+{
+ nxt_int_t ret;
+ nxt_http_route_ruleset_t **ruleset, **end;
+
+ ret = 1;
+ ruleset = &table->ruleset[0];
+ end = ruleset + table->items;
+
+ while (ruleset < end) {
+ ret = nxt_http_route_ruleset(r, *ruleset);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ ruleset++;
+ }
+
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
+{
+ nxt_int_t ret;
+ nxt_http_route_rule_t **rule, **end;
+
+ rule = &ruleset->rule[0];
+ end = rule + ruleset->items;
+
+ while (rule < end) {
+ ret = nxt_http_route_rule(r, *rule);
+
+ if (ret <= 0) {
+ return ret;
+ }
+
+ rule++;
+ }
+
+ return 1;
+}
+
+
+static nxt_int_t
nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
{
- void *p, **pp;
- u_char *start;
- size_t length;
- nxt_str_t *s;
- nxt_uint_t items;
- nxt_bool_t ret;
- nxt_http_field_t *f;
- nxt_http_route_pattern_t *pattern;
+ void *p, **pp;
+ u_char *start;
+ size_t length;
+ nxt_str_t *s;
+
+ switch (rule->object) {
+
+ case NXT_HTTP_ROUTE_HEADER:
+ return nxt_http_route_header(r, rule);
- p = nxt_pointer_to(r, rule->offset);
+ case NXT_HTTP_ROUTE_ARGUMENT:
+ return nxt_http_route_arguments(r, rule);
+
+ case NXT_HTTP_ROUTE_COOKIE:
+ return nxt_http_route_cookies(r, rule);
+
+ default:
+ break;
+ }
+
+ p = nxt_pointer_to(r, rule->u.offset);
if (rule->object == NXT_HTTP_ROUTE_STRING) {
s = p;
- length = s->length;
- start = s->start;
} else {
+ /* NXT_HTTP_ROUTE_STRING_PTR */
pp = p;
- p = *pp;
+ s = *pp;
- if (p == NULL) {
+ if (s == NULL) {
return 0;
}
+ }
- switch (rule->object) {
+ length = s->length;
+ start = s->start;
- case NXT_HTTP_ROUTE_STRING_PTR:
- s = p;
- length = s->length;
- start = s->start;
- break;
+ return nxt_http_route_test_rule(r, rule, start, length);
+}
- case NXT_HTTP_ROUTE_FIELD:
- f = p;
- length = f->value_length;
- start = f->value;
- break;
- case NXT_HTTP_ROUTE_HEADER:
- return 0;
+static nxt_int_t
+nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
+{
+ nxt_int_t ret;
+ nxt_http_field_t *f;
- case NXT_HTTP_ROUTE_ARGUMENT:
- return 0;
+ ret = 0;
- case NXT_HTTP_ROUTE_COOKIE:
- return 0;
+ nxt_list_each(f, r->fields) {
- default:
- nxt_unreachable();
- return 0;
+ if (rule->u.name.hash != f->hash
+ || rule->u.name.length != f->name_length
+ || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
+ != 0)
+ {
+ continue;
+ }
+
+ ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
+
+ if (ret == 0) {
+ return ret;
+ }
+
+ } nxt_list_loop;
+
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
+{
+ nxt_array_t *arguments;
+
+ if (r->args == NULL) {
+ return 0;
+ }
+
+ arguments = nxt_http_route_arguments_parse(r);
+ if (nxt_slow_path(arguments == NULL)) {
+ return -1;
+ }
+
+ return nxt_http_route_test_argument(r, rule, arguments);
+}
+
+
+static nxt_array_t *
+nxt_http_route_arguments_parse(nxt_http_request_t *r)
+{
+ size_t name_length;
+ u_char c, *p, *start, *end, *name;
+ uint32_t hash;
+ nxt_bool_t valid;
+ nxt_array_t *args;
+ nxt_http_name_value_t *nv;
+
+ if (r->arguments != NULL) {
+ return r->arguments;
+ }
+
+ args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
+ if (nxt_slow_path(args == NULL)) {
+ return NULL;
+ }
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+ valid = 1;
+ name = NULL;
+ name_length = 0;
+
+ start = r->args->start;
+ end = start + r->args->length;
+
+ for (p = start; p < end; p++) {
+ c = *p;
+
+ if (c == '=') {
+ name_length = p - start;
+ name = start;
+ start = p + 1;
+ valid = (name_length != 0);
+
+ } else if (c == '&') {
+ if (valid) {
+ nv = nxt_http_route_argument(args, name, name_length, hash,
+ start, p);
+ if (nxt_slow_path(nv == NULL)) {
+ return NULL;
+ }
+ }
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+ valid = 1;
+ name = NULL;
+ start = p + 1;
+
+ } else if (name == NULL) {
+ hash = nxt_http_field_hash_char(hash, c);
+ }
+ }
+
+ if (valid) {
+ nv = nxt_http_route_argument(args, name, name_length, hash, start, p);
+ if (nxt_slow_path(nv == NULL)) {
+ return NULL;
+ }
+ }
+
+ r->arguments = args;
+
+ return args;
+}
+
+
+static nxt_http_name_value_t *
+nxt_http_route_argument(nxt_array_t *array, u_char *name, size_t name_length,
+ uint32_t hash, u_char *start, u_char *end)
+{
+ size_t length;
+ nxt_http_name_value_t *nv;
+
+ nv = nxt_array_add(array);
+ if (nxt_slow_path(nv == NULL)) {
+ return NULL;
+ }
+
+ nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
+
+ length = end - start;
+
+ if (name == NULL) {
+ name_length = length;
+ name = start;
+ length = 0;
+ }
+
+ nv->name_length = name_length;
+ nv->value_length = length;
+ nv->name = name;
+ nv->value = start;
+
+ return nv;
+}
+
+
+static nxt_int_t
+nxt_http_route_test_argument(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, nxt_array_t *array)
+{
+ nxt_bool_t ret;
+ nxt_http_name_value_t *nv, *end;
+
+ ret = 0;
+
+ nv = array->elts;
+ end = nv + array->nelts;
+
+ while (nv < end) {
+
+ if (rule->u.name.hash == nv->hash
+ && rule->u.name.length == nv->name_length
+ && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
+ {
+ ret = nxt_http_route_test_rule(r, rule, nv->value,
+ nv->value_length);
+ if (ret == 0) {
+ break;
+ }
+ }
+
+ nv++;
+ }
+
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
+{
+ nxt_array_t *cookies;
+
+ cookies = nxt_http_route_cookies_parse(r);
+ if (nxt_slow_path(cookies == NULL)) {
+ return -1;
+ }
+
+ return nxt_http_route_test_cookie(r, rule, cookies);
+}
+
+
+static nxt_array_t *
+nxt_http_route_cookies_parse(nxt_http_request_t *r)
+{
+ nxt_int_t ret;
+ nxt_array_t *cookies;
+ nxt_http_field_t *f;
+
+ if (r->cookies != NULL) {
+ return r->cookies;
+ }
+
+ cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
+ if (nxt_slow_path(cookies == NULL)) {
+ return NULL;
+ }
+
+ nxt_list_each(f, r->fields) {
+
+ if (f->hash != NJS_COOKIE_HASH
+ || f->name_length != 6
+ || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0)
+ {
+ continue;
+ }
+
+ ret = nxt_http_route_cookie_parse(cookies, f->value,
+ f->value + f->value_length);
+ if (ret != NXT_OK) {
+ return NULL;
+ }
+
+ } nxt_list_loop;
+
+ r->cookies = cookies;
+
+ return cookies;
+}
+
+
+static nxt_int_t
+nxt_http_route_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end)
+{
+ size_t name_length;
+ u_char c, *p, *name;
+ nxt_http_name_value_t *nv;
+
+ name = NULL;
+ name_length = 0;
+
+ for (p = start; p < end; p++) {
+ c = *p;
+
+ if (c == '=') {
+ while (start[0] == ' ') { start++; }
+
+ name_length = p - start;
+
+ if (name_length != 0) {
+ name = start;
+ }
+
+ start = p + 1;
+
+ } else if (c == ';') {
+ if (name != NULL) {
+ nv = nxt_http_route_cookie(cookies, name, name_length,
+ start, p);
+ if (nxt_slow_path(nv == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+
+ name = NULL;
+ start = p + 1;
+ }
+ }
+
+ if (name != NULL) {
+ nv = nxt_http_route_cookie(cookies, name, name_length, start, p);
+ if (nxt_slow_path(nv == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_http_name_value_t *
+nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length,
+ u_char *start, u_char *end)
+{
+ u_char c, *p;
+ uint32_t hash;
+ nxt_http_name_value_t *nv;
+
+ nv = nxt_array_add(array);
+ if (nxt_slow_path(nv == NULL)) {
+ return NULL;
+ }
+
+ nv->name_length = name_length;
+ nv->name = name;
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+
+ for (p = name; p < name + name_length; p++) {
+ c = *p;
+ c = nxt_lowcase(c);
+ hash = nxt_http_field_hash_char(hash, c);
+ }
+
+ nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
+
+ while (start < end && end[-1] == ' ') { end--; }
+
+ nv->value_length = end - start;
+ nv->value = start;
+
+ return nv;
+}
+
+
+static nxt_int_t
+nxt_http_route_test_cookie(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, nxt_array_t *array)
+{
+ nxt_bool_t ret;
+ nxt_http_name_value_t *nv, *end;
+
+ ret = 0;
+
+ nv = array->elts;
+ end = nv + array->nelts;
+
+ while (nv < end) {
+
+ if (rule->u.name.hash == nv->hash
+ && rule->u.name.length == nv->name_length
+ && nxt_strncasecmp(rule->u.name.start, nv->name, nv->name_length)
+ == 0)
+ {
+ ret = nxt_http_route_test_rule(r, rule, nv->value,
+ nv->value_length);
+ if (ret == 0) {
+ break;
+ }
}
+
+ nv++;
}
- items = rule->items;
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
+ u_char *start, size_t length)
+{
+ nxt_int_t ret;
+ nxt_http_route_pattern_t *pattern, *end;
+
+ ret = 1;
pattern = &rule->pattern[0];
+ end = pattern + rule->items;
- do {
+ while (pattern < end) {
ret = nxt_http_route_pattern(r, pattern, start, length);
+ /* nxt_http_route_pattern() returns either 1 or 0. */
ret ^= pattern->negative;
if (pattern->any == ret) {
@@ -793,30 +1522,31 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
}
pattern++;
- items--;
-
- } while (items != 0);
+ }
return ret;
}
-static nxt_bool_t
+static nxt_int_t
nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
u_char *start, size_t length)
{
- nxt_str_t *test;
+ u_char *p, *end, *test;
+ size_t test_length;
+ nxt_int_t ret;
if (length < pattern->min_length) {
return 0;
}
- test = &pattern->test;
+ test = pattern->start1;
+ test_length = pattern->length1;
switch (pattern->type) {
case NXT_HTTP_ROUTE_PATTERN_EXACT:
- if (length != test->length) {
+ if (length != test_length) {
return 0;
}
@@ -825,25 +1555,52 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
case NXT_HTTP_ROUTE_PATTERN_BEGIN:
break;
+ case NXT_HTTP_ROUTE_PATTERN_MIDDLE:
+ ret = nxt_http_route_memcmp(start, test, test_length,
+ pattern->case_sensitive);
+ if (!ret) {
+ return ret;
+ }
+
+ test = pattern->start2;
+ test_length = pattern->length2;
+
+ /* Fall through. */
+
case NXT_HTTP_ROUTE_PATTERN_END:
- start += length - test->length;
+ start += length - test_length;
break;
case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
+ end = start + length;
+
if (pattern->case_sensitive) {
- return (nxt_memstrn(start, start + length,
- (char *) test->start, test->length)
- != NULL);
+ p = nxt_memstrn(start, end, (char *) test, test_length);
+
+ } else {
+ p = nxt_memcasestrn(start, end, (char *) test, test_length);
}
- return (nxt_memcasestrn(start, start + length,
- (char *) test->start, test->length)
- != NULL);
+ return (p != NULL);
}
- if (pattern->case_sensitive) {
- return (nxt_memcmp(start, test->start, test->length) == 0);
+ return nxt_http_route_memcmp(start, test, test_length,
+ pattern->case_sensitive);
+}
+
+
+static nxt_int_t
+nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
+ nxt_bool_t case_sensitive)
+{
+ nxt_int_t n;
+
+ if (case_sensitive) {
+ n = nxt_memcmp(start, test, test_length);
+
+ } else {
+ n = nxt_memcasecmp(start, test, test_length);
}
- return (nxt_memcasecmp(start, test->start, test->length) == 0);
+ return (n == 0);
}
diff --git a/src/nxt_job_resolve.c b/src/nxt_job_resolve.c
index a1317756..0f1fb9aa 100644
--- a/src/nxt_job_resolve.c
+++ b/src/nxt_job_resolve.c
@@ -59,11 +59,11 @@ nxt_job_resolve(nxt_job_resolve_t *jbr)
case AF_INET6:
#endif
case AF_INET:
- n++;
- break;
+ n++;
+ break;
default:
- break;
+ break;
}
}
@@ -81,15 +81,15 @@ nxt_job_resolve(nxt_job_resolve_t *jbr)
switch (r->ai_addr->sa_family) {
#if (NXT_INET6)
case AF_INET6:
- length = NXT_INET6_ADDR_STR_LEN;
- break;
+ length = NXT_INET6_ADDR_STR_LEN;
+ break;
#endif
case AF_INET:
- length = NXT_INET_ADDR_STR_LEN;
- break;
+ length = NXT_INET_ADDR_STR_LEN;
+ break;
default:
- continue;
+ continue;
}
sa = nxt_sockaddr_create(mp, r->ai_addr, r->ai_addrlen, length);
diff --git a/src/nxt_kqueue_engine.c b/src/nxt_kqueue_engine.c
index 0e68fbdc..0212b331 100644
--- a/src/nxt_kqueue_engine.c
+++ b/src/nxt_kqueue_engine.c
@@ -856,7 +856,7 @@ nxt_kqueue_conn_io_connect(nxt_task_t *task, void *obj, void *data)
state = c->write_state;
- switch (nxt_socket_connect(task, c->socket.fd, c->remote) ){
+ switch (nxt_socket_connect(task, c->socket.fd, c->remote)) {
case NXT_OK:
c->socket.write_ready = 1;
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index f756bff7..40682eb9 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -842,7 +842,7 @@ nxt_main_process_sigusr1_handler(nxt_task_t *task, void *obj, void *data)
nxt_mp_destroy(mp);
return;
- }
+ }
fail:
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 80321a85..a6ec6c60 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -699,7 +699,7 @@ fail:
static int
nxt_php_startup(sapi_module_struct *sapi_module)
{
- return php_module_startup(sapi_module, NULL, 0);
+ return php_module_startup(sapi_module, NULL, 0);
}
@@ -929,6 +929,10 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
track_vars_array TSRMLS_CC);
nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC);
+ if (r->tls) {
+ nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC);
+ }
+
f_end = r->fields + r->fields_count;
for (f = r->fields; f < f_end; f++) {
name = nxt_unit_sptr_get(&f->name);
diff --git a/src/nxt_poll_engine.c b/src/nxt_poll_engine.c
index acb44a22..f514e0a9 100644
--- a/src/nxt_poll_engine.c
+++ b/src/nxt_poll_engine.c
@@ -370,7 +370,7 @@ nxt_poll_commit_changes(nxt_event_engine_t *engine)
retval = NXT_ERROR;
- next:
+ next:
change++;
diff --git a/src/nxt_port.c b/src/nxt_port.c
index 30719ad3..aff46666 100644
--- a/src/nxt_port.c
+++ b/src/nxt_port.c
@@ -510,7 +510,7 @@ nxt_port_post(nxt_task_t *task, nxt_port_t *port,
pw = nxt_zalloc(sizeof(nxt_port_work_t));
if (nxt_slow_path(pw == NULL)) {
- return NXT_ERROR;
+ return NXT_ERROR;
}
nxt_atomic_fetch_add(&port->use_count, 1);
diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c
index 774f1f33..b908041c 100644
--- a/src/nxt_port_memory.c
+++ b/src/nxt_port_memory.c
@@ -49,10 +49,10 @@ nxt_port_mmap_at(nxt_port_mmaps_t *port_mmaps, uint32_t i)
while (i + 1 > cap) {
if (cap < 16) {
- cap = cap * 2;
+ cap = cap * 2;
} else {
- cap = cap + cap / 2;
+ cap = cap + cap / 2;
}
}
diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c
index 01fe2dab..c9b5105b 100644
--- a/src/nxt_port_socket.c
+++ b/src/nxt_port_socket.c
@@ -195,7 +195,24 @@ nxt_port_msg_create(nxt_task_t *task, nxt_port_send_msg_t *m)
static nxt_port_send_msg_t *
-nxt_port_msg_push(nxt_task_t *task, nxt_port_t *port, nxt_port_send_msg_t *msg)
+nxt_port_msg_insert_head(nxt_task_t *task, nxt_port_t *port,
+ nxt_port_send_msg_t *msg)
+{
+ if (msg->work.data == NULL) {
+ msg = nxt_port_msg_create(task, msg);
+ }
+
+ if (msg != NULL) {
+ nxt_queue_insert_head(&port->messages, &msg->link);
+ }
+
+ return msg;
+}
+
+
+static nxt_port_send_msg_t *
+nxt_port_msg_insert_tail(nxt_task_t *task, nxt_port_t *port,
+ nxt_port_send_msg_t *msg)
{
if (msg->work.data == NULL) {
msg = nxt_port_msg_create(task, msg);
@@ -260,7 +277,7 @@ nxt_port_socket_twrite(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type,
} else {
nxt_thread_mutex_lock(&port->write_mutex);
- res = nxt_port_msg_push(task, port, &msg);
+ res = nxt_port_msg_insert_tail(task, port, &msg);
nxt_thread_mutex_unlock(&port->write_mutex);
@@ -349,6 +366,8 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data)
iov[0].iov_len += sizeof(msg->tracking_msg);
}
+ sb.limit -= iov[0].iov_len;
+
nxt_sendbuf_mem_coalesce(task, &sb);
plain_size = sb.size;
@@ -407,7 +426,7 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data)
}
data = NULL;
- if (nxt_port_msg_push(task, port, msg) != NULL) {
+ if (nxt_port_msg_insert_tail(task, port, msg) != NULL) {
use_delta++;
}
}
@@ -422,16 +441,17 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data)
data = NULL;
}
- } else if (nxt_slow_path(n == NXT_ERROR)) {
+ } else {
if (msg->link.next == NULL) {
- if (nxt_port_msg_push(task, port, msg) != NULL) {
+ if (nxt_port_msg_insert_head(task, port, msg) != NULL) {
use_delta++;
}
}
- goto fail;
- }
- /* n == NXT_AGAIN */
+ if (nxt_slow_path(n == NXT_ERROR)) {
+ goto fail;
+ }
+ }
} while (port->socket.write_ready);
@@ -546,6 +566,7 @@ void
nxt_port_read_close(nxt_port_t *port)
{
port->socket.read_ready = 0;
+ port->socket.read = NXT_EVENT_INACTIVE;
nxt_socket_close(port->socket.task, port->pair[0]);
port->pair[0] = -1;
}
@@ -618,15 +639,24 @@ nxt_port_read_handler(nxt_task_t *task, void *obj, void *data)
}
+typedef struct {
+ uint32_t stream;
+ uint32_t pid;
+} nxt_port_frag_key_t;
+
+
static nxt_int_t
nxt_port_lvlhsh_frag_test(nxt_lvlhsh_query_t *lhq, void *data)
{
nxt_port_recv_msg_t *fmsg;
+ nxt_port_frag_key_t *frag_key;
fmsg = data;
+ frag_key = (nxt_port_frag_key_t *) lhq->key.start;
- if (lhq->key.length == sizeof(uint32_t)
- && *(uint32_t *) lhq->key.start == fmsg->port_msg.stream)
+ if (lhq->key.length == sizeof(nxt_port_frag_key_t)
+ && frag_key->stream == fmsg->port_msg.stream
+ && frag_key->pid == (uint32_t) fmsg->port_msg.pid)
{
return NXT_OK;
}
@@ -664,6 +694,7 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port,
nxt_int_t res;
nxt_lvlhsh_query_t lhq;
nxt_port_recv_msg_t *fmsg;
+ nxt_port_frag_key_t frag_key;
nxt_debug(task, "start frag stream #%uD", msg->port_msg.stream);
@@ -675,9 +706,12 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port,
*fmsg = *msg;
- lhq.key_hash = nxt_murmur_hash2(&fmsg->port_msg.stream, sizeof(uint32_t));
- lhq.key.length = sizeof(uint32_t);
- lhq.key.start = (u_char *) &fmsg->port_msg.stream;
+ frag_key.stream = fmsg->port_msg.stream;
+ frag_key.pid = fmsg->port_msg.pid;
+
+ lhq.key_hash = nxt_murmur_hash2(&frag_key, sizeof(nxt_port_frag_key_t));
+ lhq.key.length = sizeof(nxt_port_frag_key_t);
+ lhq.key.start = (u_char *) &frag_key;
lhq.proto = &lvlhsh_frag_proto;
lhq.replace = 0;
lhq.value = fmsg;
@@ -710,17 +744,24 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port,
static nxt_port_recv_msg_t *
-nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, uint32_t stream,
- nxt_bool_t last)
+nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, nxt_port_recv_msg_t *msg)
{
- nxt_int_t res;
- nxt_lvlhsh_query_t lhq;
+ nxt_int_t res;
+ nxt_bool_t last;
+ nxt_lvlhsh_query_t lhq;
+ nxt_port_frag_key_t frag_key;
+
+ last = msg->port_msg.mf == 0;
+
+ nxt_debug(task, "%s frag stream #%uD", last ? "last" : "next",
+ msg->port_msg.stream);
- nxt_debug(task, "%s frag stream #%uD", last ? "last" : "next", stream);
+ frag_key.stream = msg->port_msg.stream;
+ frag_key.pid = msg->port_msg.pid;
- lhq.key_hash = nxt_murmur_hash2(&stream, sizeof(uint32_t));
- lhq.key.length = sizeof(uint32_t);
- lhq.key.start = (u_char *) &stream;
+ lhq.key_hash = nxt_murmur_hash2(&frag_key, sizeof(nxt_port_frag_key_t));
+ lhq.key.length = sizeof(nxt_port_frag_key_t);
+ lhq.key.start = (u_char *) &frag_key;
lhq.proto = &lvlhsh_frag_proto;
lhq.pool = port->mem_pool;
@@ -733,7 +774,8 @@ nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, uint32_t stream,
return lhq.value;
default:
- nxt_log(task, NXT_LOG_INFO, "frag stream #%uD not found", stream);
+ nxt_log(task, NXT_LOG_INFO, "frag stream #%uD not found",
+ frag_key.stream);
return NULL;
}
@@ -773,8 +815,7 @@ nxt_port_read_msg_process(nxt_task_t *task, nxt_port_t *port,
if (nxt_slow_path(msg->port_msg.nf != 0)) {
- fmsg = nxt_port_frag_find(task, port, msg->port_msg.stream,
- msg->port_msg.mf == 0);
+ fmsg = nxt_port_frag_find(task, port, msg);
if (nxt_slow_path(fmsg == NULL)) {
goto fmsg_failed;
diff --git a/src/nxt_process.c b/src/nxt_process.c
index 59520297..c4aef21c 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -136,9 +136,11 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process)
nxt_random_init(&thread->random);
- if (init->user_cred != NULL && getuid() == 0) {
- /* Super-user. */
-
+ if (init->user_cred != NULL) {
+ /*
+ * Changing user credentials requires either root privileges
+ * or CAP_SETUID and CAP_SETGID capabilities on Linux.
+ */
ret = nxt_user_cred_set(task, init->user_cred);
if (ret != NXT_OK) {
goto fail;
@@ -434,11 +436,7 @@ nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
uc->base_gid = grp->gr_gid;
}
- if (getuid() == 0) {
- return nxt_user_groups_get(task, uc);
- }
-
- return NXT_OK;
+ return nxt_user_groups_get(task, uc);
}
@@ -505,14 +503,26 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
if (nsaved == -1) {
nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
- goto fail;
+ goto free;
}
nxt_debug(task, "getgroups(): %d", nsaved);
if (initgroups(uc->user, uc->base_gid) != 0) {
- nxt_alert(task, "initgroups(%s, %d) failed", uc->user, uc->base_gid);
- goto restore;
+ if (nxt_errno == NXT_EPERM) {
+ nxt_log(task, NXT_LOG_NOTICE,
+ "initgroups(%s, %d) failed %E, ignored",
+ uc->user, uc->base_gid, nxt_errno);
+
+ ret = NXT_OK;
+
+ goto free;
+
+ } else {
+ nxt_alert(task, "initgroups(%s, %d) failed %E",
+ uc->user, uc->base_gid, nxt_errno);
+ goto restore;
+ }
}
ngroups = getgroups(0, NULL);
@@ -567,7 +577,7 @@ restore:
ret = NXT_ERROR;
}
-fail:
+free:
nxt_free(saved);
@@ -582,8 +592,15 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid);
if (setgid(uc->base_gid) != 0) {
- nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
- return NXT_ERROR;
+ if (nxt_errno == NXT_EPERM) {
+ nxt_log(task, NXT_LOG_NOTICE, "setgid(%d) failed %E, ignored",
+ uc->base_gid, nxt_errno);
+ return NXT_OK;
+
+ } else {
+ nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
+ return NXT_ERROR;
+ }
}
if (uc->gids != NULL) {
@@ -595,8 +612,8 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
} else {
/* MacOSX fallback. */
if (initgroups(uc->user, uc->base_gid) != 0) {
- nxt_alert(task, "initgroups(%s, %d) failed",
- uc->user, uc->base_gid);
+ nxt_alert(task, "initgroups(%s, %d) failed %E",
+ uc->user, uc->base_gid, nxt_errno);
return NXT_ERROR;
}
}
diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c
index 6478f38c..a6d5f217 100644
--- a/src/nxt_python_wsgi.c
+++ b/src/nxt_python_wsgi.c
@@ -619,26 +619,6 @@ nxt_python_create_environ(nxt_task_t *task)
}
- obj = PyString_FromStringAndSize("http", nxt_length("http"));
-
- if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task,
- "Python failed to create the \"wsgi.url_scheme\" environ value");
- goto fail;
- }
-
- if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.url_scheme", obj)
- != 0))
- {
- nxt_alert(task,
- "Python failed to set the \"wsgi.url_scheme\" environ value");
- goto fail;
- }
-
- Py_DECREF(obj);
- obj = NULL;
-
-
if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) {
nxt_alert(task,
"Python failed to initialize the \"wsgi.input\" type object");
@@ -726,6 +706,13 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
RC(nxt_python_add_sptr(ctx, "REMOTE_ADDR", &r->remote, r->remote_length));
RC(nxt_python_add_sptr(ctx, "SERVER_ADDR", &r->local, r->local_length));
+ if (r->tls) {
+ RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "https", 5));
+
+ } else {
+ RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "http", 4));
+ }
+
RC(nxt_python_add_sptr(ctx, "SERVER_PROTOCOL", &r->version,
r->version_length));
diff --git a/src/nxt_router.c b/src/nxt_router.c
index e46e8f82..149a0ff3 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -62,7 +62,7 @@ typedef struct {
uint32_t stream;
nxt_app_t *app;
nxt_port_t *app_port;
- nxt_app_parse_ctx_t *ap;
+ nxt_http_request_t *request;
nxt_msg_info_t msg_info;
nxt_req_app_link_t *ra;
@@ -75,7 +75,7 @@ struct nxt_req_app_link_s {
nxt_atomic_t use_count;
nxt_port_t *app_port;
nxt_port_t *reply_port;
- nxt_app_parse_ctx_t *ap;
+ nxt_http_request_t *request;
nxt_msg_info_t msg_info;
nxt_req_conn_link_t *rc;
@@ -264,8 +264,8 @@ static nxt_int_t nxt_router_app_port(nxt_task_t *task, nxt_app_t *app,
static void nxt_router_app_prepare_request(nxt_task_t *task,
nxt_req_app_link_t *ra);
-static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
- nxt_port_t *port, const nxt_str_t *prefix);
+static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task,
+ nxt_http_request_t *r, nxt_port_t *port, const nxt_str_t *prefix);
static void nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data);
static void nxt_router_adjust_idle_timer(nxt_task_t *task, void *obj,
@@ -282,6 +282,11 @@ static void nxt_http_request_send_body(nxt_task_t *task, void *obj, void *data);
static void nxt_router_app_joint_use(nxt_task_t *task,
nxt_app_joint_t *app_joint, int i);
+static nxt_int_t nxt_router_http_request_done(nxt_task_t *task,
+ nxt_http_request_t *r);
+static void nxt_router_http_request_release(nxt_task_t *task, void *obj,
+ void *data);
+
static nxt_router_t *nxt_router;
static const nxt_str_t http_prefix = nxt_string("HTTP_");
@@ -502,7 +507,7 @@ nxt_router_ra_init(nxt_task_t *task, nxt_req_app_link_t *ra,
ra->rc = rc;
rc->ra = ra;
ra->reply_port = engine->port;
- ra->ap = rc->ap;
+ ra->request = rc->request;
ra->work.handler = NULL;
ra->work.task = &engine->task;
@@ -521,7 +526,7 @@ nxt_router_ra_create(nxt_task_t *task, nxt_req_app_link_t *ra_src)
return ra_src;
}
- mp = ra_src->ap->mem_pool;
+ mp = ra_src->request->mem_pool;
ra = nxt_mp_alloc(mp, sizeof(nxt_req_app_link_t));
@@ -645,16 +650,16 @@ nxt_router_ra_release(nxt_task_t *task, nxt_req_app_link_t *ra)
if (rc != NULL) {
if (nxt_slow_path(ra->err_code != 0)) {
- nxt_http_request_error(task, rc->ap->request, ra->err_code);
+ nxt_http_request_error(task, rc->request, ra->err_code);
} else {
rc->app_port = ra->app_port;
rc->msg_info = ra->msg_info;
if (rc->app->timeout != 0) {
- rc->ap->timer.handler = nxt_router_app_timeout;
- rc->ap->timer_data = rc;
- nxt_timer_add(task->thread->engine, &rc->ap->timer,
+ rc->request->timer.handler = nxt_router_app_timeout;
+ rc->request->timer_data = rc;
+ nxt_timer_add(task->thread->engine, &rc->request->timer,
rc->app->timeout);
}
@@ -817,12 +822,12 @@ nxt_router_rc_unlink(nxt_task_t *task, nxt_req_conn_link_t *rc)
rc->app = NULL;
}
- if (rc->ap != NULL) {
- rc->ap->timer_data = NULL;
+ if (rc->request != NULL) {
+ rc->request->timer_data = NULL;
- nxt_app_http_req_done(task, rc->ap);
+ nxt_router_http_request_done(task, rc->request);
- rc->ap = NULL;
+ rc->request = NULL;
}
}
@@ -3377,33 +3382,27 @@ static void
nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
- size_t dump_size;
nxt_int_t ret;
nxt_buf_t *b;
+ nxt_unit_field_t *f;
+ nxt_http_field_t *field;
nxt_http_request_t *r;
nxt_req_conn_link_t *rc;
- nxt_app_parse_ctx_t *ar;
nxt_unit_response_t *resp;
b = msg->buf;
rc = data;
- dump_size = nxt_buf_used_size(b);
-
- if (dump_size > 300) {
- dump_size = 300;
- }
-
if (msg->size == 0) {
b = NULL;
}
- ar = rc->ap;
- if (nxt_slow_path(ar == NULL)) {
+ r = rc->request;
+ if (nxt_slow_path(r == NULL)) {
return;
}
- if (ar->request->error) {
+ if (r->error) {
nxt_router_rc_unlink(task, rc);
return;
}
@@ -3411,15 +3410,15 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
if (msg->port_msg.last != 0) {
nxt_debug(task, "router data create last buf");
- nxt_buf_chain_add(&b, nxt_http_buf_last(ar->request));
+ nxt_buf_chain_add(&b, nxt_http_buf_last(r));
nxt_router_rc_unlink(task, rc);
} else {
if (rc->app != NULL && rc->app->timeout != 0) {
- ar->timer.handler = nxt_router_app_timeout;
- ar->timer_data = rc;
- nxt_timer_add(task->thread->engine, &ar->timer, rc->app->timeout);
+ r->timer.handler = nxt_router_app_timeout;
+ r->timer_data = rc;
+ nxt_timer_add(task->thread->engine, &r->timer, rc->app->timeout);
}
}
@@ -3432,8 +3431,6 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
msg->buf = NULL;
}
- r = ar->request;
-
if (r->header_sent) {
nxt_buf_chain_add(&r->out, b);
nxt_http_request_send_body(task, r, NULL);
@@ -3451,11 +3448,8 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
goto fail;
}
- nxt_unit_field_t *f;
- nxt_http_field_t *field;
-
for (f = resp->fields; f < resp->fields + resp->fields_count; f++) {
- field = nxt_list_add(ar->resp_parser.fields);
+ field = nxt_list_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
@@ -3473,15 +3467,8 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
(size_t) field->name_length, field->name,
(size_t) field->value_length, field->value);
}
- r->status = resp->status;
-/*
- ret = nxt_http_parse_fields(&ar->resp_parser, &b->mem);
- if (nxt_slow_path(ret != NXT_DONE)) {
- goto fail;
- }
-*/
- r->resp.fields = ar->resp_parser.fields;
+ r->status = resp->status;
ret = nxt_http_fields_process(r->resp.fields,
&nxt_response_fields_hash, r);
@@ -3590,8 +3577,8 @@ nxt_router_response_error_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
}
}
- if (rc->ap != NULL) {
- nxt_http_request_error(task, rc->ap->request,
+ if (rc->request != NULL) {
+ nxt_http_request_error(task, rc->request,
NXT_HTTP_SERVICE_UNAVAILABLE);
}
@@ -4452,17 +4439,15 @@ nxt_router_app_port(nxt_task_t *task, nxt_app_t *app, nxt_req_app_link_t *ra)
void
-nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar,
+nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
nxt_app_t *app)
{
nxt_int_t res;
nxt_port_t *port;
nxt_event_engine_t *engine;
- nxt_http_request_t *r;
nxt_req_app_link_t ra_local, *ra;
nxt_req_conn_link_t *rc;
- r = ar->request;
engine = task->thread->engine;
rc = nxt_port_rpc_register_handler_ex(task, engine->port,
@@ -4480,7 +4465,7 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar,
nxt_router_app_use(task, app, 1);
- rc->ap = ar;
+ rc->request = r;
ra = &ra_local;
nxt_router_ra_init(task, ra, rc);
@@ -4511,17 +4496,15 @@ nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, void *data)
static void
nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra)
{
- uint32_t request_failed;
- nxt_buf_t *buf;
- nxt_int_t res;
- nxt_port_t *port, *c_port, *reply_port;
- nxt_app_parse_ctx_t *ap;
+ uint32_t request_failed;
+ nxt_buf_t *buf;
+ nxt_int_t res;
+ nxt_port_t *port, *c_port, *reply_port;
nxt_assert(ra->app_port != NULL);
port = ra->app_port;
reply_port = ra->reply_port;
- ap = ra->ap;
request_failed = 1;
@@ -4539,7 +4522,7 @@ nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra)
nxt_process_connected_port_add(port->process, reply_port);
}
- buf = nxt_router_prepare_msg(task, &ap->r, port,
+ buf = nxt_router_prepare_msg(task, ra->request, port,
nxt_app_msg_prefix[port->app->type]);
if (nxt_slow_path(buf == NULL)) {
@@ -4642,34 +4625,33 @@ nxt_fields_next(nxt_fields_iter_t *i)
static nxt_buf_t *
-nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
+nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
nxt_port_t *port, const nxt_str_t *prefix)
{
- void *target_pos, *query_pos;
- u_char *pos, *end, *p, c;
- size_t fields_count, req_size, size, free_size;
- size_t copy_size;
- nxt_buf_t *b, *buf, *out, **tail;
- nxt_http_field_t *field, *dup;
- nxt_unit_field_t *dst_field;
- nxt_fields_iter_t iter, dup_iter;
- nxt_unit_request_t *req;
- nxt_app_request_header_t *h;
-
- h = &r->header;
+ void *target_pos, *query_pos;
+ u_char *pos, *end, *p, c;
+ size_t fields_count, req_size, size, free_size;
+ size_t copy_size;
+ nxt_off_t content_length;
+ nxt_buf_t *b, *buf, *out, **tail;
+ nxt_http_field_t *field, *dup;
+ nxt_unit_field_t *dst_field;
+ nxt_fields_iter_t iter, dup_iter;
+ nxt_unit_request_t *req;
req_size = sizeof(nxt_unit_request_t)
- + h->method.length + 1
- + h->version.length + 1
- + r->remote.length + 1
- + r->local.length + 1
- + h->server_name.length + 1
- + h->target.length + 1
- + (h->path.start != h->target.start ? h->path.length + 1 : 0);
-
+ + r->method->length + 1
+ + r->version.length + 1
+ + r->remote->length + 1
+ + r->local->length + 1
+ + r->server_name.length + 1
+ + r->target.length + 1
+ + (r->path->start != r->target.start ? r->path->length + 1 : 0);
+
+ content_length = r->content_length_n < 0 ? 0 : r->content_length_n;
fields_count = 0;
- nxt_list_each(field, h->fields) {
+ nxt_list_each(field, r->fields) {
fields_count++;
req_size += field->name_length + prefix->length + 1
@@ -4686,7 +4668,7 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
}
out = nxt_port_mmap_get_buf(task, port,
- nxt_min(req_size + r->body.preread_size, PORT_MMAP_DATA_SIZE));
+ nxt_min(req_size + content_length, PORT_MMAP_DATA_SIZE));
if (nxt_slow_path(out == NULL)) {
return NULL;
}
@@ -4694,57 +4676,60 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
req = (nxt_unit_request_t *) out->mem.free;
out->mem.free += req_size;
- req->content_length = h->parsed_content_length;
+ req->content_length = content_length;
p = (u_char *) (req->fields + fields_count);
nxt_debug(task, "fields_count=%d", (int) fields_count);
- req->method_length = h->method.length;
+ req->method_length = r->method->length;
nxt_unit_sptr_set(&req->method, p);
- p = nxt_cpymem(p, h->method.start, h->method.length);
+ p = nxt_cpymem(p, r->method->start, r->method->length);
*p++ = '\0';
- req->version_length = h->version.length;
+ req->version_length = r->version.length;
nxt_unit_sptr_set(&req->version, p);
- p = nxt_cpymem(p, h->version.start, h->version.length);
+ p = nxt_cpymem(p, r->version.start, r->version.length);
*p++ = '\0';
- req->remote_length = r->remote.length;
+ req->remote_length = r->remote->address_length;
nxt_unit_sptr_set(&req->remote, p);
- p = nxt_cpymem(p, r->remote.start, r->remote.length);
+ p = nxt_cpymem(p, nxt_sockaddr_address(r->remote),
+ r->remote->address_length);
*p++ = '\0';
- req->local_length = r->local.length;
+ req->local_length = r->local->address_length;
nxt_unit_sptr_set(&req->local, p);
- p = nxt_cpymem(p, r->local.start, r->local.length);
+ p = nxt_cpymem(p, nxt_sockaddr_address(r->local), r->local->address_length);
*p++ = '\0';
- req->server_name_length = h->server_name.length;
+ req->tls = (r->tls != NULL);
+
+ req->server_name_length = r->server_name.length;
nxt_unit_sptr_set(&req->server_name, p);
- p = nxt_cpymem(p, h->server_name.start, h->server_name.length);
+ p = nxt_cpymem(p, r->server_name.start, r->server_name.length);
*p++ = '\0';
target_pos = p;
- req->target_length = h->target.length;
+ req->target_length = (uint32_t) r->target.length;
nxt_unit_sptr_set(&req->target, p);
- p = nxt_cpymem(p, h->target.start, h->target.length);
+ p = nxt_cpymem(p, r->target.start, r->target.length);
*p++ = '\0';
- req->path_length = h->path.length;
- if (h->path.start == h->target.start) {
+ req->path_length = (uint32_t) r->path->length;
+ if (r->path->start == r->target.start) {
nxt_unit_sptr_set(&req->path, target_pos);
} else {
nxt_unit_sptr_set(&req->path, p);
- p = nxt_cpymem(p, h->path.start, h->path.length);
+ p = nxt_cpymem(p, r->path->start, r->path->length);
*p++ = '\0';
}
- req->query_length = h->query.length;
- if (h->query.start != NULL) {
+ req->query_length = r->args != NULL ? (uint32_t) r->args->length : 0;
+ if (r->args != NULL && r->args->start != NULL) {
query_pos = nxt_pointer_to(target_pos,
- h->query.start - h->target.start);
+ r->args->start - r->target.start);
nxt_unit_sptr_set(&req->query, query_pos);
@@ -4758,7 +4743,7 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
dst_field = req->fields;
- for (field = nxt_fields_first(h->fields, &iter);
+ for (field = nxt_fields_first(r->fields, &iter);
field != NULL;
field = nxt_fields_next(&iter))
{
@@ -4771,13 +4756,13 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
dst_field->name_length = field->name_length + prefix->length;
dst_field->value_length = field->value_length;
- if (field->value == h->content_length.start) {
+ if (field == r->content_length) {
req->content_length_field = dst_field - req->fields;
- } else if (field->value == h->content_type.start) {
+ } else if (field == r->content_type) {
req->content_type_field = dst_field - req->fields;
- } else if (field->value == h->cookie.start) {
+ } else if (field == r->cookie) {
req->cookie_field = dst_field - req->fields;
}
@@ -4846,14 +4831,14 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
dst_field++;
}
- req->fields_count = dst_field - req->fields;
+ req->fields_count = (uint32_t) (dst_field - req->fields);
nxt_unit_sptr_set(&req->preread_content, out->mem.free);
buf = out;
tail = &buf->next;
- for (b = r->body.buf; b != NULL; b = b->next) {
+ for (b = r->body; b != NULL; b = b->next) {
size = nxt_buf_mem_used_size(&b->mem);
pos = b->mem.pos;
@@ -4913,8 +4898,8 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data)
nxt_port_t *port;
nxt_timer_t *timer;
nxt_queue_link_t *lnk;
+ nxt_http_request_t *r;
nxt_req_app_link_t *pending_ra;
- nxt_app_parse_ctx_t *ar;
nxt_req_conn_link_t *rc;
nxt_port_select_state_t state;
@@ -4922,8 +4907,8 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "router app timeout");
- ar = nxt_timer_data(timer, nxt_app_parse_ctx_t, timer);
- rc = ar->timer_data;
+ r = nxt_timer_data(timer, nxt_http_request_t, timer);
+ rc = r->timer_data;
app = rc->app;
if (app == NULL) {
@@ -4994,7 +4979,30 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data)
generate_error:
- nxt_http_request_error(task, ar->request, NXT_HTTP_SERVICE_UNAVAILABLE);
+ nxt_http_request_error(task, r, NXT_HTTP_SERVICE_UNAVAILABLE);
nxt_router_rc_unlink(task, rc);
}
+
+
+static nxt_int_t
+nxt_router_http_request_done(nxt_task_t *task, nxt_http_request_t *r)
+{
+ r->timer.handler = nxt_router_http_request_release;
+ nxt_timer_add(task->thread->engine, &r->timer, 0);
+
+ return NXT_OK;
+}
+
+
+static void
+nxt_router_http_request_release(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_http_request_t *r;
+
+ nxt_debug(task, "http app release");
+
+ r = nxt_timer_data(obj, nxt_http_request_t, timer);
+
+ nxt_mp_release(r->mem_pool);
+}
diff --git a/src/nxt_router.h b/src/nxt_router.h
index dec56bd5..d9fbfe05 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -21,6 +21,9 @@ typedef struct nxt_http_routes_s nxt_http_routes_t;
typedef struct nxt_router_access_log_s nxt_router_access_log_t;
+#define NXT_HTTP_PASS_ERROR ((nxt_http_pass_t *) -1)
+
+
typedef struct {
nxt_thread_spinlock_t lock;
nxt_queue_t engines;
@@ -192,7 +195,7 @@ void nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
void nxt_router_access_log_reopen_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
-void nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar,
+void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r,
nxt_app_t *app);
void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port);
nxt_app_t *nxt_router_listener_application(nxt_router_temp_conf_t *tmcf,
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index 547c7494..06478f72 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -53,14 +53,13 @@ nxt_runtime_create(nxt_task_t *task)
nxt_app_lang_module_t *lang;
mp = nxt_mp_create(1024, 128, 256, 32);
-
if (nxt_slow_path(mp == NULL)) {
return NXT_ERROR;
}
rt = nxt_mp_zget(mp, sizeof(nxt_runtime_t));
if (nxt_slow_path(rt == NULL)) {
- return NXT_ERROR;
+ goto fail;
}
task->thread->runtime = rt;
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c
index a001c730..99cf54b4 100644
--- a/src/nxt_sockaddr.c
+++ b/src/nxt_sockaddr.c
@@ -197,23 +197,23 @@ nxt_getsockname(nxt_task_t *task, nxt_mp_t *mp, nxt_socket_t s)
switch (sockaddr.buf.sa_family) {
#if (NXT_INET6)
case AF_INET6:
- length = NXT_INET6_ADDR_STR_LEN;
- break;
+ length = NXT_INET6_ADDR_STR_LEN;
+ break;
#endif
#if (NXT_HAVE_UNIX_DOMAIN)
case AF_UNIX:
- length = nxt_length("unix:") + socklen;
+ length = nxt_length("unix:") + socklen;
#endif
- break;
+ break;
case AF_INET:
- length = NXT_INET_ADDR_STR_LEN;
- break;
+ length = NXT_INET_ADDR_STR_LEN;
+ break;
default:
- length = 0;
- break;
+ length = 0;
+ break;
}
return nxt_sockaddr_create(mp, &sockaddr.buf, socklen, length);
diff --git a/src/nxt_socket.h b/src/nxt_socket.h
index 42ef6c53..3f00648d 100644
--- a/src/nxt_socket.h
+++ b/src/nxt_socket.h
@@ -112,9 +112,9 @@ NXT_EXPORT nxt_int_t nxt_socketpair_create(nxt_task_t *task,
nxt_socket_t *pair);
NXT_EXPORT void nxt_socketpair_close(nxt_task_t *task, nxt_socket_t *pair);
NXT_EXPORT ssize_t nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd,
- nxt_iobuf_t *iob, nxt_uint_t niob);
+ nxt_iobuf_t *iob, nxt_uint_t niob);
NXT_EXPORT ssize_t nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_fd_t *fd,
- nxt_iobuf_t *iob, nxt_uint_t niob);
+ nxt_iobuf_t *iob, nxt_uint_t niob);
#define \
diff --git a/src/nxt_socketpair.c b/src/nxt_socketpair.c
index a7396b31..10ea562e 100644
--- a/src/nxt_socketpair.c
+++ b/src/nxt_socketpair.c
@@ -21,9 +21,9 @@
static ssize_t nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob,
- nxt_uint_t niob);
+ nxt_uint_t niob);
static ssize_t nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob,
- nxt_uint_t niob);
+ nxt_uint_t niob);
nxt_int_t
@@ -94,9 +94,14 @@ nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob,
case NXT_EAGAIN:
nxt_debug(ev->task, "sendmsg(%d) not ready", ev->fd);
- ev->write_ready = 0;
+ break;
- return NXT_AGAIN;
+ /*
+ * Returned (at least on OSX) when trying to send many small messages.
+ */
+ case NXT_ENOBUFS:
+ nxt_debug(ev->task, "sendmsg(%d) no buffers", ev->fd);
+ break;
case NXT_EINTR:
nxt_debug(ev->task, "sendmsg(%d) interrupted", ev->fd);
@@ -108,6 +113,10 @@ nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob,
return NXT_ERROR;
}
+
+ ev->write_ready = 0;
+
+ return NXT_AGAIN;
}
}
diff --git a/src/nxt_sprintf.c b/src/nxt_sprintf.c
index 0b387883..240f47ef 100644
--- a/src/nxt_sprintf.c
+++ b/src/nxt_sprintf.c
@@ -76,12 +76,12 @@ nxt_sprintf(u_char *buf, u_char *end, const char *fmt, ...)
*/
typedef struct {
- u_char *end;
- const u_char *hex;
- uint32_t width;
- int32_t frac_width;
- uint8_t max_width;
- u_char padding;
+ u_char *end;
+ const u_char *hex;
+ uint32_t width;
+ int32_t frac_width;
+ uint8_t max_width;
+ u_char padding;
} nxt_sprintf_t;
diff --git a/src/nxt_timer.h b/src/nxt_timer.h
index 4199f0dd..3ccff848 100644
--- a/src/nxt_timer.h
+++ b/src/nxt_timer.h
@@ -69,7 +69,7 @@ typedef struct {
nxt_uint_t mchanges;
nxt_uint_t nchanges;
- nxt_timer_change_t *changes;
+ nxt_timer_change_t *changes;
} nxt_timers_t;
diff --git a/src/nxt_unicode_lowcase.pl b/src/nxt_unicode_lowcase.pl
index 974ae23a..abf64965 100644
--- a/src/nxt_unicode_lowcase.pl
+++ b/src/nxt_unicode_lowcase.pl
@@ -29,9 +29,9 @@ my $last_block_size = $max_lowcase % BLOCK_SIZE + 1;
for my $block (sort { $a <=> $b } keys %blocks) {
- if ($max_block < $block) {
- $max_block = $block;
- }
+ if ($max_block < $block) {
+ $max_block = $block;
+ }
}
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index 6339aec5..88c7fa6a 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -1946,10 +1946,10 @@ nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i)
while (i + 1 > cap) {
if (cap < 16) {
- cap = cap * 2;
+ cap = cap * 2;
} else {
- cap = cap + cap / 2;
+ cap = cap + cap / 2;
}
}
diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h
index 88d569a6..2207cefa 100644
--- a/src/nxt_unit_request.h
+++ b/src/nxt_unit_request.h
@@ -19,6 +19,7 @@ struct nxt_unit_request_s {
uint8_t version_length;
uint8_t remote_length;
uint8_t local_length;
+ uint8_t tls;
uint32_t server_name_length;
uint32_t target_length;
uint32_t path_length;
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index 0b4b31d7..b99d3269 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -656,8 +656,11 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl,
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.version"),
newRV_noinc((SV *) array_version)));
+
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.url_scheme"),
- newSVpv("http", 4)));
+ r->tls ? newSVpv("https", 5)
+ : newSVpv("http", 4)));
+
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.input"),
SvREFCNT_inc(nxt_perl_psgi_arg_input.io)));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.errors"),
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index b2398abe..ab9f7020 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -327,7 +327,6 @@ nxt_ruby_rack_env_create(VALUE arg)
rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
rb_hash_aset(hash_env, rb_str_new2("rack.version"), version);
- rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), rb_str_new2("http"));
rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input);
rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error);
rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse);
@@ -454,6 +453,9 @@ nxt_ruby_read_request(VALUE hash_env)
r->server_name_length);
nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), "80", 2);
+ rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"),
+ r->tls ? rb_str_new2("https") : rb_str_new2("http"));
+
for (i = 0; i < r->fields_count; i++) {
f = r->fields + i;
diff --git a/test/node/has_header/app.js b/test/node/has_header/app.js
index 040f551e..eff7f4ff 100755
--- a/test/node/has_header/app.js
+++ b/test/node/has_header/app.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.setHeader('X-Has-Header', res.hasHeader(req['headers']['X-Header']) + '');
+ res.setHeader('X-Has-Header', res.hasHeader(req.headers['x-header']) + '');
res.end();
}).listen(7080);
diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js
index 54df09d1..60b0c3bb 100755
--- a/test/node/promise_handler/app.js
+++ b/test/node/promise_handler/app.js
@@ -5,7 +5,7 @@ var fs = require('fs');
require('unit-http').createServer(function (req, res) {
res.end();
- if (req.headers['X-Write-Call']) {
+ if (req.headers['x-write-call']) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('blah');
}
diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js
index 578b72a7..cd7b80c3 100755
--- a/test/node/remove_header/app.js
+++ b/test/node/remove_header/app.js
@@ -4,7 +4,7 @@ require('unit-http').createServer(function (req, res) {
res.setHeader('X-Header', 'test');
res.setHeader('Was-Header', res.hasHeader('X-Header').toString());
- res.removeHeader(req['headers']['X-Remove']);
+ res.removeHeader(req.headers['x-remove']);
res.setHeader('Has-Header', res.hasHeader('X-Header').toString());
res.end();
diff --git a/test/node/variables/app.js b/test/node/variables/app.js
index 968afba5..4ed94d09 100755
--- a/test/node/variables/app.js
+++ b/test/node/variables/app.js
@@ -11,9 +11,9 @@ require('unit-http').createServer(function (req, res) {
res.setHeader('Server-Protocol', req.httpVersion);
res.setHeader('Request-Raw-Headers', req.rawHeaders.join());
res.setHeader('Content-Length', Buffer.byteLength(body));
- res.setHeader('Content-Type', req.headers['Content-Type']);
- res.setHeader('Custom-Header', req.headers['Custom-Header']);
- res.setHeader('Http-Host', req.headers['Host']);
+ res.setHeader('Content-Type', req.headers['content-type']);
+ res.setHeader('Custom-Header', req.headers['custom-header']);
+ res.setHeader('Http-Host', req.headers['host']);
res.writeHead(200, {});
res.end(body);
});
diff --git a/test/php/404/index.php b/test/php/404/index.php
index 92afdf19..561dbec2 100644
--- a/test/php/404/index.php
+++ b/test/php/404/index.php
@@ -1,4 +1,10 @@
<?php
-http_response_code(404);
+if (!function_exists('http_response_code')) {
+ header('Temporary-Header: True', true, 404);
+ header_remove('Temporary-Header');
+} else {
+ http_response_code(404);
+}
+
include('404.html');
?>
diff --git a/test/php/ini_precision/php.ini b/test/php/ini_precision/ini/php.ini
index 51dbdede..51dbdede 100644
--- a/test/php/ini_precision/php.ini
+++ b/test/php/ini_precision/ini/php.ini
diff --git a/test/python/mirror/wsgi.py b/test/python/mirror/wsgi.py
index e4df67ec..eb1fb922 100644
--- a/test/python/mirror/wsgi.py
+++ b/test/python/mirror/wsgi.py
@@ -4,7 +4,6 @@ def application(environ, start_response):
body = bytes(environ['wsgi.input'].read(content_length))
start_response('200', [
- ('Content-Type', environ.get('CONTENT_TYPE')),
('Content-Length', str(len(body)))
])
return [body]
diff --git a/test/test_access_log.py b/test/test_access_log.py
index d6741c28..49497ad2 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -1,55 +1,63 @@
import os
import re
import time
-from subprocess import call
import unittest
-import unit
+from subprocess import call
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitAccessLog(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestAccessLog(TestApplicationPython):
+ prerequisites = ['python']
def load(self, script):
super().load(script)
self.conf('"' + self.testdir + '/access.log"', 'access_log')
- def search_in_log(self, pattern, name='access.log'):
- with open(self.testdir + '/' + name, 'r') as f:
- return re.search(pattern, f.read())
+ def wait_for_record(self, pattern, name='access.log'):
+ return super().wait_for_record(pattern, name)
def test_access_log_keepalive(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='01234')
+ self.assertEqual(self.get()['status'], 200, 'init')
- time.sleep(0.2)
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='01234',
+ read_timeout=1,
+ )
self.assertIsNotNone(
- self.search_in_log(r'"POST / HTTP/1.1" 200 5'), 'keepalive 1')
-
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
-
- time.sleep(0.2)
+ self.wait_for_record(r'"POST / HTTP/1.1" 200 5'), 'keepalive 1'
+ )
+
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"POST / HTTP/1.1" 200 10'), 'keepalive 2')
+ self.wait_for_record(r'"POST / HTTP/1.1" 200 10'), 'keepalive 2'
+ )
def test_access_log_pipeline(self):
self.load('empty')
- self.http(b"""GET / HTTP/1.1
+ self.http(
+ b"""GET / HTTP/1.1
Host: localhost
Referer: Referer-1
@@ -62,180 +70,192 @@ Host: localhost
Referer: Referer-3
Connection: close
-""", raw_resp=True, raw=True)
-
- time.sleep(0.2)
+""",
+ raw_resp=True,
+ raw=True,
+ )
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "Referer-1" "-"'),
- 'pipeline 1')
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-1" "-"'),
+ 'pipeline 1',
+ )
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "Referer-2" "-"'),
- 'pipeline 2')
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-2" "-"'),
+ 'pipeline 2',
+ )
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "Referer-3" "-"'),
- 'pipeline 3')
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-3" "-"'),
+ 'pipeline 3',
+ )
def test_access_log_ipv6(self):
self.load('empty')
- self.conf({
- "[::1]:7080": {
- "application": "empty"
- }
- }, 'listeners')
+ self.conf({"[::1]:7080": {"pass": "applications/empty"}}, 'listeners')
self.get(sock_type='ipv6')
- time.sleep(0.2)
-
self.stop()
self.assertIsNotNone(
- self.search_in_log(
- r'::1 - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'), 'ipv6')
+ self.wait_for_record(
+ r'::1 - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
+ ),
+ 'ipv6',
+ )
def test_access_log_unix(self):
self.load('empty')
addr = self.testdir + '/sock'
- self.conf({
- "unix:" + addr: {
- "application": "empty"
- }
- }, 'listeners')
+ self.conf({"unix:" + addr: {"pass": "applications/empty"}}, 'listeners')
self.get(sock_type='unix', addr=addr)
- time.sleep(0.2)
-
self.stop()
- self.assertIsNotNone(self.search_in_log(
- r'unix: - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'), 'unix')
+ self.assertIsNotNone(
+ self.wait_for_record(
+ r'unix: - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
+ ),
+ 'unix',
+ )
def test_access_log_referer(self):
self.load('empty')
- self.get(headers={
- 'Host': 'localhost',
- 'Referer': 'referer-value',
- 'Connection': 'close'
- })
-
- time.sleep(0.2)
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Referer': 'referer-value',
+ 'Connection': 'close',
+ }
+ )
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "referer-value" "-"'),
- 'referer')
+ self.wait_for_record(
+ r'"GET / HTTP/1.1" 200 0 "referer-value" "-"'
+ ),
+ 'referer',
+ )
def test_access_log_user_agent(self):
self.load('empty')
- self.get(headers={
- 'Host': 'localhost',
- 'User-Agent': 'user-agent-value',
- 'Connection': 'close'
- })
-
- time.sleep(0.2)
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'User-Agent': 'user-agent-value',
+ 'Connection': 'close',
+ }
+ )
self.stop()
self.assertIsNotNone(
- self.search_in_log(
- r'"GET / HTTP/1.1" 200 0 "-" "user-agent-value"'), 'user agent')
+ self.wait_for_record(
+ r'"GET / HTTP/1.1" 200 0 "-" "user-agent-value"'
+ ),
+ 'user agent',
+ )
def test_access_log_http10(self):
self.load('empty')
self.get(http_10=True)
- time.sleep(0.2)
-
self.stop()
self.assertIsNotNone(
- self.search_in_log(
- r'"GET / HTTP/1.0" 200 0 "-" "-"'), 'http 1.0')
+ self.wait_for_record(r'"GET / HTTP/1.0" 200 0 "-" "-"'), 'http 1.0'
+ )
def test_access_log_partial(self):
self.load('empty')
- self.http(b"""GE""", raw=True)
+ self.assertEqual(self.post()['status'], 200, 'init')
- time.sleep(0.2)
+ resp = self.http(b"""GE""", raw=True, read_timeout=5)
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GE" 400 0 "-" "-"'), 'partial')
+ self.wait_for_record(r'"GE" 400 0 "-" "-"'), 'partial'
+ )
def test_access_log_partial_2(self):
self.load('empty')
- self.http(b"""GET /\n""", raw=True)
+ self.assertEqual(self.post()['status'], 200, 'init')
- time.sleep(0.2)
+ self.http(b"""GET /\n""", raw=True)
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET /" 400 \d+ "-" "-"'), 'partial 2')
+ self.wait_for_record(r'"GET /" 400 \d+ "-" "-"'), 'partial 2'
+ )
def test_access_log_partial_3(self):
self.load('empty')
- self.http(b"""GET / HTTP/1.1""", raw=True)
+ self.assertEqual(self.post()['status'], 200, 'init')
- time.sleep(0.2)
+ resp = self.http(b"""GET / HTTP/1.1""", raw=True, read_timeout=5)
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET /" 400 0 "-" "-"'), 'partial 3')
+ self.wait_for_record(r'"GET /" 400 0 "-" "-"'), 'partial 3'
+ )
def test_access_log_partial_4(self):
self.load('empty')
- resp = self.http(b"""GET / HTTP/1.1\n""", raw=True)
+ self.assertEqual(self.post()['status'], 200, 'init')
- time.sleep(0.2)
+ resp = self.http(b"""GET / HTTP/1.1\n""", raw=True, read_timeout=5)
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 400 0 "-" "-"'),
- 'partial 4')
+ self.wait_for_record(r'"GET / HTTP/1.1" 400 0 "-" "-"'),
+ 'partial 4',
+ )
+ @unittest.skip('not yet')
def test_access_log_partial_5(self):
self.load('empty')
- self.http(b"""GET / HTTP/1.1\n\n""", raw=True)
+ self.assertEqual(self.post()['status'], 200, 'init')
+
+ self.get(headers={'Connection': 'close'})
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "-" "-"'), 'partial 5')
+ self.wait_for_record(r'"GET / HTTP/1.1" 400 \d+ "-" "-"'),
+ 'partial 5',
+ )
def test_access_log_get_parameters(self):
self.load('empty')
self.get(url='/?blah&var=val')
- time.sleep(0.2)
-
self.stop()
self.assertIsNotNone(
- self.search_in_log(
- r'"GET /\?blah&var=val HTTP/1.1" 200 0 "-" "-"'),
- 'get parameters')
+ self.wait_for_record(
+ r'"GET /\?blah&var=val HTTP/1.1" 200 0 "-" "-"'
+ ),
+ 'get parameters',
+ )
def test_access_log_delete(self):
self.load('empty')
@@ -244,11 +264,11 @@ Connection: close
self.get(url='/delete')
- time.sleep(0.2)
-
self.stop()
- self.assertIsNone(self.search_in_log(r'/delete'), 'delete')
+ self.assertIsNone(
+ self.search_in_log(r'/delete', 'access.log'), 'delete'
+ )
def test_access_log_change(self):
self.load('empty')
@@ -259,13 +279,12 @@ Connection: close
self.get()
- time.sleep(0.2)
-
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'),
- 'change')
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'),
+ 'change',
+ )
def test_access_log_reopen(self):
self.load('empty')
@@ -280,11 +299,10 @@ Connection: close
self.get()
- time.sleep(0.2)
-
self.assertIsNotNone(
- self.search_in_log(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'),
- 'rename new')
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'),
+ 'rename new',
+ )
self.assertFalse(os.path.isfile(log_path), 'rename old')
with open(self.testdir + '/unit.pid', 'r') as f:
@@ -296,13 +314,14 @@ Connection: close
self.get(url='/usr1')
- time.sleep(0.2)
-
- self.assertIsNone(
- self.search_in_log(r'/usr1', 'new.log'), 'rename new 2')
self.assertIsNotNone(
- self.search_in_log(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"'),
- 'reopen 2')
+ self.wait_for_record(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"'),
+ 'reopen 2',
+ )
+ self.assertIsNone(
+ self.search_in_log(r'/usr1', 'new.log'), 'rename new 2'
+ )
+
if __name__ == '__main__':
- TestUnitAccessLog.main()
+ TestAccessLog.main()
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 52a67d38..6e59c0a7 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -1,10 +1,9 @@
import unittest
-import unit
+from unit.control import TestControl
-class TestUnitConfiguration(unit.TestUnitControl):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestConfiguration(TestControl):
+ prerequisites = ['python']
def test_json_empty(self):
self.assertIn('error', self.conf(''), 'empty')
@@ -13,7 +12,10 @@ class TestUnitConfiguration(unit.TestUnitControl):
self.assertIn('error', self.conf('00'), 'leading zero')
def test_json_unicode(self):
- self.assertIn('success', self.conf(b"""
+ self.assertIn(
+ 'success',
+ self.conf(
+ b"""
{
"ap\u0070": {
"type": "\u0070ython",
@@ -22,32 +24,51 @@ class TestUnitConfiguration(unit.TestUnitControl):
"module": "wsgi"
}
}
- """, 'applications'), 'unicode')
-
- self.assertDictEqual(self.conf_get('applications'), {
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
- }
- }, 'unicode get')
+ """,
+ 'applications',
+ ),
+ 'unicode',
+ )
+
+ self.assertDictEqual(
+ self.conf_get('applications'),
+ {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
+ 'unicode get',
+ )
def test_json_unicode_2(self):
- self.assertIn('success', self.conf({
- "приложение": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
- }
- }, 'applications'), 'unicode 2')
-
- self.assertIn('приложение', self.conf_get('applications'),
- 'unicode 2 get')
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "приложение": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
+ 'applications',
+ ),
+ 'unicode 2',
+ )
+
+ self.assertIn(
+ 'приложение', self.conf_get('applications'), 'unicode 2 get'
+ )
def test_json_unicode_number(self):
- self.assertIn('error', self.conf(b"""
+ self.assertIn(
+ 'error',
+ self.conf(
+ b"""
{
"app": {
"type": "python",
@@ -56,7 +77,11 @@ class TestUnitConfiguration(unit.TestUnitControl):
"module": "wsgi"
}
}
- """, 'applications'), 'unicode number')
+ """,
+ 'applications',
+ ),
+ 'unicode number',
+ )
def test_applications_open_brace(self):
self.assertIn('error', self.conf('{', 'applications'), 'open brace')
@@ -64,21 +89,19 @@ class TestUnitConfiguration(unit.TestUnitControl):
def test_applications_string(self):
self.assertIn('error', self.conf('"{}"', 'applications'), 'string')
+ @unittest.skip('not yet, unsafe')
def test_applications_type_only(self):
- self.skip_alerts.extend([
- r'python module is empty',
- r'failed to apply new conf',
- r'process \d+ exited on signal'
- ])
-
- self.assertIn('error', self.conf({
- "app": {
- "type": "python"
- }
- }, 'applications'), 'type only')
+ self.assertIn(
+ 'error',
+ self.conf({"app": {"type": "python"}}, 'applications'),
+ 'type only',
+ )
def test_applications_miss_quote(self):
- self.assertIn('error', self.conf("""
+ self.assertIn(
+ 'error',
+ self.conf(
+ """
{
app": {
"type": "python",
@@ -87,10 +110,17 @@ class TestUnitConfiguration(unit.TestUnitControl):
"module": "wsgi"
}
}
- """, 'applications'), 'miss quote')
+ """,
+ 'applications',
+ ),
+ 'miss quote',
+ )
def test_applications_miss_colon(self):
- self.assertIn('error', self.conf("""
+ self.assertIn(
+ 'error',
+ self.conf(
+ """
{
"app" {
"type": "python",
@@ -99,10 +129,17 @@ class TestUnitConfiguration(unit.TestUnitControl):
"module": "wsgi"
}
}
- """, 'applications'), 'miss colon')
+ """,
+ 'applications',
+ ),
+ 'miss colon',
+ )
def test_applications_miss_comma(self):
- self.assertIn('error', self.conf("""
+ self.assertIn(
+ 'error',
+ self.conf(
+ """
{
"app": {
"type": "python"
@@ -111,158 +148,184 @@ class TestUnitConfiguration(unit.TestUnitControl):
"module": "wsgi"
}
}
- """, 'applications'), 'miss comma')
+ """,
+ 'applications',
+ ),
+ 'miss comma',
+ )
def test_applications_skip_spaces(self):
- self.assertIn('success', self.conf(b'{ \n\r\t}', 'applications'),
- 'skip spaces')
+ self.assertIn(
+ 'success', self.conf(b'{ \n\r\t}', 'applications'), 'skip spaces'
+ )
def test_applications_relative_path(self):
- self.assertIn('success', self.conf({
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "../app",
- "module": "wsgi"
- }
- }, 'applications'), 'relative path')
-
- @unittest.expectedFailure
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "../app",
+ "module": "wsgi",
+ }
+ },
+ 'applications',
+ ),
+ 'relative path',
+ )
+
+ @unittest.skip('not yet, unsafe')
def test_listeners_empty(self):
- self.skip_sanitizer = True
- self.skip_alerts.extend([
- r'failed to apply previous configuration',
- r'process \d+ exited on signal'
- ])
-
- self.assertIn('error', self.conf({"*:7080":{}}, 'listeners'),
- 'listener empty')
+ self.assertIn(
+ 'error', self.conf({"*:7080": {}}, 'listeners'), 'listener empty'
+ )
def test_listeners_no_app(self):
- self.assertIn('error', self.conf({"*:7080":{"application":"app"}},
- 'listeners'), 'listeners no app')
+ self.assertIn(
+ 'error',
+ self.conf({"*:7080": {"pass": "applications/app"}}, 'listeners'),
+ 'listeners no app',
+ )
def test_listeners_wildcard(self):
- self.assertIn('success', self.conf({
- "listeners": {
- "*:7080": {
- "application":"app"
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/app"}},
+ "applications": {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
}
- },
- "applications": {
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
- }
- }
- }), 'listeners wildcard')
+ ),
+ 'listeners wildcard',
+ )
def test_listeners_explicit(self):
- self.assertIn('success', self.conf({
- "listeners": {
- "127.0.0.1:7080": {
- "application":"app"
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"127.0.0.1:7080": {"pass": "applications/app"}},
+ "applications": {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
}
- },
- "applications": {
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
- }
- }
- }), 'explicit')
+ ),
+ 'explicit',
+ )
def test_listeners_explicit_ipv6(self):
- self.assertIn('success', self.conf({
- "listeners": {
- "[::1]:7080": {
- "application":"app"
- }
- },
- "applications": {
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"[::1]:7080": {"pass": "applications/app"}},
+ "applications": {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
}
- }
- }), 'explicit ipv6')
+ ),
+ 'explicit ipv6',
+ )
+ @unittest.skip('not yet, unsafe')
def test_listeners_no_port(self):
- self.skip_alerts.extend([
- r'invalid listener "127\.0\.0\.1"',
- r'failed to apply new conf',
- r'process \d+ exited on signal'
- ])
-
- self.assertIn('error', self.conf({
- "listeners": {
- "127.0.0.1": {
- "application":"app"
- }
- },
- "applications": {
- "app": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": "/app",
- "module": "wsgi"
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "listeners": {"127.0.0.1": {"pass": "applications/app"}},
+ "applications": {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
}
- }
- }), 'no port')
+ ),
+ 'no port',
+ )
- @unittest.expectedFailure
def test_json_application_name_large(self):
- self.skip_alerts.append(r'epoll_ctl.+failed')
name = "X" * 1024 * 1024
- self.assertIn('success', self.conf({
- "listeners": {
- "*:7080": {
- "application": name
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + name}},
+ "applications": {
+ name: {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
}
- },
+ ),
+ )
+
+ @unittest.skip('not yet')
+ def test_json_application_many(self):
+ apps = 999
+
+ conf = {
"applications": {
- name: {
+ "app-"
+ + str(a): {
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module": "wsgi"
+ "module": "wsgi",
}
- }
- }))
+ for a in range(apps)
+ },
+ "listeners": {
+ "*:" + str(7000 + a): {"pass": "applications/app-" + str(a)}
+ for a in range(apps)
+ },
+ }
- @unittest.expectedFailure
- def test_json_application_many(self):
- self.skip_alerts.extend([
- r'eventfd.+failed',
- r'epoll_create.+failed',
- r'failed to apply new conf'
- ])
- apps = 999
+ self.assertIn('success', self.conf(conf))
+ def test_json_application_many2(self):
conf = {
- "applications":
- {"app-" + str(a): {
+ "applications": {
+ "app-"
+ + str(a): {
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module": "wsgi"
- } for a in range(apps)
+ "module": "wsgi",
+ }
+ for a in range(999)
},
- "listeners": {
- "*:" + str(7000 + a): {
- "application": "app-" + str(a)
- } for a in range(apps)
- }
+ "listeners": {"*:7001": {"pass": "applications/app-1"}},
}
self.assertIn('success', self.conf(conf))
+
if __name__ == '__main__':
- TestUnitConfiguration.main()
+ TestConfiguration.main()
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 1ecc2536..488bfdd5 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -1,22 +1,23 @@
-import unittest
-import unit
+from unit.applications.lang.go import TestApplicationGo
-class TestUnitGoApplication(unit.TestUnitApplicationGo):
- def setUpClass():
- unit.TestUnit().check_modules('go')
+class TestGoApplication(TestApplicationGo):
+ prerequisites = ['go']
def test_go_application_variables(self):
self.load('variables')
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
@@ -25,21 +26,28 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
-
- self.assertDictEqual(headers, {
- 'Content-Length': str(len(body)),
- 'Content-Type': 'text/html',
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Server-Protocol-Major': '1',
- 'Server-Protocol-Minor': '1',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, 'headers')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
+
+ self.assertDictEqual(
+ headers,
+ {
+ 'Content-Length': str(len(body)),
+ 'Content-Type': 'text/html',
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Server-Protocol-Major': '1',
+ 'Server-Protocol-Minor': '1',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_go_application_get_variables(self):
@@ -53,11 +61,14 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
def test_go_application_post_variables(self):
self.load('post_variables')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Connection': 'close'
- }, body='var1=val1&var2=&var3')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Connection': 'close',
+ },
+ body='var1=val1&var2=&var3',
+ )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables')
self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2')
@@ -69,36 +80,50 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
resp = self.get()
self.assertEqual(resp['status'], 404, '404 status')
- self.assertRegex(resp['body'], r'<title>404 Not Found</title>',
- '404 body')
+ self.assertRegex(
+ resp['body'], r'<title>404 Not Found</title>', '404 body'
+ )
def test_go_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
def test_go_application_cookies(self):
self.load('cookies')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'var1=val1; var2=val2',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var1=val1; var2=val2',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['headers']['X-Cookie-1'], 'val1', 'cookie 1')
self.assertEqual(resp['headers']['X-Cookie-2'], 'val2', 'cookie 2')
@@ -106,15 +131,22 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
def test_go_application_command_line_arguments_type(self):
self.load('command_line_arguments')
- self.assertIn('error', self.conf(''"a b c",
- 'applications/command_line_arguments/arguments'), 'arguments type')
+ self.assertIn(
+ 'error',
+ self.conf(
+ '' "a b c", 'applications/command_line_arguments/arguments'
+ ),
+ 'arguments type',
+ )
def test_go_application_command_line_arguments_0(self):
self.load('command_line_arguments')
- self.assertEqual(self.get()['headers']['X-Arg-0'],
+ self.assertEqual(
+ self.get()['headers']['X-Arg-0'],
self.conf_get('applications/command_line_arguments/executable'),
- 'argument 0')
+ 'argument 0',
+ )
def test_go_application_command_line_arguments(self):
self.load('command_line_arguments')
@@ -123,11 +155,14 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
arg2 = '--cc-opt=\'-O0 -DNXT_DEBUG_MEMORY=1 -fsanitize=address\''
arg3 = '--debug'
- self.conf('["' + arg1 + '", "' + arg2 + '", "' + arg3 + '"]',
- 'applications/command_line_arguments/arguments')
+ self.conf(
+ '["' + arg1 + '", "' + arg2 + '", "' + arg3 + '"]',
+ 'applications/command_line_arguments/arguments',
+ )
- self.assertEqual(self.get()['body'], arg1 + ',' + arg2 + ',' + arg3,
- 'arguments')
+ self.assertEqual(
+ self.get()['body'], arg1 + ',' + arg2 + ',' + arg3, 'arguments'
+ )
def test_go_application_command_line_arguments_change(self):
self.load('command_line_arguments')
@@ -144,8 +179,10 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
self.conf('[]', args_path)
- self.assertEqual(self.get()['headers']['Content-Length'], '0',
- 'arguments empty')
+ self.assertEqual(
+ self.get()['headers']['Content-Length'], '0', 'arguments empty'
+ )
+
if __name__ == '__main__':
- TestUnitGoApplication.main()
+ TestGoApplication.main()
diff --git a/test/test_http_header.py b/test/test_http_header.py
index f2294371..603f6f0f 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -1,363 +1,482 @@
import unittest
-import unit
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitHTTPHeader(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestHTTPHeader(TestApplicationPython):
+ prerequisites = ['python']
def test_http_header_value_leading_sp(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': ' ,',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': ' ,',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value leading sp status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value leading sp custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value leading sp custom header',
+ )
def test_http_header_value_leading_htab(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': '\t,',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': '\t,',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value leading htab status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value leading htab custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value leading htab custom header',
+ )
def test_http_header_value_trailing_sp(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': ', ',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': ', ',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value trailing sp status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value trailing sp custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value trailing sp custom header',
+ )
def test_http_header_value_trailing_htab(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': ',\t',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': ',\t',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value trailing htab status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value trailing htab custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value trailing htab custom header',
+ )
def test_http_header_value_both_sp(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': ' , ',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': ' , ',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value both sp status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value both sp custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value both sp custom header',
+ )
def test_http_header_value_both_htab(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': '\t,\t',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': '\t,\t',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value both htab status')
- self.assertEqual(resp['headers']['Custom-Header'], ',',
- 'value both htab custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ',',
+ 'value both htab custom header',
+ )
def test_http_header_value_chars(self):
self.load('custom_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header': '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header': '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 200, 'value chars status')
- self.assertEqual(resp['headers']['Custom-Header'],
- '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~', 'value chars custom header')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~',
+ 'value chars custom header',
+ )
def test_http_header_value_chars_edge(self):
self.load('custom_header')
- resp = self.http(b"""GET / HTTP/1.1
+ resp = self.http(
+ b"""GET / HTTP/1.1
Host: localhost
Custom-Header: \x20\xFF
Connection: close
-""", raw=True, encoding='latin1')
+""",
+ raw=True,
+ encoding='latin1',
+ )
self.assertEqual(resp['status'], 200, 'value chars edge status')
- self.assertEqual(resp['headers']['Custom-Header'], '\xFF',
- 'value chars edge')
+ self.assertEqual(
+ resp['headers']['Custom-Header'], '\xFF', 'value chars edge'
+ )
def test_http_header_value_chars_below(self):
self.load('custom_header')
- resp = self.http(b"""GET / HTTP/1.1
+ resp = self.http(
+ b"""GET / HTTP/1.1
Host: localhost
Custom-Header: \x1F
Connection: close
-""", raw=True)
+""",
+ raw=True,
+ )
self.assertEqual(resp['status'], 400, 'value chars below')
def test_http_header_field_leading_sp(self):
self.load('empty')
- resp = self.get(headers={
- 'Host': 'localhost',
- ' Custom-Header': 'blah',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ ' Custom-Header': 'blah',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 400, 'field leading sp')
def test_http_header_field_leading_htab(self):
self.load('empty')
- resp = self.get(headers={
- 'Host': 'localhost',
- '\tCustom-Header': 'blah',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ '\tCustom-Header': 'blah',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 400, 'field leading htab')
def test_http_header_field_trailing_sp(self):
self.load('empty')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header ': 'blah',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header ': 'blah',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 400, 'field trailing sp')
def test_http_header_field_trailing_htab(self):
self.load('empty')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Custom-Header\t': 'blah',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Custom-Header\t': 'blah',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['status'], 400, 'field trailing htab')
def test_http_header_content_length_big(self):
self.load('empty')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Length': str(2 ** 64),
- 'Connection': 'close'
- }, body='X' * 1000)['status'], 400, 'Content-Length big')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': str(2 ** 64),
+ 'Connection': 'close',
+ },
+ body='X' * 1000,
+ )['status'],
+ 400,
+ 'Content-Length big',
+ )
def test_http_header_content_length_negative(self):
self.load('empty')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Length': '-100',
- 'Connection': 'close'
- }, body='X' * 1000)['status'], 400, 'Content-Length negative')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': '-100',
+ 'Connection': 'close',
+ },
+ body='X' * 1000,
+ )['status'],
+ 400,
+ 'Content-Length negative',
+ )
def test_http_header_content_length_text(self):
self.load('empty')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Length': 'blah',
- 'Connection': 'close'
- }, body='X' * 1000)['status'], 400, 'Content-Length text')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': 'blah',
+ 'Connection': 'close',
+ },
+ body='X' * 1000,
+ )['status'],
+ 400,
+ 'Content-Length text',
+ )
def test_http_header_content_length_multiple_values(self):
self.load('empty')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Length': '41, 42',
- 'Connection': 'close'
- }, body='X' * 1000)['status'], 400, 'Content-Length multiple value')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': '41, 42',
+ 'Connection': 'close',
+ },
+ body='X' * 1000,
+ )['status'],
+ 400,
+ 'Content-Length multiple value',
+ )
def test_http_header_content_length_multiple_fields(self):
self.load('empty')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Length': ['41', '42'],
- 'Connection': 'close'
- }, body='X' * 1000)['status'], 400, 'Content-Length multiple fields')
-
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Length': ['41', '42'],
+ 'Connection': 'close',
+ },
+ body='X' * 1000,
+ )['status'],
+ 400,
+ 'Content-Length multiple fields',
+ )
+
+ @unittest.skip('not yet')
def test_http_header_host_absent(self):
self.load('host')
resp = self.get(headers={'Connection': 'close'})
- self.assertEqual(resp['status'], 200, 'Host absent status')
- self.assertNotEqual(resp['headers']['X-Server-Name'], '',
- 'Host absent SERVER_NAME')
+ self.assertEqual(resp['status'], 400, 'Host absent status')
def test_http_header_host_empty(self):
self.load('host')
- resp = self.get(headers={
- 'Host': '',
- 'Connection': 'close'
- })
+ resp = self.get(headers={'Host': '', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host empty status')
- self.assertNotEqual(resp['headers']['X-Server-Name'], '',
- 'Host empty SERVER_NAME')
+ self.assertNotEqual(
+ resp['headers']['X-Server-Name'], '', 'Host empty SERVER_NAME'
+ )
def test_http_header_host_big(self):
self.load('empty')
- self.assertEqual(self.get(headers={
- 'Host': 'X' * 10000,
- 'Connection': 'close'
- })['status'], 431, 'Host big')
+ self.assertEqual(
+ self.get(headers={'Host': 'X' * 10000, 'Connection': 'close'})[
+ 'status'
+ ],
+ 431,
+ 'Host big',
+ )
def test_http_header_host_port(self):
self.load('host')
- resp = self.get(headers={
- 'Host': 'exmaple.com:7080',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={'Host': 'exmaple.com:7080', 'Connection': 'close'}
+ )
self.assertEqual(resp['status'], 200, 'Host port status')
- self.assertEqual(resp['headers']['X-Server-Name'], 'exmaple.com',
- 'Host port SERVER_NAME')
- self.assertEqual(resp['headers']['X-Http-Host'], 'exmaple.com:7080',
- 'Host port HTTP_HOST')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ 'exmaple.com',
+ 'Host port SERVER_NAME',
+ )
+ self.assertEqual(
+ resp['headers']['X-Http-Host'],
+ 'exmaple.com:7080',
+ 'Host port HTTP_HOST',
+ )
def test_http_header_host_port_empty(self):
self.load('host')
- resp = self.get(headers={
- 'Host': 'exmaple.com:',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={'Host': 'exmaple.com:', 'Connection': 'close'}
+ )
self.assertEqual(resp['status'], 200, 'Host port empty status')
- self.assertEqual(resp['headers']['X-Server-Name'], 'exmaple.com',
- 'Host port empty SERVER_NAME')
- self.assertEqual(resp['headers']['X-Http-Host'], 'exmaple.com:',
- 'Host port empty HTTP_HOST')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ 'exmaple.com',
+ 'Host port empty SERVER_NAME',
+ )
+ self.assertEqual(
+ resp['headers']['X-Http-Host'],
+ 'exmaple.com:',
+ 'Host port empty HTTP_HOST',
+ )
def test_http_header_host_literal(self):
self.load('host')
- resp = self.get(headers={
- 'Host': '127.0.0.1',
- 'Connection': 'close'
- })
+ resp = self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host literal status')
- self.assertEqual(resp['headers']['X-Server-Name'], '127.0.0.1',
- 'Host literal SERVER_NAME')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ '127.0.0.1',
+ 'Host literal SERVER_NAME',
+ )
def test_http_header_host_literal_ipv6(self):
self.load('host')
- resp = self.get(headers={
- 'Host': '[::1]:7080',
- 'Connection': 'close'
- })
+ resp = self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host literal ipv6 status')
- self.assertEqual(resp['headers']['X-Server-Name'], '[::1]',
- 'Host literal ipv6 SERVER_NAME')
- self.assertEqual(resp['headers']['X-Http-Host'], '[::1]:7080',
- 'Host literal ipv6 HTTP_HOST')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ '[::1]',
+ 'Host literal ipv6 SERVER_NAME',
+ )
+ self.assertEqual(
+ resp['headers']['X-Http-Host'],
+ '[::1]:7080',
+ 'Host literal ipv6 HTTP_HOST',
+ )
def test_http_header_host_trailing_period(self):
self.load('host')
- resp = self.get(headers={
- 'Host': '127.0.0.1.',
- 'Connection': 'close'
- })
+ resp = self.get(headers={'Host': '127.0.0.1.', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host trailing period status')
- self.assertEqual(resp['headers']['X-Server-Name'], '127.0.0.1',
- 'Host trailing period SERVER_NAME')
- self.assertEqual(resp['headers']['X-Http-Host'], '127.0.0.1.',
- 'Host trailing period HTTP_HOST')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ '127.0.0.1',
+ 'Host trailing period SERVER_NAME',
+ )
+ self.assertEqual(
+ resp['headers']['X-Http-Host'],
+ '127.0.0.1.',
+ 'Host trailing period HTTP_HOST',
+ )
def test_http_header_host_trailing_period_2(self):
self.load('host')
- resp = self.get(headers={
- 'Host': 'EXAMPLE.COM.',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={'Host': 'EXAMPLE.COM.', 'Connection': 'close'}
+ )
self.assertEqual(resp['status'], 200, 'Host trailing period 2 status')
- self.assertEqual(resp['headers']['X-Server-Name'], 'example.com',
- 'Host trailing period 2 SERVER_NAME')
- self.assertEqual(resp['headers']['X-Http-Host'], 'EXAMPLE.COM.',
- 'Host trailing period 2 HTTP_HOST')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ 'example.com',
+ 'Host trailing period 2 SERVER_NAME',
+ )
+ self.assertEqual(
+ resp['headers']['X-Http-Host'],
+ 'EXAMPLE.COM.',
+ 'Host trailing period 2 HTTP_HOST',
+ )
def test_http_header_host_case_insensitive(self):
self.load('host')
- resp = self.get(headers={
- 'Host': 'EXAMPLE.COM',
- 'Connection': 'close'
- })
+ resp = self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host case insensitive')
- self.assertEqual(resp['headers']['X-Server-Name'], 'example.com',
- 'Host case insensitive SERVER_NAME')
+ self.assertEqual(
+ resp['headers']['X-Server-Name'],
+ 'example.com',
+ 'Host case insensitive SERVER_NAME',
+ )
def test_http_header_host_double_dot(self):
self.load('empty')
- self.assertEqual(self.get(headers={
- 'Host': '127.0.0..1',
- 'Connection': 'close'
- })['status'], 400, 'Host double dot')
+ self.assertEqual(
+ self.get(headers={'Host': '127.0.0..1', 'Connection': 'close'})[
+ 'status'
+ ],
+ 400,
+ 'Host double dot',
+ )
def test_http_header_host_slash(self):
self.load('empty')
- self.assertEqual(self.get(headers={
- 'Host': '/localhost',
- 'Connection': 'close'
- })['status'], 400, 'Host slash')
+ self.assertEqual(
+ self.get(headers={'Host': '/localhost', 'Connection': 'close'})[
+ 'status'
+ ],
+ 400,
+ 'Host slash',
+ )
def test_http_header_host_multiple_fields(self):
self.load('empty')
- self.assertEqual(self.get(headers={
- 'Host': ['localhost', 'example.com'],
- 'Connection': 'close'
- })['status'], 400, 'Host multiple fields')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': ['localhost', 'example.com'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 400,
+ 'Host multiple fields',
+ )
+
if __name__ == '__main__':
- TestUnitHTTPHeader.main()
+ TestHTTPHeader.main()
diff --git a/test/test_java_application.py b/test/test_java_application.py
index d603ed0f..5d0350fa 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -1,20 +1,20 @@
import time
-import unittest
-import unit
+from unit.applications.lang.java import TestApplicationJava
-class TestUnitJavaApplication(unit.TestUnitApplicationJava):
- def setUpClass():
- unit.TestUnit().check_modules('java')
+class TestJavaApplication(TestApplicationJava):
+ prerequisites = ['java']
def test_java_application_cookies(self):
self.load('cookies')
- headers = self.get(headers={
- 'Cookie': 'var1=val1; var2=val2',
- 'Host': 'localhost',
- 'Connection': 'close'
- })['headers']
+ headers = self.get(
+ headers={
+ 'Cookie': 'var1=val1; var2=val2',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )['headers']
self.assertEqual(headers['X-Cookie-1'], 'val1', 'cookie 1')
self.assertEqual(headers['X-Cookie-2'], 'val2', 'cookie 2')
@@ -27,33 +27,46 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
self.assertEqual(headers['X-Filter-Before'], '1', 'filter before')
self.assertEqual(headers['X-Filter-After'], '1', 'filter after')
- self.assertEqual(self.get(url='/test')['headers']['X-Filter-After'],
- '0', 'filter after 2')
+ self.assertEqual(
+ self.get(url='/test')['headers']['X-Filter-After'],
+ '0',
+ 'filter after 2',
+ )
def test_java_application_get_variables(self):
self.load('get_params')
- headers = self.get(url='/?var1=val1&var2=&var4=val4&var4=foo')['headers']
+ headers = self.get(url='/?var1=val1&var2=&var4=val4&var4=foo')[
+ 'headers'
+ ]
self.assertEqual(headers['X-Var-1'], 'val1', 'GET variables')
self.assertEqual(headers['X-Var-2'], 'true', 'GET variables 2')
self.assertEqual(headers['X-Var-3'], 'false', 'GET variables 3')
- self.assertEqual(headers['X-Param-Names'], 'var4 var2 var1 ',
- 'getParameterNames')
- self.assertEqual(headers['X-Param-Values'], 'val4 foo ',
- 'getParameterValues')
- self.assertEqual(headers['X-Param-Map'],
- 'var2= var1=val1 var4=val4,foo ', 'getParameterMap')
+ self.assertEqual(
+ headers['X-Param-Names'], 'var4 var2 var1 ', 'getParameterNames'
+ )
+ self.assertEqual(
+ headers['X-Param-Values'], 'val4 foo ', 'getParameterValues'
+ )
+ self.assertEqual(
+ headers['X-Param-Map'],
+ 'var2= var1=val1 var4=val4,foo ',
+ 'getParameterMap',
+ )
def test_java_application_post_variables(self):
self.load('post_params')
- headers = self.post(headers={
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Host': 'localhost',
- 'Connection': 'close'
- }, body='var1=val1&var2=')['headers']
+ headers = self.post(
+ headers={
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ },
+ body='var1=val1&var2=',
+ )['headers']
self.assertEqual(headers['X-Var-1'], 'val1', 'POST variables')
self.assertEqual(headers['X-Var-2'], 'true', 'POST variables 2')
@@ -68,15 +81,20 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
self.assertEqual(headers['X-Var-1'], 'null', 'variable empty')
self.assertEqual(headers['X-Session-New'], 'true', 'session create')
- headers = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- }, url='/?var1=val2')['headers']
+ headers = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ },
+ url='/?var1=val2',
+ )['headers']
self.assertEqual(headers['X-Var-1'], 'val1', 'variable')
self.assertEqual(headers['X-Session-New'], 'false', 'session resume')
- self.assertEqual(session_id, headers['X-Session-Id'], 'session same id')
+ self.assertEqual(
+ session_id, headers['X-Session-Id'], 'session same id'
+ )
def test_java_application_session_active(self):
self.load('session_inactive')
@@ -85,46 +103,63 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
session_id = resp['headers']['X-Session-Id']
self.assertEqual(resp['status'], 200, 'session init')
- self.assertEqual(resp['headers']['X-Session-Interval'], '2',
- 'session interval')
- self.assertLess(abs(self.date_to_sec_epoch(
- resp['headers']['X-Session-Last-Access-Time']) - self.sec_epoch()),
- 5, 'session last access time')
+ self.assertEqual(
+ resp['headers']['X-Session-Interval'], '2', 'session interval'
+ )
+ self.assertLess(
+ abs(
+ self.date_to_sec_epoch(
+ resp['headers']['X-Session-Last-Access-Time']
+ )
+ - self.sec_epoch()
+ ),
+ 5,
+ 'session last access time',
+ )
time.sleep(1)
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ }
+ )
- self.assertEqual(resp['headers']['X-Session-Id'], session_id,
- 'session active')
+ self.assertEqual(
+ resp['headers']['X-Session-Id'], session_id, 'session active'
+ )
session_id = resp['headers']['X-Session-Id']
time.sleep(1)
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ }
+ )
- self.assertEqual(resp['headers']['X-Session-Id'], session_id,
- 'session active 2')
+ self.assertEqual(
+ resp['headers']['X-Session-Id'], session_id, 'session active 2'
+ )
time.sleep(1)
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ }
+ )
- self.assertEqual(resp['headers']['X-Session-Id'], session_id,
- 'session active 3')
+ self.assertEqual(
+ resp['headers']['X-Session-Id'], session_id, 'session active 3'
+ )
def test_java_application_session_inactive(self):
self.load('session_inactive')
@@ -134,14 +169,17 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
time.sleep(3)
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ }
+ )
- self.assertNotEqual(resp['headers']['X-Session-Id'], session_id,
- 'session inactive')
+ self.assertNotEqual(
+ resp['headers']['X-Session-Id'], session_id, 'session inactive'
+ )
def test_java_application_session_invalidate(self):
self.load('session_invalidate')
@@ -149,14 +187,17 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
resp = self.get()
session_id = resp['headers']['X-Session-Id']
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ }
+ )
- self.assertNotEqual(resp['headers']['X-Session-Id'], session_id,
- 'session invalidate')
+ self.assertNotEqual(
+ resp['headers']['X-Session-Id'], session_id, 'session invalidate'
+ )
def test_java_application_session_listeners(self):
self.load('session_listeners')
@@ -164,30 +205,42 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/test?var1=val1')['headers']
session_id = headers['X-Session-Id']
- self.assertEqual(headers['X-Session-Created'], session_id,
- 'session create')
- self.assertEqual(headers['X-Attr-Added'], 'var1=val1',
- 'attribute add')
-
- headers = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- }, url='/?var1=val2')['headers']
-
- self.assertEqual(session_id, headers['X-Session-Id'], 'session same id')
- self.assertEqual(headers['X-Attr-Replaced'], 'var1=val1',
- 'attribute replace')
+ self.assertEqual(
+ headers['X-Session-Created'], session_id, 'session create'
+ )
+ self.assertEqual(headers['X-Attr-Added'], 'var1=val1', 'attribute add')
+
+ headers = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ },
+ url='/?var1=val2',
+ )['headers']
- headers = self.get(headers={
- 'Host': 'localhost',
- 'Cookie': 'JSESSIONID=' + session_id,
- 'Connection': 'close'
- }, url='/')['headers']
+ self.assertEqual(
+ session_id, headers['X-Session-Id'], 'session same id'
+ )
+ self.assertEqual(
+ headers['X-Attr-Replaced'], 'var1=val1', 'attribute replace'
+ )
+
+ headers = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'JSESSIONID=' + session_id,
+ 'Connection': 'close',
+ },
+ url='/',
+ )['headers']
- self.assertEqual(session_id, headers['X-Session-Id'], 'session same id')
- self.assertEqual(headers['X-Attr-Removed'], 'var1=val2',
- 'attribute remove')
+ self.assertEqual(
+ session_id, headers['X-Session-Id'], 'session same id'
+ )
+ self.assertEqual(
+ headers['X-Attr-Removed'], 'var1=val2', 'attribute remove'
+ )
def test_java_application_jsp(self):
self.load('jsp')
@@ -202,15 +255,23 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/foo/bar/index.html')['headers']
self.assertEqual(headers['X-Id'], 'servlet1', '#1 Servlet1 request')
- self.assertEqual(headers['X-Request-URI'], '/foo/bar/index.html', '#1 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/foo/bar', '#1 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/foo/bar/index.html', '#1 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/foo/bar', '#1 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], '/index.html', '#1 path info')
headers = self.get(url='/foo/bar/index.bop')['headers']
self.assertEqual(headers['X-Id'], 'servlet1', '#2 Servlet1 request')
- self.assertEqual(headers['X-Request-URI'], '/foo/bar/index.bop', '#2 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/foo/bar', '#2 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/foo/bar/index.bop', '#2 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/foo/bar', '#2 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], '/index.bop', '#2 path info')
headers = self.get(url='/baz')['headers']
@@ -223,42 +284,64 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/baz/index.html')['headers']
self.assertEqual(headers['X-Id'], 'servlet2', '#4 Servlet2 request')
- self.assertEqual(headers['X-Request-URI'], '/baz/index.html', '#4 request URI')
+ self.assertEqual(
+ headers['X-Request-URI'], '/baz/index.html', '#4 request URI'
+ )
self.assertEqual(headers['X-Servlet-Path'], '/baz', '#4 servlet path')
self.assertEqual(headers['X-Path-Info'], '/index.html', '#4 path info')
headers = self.get(url='/catalog')['headers']
self.assertEqual(headers['X-Id'], 'servlet3', '#5 Servlet3 request')
- self.assertEqual(headers['X-Request-URI'], '/catalog', '#5 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/catalog', '#5 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/catalog', '#5 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/catalog', '#5 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], 'null', '#5 path info')
headers = self.get(url='/catalog/index.html')['headers']
self.assertEqual(headers['X-Id'], 'default', '#6 default request')
- self.assertEqual(headers['X-Request-URI'], '/catalog/index.html', '#6 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/catalog/index.html', '#6 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/catalog/index.html', '#6 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/catalog/index.html', '#6 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], 'null', '#6 path info')
headers = self.get(url='/catalog/racecar.bop')['headers']
self.assertEqual(headers['X-Id'], 'servlet4', '#7 servlet4 request')
- self.assertEqual(headers['X-Request-URI'], '/catalog/racecar.bop', '#7 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/catalog/racecar.bop', '#7 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/catalog/racecar.bop', '#7 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'],
+ '/catalog/racecar.bop',
+ '#7 servlet path',
+ )
self.assertEqual(headers['X-Path-Info'], 'null', '#7 path info')
- headers = self.get( url='/index.bop')['headers']
+ headers = self.get(url='/index.bop')['headers']
self.assertEqual(headers['X-Id'], 'servlet4', '#8 servlet4 request')
- self.assertEqual(headers['X-Request-URI'], '/index.bop', '#8 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/index.bop', '#8 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/index.bop', '#8 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/index.bop', '#8 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], 'null', '#8 path info')
headers = self.get(url='/foo/baz')['headers']
self.assertEqual(headers['X-Id'], 'servlet0', '#9 servlet0 request')
- self.assertEqual(headers['X-Request-URI'], '/foo/baz', '#9 request URI')
+ self.assertEqual(
+ headers['X-Request-URI'], '/foo/baz', '#9 request URI'
+ )
self.assertEqual(headers['X-Servlet-Path'], '/foo', '#9 servlet path')
self.assertEqual(headers['X-Path-Info'], '/baz', '#9 path info')
@@ -272,8 +355,12 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/index.bop/')['headers']
self.assertEqual(headers['X-Id'], 'default', '#11 default request')
- self.assertEqual(headers['X-Request-URI'], '/index.bop/', '#11 request URI')
- self.assertEqual(headers['X-Servlet-Path'], '/index.bop/', '#11 servlet path')
+ self.assertEqual(
+ headers['X-Request-URI'], '/index.bop/', '#11 request URI'
+ )
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/index.bop/', '#11 servlet path'
+ )
self.assertEqual(headers['X-Path-Info'], 'null', '#11 path info')
def test_java_application_header(self):
@@ -281,10 +368,18 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get()['headers']
- self.assertEqual(headers['X-Set-Utf8-Value'], '????', 'set Utf8 header value')
- self.assertEqual(headers['X-Set-Utf8-Name-???'], 'x', 'set Utf8 header name')
- self.assertEqual(headers['X-Add-Utf8-Value'], '????', 'add Utf8 header value')
- self.assertEqual(headers['X-Add-Utf8-Name-???'], 'y', 'add Utf8 header name')
+ self.assertEqual(
+ headers['X-Set-Utf8-Value'], '????', 'set Utf8 header value'
+ )
+ self.assertEqual(
+ headers['X-Set-Utf8-Name-???'], 'x', 'set Utf8 header name'
+ )
+ self.assertEqual(
+ headers['X-Add-Utf8-Value'], '????', 'add Utf8 header value'
+ )
+ self.assertEqual(
+ headers['X-Add-Utf8-Name-???'], 'y', 'add Utf8 header name'
+ )
self.assertEqual(headers['X-Add-Test'], 'v1', 'add null header')
self.assertEqual('X-Set-Test1' in headers, False, 'set null header')
self.assertEqual(headers['X-Set-Test2'], '', 'set empty header')
@@ -294,52 +389,135 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/1')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=utf-8', '#1 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=utf-8', '#1 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'utf-8', '#1 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=utf-8',
+ '#1 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=utf-8',
+ '#1 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'], 'utf-8', '#1 response charset'
+ )
headers = self.get(url='/2')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=iso-8859-1', '#2 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=iso-8859-1', '#2 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'iso-8859-1', '#2 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=iso-8859-1',
+ '#2 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=iso-8859-1',
+ '#2 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'],
+ 'iso-8859-1',
+ '#2 response charset',
+ )
headers = self.get(url='/3')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=windows-1251', '#3 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=windows-1251', '#3 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'windows-1251', '#3 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=windows-1251',
+ '#3 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=windows-1251',
+ '#3 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'],
+ 'windows-1251',
+ '#3 response charset',
+ )
headers = self.get(url='/4')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=windows-1251', '#4 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=windows-1251', '#4 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'windows-1251', '#4 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=windows-1251',
+ '#4 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=windows-1251',
+ '#4 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'],
+ 'windows-1251',
+ '#4 response charset',
+ )
headers = self.get(url='/5')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=iso-8859-1', '#5 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=iso-8859-1', '#5 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'iso-8859-1', '#5 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=iso-8859-1',
+ '#5 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=iso-8859-1',
+ '#5 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'],
+ 'iso-8859-1',
+ '#5 response charset',
+ )
headers = self.get(url='/6')['headers']
- self.assertEqual('Content-Type' in headers, False, '#6 no Content-Type header')
- self.assertEqual('X-Content-Type' in headers, False, '#6 no response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'utf-8', '#6 response charset')
-
+ self.assertEqual(
+ 'Content-Type' in headers, False, '#6 no Content-Type header'
+ )
+ self.assertEqual(
+ 'X-Content-Type' in headers, False, '#6 no response Content-Type'
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'], 'utf-8', '#6 response charset'
+ )
headers = self.get(url='/7')['headers']
- self.assertEqual(headers['Content-Type'], 'text/plain;charset=utf-8', '#7 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/plain;charset=utf-8', '#7 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'utf-8', '#7 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/plain;charset=utf-8',
+ '#7 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/plain;charset=utf-8',
+ '#7 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'], 'utf-8', '#7 response charset'
+ )
headers = self.get(url='/8')['headers']
- self.assertEqual(headers['Content-Type'], 'text/html;charset=utf-8', '#8 Content-Type header')
- self.assertEqual(headers['X-Content-Type'], 'text/html;charset=utf-8', '#8 response Content-Type')
- self.assertEqual(headers['X-Character-Encoding'], 'utf-8', '#8 response charset')
+ self.assertEqual(
+ headers['Content-Type'],
+ 'text/html;charset=utf-8',
+ '#8 Content-Type header',
+ )
+ self.assertEqual(
+ headers['X-Content-Type'],
+ 'text/html;charset=utf-8',
+ '#8 response Content-Type',
+ )
+ self.assertEqual(
+ headers['X-Character-Encoding'], 'utf-8', '#8 response charset'
+ )
def test_java_application_welcome_files(self):
self.load('welcome_files')
@@ -352,8 +530,12 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
resp = self.get(url='/dir1/')
- self.assertEqual('This is index.txt.' in resp['body'], True, 'dir1 index body')
- self.assertEqual(resp['headers']['X-TXT-Filter'], '1', 'TXT Filter header')
+ self.assertEqual(
+ 'This is index.txt.' in resp['body'], True, 'dir1 index body'
+ )
+ self.assertEqual(
+ resp['headers']['X-TXT-Filter'], '1', 'TXT Filter header'
+ )
headers = self.get(url='/dir2/')['headers']
@@ -362,124 +544,216 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = self.get(url='/dir3/')['headers']
- self.assertEqual(headers['X-App-Servlet'], '1', 'URL pattern overrides welcome file')
+ self.assertEqual(
+ headers['X-App-Servlet'], '1', 'URL pattern overrides welcome file'
+ )
headers = self.get(url='/dir4/')['headers']
- self.assertEqual('X-App-Servlet' in headers, False, 'Static welcome file served first')
+ self.assertEqual(
+ 'X-App-Servlet' in headers,
+ False,
+ 'Static welcome file served first',
+ )
headers = self.get(url='/dir5/')['headers']
- self.assertEqual(headers['X-App-Servlet'], '1', 'Servlet for welcome file served when no static file found')
+ self.assertEqual(
+ headers['X-App-Servlet'],
+ '1',
+ 'Servlet for welcome file served when no static file found',
+ )
def test_java_application_request_listeners(self):
self.load('request_listeners')
headers = self.get(url='/test1')['headers']
- self.assertEqual(headers['X-Request-Initialized'], '/test1',
- 'request initialized event')
- self.assertEqual(headers['X-Request-Destroyed'], '',
- 'request destroyed event')
- self.assertEqual(headers['X-Attr-Added'], '',
- 'attribute added event')
- self.assertEqual(headers['X-Attr-Removed'], '',
- 'attribute removed event')
- self.assertEqual(headers['X-Attr-Replaced'], '',
- 'attribute replaced event')
+ self.assertEqual(
+ headers['X-Request-Initialized'],
+ '/test1',
+ 'request initialized event',
+ )
+ self.assertEqual(
+ headers['X-Request-Destroyed'], '', 'request destroyed event'
+ )
+ self.assertEqual(headers['X-Attr-Added'], '', 'attribute added event')
+ self.assertEqual(
+ headers['X-Attr-Removed'], '', 'attribute removed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Replaced'], '', 'attribute replaced event'
+ )
headers = self.get(url='/test2?var1=1')['headers']
- self.assertEqual(headers['X-Request-Initialized'], '/test2',
- 'request initialized event')
- self.assertEqual(headers['X-Request-Destroyed'], '/test1',
- 'request destroyed event')
- self.assertEqual(headers['X-Attr-Added'], 'var=1;',
- 'attribute added event')
- self.assertEqual(headers['X-Attr-Removed'], 'var=1;',
- 'attribute removed event')
- self.assertEqual(headers['X-Attr-Replaced'], '',
- 'attribute replaced event')
+ self.assertEqual(
+ headers['X-Request-Initialized'],
+ '/test2',
+ 'request initialized event',
+ )
+ self.assertEqual(
+ headers['X-Request-Destroyed'], '/test1', 'request destroyed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Added'], 'var=1;', 'attribute added event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Removed'], 'var=1;', 'attribute removed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Replaced'], '', 'attribute replaced event'
+ )
headers = self.get(url='/test3?var1=1&var2=2')['headers']
- self.assertEqual(headers['X-Request-Initialized'], '/test3',
- 'request initialized event')
- self.assertEqual(headers['X-Request-Destroyed'], '/test2',
- 'request destroyed event')
- self.assertEqual(headers['X-Attr-Added'], 'var=1;',
- 'attribute added event')
- self.assertEqual(headers['X-Attr-Removed'], 'var=2;',
- 'attribute removed event')
- self.assertEqual(headers['X-Attr-Replaced'], 'var=1;',
- 'attribute replaced event')
+ self.assertEqual(
+ headers['X-Request-Initialized'],
+ '/test3',
+ 'request initialized event',
+ )
+ self.assertEqual(
+ headers['X-Request-Destroyed'], '/test2', 'request destroyed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Added'], 'var=1;', 'attribute added event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Removed'], 'var=2;', 'attribute removed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Replaced'], 'var=1;', 'attribute replaced event'
+ )
headers = self.get(url='/test4?var1=1&var2=2&var3=3')['headers']
- self.assertEqual(headers['X-Request-Initialized'], '/test4',
- 'request initialized event')
- self.assertEqual(headers['X-Request-Destroyed'], '/test3',
- 'request destroyed event')
- self.assertEqual(headers['X-Attr-Added'], 'var=1;',
- 'attribute added event')
- self.assertEqual(headers['X-Attr-Removed'], '',
- 'attribute removed event')
- self.assertEqual(headers['X-Attr-Replaced'], 'var=1;var=2;',
- 'attribute replaced event')
+ self.assertEqual(
+ headers['X-Request-Initialized'],
+ '/test4',
+ 'request initialized event',
+ )
+ self.assertEqual(
+ headers['X-Request-Destroyed'], '/test3', 'request destroyed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Added'], 'var=1;', 'attribute added event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Removed'], '', 'attribute removed event'
+ )
+ self.assertEqual(
+ headers['X-Attr-Replaced'],
+ 'var=1;var=2;',
+ 'attribute replaced event',
+ )
def test_java_application_request_uri_forward(self):
self.load('forward')
- resp = self.get(url='/fwd?uri=%2Fdata%2Ftest%3Furi%3Dnew_uri%26a%3D2%26b%3D3&a=1&c=4')
+ resp = self.get(
+ url='/fwd?uri=%2Fdata%2Ftest%3Furi%3Dnew_uri%26a%3D2%26b%3D3&a=1&c=4'
+ )
headers = resp['headers']
- self.assertEqual(headers['X-REQUEST-Id'], 'fwd',
- 'initial request servlet mapping')
- self.assertEqual(headers['X-Forward-To'], '/data/test?uri=new_uri&a=2&b=3',
- 'forwarding triggered')
- self.assertEqual(headers['X-REQUEST-Param-uri'], '/data/test?uri=new_uri&a=2&b=3',
- 'original uri parameter')
- self.assertEqual(headers['X-REQUEST-Param-a'], '1',
- 'original a parameter')
- self.assertEqual(headers['X-REQUEST-Param-c'], '4',
- 'original c parameter')
-
- self.assertEqual(headers['X-FORWARD-Id'], 'data',
- 'forward request servlet mapping')
- self.assertEqual(headers['X-FORWARD-Request-URI'], '/data/test',
- 'forward request uri')
- self.assertEqual(headers['X-FORWARD-Servlet-Path'], '/data',
- 'forward request servlet path')
- self.assertEqual(headers['X-FORWARD-Path-Info'], '/test',
- 'forward request path info')
- self.assertEqual(headers['X-FORWARD-Query-String'], 'uri=new_uri&a=2&b=3',
- 'forward request query string')
- self.assertEqual(headers['X-FORWARD-Param-uri'], 'new_uri,/data/test?uri=new_uri&a=2&b=3',
- 'forward uri parameter')
- self.assertEqual(headers['X-FORWARD-Param-a'], '2,1',
- 'forward a parameter')
- self.assertEqual(headers['X-FORWARD-Param-b'], '3',
- 'forward b parameter')
- self.assertEqual(headers['X-FORWARD-Param-c'], '4',
- 'forward c parameter')
-
- self.assertEqual(headers['X-javax.servlet.forward.request_uri'], '/fwd',
- 'original request uri')
- self.assertEqual(headers['X-javax.servlet.forward.context_path'], '',
- 'original request context path')
- self.assertEqual(headers['X-javax.servlet.forward.servlet_path'], '/fwd',
- 'original request servlet path')
- self.assertEqual(headers['X-javax.servlet.forward.path_info'], 'null',
- 'original request path info')
- self.assertEqual(headers['X-javax.servlet.forward.query_string'], 'uri=%2Fdata%2Ftest%3Furi%3Dnew_uri%26a%3D2%26b%3D3&a=1&c=4',
- 'original request query')
-
- self.assertEqual('Before forwarding' in resp['body'], False,
- 'discarded data added before forward() call')
- self.assertEqual('X-After-Forwarding' in headers, False,
- 'cannot add headers after forward() call')
- self.assertEqual('After forwarding' in resp['body'], False,
- 'cannot add data after forward() call')
+ self.assertEqual(
+ headers['X-REQUEST-Id'], 'fwd', 'initial request servlet mapping'
+ )
+ self.assertEqual(
+ headers['X-Forward-To'],
+ '/data/test?uri=new_uri&a=2&b=3',
+ 'forwarding triggered',
+ )
+ self.assertEqual(
+ headers['X-REQUEST-Param-uri'],
+ '/data/test?uri=new_uri&a=2&b=3',
+ 'original uri parameter',
+ )
+ self.assertEqual(
+ headers['X-REQUEST-Param-a'], '1', 'original a parameter'
+ )
+ self.assertEqual(
+ headers['X-REQUEST-Param-c'], '4', 'original c parameter'
+ )
+
+ self.assertEqual(
+ headers['X-FORWARD-Id'], 'data', 'forward request servlet mapping'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Request-URI'],
+ '/data/test',
+ 'forward request uri',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Servlet-Path'],
+ '/data',
+ 'forward request servlet path',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Path-Info'],
+ '/test',
+ 'forward request path info',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Query-String'],
+ 'uri=new_uri&a=2&b=3',
+ 'forward request query string',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Param-uri'],
+ 'new_uri,/data/test?uri=new_uri&a=2&b=3',
+ 'forward uri parameter',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Param-a'], '2,1', 'forward a parameter'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Param-b'], '3', 'forward b parameter'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Param-c'], '4', 'forward c parameter'
+ )
+
+ self.assertEqual(
+ headers['X-javax.servlet.forward.request_uri'],
+ '/fwd',
+ 'original request uri',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.context_path'],
+ '',
+ 'original request context path',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.servlet_path'],
+ '/fwd',
+ 'original request servlet path',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.path_info'],
+ 'null',
+ 'original request path info',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.query_string'],
+ 'uri=%2Fdata%2Ftest%3Furi%3Dnew_uri%26a%3D2%26b%3D3&a=1&c=4',
+ 'original request query',
+ )
+
+ self.assertEqual(
+ 'Before forwarding' in resp['body'],
+ False,
+ 'discarded data added before forward() call',
+ )
+ self.assertEqual(
+ 'X-After-Forwarding' in headers,
+ False,
+ 'cannot add headers after forward() call',
+ )
+ self.assertEqual(
+ 'After forwarding' in resp['body'],
+ False,
+ 'cannot add data after forward() call',
+ )
def test_java_application_named_dispatcher_forward(self):
self.load('forward')
@@ -487,39 +761,74 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
resp = self.get(url='/fwd?disp=name&uri=data')
headers = resp['headers']
- self.assertEqual(headers['X-REQUEST-Id'], 'fwd',
- 'initial request servlet mapping')
- self.assertEqual(headers['X-Forward-To'], 'data',
- 'forwarding triggered')
-
- self.assertEqual(headers['X-FORWARD-Id'], 'data',
- 'forward request servlet mapping')
- self.assertEqual(headers['X-FORWARD-Request-URI'], '/fwd',
- 'forward request uri')
- self.assertEqual(headers['X-FORWARD-Servlet-Path'], '/fwd',
- 'forward request servlet path')
- self.assertEqual(headers['X-FORWARD-Path-Info'], 'null',
- 'forward request path info')
- self.assertEqual(headers['X-FORWARD-Query-String'], 'disp=name&uri=data',
- 'forward request query string')
-
- self.assertEqual(headers['X-javax.servlet.forward.request_uri'], 'null',
- 'original request uri')
- self.assertEqual(headers['X-javax.servlet.forward.context_path'], 'null',
- 'original request context path')
- self.assertEqual(headers['X-javax.servlet.forward.servlet_path'], 'null',
- 'original request servlet path')
- self.assertEqual(headers['X-javax.servlet.forward.path_info'], 'null',
- 'original request path info')
- self.assertEqual(headers['X-javax.servlet.forward.query_string'], 'null',
- 'original request query')
-
- self.assertEqual('Before forwarding' in resp['body'], False,
- 'discarded data added before forward() call')
- self.assertEqual('X-After-Forwarding' in headers, False,
- 'cannot add headers after forward() call')
- self.assertEqual('After forwarding' in resp['body'], False,
- 'cannot add data after forward() call')
+ self.assertEqual(
+ headers['X-REQUEST-Id'], 'fwd', 'initial request servlet mapping'
+ )
+ self.assertEqual(
+ headers['X-Forward-To'], 'data', 'forwarding triggered'
+ )
+
+ self.assertEqual(
+ headers['X-FORWARD-Id'], 'data', 'forward request servlet mapping'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Request-URI'], '/fwd', 'forward request uri'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Servlet-Path'],
+ '/fwd',
+ 'forward request servlet path',
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Path-Info'], 'null', 'forward request path info'
+ )
+ self.assertEqual(
+ headers['X-FORWARD-Query-String'],
+ 'disp=name&uri=data',
+ 'forward request query string',
+ )
+
+ self.assertEqual(
+ headers['X-javax.servlet.forward.request_uri'],
+ 'null',
+ 'original request uri',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.context_path'],
+ 'null',
+ 'original request context path',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.servlet_path'],
+ 'null',
+ 'original request servlet path',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.path_info'],
+ 'null',
+ 'original request path info',
+ )
+ self.assertEqual(
+ headers['X-javax.servlet.forward.query_string'],
+ 'null',
+ 'original request query',
+ )
+
+ self.assertEqual(
+ 'Before forwarding' in resp['body'],
+ False,
+ 'discarded data added before forward() call',
+ )
+ self.assertEqual(
+ 'X-After-Forwarding' in headers,
+ False,
+ 'cannot add headers after forward() call',
+ )
+ self.assertEqual(
+ 'After forwarding' in resp['body'],
+ False,
+ 'cannot add data after forward() call',
+ )
def test_java_application_request_uri_include(self):
self.load('include')
@@ -528,31 +837,55 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = resp['headers']
body = resp['body']
- self.assertEqual(headers['X-REQUEST-Id'], 'inc',
- 'initial request servlet mapping')
- self.assertEqual(headers['X-Include'], '/data/test',
- 'including triggered')
-
- self.assertEqual('X-INCLUDE-Id' in headers, False,
- 'unable to add headers in include request')
-
- self.assertEqual('javax.servlet.include.request_uri: /data/test' in body,
- True, 'include request uri')
-# self.assertEqual('javax.servlet.include.context_path: ' in body,
-# 'include request context path')
- self.assertEqual('javax.servlet.include.servlet_path: /data' in body,
- True, 'include request servlet path')
- self.assertEqual('javax.servlet.include.path_info: /test' in body,
- True, 'include request path info')
- self.assertEqual('javax.servlet.include.query_string: null' in body,
- True, 'include request query')
-
- self.assertEqual('Before include' in body, True,
- 'preserve data added before include() call')
- self.assertEqual(headers['X-After-Include'], 'you-should-see-this',
- 'add headers after include() call')
- self.assertEqual('After include' in body, True,
- 'add data after include() call')
+ self.assertEqual(
+ headers['X-REQUEST-Id'], 'inc', 'initial request servlet mapping'
+ )
+ self.assertEqual(
+ headers['X-Include'], '/data/test', 'including triggered'
+ )
+
+ self.assertEqual(
+ 'X-INCLUDE-Id' in headers,
+ False,
+ 'unable to add headers in include request',
+ )
+
+ self.assertEqual(
+ 'javax.servlet.include.request_uri: /data/test' in body,
+ True,
+ 'include request uri',
+ )
+ # self.assertEqual('javax.servlet.include.context_path: ' in body,
+ # 'include request context path')
+ self.assertEqual(
+ 'javax.servlet.include.servlet_path: /data' in body,
+ True,
+ 'include request servlet path',
+ )
+ self.assertEqual(
+ 'javax.servlet.include.path_info: /test' in body,
+ True,
+ 'include request path info',
+ )
+ self.assertEqual(
+ 'javax.servlet.include.query_string: null' in body,
+ True,
+ 'include request query',
+ )
+
+ self.assertEqual(
+ 'Before include' in body,
+ True,
+ 'preserve data added before include() call',
+ )
+ self.assertEqual(
+ headers['X-After-Include'],
+ 'you-should-see-this',
+ 'add headers after include() call',
+ )
+ self.assertEqual(
+ 'After include' in body, True, 'add data after include() call'
+ )
def test_java_application_named_dispatcher_include(self):
self.load('include')
@@ -561,82 +894,134 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
headers = resp['headers']
body = resp['body']
- self.assertEqual(headers['X-REQUEST-Id'], 'inc',
- 'initial request servlet mapping')
- self.assertEqual(headers['X-Include'], 'data',
- 'including triggered')
-
- self.assertEqual('X-INCLUDE-Id' in headers, False,
- 'unable to add headers in include request')
-
- self.assertEqual('javax.servlet.include.request_uri: null' in body,
- True, 'include request uri')
-# self.assertEqual('javax.servlet.include.context_path: null' in body,
-# 'include request context path')
- self.assertEqual('javax.servlet.include.servlet_path: null' in body,
- True, 'include request servlet path')
- self.assertEqual('javax.servlet.include.path_info: null' in body,
- True, 'include request path info')
- self.assertEqual('javax.servlet.include.query_string: null' in body,
- True, 'include request query')
-
- self.assertEqual('Before include' in body, True,
- 'preserve data added before include() call')
- self.assertEqual(headers['X-After-Include'], 'you-should-see-this',
- 'add headers after include() call')
- self.assertEqual('After include' in body, True,
- 'add data after include() call')
+ self.assertEqual(
+ headers['X-REQUEST-Id'], 'inc', 'initial request servlet mapping'
+ )
+ self.assertEqual(headers['X-Include'], 'data', 'including triggered')
+
+ self.assertEqual(
+ 'X-INCLUDE-Id' in headers,
+ False,
+ 'unable to add headers in include request',
+ )
+
+ self.assertEqual(
+ 'javax.servlet.include.request_uri: null' in body,
+ True,
+ 'include request uri',
+ )
+ # self.assertEqual('javax.servlet.include.context_path: null' in body,
+ # 'include request context path')
+ self.assertEqual(
+ 'javax.servlet.include.servlet_path: null' in body,
+ True,
+ 'include request servlet path',
+ )
+ self.assertEqual(
+ 'javax.servlet.include.path_info: null' in body,
+ True,
+ 'include request path info',
+ )
+ self.assertEqual(
+ 'javax.servlet.include.query_string: null' in body,
+ True,
+ 'include request query',
+ )
+
+ self.assertEqual(
+ 'Before include' in body,
+ True,
+ 'preserve data added before include() call',
+ )
+ self.assertEqual(
+ headers['X-After-Include'],
+ 'you-should-see-this',
+ 'add headers after include() call',
+ )
+ self.assertEqual(
+ 'After include' in body, True, 'add data after include() call'
+ )
def test_java_application_path_translation(self):
self.load('path_translation')
headers = self.get(url='/pt/test?path=/')['headers']
- self.assertEqual(headers['X-Servlet-Path'], '/pt',
- 'matched servlet path')
- self.assertEqual(headers['X-Path-Info'], '/test',
- 'the rest of the path')
- self.assertEqual(headers['X-Path-Translated'],
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/pt', 'matched servlet path'
+ )
+ self.assertEqual(
+ headers['X-Path-Info'], '/test', 'the rest of the path'
+ )
+ self.assertEqual(
+ headers['X-Path-Translated'],
headers['X-Real-Path'] + headers['X-Path-Info'],
- 'translated path is the app root + path info')
+ 'translated path is the app root + path info',
+ )
self.assertEqual(
headers['X-Resource-Paths'].endswith('/WEB-INF/, /index.html]'),
- True, 'app root directory content')
- self.assertEqual(headers['X-Resource-As-Stream'], 'null',
- 'no resource stream for root path')
+ True,
+ 'app root directory content',
+ )
+ self.assertEqual(
+ headers['X-Resource-As-Stream'],
+ 'null',
+ 'no resource stream for root path',
+ )
headers = self.get(url='/test?path=/none')['headers']
- self.assertEqual(headers['X-Servlet-Path'], '/test',
- 'matched whole path')
- self.assertEqual(headers['X-Path-Info'], 'null',
- 'the rest of the path is null, whole path matched')
- self.assertEqual(headers['X-Path-Translated'], 'null',
- 'translated path is null because path info is null')
- self.assertEqual(headers['X-Real-Path'].endswith('/none'), True,
- 'read path is not null')
- self.assertEqual(headers['X-Resource-Paths'], 'null',
- 'no resource found')
- self.assertEqual(headers['X-Resource-As-Stream'], 'null',
- 'no resource stream')
+ self.assertEqual(
+ headers['X-Servlet-Path'], '/test', 'matched whole path'
+ )
+ self.assertEqual(
+ headers['X-Path-Info'],
+ 'null',
+ 'the rest of the path is null, whole path matched',
+ )
+ self.assertEqual(
+ headers['X-Path-Translated'],
+ 'null',
+ 'translated path is null because path info is null',
+ )
+ self.assertEqual(
+ headers['X-Real-Path'].endswith('/none'),
+ True,
+ 'read path is not null',
+ )
+ self.assertEqual(
+ headers['X-Resource-Paths'], 'null', 'no resource found'
+ )
+ self.assertEqual(
+ headers['X-Resource-As-Stream'], 'null', 'no resource stream'
+ )
def test_java_application_query_string(self):
self.load('query_string')
- self.assertEqual(self.get(url='/?a=b')['headers']['X-Query-String'],
- 'a=b', 'query string')
+ self.assertEqual(
+ self.get(url='/?a=b')['headers']['X-Query-String'],
+ 'a=b',
+ 'query string',
+ )
def test_java_application_query_empty(self):
self.load('query_string')
- self.assertEqual(self.get(url='/?')['headers']['X-Query-String'], '',
- 'query string empty')
+ self.assertEqual(
+ self.get(url='/?')['headers']['X-Query-String'],
+ '',
+ 'query string empty',
+ )
def test_java_application_query_absent(self):
self.load('query_string')
- self.assertEqual(self.get()['headers']['X-Query-String'], 'null',
- 'query string absent')
+ self.assertEqual(
+ self.get()['headers']['X-Query-String'],
+ 'null',
+ 'query string absent',
+ )
def test_java_application_empty(self):
self.load('empty')
@@ -646,19 +1031,30 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
def test_java_application_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.post()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Connection': 'close',
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
@@ -675,11 +1071,18 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
def test_java_application_get_header(self):
self.load('get_header')
- self.assertEqual(self.get(headers={
- 'X-Header': 'blah',
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- })['headers']['X-Reply'], 'blah', 'get header')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'X-Header': 'blah',
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )['headers']['X-Reply'],
+ 'blah',
+ 'get header',
+ )
def test_java_application_get_header_empty(self):
self.load('get_header')
@@ -689,11 +1092,14 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
def test_java_application_get_headers(self):
self.load('get_headers')
- headers = self.get(headers={
- 'X-Header': ['blah', 'blah'],
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- })['headers']
+ headers = self.get(
+ headers={
+ 'X-Header': ['blah', 'blah'],
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )['headers']
self.assertEqual(headers['X-Reply-0'], 'blah', 'get headers')
self.assertEqual(headers['X-Reply-1'], 'blah', 'get headers 2')
@@ -701,35 +1107,38 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
def test_java_application_get_headers_empty(self):
self.load('get_headers')
- self.assertNotIn('X-Reply-0', self.get()['headers'],
- 'get headers empty')
+ self.assertNotIn(
+ 'X-Reply-0', self.get()['headers'], 'get headers empty'
+ )
def test_java_application_get_header_names(self):
self.load('get_header_names')
headers = self.get()['headers']
- self.assertRegex(headers['X-Reply-0'], r'(?:Host|Connection)',
- 'get header names')
- self.assertRegex(headers['X-Reply-1'], r'(?:Host|Connection)',
- 'get header names 2')
- self.assertNotEqual(headers['X-Reply-0'], headers['X-Reply-1'],
- 'get header names not equal')
-
- def test_java_application_get_header_names_empty(self):
- self.load('get_header_names')
-
- self.assertNotIn('X-Reply-0', self.get(headers={})['headers'],
- 'get header names empty')
+ self.assertRegex(
+ headers['X-Reply-0'], r'(?:Host|Connection)', 'get header names'
+ )
+ self.assertRegex(
+ headers['X-Reply-1'], r'(?:Host|Connection)', 'get header names 2'
+ )
+ self.assertNotEqual(
+ headers['X-Reply-0'],
+ headers['X-Reply-1'],
+ 'get header names not equal',
+ )
def test_java_application_header_int(self):
self.load('header_int')
- headers = self.get(headers={
- 'X-Header': '2',
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- })['headers']
+ headers = self.get(
+ headers={
+ 'X-Header': '2',
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )['headers']
self.assertEqual(headers['X-Set-Int'], '1', 'set int header')
self.assertEqual(headers['X-Get-Int'], '2', 'get int header')
@@ -739,15 +1148,22 @@ class TestUnitJavaApplication(unit.TestUnitApplicationJava):
date = 'Fri, 15 Mar 2019 14:45:34 GMT'
- headers = self.get(headers={
- 'X-Header': date,
- 'Content-Type': 'text/html',
- 'Host': 'localhost'
- })['headers']
+ headers = self.get(
+ headers={
+ 'X-Header': date,
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )['headers']
- self.assertEqual(headers['X-Set-Date'], 'Thu, 01 Jan 1970 00:00:01 GMT',
- 'set date header')
+ self.assertEqual(
+ headers['X-Set-Date'],
+ 'Thu, 01 Jan 1970 00:00:01 GMT',
+ 'set date header',
+ )
self.assertEqual(headers['X-Get-Date'], date, 'get date header')
+
if __name__ == '__main__':
- TestUnitJavaApplication.main()
+ TestJavaApplication.main()
diff --git a/test/test_node_application.py b/test/test_node_application.py
index cd64fefa..0354c978 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -1,17 +1,17 @@
import unittest
-import unit
+from unit.applications.lang.node import TestApplicationNode
-class TestUnitNodeApplication(unit.TestUnitApplicationNode):
- def setUpClass():
- u = unit.TestUnit().check_modules('node')
+class TestNodeApplication(TestApplicationNode):
+ prerequisites = ['node']
def test_node_application_basic(self):
self.load('basic')
resp = self.get()
- self.assertEqual(resp['headers']['Content-Type'], 'text/plain',
- 'basic header')
+ self.assertEqual(
+ resp['headers']['Content-Type'], 'text/plain', 'basic header'
+ )
self.assertEqual(resp['body'], 'Hello World\n', 'basic body')
def test_node_application_seq(self):
@@ -25,12 +25,15 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
@@ -39,24 +42,35 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
raw_headers = headers.pop('Request-Raw-Headers')
- self.assertRegex(raw_headers, r'^(?:Host|localhost|Content-Type|' \
- 'text\/html|Custom-Header|blah|Content-Length|17|Connection|' \
- 'close|,)+$', 'raw headers')
-
- self.assertDictEqual(headers, {
- 'Connection': 'close',
- 'Content-Length': str(len(body)),
- 'Content-Type': 'text/html',
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Custom-Header': 'blah'
- }, 'headers')
+ self.assertRegex(
+ raw_headers,
+ r'^(?:Host|localhost|Content-Type|'
+ 'text\/html|Custom-Header|blah|Content-Length|17|Connection|'
+ 'close|,)+$',
+ 'raw headers',
+ )
+
+ self.assertDictEqual(
+ headers,
+ {
+ 'Connection': 'close',
+ 'Content-Length': str(len(body)),
+ 'Content-Type': 'text/html',
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Custom-Header': 'blah',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_node_application_get_variables(self):
@@ -70,11 +84,14 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
def test_node_application_post_variables(self):
self.load('post_variables')
- resp = self.post(headers={
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Host': 'localhost',
- 'Connection': 'close'
- }, body='var1=val1&var2=&var3')
+ resp = self.post(
+ headers={
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ },
+ body='var1=val1&var2=&var3',
+ )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables')
self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2')
@@ -86,41 +103,59 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
resp = self.get()
self.assertEqual(resp['status'], 404, '404 status')
- self.assertRegex(resp['body'], r'<title>404 Not Found</title>',
- '404 body')
+ self.assertRegex(
+ resp['body'], r'<title>404 Not Found</title>', '404 body'
+ )
def test_node_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
def test_node_application_write_buffer(self):
self.load('write_buffer')
- self.assertEqual(self.get()['body'], '6\r\nbuffer\r\n0\r\n\r\n',
- 'write buffer')
+ self.assertEqual(
+ self.get()['body'], '6\r\nbuffer\r\n0\r\n\r\n', 'write buffer'
+ )
def test_node_application_write_callback(self):
self.load('write_callback')
- self.assertEqual(self.get()['body'],
- '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n', 'write callback order')
- self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'),
- 'write callback')
+ self.assertEqual(
+ self.get()['body'],
+ '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n',
+ 'write callback order',
+ )
+ self.assertTrue(
+ self.waitforfiles(self.testdir + '/node/callback'),
+ 'write callback',
+ )
def test_node_application_write_before_write_head(self):
self.load('write_before_write_head')
@@ -136,17 +171,22 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
def test_node_application_write_return(self):
self.load('write_return')
- self.assertEqual(self.get()['body'],
- '4\r\nbody\r\n4\r\ntrue\r\n0\r\n\r\n', 'write return')
+ self.assertEqual(
+ self.get()['body'],
+ '4\r\nbody\r\n4\r\ntrue\r\n0\r\n\r\n',
+ 'write return',
+ )
def test_node_application_remove_header(self):
self.load('remove_header')
- resp = self.get(headers={
- 'Host': 'localhost',
- 'X-Remove': 'X-Header',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Remove': 'X-Header',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['headers']['Was-Header'], 'true', 'was header')
self.assertEqual(resp['headers']['Has-Header'], 'false', 'has header')
self.assertFalse('X-Header' in resp['headers'], 'remove header')
@@ -154,35 +194,48 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
def test_node_application_remove_header_nonexisting(self):
self.load('remove_header')
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Remove': 'blah',
- 'Connection': 'close'
- })['headers']['Has-Header'], 'true', 'remove header nonexisting')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Remove': 'blah',
+ 'Connection': 'close',
+ }
+ )['headers']['Has-Header'],
+ 'true',
+ 'remove header nonexisting',
+ )
def test_node_application_update_header(self):
self.load('update_header')
- self.assertEqual(self.get()['headers']['X-Header'], 'new',
- 'update header')
+ self.assertEqual(
+ self.get()['headers']['X-Header'], 'new', 'update header'
+ )
def test_node_application_set_header_array(self):
self.load('set_header_array')
- self.assertListEqual(self.get()['headers']['Set-Cookie'],
- ['tc=one,two,three', 'tc=four,five,six'], 'set header array')
+ self.assertListEqual(
+ self.get()['headers']['Set-Cookie'],
+ ['tc=one,two,three', 'tc=four,five,six'],
+ 'set header array',
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_node_application_status_message(self):
self.load('status_message')
- self.assertRegex(self.get(raw_resp=True), r'200 blah', 'status message')
+ self.assertRegex(
+ self.get(raw_resp=True), r'200 blah', 'status message'
+ )
def test_node_application_get_header_type(self):
self.load('get_header_type')
- self.assertEqual(self.get()['headers']['X-Type'], 'number',
- 'get header type')
+ self.assertEqual(
+ self.get()['headers']['X-Type'], 'number', 'get header type'
+ )
def test_node_application_header_name_case(self):
self.load('header_name_case')
@@ -196,58 +249,91 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
def test_node_application_promise_handler(self):
self.load('promise_handler')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close'
- }, body='callback')['status'], 200, 'promise handler request')
- self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'),
- 'promise handler')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ body='callback',
+ )['status'],
+ 200,
+ 'promise handler request',
+ )
+ self.assertTrue(
+ self.waitforfiles(self.testdir + '/node/callback'),
+ 'promise handler',
+ )
def test_node_application_promise_handler_write_after_end(self):
self.load('promise_handler')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'X-Write-Call': '1',
- 'Connection': 'close'
- }, body='callback')['status'], 200,
- 'promise handler request write after end')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'X-Write-Call': '1',
+ 'Connection': 'close',
+ },
+ body='callback',
+ )['status'],
+ 200,
+ 'promise handler request write after end',
+ )
def test_node_application_promise_end(self):
self.load('promise_end')
- self.assertEqual(self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close'
- }, body='end')['status'], 200, 'promise end request')
- self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'),
- 'promise end')
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ body='end',
+ )['status'],
+ 200,
+ 'promise end request',
+ )
+ self.assertTrue(
+ self.waitforfiles(self.testdir + '/node/callback'), 'promise end'
+ )
def test_node_application_promise_multiple_calls(self):
self.load('promise_handler')
- self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close'
- }, body='callback1')
-
- self.assertTrue(self.waitforfiles(self.testdir + '/node/callback1'),
- 'promise first call')
-
- self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close'
- }, body='callback2')
-
- self.assertTrue(self.waitforfiles(self.testdir + '/node/callback2'),
- 'promise second call')
-
- @unittest.expectedFailure
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ body='callback1',
+ )
+
+ self.assertTrue(
+ self.waitforfiles(self.testdir + '/node/callback1'),
+ 'promise first call',
+ )
+
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ body='callback2',
+ )
+
+ self.assertTrue(
+ self.waitforfiles(self.testdir + '/node/callback2'),
+ 'promise second call',
+ )
+
+ @unittest.skip('not yet')
def test_node_application_header_name_valid(self):
self.load('header_name_valid')
@@ -261,28 +347,46 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode):
def test_node_application_get_header_names(self):
self.load('get_header_names')
- self.assertListEqual(self.get()['headers']['X-Names'],
- ['date', 'x-header'], 'get header names')
+ self.assertListEqual(
+ self.get()['headers']['X-Names'],
+ ['date', 'x-header'],
+ 'get header names',
+ )
def test_node_application_has_header(self):
self.load('has_header')
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Header': 'length',
- 'Connection': 'close'
- })['headers']['X-Has-Header'], 'false', 'has header length')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Header': 'Date',
- 'Connection': 'close'
- })['headers']['X-Has-Header'], 'false', 'has header date')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Header': 'length',
+ 'Connection': 'close',
+ }
+ )['headers']['X-Has-Header'],
+ 'false',
+ 'has header length',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Header': 'Date',
+ 'Connection': 'close',
+ }
+ )['headers']['X-Has-Header'],
+ 'false',
+ 'has header date',
+ )
def test_node_application_write_multiple(self):
self.load('write_multiple')
- self.assertEqual(self.get()['body'], 'writewrite2end', 'write multiple')
+ self.assertEqual(
+ self.get()['body'], 'writewrite2end', 'write multiple'
+ )
+
if __name__ == '__main__':
- TestUnitNodeApplication.main()
+ TestNodeApplication.main()
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index b169baab..bc26b000 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -1,52 +1,64 @@
import unittest
-import unit
+from unit.applications.lang.perl import TestApplicationPerl
-class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
- def setUpClass():
- unit.TestUnit().check_modules('perl')
+class TestPerlApplication(TestApplicationPerl):
+ prerequisites = ['perl']
def test_perl_application(self):
self.load('variables')
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header')
- self.assertEqual(headers.pop('Server-Software'), header_server,
- 'server software header')
+ self.assertEqual(
+ headers.pop('Server-Software'),
+ header_server,
+ 'server software header',
+ )
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
-
- self.assertDictEqual(headers, {
- 'Connection': 'close',
- 'Content-Length': str(len(body)),
- 'Content-Type': 'text/html',
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Custom-Header': 'blah',
- 'Psgi-Version': '11',
- 'Psgi-Url-Scheme': 'http',
- 'Psgi-Multithread': '',
- 'Psgi-Multiprocess': '1',
- 'Psgi-Run-Once': '',
- 'Psgi-Nonblocking': '',
- 'Psgi-Streaming': '1'
- }, 'headers')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
+
+ self.assertDictEqual(
+ headers,
+ {
+ 'Connection': 'close',
+ 'Content-Length': str(len(body)),
+ 'Content-Type': 'text/html',
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Custom-Header': 'blah',
+ 'Psgi-Version': '11',
+ 'Psgi-Url-Scheme': 'http',
+ 'Psgi-Multithread': '',
+ 'Psgi-Multiprocess': '1',
+ 'Psgi-Run-Once': '',
+ 'Psgi-Nonblocking': '',
+ 'Psgi-Streaming': '1',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_perl_application_query_string(self):
@@ -54,8 +66,11 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
resp = self.get(url='/?var1=val1&var2=val2')
- self.assertEqual(resp['headers']['Query-String'], 'var1=val1&var2=val2',
- 'Query-String header')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String header',
+ )
def test_perl_application_query_string_empty(self):
self.load('query_string')
@@ -63,25 +78,27 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string empty')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string empty'
+ )
- @unittest.expectedFailure
def test_perl_application_query_string_absent(self):
self.load('query_string')
resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string absent')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string absent'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_perl_application_server_port(self):
self.load('server_port')
- self.assertEqual(self.get()['headers']['Server-Port'], '7080',
- 'Server-Port header')
+ self.assertEqual(
+ self.get()['headers']['Server-Port'], '7080', 'Server-Port header'
+ )
def test_perl_application_input_read_empty(self):
self.load('input_read_empty')
@@ -91,15 +108,19 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
def test_perl_application_input_read_parts(self):
self.load('input_read_parts')
- self.assertEqual(self.post(body='0123456789')['body'], '0123456789',
- 'input read parts')
+ self.assertEqual(
+ self.post(body='0123456789')['body'],
+ '0123456789',
+ 'input read parts',
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_perl_application_input_read_offset(self):
self.load('input_read_offset')
- self.assertEqual(self.post(body='0123456789')['body'], '4567',
- 'read offset')
+ self.assertEqual(
+ self.post(body='0123456789')['body'], '4567', 'read offset'
+ )
def test_perl_application_input_copy(self):
self.load('input_copy')
@@ -115,14 +136,18 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+Error in application'),
- 'errors print')
+ self.wait_for_record(r'\[error\].+Error in application'),
+ 'errors print',
+ )
def test_perl_application_header_equal_names(self):
self.load('header_equal_names')
- self.assertListEqual(self.get()['headers']['Set-Cookie'],
- ['tc=one,two,three', 'tc=four,five,six'], 'header equal names')
+ self.assertListEqual(
+ self.get()['headers']['Set-Cookie'],
+ ['tc=one,two,three', 'tc=four,five,six'],
+ 'header equal names',
+ )
def test_perl_application_header_pairs(self):
self.load('header_pairs')
@@ -158,12 +183,11 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.assertEqual(self.get()['body'], 'body\n', 'body io file')
- @unittest.expectedFailure
+ @unittest.skip('not yet, unsafe')
def test_perl_application_syntax_error(self):
- self.skip_alerts.extend([
- r'PSGI: Failed to parse script',
- r'process \d+ exited on signal'
- ])
+ self.skip_alerts.extend(
+ [r'PSGI: Failed to parse script']
+ )
self.load('syntax_error')
self.assertEqual(self.get()['status'], 500, 'syntax error')
@@ -171,19 +195,30 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
def test_perl_keepalive_body(self):
self.load('variables')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
@@ -193,12 +228,14 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.assertEqual(self.get()['body'], '21', 'body io fake')
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+IOFake getline\(\) \$\/ is \d+'),
- 'body io fake $/ value')
+ self.wait_for_record(r'\[error\].+IOFake getline\(\) \$\/ is \d+'),
+ 'body io fake $/ value',
+ )
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+IOFake close\(\) called'),
- 'body io fake close')
+ self.wait_for_record(r'\[error\].+IOFake close\(\) called'),
+ 'body io fake close',
+ )
def test_perl_delayed_response(self):
self.load('delayed_response')
@@ -216,5 +253,6 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.assertEqual(resp['status'], 200, 'status')
self.assertEqual(resp['body'], 'Hello World!', 'body')
+
if __name__ == '__main__':
- TestUnitPerlApplication.main()
+ TestPerlApplication.main()
diff --git a/test/test_php_application.py b/test/test_php_application.py
index ac74359d..8032e96e 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -1,11 +1,9 @@
-import unittest
-import unit
import re
+import unittest
+from unit.applications.lang.php import TestApplicationPHP
-class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
-
- def setUpClass():
- unit.TestUnit().check_modules('php')
+class TestPHPApplication(TestApplicationPHP):
+ prerequisites = ['php']
def before_disable_functions(self):
body = self.get()['body']
@@ -18,38 +16,51 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header')
- self.assertEqual(headers.pop('Server-Software'), header_server,
- 'server software header')
+ self.assertEqual(
+ headers.pop('Server-Software'),
+ header_server,
+ 'server software header',
+ )
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
if 'X-Powered-By' in headers:
headers.pop('X-Powered-By')
headers.pop('Content-type')
- self.assertDictEqual(headers, {
- 'Connection': 'close',
- 'Content-Length': str(len(body)),
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Custom-Header': 'blah'
- }, 'headers')
+ self.assertDictEqual(
+ headers,
+ {
+ 'Connection': 'close',
+ 'Content-Length': str(len(body)),
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Custom-Header': 'blah',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_php_application_query_string(self):
@@ -57,8 +68,11 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
resp = self.get(url='/?var1=val1&var2=val2')
- self.assertEqual(resp['headers']['Query-String'], 'var1=val1&var2=val2',
- 'query string')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'query string',
+ )
def test_php_application_query_string_empty(self):
self.load('query_string')
@@ -66,18 +80,19 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string empty')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string empty'
+ )
- @unittest.expectedFailure
def test_php_application_query_string_absent(self):
self.load('query_string')
resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string absent')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string absent'
+ )
def test_php_application_phpinfo(self):
self.load('phpinfo')
@@ -93,25 +108,37 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
resp = self.get()
self.assertEqual(resp['status'], 404, '404 status')
- self.assertRegex(resp['body'], r'<title>404 Not Found</title>',
- '404 body')
+ self.assertRegex(
+ resp['body'], r'<title>404 Not Found</title>', '404 body'
+ )
def test_php_application_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
@@ -133,11 +160,14 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
def test_php_application_post_variables(self):
self.load('post_variables')
- resp = self.post(headers={
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Host': 'localhost',
- 'Connection': 'close'
- }, body='var1=val1&var2=')
+ resp = self.post(
+ headers={
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ },
+ body='var1=val1&var2=',
+ )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables')
self.assertEqual(resp['headers']['X-Var-2'], '1', 'POST variables 2')
self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3')
@@ -145,11 +175,13 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
def test_php_application_cookies(self):
self.load('cookies')
- resp = self.get(headers={
- 'Cookie': 'var=val; var2=val2',
- 'Host': 'localhost',
- 'Connection': 'close'
- })
+ resp = self.get(
+ headers={
+ 'Cookie': 'var=val; var2=val2',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ }
+ )
self.assertEqual(resp['headers']['X-Cookie-1'], 'val', 'cookie')
self.assertEqual(resp['headers']['X-Cookie-2'], 'val2', 'cookie')
@@ -157,96 +189,129 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
def test_php_application_ini_precision(self):
self.load('ini_precision')
- self.assertNotEqual(self.get()['headers']['X-Precision'], '4',
- 'ini value default')
+ self.assertNotEqual(
+ self.get()['headers']['X-Precision'], '4', 'ini value default'
+ )
- self.conf({"file": "php.ini"}, 'applications/ini_precision/options')
+ self.conf(
+ {"file": "ini/php.ini"}, 'applications/ini_precision/options'
+ )
- self.assertEqual(self.get()['headers']['X-File'],
- self.current_dir + '/php/ini_precision/php.ini', 'ini file')
- self.assertEqual(self.get()['headers']['X-Precision'], '4', 'ini value')
+ self.assertEqual(
+ self.get()['headers']['X-File'],
+ self.current_dir + '/php/ini_precision/ini/php.ini',
+ 'ini file',
+ )
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '4', 'ini value'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_php_application_ini_admin_user(self):
self.load('ini_precision')
- self.assertIn('error', self.conf({
- "user": { "precision": "4" },
- "admin": { "precision": "5" }
- }, 'applications/ini_precision/options'), 'ini admin user')
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"user": {"precision": "4"}, "admin": {"precision": "5"}},
+ 'applications/ini_precision/options',
+ ),
+ 'ini admin user',
+ )
def test_php_application_ini_admin(self):
self.load('ini_precision')
- self.conf({
- "file": "php.ini",
- "admin": { "precision": "5" }
- }, 'applications/ini_precision/options')
+ self.conf(
+ {"file": "php.ini", "admin": {"precision": "5"}},
+ 'applications/ini_precision/options',
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '5',
- 'ini value admin')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '5', 'ini value admin'
+ )
def test_php_application_ini_user(self):
self.load('ini_precision')
- self.conf({
- "file": "php.ini",
- "user": { "precision": "5" }
- }, 'applications/ini_precision/options')
+ self.conf(
+ {"file": "php.ini", "user": {"precision": "5"}},
+ 'applications/ini_precision/options',
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '5',
- 'ini value user')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '5', 'ini value user'
+ )
def test_php_application_ini_user_2(self):
self.load('ini_precision')
- self.conf({"file": "php.ini"}, 'applications/ini_precision/options')
+ self.conf(
+ {"file": "ini/php.ini"}, 'applications/ini_precision/options'
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '4',
- 'ini user file')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '4', 'ini user file'
+ )
- self.conf({ "precision": "5" },
- 'applications/ini_precision/options/user')
+ self.conf(
+ {"precision": "5"}, 'applications/ini_precision/options/user'
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '5',
- 'ini value user')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '5', 'ini value user'
+ )
def test_php_application_ini_set_admin(self):
self.load('ini_precision')
- self.conf({"admin": { "precision": "5" }},
- 'applications/ini_precision/options')
+ self.conf(
+ {"admin": {"precision": "5"}}, 'applications/ini_precision/options'
+ )
- self.assertEqual(self.get(url='/?precision=6')['headers']['X-Precision'],
- '5', 'ini set admin')
+ self.assertEqual(
+ self.get(url='/?precision=6')['headers']['X-Precision'],
+ '5',
+ 'ini set admin',
+ )
def test_php_application_ini_set_user(self):
self.load('ini_precision')
- self.conf({"user": { "precision": "5" }},
- 'applications/ini_precision/options')
+ self.conf(
+ {"user": {"precision": "5"}}, 'applications/ini_precision/options'
+ )
- self.assertEqual(self.get(url='/?precision=6')['headers']['X-Precision'],
- '6', 'ini set user')
+ self.assertEqual(
+ self.get(url='/?precision=6')['headers']['X-Precision'],
+ '6',
+ 'ini set user',
+ )
def test_php_application_ini_repeat(self):
self.load('ini_precision')
- self.conf({"user": { "precision": "5" }},
- 'applications/ini_precision/options')
+ self.conf(
+ {"user": {"precision": "5"}}, 'applications/ini_precision/options'
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '5', 'ini value')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '5', 'ini value'
+ )
- self.assertEqual(self.get()['headers']['X-Precision'], '5',
- 'ini value repeat')
+ self.assertEqual(
+ self.get()['headers']['X-Precision'], '5', 'ini value repeat'
+ )
def test_php_application_disable_functions_exec(self):
self.load('time_exec')
self.before_disable_functions()
- self.conf({"admin": { "disable_functions": "exec" }},
- 'applications/time_exec/options')
+ self.conf(
+ {"admin": {"disable_functions": "exec"}},
+ 'applications/time_exec/options',
+ )
body = self.get()['body']
@@ -258,80 +323,103 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
self.before_disable_functions()
- self.conf({"admin": { "disable_functions": "exec,time" }},
- 'applications/time_exec/options')
+ self.conf(
+ {"admin": {"disable_functions": "exec,time"}},
+ 'applications/time_exec/options',
+ )
body = self.get()['body']
self.assertNotRegex(body, r'time: \d+', 'disable_functions comma time')
- self.assertNotRegex(body, r'exec: \/\w+',
- 'disable_functions comma exec')
+ self.assertNotRegex(
+ body, r'exec: \/\w+', 'disable_functions comma exec'
+ )
def test_php_application_disable_functions_space(self):
self.load('time_exec')
self.before_disable_functions()
- self.conf({"admin": { "disable_functions": "exec time" }},
- 'applications/time_exec/options')
+ self.conf(
+ {"admin": {"disable_functions": "exec time"}},
+ 'applications/time_exec/options',
+ )
body = self.get()['body']
self.assertNotRegex(body, r'time: \d+', 'disable_functions space time')
- self.assertNotRegex(body, r'exec: \/\w+',
- 'disable_functions space exec')
+ self.assertNotRegex(
+ body, r'exec: \/\w+', 'disable_functions space exec'
+ )
def test_php_application_disable_functions_user(self):
self.load('time_exec')
self.before_disable_functions()
- self.conf({"user": { "disable_functions": "exec" }},
- 'applications/time_exec/options')
+ self.conf(
+ {"user": {"disable_functions": "exec"}},
+ 'applications/time_exec/options',
+ )
body = self.get()['body']
self.assertRegex(body, r'time: \d+', 'disable_functions user time')
- self.assertNotRegex(body, r'exec: \/\w+', 'disable_functions user exec')
+ self.assertNotRegex(
+ body, r'exec: \/\w+', 'disable_functions user exec'
+ )
def test_php_application_disable_functions_nonexistent(self):
self.load('time_exec')
self.before_disable_functions()
- self.conf({"admin": { "disable_functions": "blah" }},
- 'applications/time_exec/options')
+ self.conf(
+ {"admin": {"disable_functions": "blah"}},
+ 'applications/time_exec/options',
+ )
body = self.get()['body']
- self.assertRegex(body, r'time: \d+',
- 'disable_functions nonexistent time')
- self.assertRegex(body, r'exec: \/\w+',
- 'disable_functions nonexistent exec')
+ self.assertRegex(
+ body, r'time: \d+', 'disable_functions nonexistent time'
+ )
+ self.assertRegex(
+ body, r'exec: \/\w+', 'disable_functions nonexistent exec'
+ )
def test_php_application_disable_classes(self):
self.load('date_time')
- self.assertRegex(self.get()['body'], r'012345',
- 'disable_classes before')
+ self.assertRegex(
+ self.get()['body'], r'012345', 'disable_classes before'
+ )
- self.conf({"admin": { "disable_classes": "DateTime" }},
- 'applications/date_time/options')
+ self.conf(
+ {"admin": {"disable_classes": "DateTime"}},
+ 'applications/date_time/options',
+ )
- self.assertNotRegex(self.get()['body'], r'012345',
- 'disable_classes before')
+ self.assertNotRegex(
+ self.get()['body'], r'012345', 'disable_classes before'
+ )
def test_php_application_disable_classes_user(self):
self.load('date_time')
- self.assertRegex(self.get()['body'], r'012345',
- 'disable_classes before')
+ self.assertRegex(
+ self.get()['body'], r'012345', 'disable_classes before'
+ )
+
+ self.conf(
+ {"user": {"disable_classes": "DateTime"}},
+ 'applications/date_time/options',
+ )
- self.conf({"user": { "disable_classes": "DateTime" }},
- 'applications/date_time/options')
+ self.assertNotRegex(
+ self.get()['body'], r'012345', 'disable_classes before'
+ )
- self.assertNotRegex(self.get()['body'], r'012345',
- 'disable_classes before')
if __name__ == '__main__':
- TestUnitPHPApplication.main()
+ TestPHPApplication.main()
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 1ea46c91..02ff81de 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -1,27 +1,21 @@
-import unittest
-import unit
+from unit.control import TestControl
-class TestUnitPHPBasic(unit.TestUnitControl):
- def setUpClass():
- unit.TestUnit().check_modules('php')
+class TestPHPBasic(TestControl):
+ prerequisites = ['php']
conf_app = {
"app": {
"type": "php",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"root": "/app",
- "index": "index.php"
+ "index": "index.php",
}
}
conf_basic = {
- "listeners": {
- "*:7080": {
- "application": "app"
- }
- },
- "applications": conf_app
+ "listeners": {"*:7080": {"pass": "applications/app"}},
+ "applications": conf_app,
}
def test_php_get_applications(self):
@@ -30,113 +24,146 @@ class TestUnitPHPBasic(unit.TestUnitControl):
conf = self.conf_get()
self.assertEqual(conf['listeners'], {}, 'listeners')
- self.assertEqual(conf['applications'],
+ self.assertEqual(
+ conf['applications'],
{
"app": {
"type": "php",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"root": "/app",
- "index": "index.php"
+ "index": "index.php",
}
- },
- 'applications')
+ },
+ 'applications',
+ )
def test_php_get_applications_prefix(self):
self.conf(self.conf_app, 'applications')
- self.assertEqual(self.conf_get('applications'),
+ self.assertEqual(
+ self.conf_get('applications'),
{
"app": {
"type": "php",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"root": "/app",
- "index": "index.php"
+ "index": "index.php",
}
},
- 'applications prefix')
+ 'applications prefix',
+ )
def test_php_get_applications_prefix_2(self):
self.conf(self.conf_app, 'applications')
- self.assertEqual(self.conf_get('applications/app'),
+ self.assertEqual(
+ self.conf_get('applications/app'),
{
"type": "php",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"root": "/app",
- "index": "index.php"
+ "index": "index.php",
},
- 'applications prefix 2')
+ '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'), 0,
- 'spare processes')
+ self.assertEqual(self.conf_get('applications/app/type'), 'php', 'type')
+ self.assertEqual(
+ self.conf_get('applications/app/processes/spare'),
+ 0,
+ 'spare processes',
+ )
def test_php_get_listeners(self):
self.conf(self.conf_basic)
- self.assertEqual(self.conf_get()['listeners'],
- {"*:7080":{"application":"app"}}, 'listeners')
+ self.assertEqual(
+ self.conf_get()['listeners'],
+ {"*:7080": {"pass": "applications/app"}},
+ 'listeners',
+ )
def test_php_get_listeners_prefix(self):
self.conf(self.conf_basic)
- self.assertEqual(self.conf_get('listeners'),
- {"*:7080":{"application":"app"}}, 'listeners prefix')
+ 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'),
- {"application":"app"}, 'listeners prefix 2')
+ self.assertEqual(
+ self.conf_get('listeners/*:7080'),
+ {"pass": "applications/app"},
+ 'listeners prefix 2',
+ )
def test_php_change_listener(self):
self.conf(self.conf_basic)
- self.conf({"*:7081":{"application":"app"}}, 'listeners')
+ self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
- self.assertEqual(self.conf_get('listeners'),
- {"*:7081": {"application":"app"}}, 'change listener')
+ self.assertEqual(
+ self.conf_get('listeners'),
+ {"*:7081": {"pass": "applications/app"}},
+ 'change listener',
+ )
def test_php_add_listener(self):
self.conf(self.conf_basic)
- self.conf({"application":"app"}, 'listeners/*:7082')
+ self.conf({"pass": "applications/app"}, 'listeners/*:7082')
- self.assertEqual(self.conf_get('listeners'),
+ self.assertEqual(
+ self.conf_get('listeners'),
{
- "*:7080": {
- "application": "app"
- },
- "*:7082": {
- "application": "app"
- }
+ "*:7080": {"pass": "applications/app"},
+ "*:7082": {"pass": "applications/app"},
},
- 'add listener')
+ 'add listener',
+ )
def test_php_change_application(self):
self.conf(self.conf_basic)
self.conf('30', 'applications/app/processes/max')
- self.assertEqual(self.conf_get('applications/app/processes/max'), 30,
- 'change application max')
+ self.assertEqual(
+ self.conf_get('applications/app/processes/max'),
+ 30,
+ 'change application max',
+ )
self.conf('"/www"', 'applications/app/root')
- self.assertEqual(self.conf_get('applications/app/root'), '/www',
- 'change application root')
+ self.assertEqual(
+ self.conf_get('applications/app/root'),
+ '/www',
+ 'change application root',
+ )
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'),
+ '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'
+ )
+
if __name__ == '__main__':
- TestUnitPHPBasic.main()
+ TestPHPBasic.main()
diff --git a/test/test_python_application.py b/test/test_python_application.py
index a8631085..3484b25e 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -1,51 +1,63 @@
import time
import unittest
-import unit
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitPythonApplication(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestPythonApplication(TestApplicationPython):
+ prerequisites = ['python']
def test_python_application_variables(self):
self.load('variables')
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header')
- self.assertEqual(headers.pop('Server-Software'), header_server,
- 'server software header')
+ self.assertEqual(
+ headers.pop('Server-Software'),
+ header_server,
+ 'server software header',
+ )
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
-
- self.assertDictEqual(headers, {
- 'Connection': 'close',
- 'Content-Length': str(len(body)),
- 'Content-Type': 'text/html',
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Custom-Header': 'blah',
- 'Wsgi-Version': '(1, 0)',
- 'Wsgi-Url-Scheme': 'http',
- 'Wsgi-Multithread': 'False',
- 'Wsgi-Multiprocess': 'True',
- 'Wsgi-Run-Once': 'False'
- }, 'headers')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
+
+ self.assertDictEqual(
+ headers,
+ {
+ 'Connection': 'close',
+ 'Content-Length': str(len(body)),
+ 'Content-Type': 'text/html',
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Custom-Header': 'blah',
+ 'Wsgi-Version': '(1, 0)',
+ 'Wsgi-Url-Scheme': 'http',
+ 'Wsgi-Multithread': 'False',
+ 'Wsgi-Multiprocess': 'True',
+ 'Wsgi-Run-Once': 'False',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_python_application_query_string(self):
@@ -53,8 +65,11 @@ class TestUnitPythonApplication(unit.TestUnitApplicationPython):
resp = self.get(url='/?var1=val1&var2=val2')
- self.assertEqual(resp['headers']['Query-String'], 'var1=val1&var2=val2',
- 'Query-String header')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String header',
+ )
def test_python_application_query_string_empty(self):
self.load('query_string')
@@ -62,8 +77,9 @@ class TestUnitPythonApplication(unit.TestUnitApplicationPython):
resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string empty')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string empty'
+ )
def test_python_application_query_string_absent(self):
self.load('query_string')
@@ -71,141 +87,198 @@ class TestUnitPythonApplication(unit.TestUnitApplicationPython):
resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string absent')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string absent'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_application_server_port(self):
self.load('server_port')
- self.assertEqual(self.get()['headers']['Server-Port'], '7080',
- 'Server-Port header')
+ self.assertEqual(
+ self.get()['headers']['Server-Port'], '7080', 'Server-Port header'
+ )
def test_python_application_204_transfer_encoding(self):
self.load('204_no_content')
- self.assertNotIn('Transfer-Encoding', self.get()['headers'],
- '204 header transfer encoding')
+ self.assertNotIn(
+ 'Transfer-Encoding',
+ self.get()['headers'],
+ '204 header transfer encoding',
+ )
def test_python_application_ctx_iter_atexit(self):
self.load('ctx_iter_atexit')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ body='0123456789',
+ )
self.assertEqual(resp['status'], 200, 'ctx iter status')
self.assertEqual(resp['body'], '0123456789', 'ctx iter body')
- self.conf({
- "listeners": {},
- "applications": {}
- })
+ self.conf({"listeners": {}, "applications": {}})
self.stop()
- time.sleep(0.2)
-
- self.assertIsNotNone(self.search_in_log(r'RuntimeError'),
- 'ctx iter atexit')
+ self.assertIsNotNone(
+ self.wait_for_record(r'RuntimeError'), 'ctx iter atexit'
+ )
def test_python_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
def test_python_keepalive_reconfigure(self):
+ self.skip_alerts.extend(
+ [
+ r'pthread_mutex.+failed',
+ r'failed to apply',
+ r'process \d+ exited on signal',
+ ]
+ )
self.load('mirror')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
body = '0123456789'
conns = 3
socks = []
for i in range(conns):
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body=body)
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body=body,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], body, 'keep-alive open')
- self.assertIn('success', self.conf({
- "spare": i % 4,
- "max": (i % 4) + 1
- }, 'applications/mirror/processes'), 'reconfigure')
+ self.assertIn(
+ 'success',
+ self.conf(str(i + 1), 'applications/mirror/processes'),
+ 'reconfigure',
+ )
socks.append(sock)
for i in range(conns):
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, sock=socks[i], body=body)
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ sock=socks[i],
+ body=body,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], body, 'keep-alive request')
- self.assertIn('success', self.conf({
- "spare": i % 4,
- "max": (i % 4) + 1
- }, 'applications/mirror/processes'), 'reconfigure 2')
+ self.assertIn(
+ 'success',
+ self.conf(str(i + 1), 'applications/mirror/processes'),
+ 'reconfigure 2',
+ )
for i in range(conns):
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=socks[i], body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=socks[i],
+ body=body,
+ )
self.assertEqual(resp['body'], body, 'keep-alive close')
- self.assertIn('success', self.conf({
- "spare": i % 4,
- "max": (i % 4) + 1
- }, 'applications/mirror/processes'), 'reconfigure 3')
+ self.assertIn(
+ 'success',
+ self.conf(str(i + 1), 'applications/mirror/processes'),
+ 'reconfigure 3',
+ )
def test_python_keepalive_reconfigure_2(self):
self.load('mirror')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
body = '0123456789'
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body=body)
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body=body,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], body, 'reconfigure 2 keep-alive 1')
self.load('empty')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, start=True, sock=sock, body=body)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ sock=sock,
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'reconfigure 2 keep-alive 2')
self.assertEqual(resp['body'], '', 'reconfigure 2 keep-alive 2 body')
- self.assertIn('success', self.conf({
- "listeners": {},
- "applications": {}
- }), 'reconfigure 2 clear configuration')
+ self.assertIn(
+ 'success',
+ self.conf({"listeners": {}, "applications": {}}),
+ 'reconfigure 2 clear configuration',
+ )
resp = self.get(sock=sock)
@@ -214,18 +287,30 @@ class TestUnitPythonApplication(unit.TestUnitApplicationPython):
def test_python_keepalive_reconfigure_3(self):
self.load('empty')
- (resp, sock) = self.http(b"""GET / HTTP/1.1
-""", start=True, raw=True)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.http(
+ b"""GET / HTTP/1.1
+""",
+ start=True,
+ raw=True,
+ read_timeout=5,
+ )
- self.assertIn('success', self.conf({
- "listeners": {},
- "applications": {}
- }), 'reconfigure 3 clear configuration')
+ self.assertIn(
+ 'success',
+ self.conf({"listeners": {}, "applications": {}}),
+ 'reconfigure 3 clear configuration',
+ )
- resp = self.http(b"""Host: localhost
+ resp = self.http(
+ b"""Host: localhost
Connection: close
-""", sock=sock, raw=True)
+""",
+ sock=sock,
+ raw=True,
+ )
self.assertEqual(resp['status'], 200, 'reconfigure 3')
@@ -234,22 +319,21 @@ Connection: close
self.get()
- self.conf({
- "listeners": {},
- "applications": {}
- })
+ self.conf({"listeners": {}, "applications": {}})
self.stop()
- self.assertIsNotNone(self.search_in_log(r'At exit called\.'), 'atexit')
+ self.assertIsNotNone(
+ self.wait_for_record(r'At exit called\.'), 'atexit'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_application_start_response_exit(self):
self.load('start_response_exit')
self.assertEqual(self.get()['status'], 500, 'start response exit')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_application_input_iter(self):
self.load('input_iter')
@@ -262,39 +346,51 @@ Connection: close
body = '0123456789'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Input-Length': '5',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Input-Length': '5',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['body'], body[:5], 'input read length lt body')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Input-Length': '15',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Input-Length': '15',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['body'], body, 'input read length gt body')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Input-Length': '0',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Input-Length': '0',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['body'], '', 'input read length zero')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Input-Length': '-1',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Input-Length': '-1',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['body'], body, 'input read length negative')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_application_errors_write(self):
self.load('errors_write')
@@ -303,8 +399,9 @@ Connection: close
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+Error in application\.'),
- 'errors write')
+ self.wait_for_record(r'\[error\].+Error in application\.'),
+ 'errors write',
+ )
def test_python_application_body_array(self):
self.load('body_array')
@@ -321,7 +418,7 @@ Connection: close
self.assertEqual(self.get()['body'], 'body\n', 'body io file')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_application_syntax_error(self):
self.skip_alerts.append(r'Python failed to import module "wsgi"')
self.load('syntax_error')
@@ -335,7 +432,7 @@ Connection: close
self.stop()
- self.assertIsNotNone(self.search_in_log(r'Close called\.'), 'close')
+ self.assertIsNotNone(self.wait_for_record(r'Close called\.'), 'close')
def test_python_application_close_error(self):
self.load('close_error')
@@ -344,8 +441,9 @@ Connection: close
self.stop()
- self.assertIsNotNone(self.search_in_log(r'Close called\.'),
- 'close error')
+ self.assertIsNotNone(
+ self.wait_for_record(r'Close called\.'), 'close error'
+ )
def test_python_application_not_iterable(self):
self.load('not_iterable')
@@ -354,14 +452,18 @@ Connection: close
self.stop()
- self.assertIsNotNone(self.search_in_log(
- r'\[error\].+the application returned not an iterable object'),
- 'not iterable')
+ self.assertIsNotNone(
+ self.wait_for_record(
+ r'\[error\].+the application returned not an iterable object'
+ ),
+ 'not iterable',
+ )
def test_python_application_write(self):
self.load('write')
self.assertEqual(self.get()['body'], '0123456789', 'write')
+
if __name__ == '__main__':
- TestUnitPythonApplication.main()
+ TestPythonApplication.main()
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index b5179dea..9987e886 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -1,38 +1,35 @@
-import unittest
-import unit
+from unit.control import TestControl
-class TestUnitPythonBasic(unit.TestUnitControl):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestPythonBasic(TestControl):
+ prerequisites = ['python']
conf_app = {
"app": {
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module": "wsgi"
+ "module": "wsgi",
}
}
conf_basic = {
- "listeners": {
- "*:7080": {
- "application": "app"
- }
- },
- "applications": conf_app
+ "listeners": {"*:7080": {"pass": "applications/app"}},
+ "applications": conf_app,
}
def test_python_get_empty(self):
- self.assertEqual(self.conf_get(),
- {'listeners': {}, 'applications': {}}, 'empty')
+ 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('applications'), {}, 'applications prefix'
+ )
def test_python_get_applications(self):
self.conf(self.conf_app, 'applications')
@@ -40,113 +37,146 @@ class TestUnitPythonBasic(unit.TestUnitControl):
conf = self.conf_get()
self.assertEqual(conf['listeners'], {}, 'listeners')
- self.assertEqual(conf['applications'],
+ self.assertEqual(
+ conf['applications'],
{
"app": {
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module": "wsgi"
+ "module": "wsgi",
}
},
- 'applications')
+ 'applications',
+ )
def test_python_get_applications_prefix(self):
self.conf(self.conf_app, 'applications')
- self.assertEqual(self.conf_get('applications'),
+ self.assertEqual(
+ self.conf_get('applications'),
{
"app": {
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module":"wsgi"
+ "module": "wsgi",
}
},
- 'applications prefix')
+ 'applications prefix',
+ )
def test_python_get_applications_prefix_2(self):
self.conf(self.conf_app, 'applications')
- self.assertEqual(self.conf_get('applications/app'),
+ self.assertEqual(
+ self.conf_get('applications/app'),
{
"type": "python",
- "processes": { "spare": 0 },
+ "processes": {"spare": 0},
"path": "/app",
- "module": "wsgi"
+ "module": "wsgi",
},
- 'applications prefix 2')
+ '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')
- self.assertEqual(self.conf_get('applications/app/processes/spare'), 0,
- 'spare')
+ self.assertEqual(
+ self.conf_get('applications/app/type'), 'python', 'type'
+ )
+ self.assertEqual(
+ self.conf_get('applications/app/processes/spare'), 0, 'spare'
+ )
def test_python_get_listeners(self):
self.conf(self.conf_basic)
- self.assertEqual(self.conf_get()['listeners'],
- {"*:7080":{"application":"app"}}, 'listeners')
+ self.assertEqual(
+ self.conf_get()['listeners'],
+ {"*:7080": {"pass": "applications/app"}},
+ 'listeners',
+ )
def test_python_get_listeners_prefix(self):
self.conf(self.conf_basic)
- self.assertEqual(self.conf_get('listeners'),
- {"*:7080":{"application":"app"}}, 'listeners prefix')
+ 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'),
- {"application":"app"}, 'listeners prefix 2')
+ self.assertEqual(
+ self.conf_get('listeners/*:7080'),
+ {"pass": "applications/app"},
+ 'listeners prefix 2',
+ )
def test_python_change_listener(self):
self.conf(self.conf_basic)
- self.conf({"*:7081":{"application":"app"}}, 'listeners')
+ self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
- self.assertEqual(self.conf_get('listeners'),
- {"*:7081": {"application":"app"}}, 'change listener')
+ self.assertEqual(
+ self.conf_get('listeners'),
+ {"*:7081": {"pass": "applications/app"}},
+ 'change listener',
+ )
def test_python_add_listener(self):
self.conf(self.conf_basic)
- self.conf({"application":"app"}, 'listeners/*:7082')
+ self.conf({"pass": "applications/app"}, 'listeners/*:7082')
- self.assertEqual(self.conf_get('listeners'),
+ self.assertEqual(
+ self.conf_get('listeners'),
{
- "*:7080": {
- "application": "app"
- },
- "*:7082": {
- "application": "app"
- }
+ "*:7080": {"pass": "applications/app"},
+ "*:7082": {"pass": "applications/app"},
},
- 'add listener')
+ 'add listener',
+ )
def test_python_change_application(self):
self.conf(self.conf_basic)
self.conf('30', 'applications/app/processes/max')
- self.assertEqual(self.conf_get('applications/app/processes/max'), 30,
- 'change application max')
+ self.assertEqual(
+ self.conf_get('applications/app/processes/max'),
+ 30,
+ 'change application max',
+ )
self.conf('"/www"', 'applications/app/path')
- self.assertEqual(self.conf_get('applications/app/path'), '/www',
- 'change application path')
+ self.assertEqual(
+ self.conf_get('applications/app/path'),
+ '/www',
+ 'change application path',
+ )
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'),
+ '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'
+ )
+
if __name__ == '__main__':
- TestUnitPythonBasic.main()
+ TestPythonBasic.main()
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index 71e4d5b7..744f4947 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -1,128 +1,179 @@
-import unittest
-import unit
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitPythonEnvironment(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestPythonEnvironment(TestApplicationPython):
+ prerequisites = ['python']
def test_python_environment_name_null(self):
self.load('environment')
- self.assertIn('error', self.conf({
- "va\0r": "val1"
- }, 'applications/environment/environment'), 'name null')
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"va\0r": "val1"}, 'applications/environment/environment'
+ ),
+ 'name null',
+ )
def test_python_environment_name_equals(self):
self.load('environment')
- self.assertIn('error', self.conf({
- "var=": "val1"
- }, 'applications/environment/environment'), 'name equals')
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"var=": "val1"}, 'applications/environment/environment'
+ ),
+ 'name equals',
+ )
def test_python_environment_value_null(self):
self.load('environment')
- self.assertIn('error', self.conf({
- "var": "\0val"
- }, 'applications/environment/environment'), 'value null')
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"var": "\0val"}, 'applications/environment/environment'
+ ),
+ 'value null',
+ )
def test_python_environment_update(self):
self.load('environment')
- self.conf({
- "var": "val1"
- }, 'applications/environment/environment')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var',
- 'Connection': 'close'
- })['body'], 'val1,', 'set')
-
- self.conf({
- "var": "val2"
- }, 'applications/environment/environment')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var',
- 'Connection': 'close'
- })['body'], 'val2,', 'update')
+ self.conf({"var": "val1"}, 'applications/environment/environment')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'val1,',
+ 'set',
+ )
+
+ self.conf({"var": "val2"}, 'applications/environment/environment')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'val2,',
+ 'update',
+ )
def test_python_environment_replace(self):
self.load('environment')
- self.conf({
- "var1": "val1"
- }, 'applications/environment/environment')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var1',
- 'Connection': 'close'
- })['body'], 'val1,', 'set')
-
- self.conf({
- "var2": "val2"
- }, 'applications/environment/environment')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var1,var2',
- 'Connection': 'close'
- })['body'], 'val2,', 'replace')
+ self.conf({"var1": "val1"}, 'applications/environment/environment')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var1',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'val1,',
+ 'set',
+ )
+
+ self.conf({"var2": "val2"}, 'applications/environment/environment')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var1,var2',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'val2,',
+ 'replace',
+ )
def test_python_environment_clear(self):
self.load('environment')
- self.conf({
- "var1": "val1",
- "var2": "val2"
- }, 'applications/environment/environment')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var1,var2',
- 'Connection': 'close'
- })['body'], 'val1,val2,', 'set')
+ self.conf(
+ {"var1": "val1", "var2": "val2"},
+ 'applications/environment/environment',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var1,var2',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'val1,val2,',
+ 'set',
+ )
self.conf({}, 'applications/environment/environment')
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'var1,var2',
- 'Connection': 'close'
- })['body'], '', 'clear')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'var1,var2',
+ 'Connection': 'close',
+ }
+ )['body'],
+ '',
+ 'clear',
+ )
def test_python_environment_replace_default(self):
self.load('environment')
- pwd_default = self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'PWD',
- 'Connection': 'close'
- })['body']
+ pwd_default = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'PWD',
+ 'Connection': 'close',
+ }
+ )['body']
self.assertGreater(len(pwd_default), 1, 'get default')
- self.conf({
- "PWD": "new/pwd"
- }, 'applications/environment/environment')
+ self.conf({"PWD": "new/pwd"}, 'applications/environment/environment')
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'PWD',
- 'Connection': 'close'
- })['body'], 'new/pwd,', 'replace default')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'PWD',
+ 'Connection': 'close',
+ }
+ )['body'],
+ 'new/pwd,',
+ 'replace default',
+ )
self.conf({}, 'applications/environment/environment')
- self.assertEqual(self.get(headers={
- 'Host': 'localhost',
- 'X-Variables': 'PWD',
- 'Connection': 'close'
- })['body'], pwd_default, 'restore default')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'X-Variables': 'PWD',
+ 'Connection': 'close',
+ }
+ )['body'],
+ pwd_default,
+ 'restore default',
+ )
+
if __name__ == '__main__':
- TestUnitPythonEnvironment.main()
+ TestPythonEnvironment.main()
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index 2efe59c0..b0c70e53 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -2,12 +2,11 @@ import re
import time
import subprocess
import unittest
-import unit
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitPythonProcman(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestPythonProcman(TestApplicationPython):
+ prerequisites = ['python']
def pids_for_process(self):
time.sleep(0.2)
@@ -29,55 +28,88 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
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')
+ 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')
+ 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')
+ 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')
+ 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')
+ 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')
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"spare": 2, "max": 1, "idle_timeout": 1},
+ '/applications/' + self.app_name + '/processes',
+ ),
+ 'spare greater than max',
+ )
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(
+ 'error',
+ self.conf(
+ {"spare": 0, "max": 0, "idle_timeout": 1},
+ 'applications/' + self.app_name + '/processes',
+ ),
+ 'max 0',
+ )
def test_python_processes_idle_timeout_zero(self):
- self.conf({
- "spare": 0,
- "max": 2,
- "idle_timeout": 0
- }, 'applications/' + self.app_name + '/processes')
+ self.conf(
+ {"spare": 0, "max": 2, "idle_timeout": 0},
+ 'applications/' + self.app_name + '/processes',
+ )
self.get()
self.assertEqual(len(self.pids_for_process()), 0, 'idle timeout 0')
@@ -101,7 +133,7 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
self.stop_all()
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_python_prefork_same_processes(self):
self.conf('2', 'applications/' + self.app_name + '/processes')
@@ -114,11 +146,10 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
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(
+ {"spare": 0, "max": 8, "idle_timeout": 1},
+ 'applications/' + self.app_name + '/processes',
+ )
self.assertEqual(len(self.pids_for_process()), 0, 'on-demand 0')
@@ -131,16 +162,17 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
time.sleep(1)
- self.assertEqual(len(self.pids_for_process()), 0, 'on-demand stop idle')
+ self.assertEqual(
+ len(self.pids_for_process()), 0, 'on-demand stop idle'
+ )
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(
+ {"spare": 2, "max": 8, "idle_timeout": 1},
+ 'applications/' + self.app_name + '/processes',
+ )
pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'updown 2')
@@ -151,7 +183,9 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
self.assertTrue(pids.issubset(pids_new), 'updown 3 only 1 new')
self.get()
- self.assertSetEqual(self.pids_for_process(), pids_new, 'updown still 3')
+ self.assertSetEqual(
+ self.pids_for_process(), pids_new, 'updown still 3'
+ )
time.sleep(1)
@@ -166,11 +200,10 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
self.stop_all()
def test_python_reconfigure(self):
- self.conf({
- "spare": 2,
- "max": 6,
- "idle_timeout": 1
- }, 'applications/' + self.app_name + '/processes')
+ self.conf(
+ {"spare": 2, "max": 6, "idle_timeout": 1},
+ 'applications/' + self.app_name + '/processes',
+ )
pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'reconf 2')
@@ -191,11 +224,10 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
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(
+ {"spare": 0, "max": 6, "idle_timeout": 2},
+ 'applications/' + self.app_name + '/processes',
+ )
self.get()
pids = self.pids_for_process()
@@ -209,40 +241,42 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
pids_new = self.pids_for_process()
self.assertEqual(len(pids_new), 1, 'idle timeout still 1')
- self.assertSetEqual(self.pids_for_process(), pids,
- 'idle timeout still 1 same pid')
+ self.assertSetEqual(
+ self.pids_for_process(), pids, 'idle timeout still 1 same pid'
+ )
time.sleep(1)
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')
-
- (resp, sock) = self.get(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive'
- }, start=True, read_timeout=1)
- self.assertEqual(len(self.pids_for_process()), 1,
- 'keepalive connection 1')
+ self.conf(
+ {"spare": 0, "max": 6, "idle_timeout": 2},
+ 'applications/' + self.app_name + '/processes',
+ )
+
+ (resp, sock) = self.get(
+ headers={'Host': 'localhost', 'Connection': 'keep-alive'},
+ start=True,
+ read_timeout=1,
+ )
+ self.assertEqual(
+ len(self.pids_for_process()), 1, 'keepalive connection 1'
+ )
time.sleep(2)
- self.assertEqual(len(self.pids_for_process()), 0, 'keepalive connection 0')
+ self.assertEqual(
+ len(self.pids_for_process()), 0, 'keepalive connection 0'
+ )
sock.close()
def stop_all(self):
- self.conf({
- "listeners": {},
- "applications": {}
- })
+ self.conf({"listeners": {}, "applications": {}})
self.assertEqual(len(self.pids_for_process()), 0, 'stop all')
+
if __name__ == '__main__':
- TestUnitPythonProcman.main()
+ TestPythonProcman.main()
diff --git a/test/test_routing.py b/test/test_routing.py
index 07097fc8..ac2e0de8 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1,458 +1,2889 @@
import unittest
-import unit
+from unit.applications.proto import TestApplicationProto
-class TestUnitRouting(unit.TestUnitApplicationProto):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestRouting(TestApplicationProto):
+ prerequisites = ['python']
def setUp(self):
super().setUp()
- self.conf({
- "listeners": {
- "*:7080": {
- "pass": "routes"
- }
- },
- "routes": [{
- "match": { "method": "GET" },
- "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"
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {
+ "match": {"method": "GET"},
+ "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",
+ },
+ "mirror": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + '/python/mirror',
+ "working_directory": self.current_dir
+ + '/python/mirror',
+ "module": "wsgi",
+ },
},
- "mirror": {
- "type": "python",
- "processes": { "spare": 0 },
- "path": self.current_dir + '/python/mirror',
- "working_directory": self.current_dir + '/python/mirror',
- "module": "wsgi"
- }
}
- })
+ )
def test_routes_match_method_positive(self):
self.assertEqual(self.get()['status'], 200, 'method positive GET')
self.assertEqual(self.post()['status'], 404, 'method positive POST')
def test_routes_match_method_positive_many(self):
- self.assertIn('success', self.conf([{
- "match": { "method": ["GET", "POST"] },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method positive many configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": ["GET", "POST"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method positive many configure',
+ )
self.assertEqual(self.get()['status'], 200, 'method positive many GET')
- self.assertEqual(self.post()['status'], 200,
- 'method positive many POST')
- self.assertEqual(self.delete()['status'], 404,
- 'method positive many DELETE')
+ self.assertEqual(
+ self.post()['status'], 200, 'method positive many POST'
+ )
+ self.assertEqual(
+ self.delete()['status'], 404, 'method positive many DELETE'
+ )
def test_routes_match_method_negative(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "!GET" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method negative configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "!GET"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method negative configure',
+ )
self.assertEqual(self.get()['status'], 404, 'method negative GET')
self.assertEqual(self.post()['status'], 200, 'method negative POST')
def test_routes_match_method_negative_many(self):
- self.assertIn('success', self.conf([{
- "match": { "method": ["!GET", "!POST"] },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method negative many configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": ["!GET", "!POST"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method negative many configure',
+ )
self.assertEqual(self.get()['status'], 404, 'method negative many GET')
- self.assertEqual(self.post()['status'], 404,
- 'method negative many POST')
- self.assertEqual(self.delete()['status'], 200,
- 'method negative many DELETE')
+ self.assertEqual(
+ self.post()['status'], 404, 'method negative many POST'
+ )
+ self.assertEqual(
+ self.delete()['status'], 200, 'method negative many DELETE'
+ )
def test_routes_match_method_wildcard_left(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "*ET" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method wildcard left configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "*ET"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method wildcard left configure',
+ )
self.assertEqual(self.get()['status'], 200, 'method wildcard left GET')
- self.assertEqual(self.post()['status'], 404,
- 'method wildcard left POST')
+ self.assertEqual(
+ self.post()['status'], 404, 'method wildcard left POST'
+ )
def test_routes_match_method_wildcard_right(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "GE*" },
- "action": { "pass": "applications/empty"}
- }], 'routes'), 'method wildcard right configure')
-
- self.assertEqual(self.get()['status'], 200,
- 'method wildcard right GET')
- self.assertEqual(self.post()['status'], 404,
- 'method wildcard right POST')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "GE*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method wildcard right configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'method wildcard right GET'
+ )
+ self.assertEqual(
+ self.post()['status'], 404, 'method wildcard right POST'
+ )
def test_routes_match_method_wildcard_left_right(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "*GET*" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method wildcard left right configure')
-
- self.assertEqual(self.get()['status'], 200,
- 'method wildcard right GET')
- self.assertEqual(self.post()['status'], 404,
- 'method wildcard right POST')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "*GET*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method wildcard left right configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'method wildcard right GET'
+ )
+ self.assertEqual(
+ self.post()['status'], 404, 'method wildcard right POST'
+ )
def test_routes_match_method_wildcard(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "*" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method wildcard configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method wildcard configure',
+ )
self.assertEqual(self.get()['status'], 200, 'method wildcard')
+ def test_routes_match_invalid(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"method": "**"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'wildcard invalid',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"method": "blah**"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'wildcard invalid 2',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"host": "*blah*blah"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'wildcard invalid 3',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"host": "blah*blah*blah"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'wildcard invalid 4',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"host": "blah*blah*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'wildcard invalid 5',
+ )
+
+ def test_routes_match_wildcard_middle(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "ex*le"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'host wildcard middle configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'host wildcard middle',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'www.example', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'host wildcard middle 2',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'host wildcard middle 3',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'exampl', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'host wildcard middle 4',
+ )
+
def test_routes_match_method_case_insensitive(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "get" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'method case insensitive configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "get"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'method case insensitive configure',
+ )
self.assertEqual(self.get()['status'], 200, 'method case insensitive')
+ def test_routes_match_wildcard_left_case_insensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "*et"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match wildcard case insensitive'
+ )
+
+ def test_routes_match_wildcard_middle_case_insensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "g*t"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match wildcard case insensitive'
+ )
+
+ def test_routes_match_wildcard_right_case_insensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "get*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match wildcard case insensitive'
+ )
+
+ def test_routes_match_wildcard_substring_case_insensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "*et*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard substring case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'],
+ 200,
+ 'match wildcard substring case insensitive',
+ )
+
+ def test_routes_match_wildcard_left_case_sensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "*blah"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard left case sensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 200,
+ 'match wildcard left case sensitive /blah',
+ )
+
+ self.assertEqual(
+ self.get(url='/BLAH')['status'],
+ 404,
+ 'match wildcard left case sensitive /BLAH',
+ )
+
+ def test_routes_match_wildcard_middle_case_sensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "/b*h"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard middle case sensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 200,
+ 'match wildcard middle case sensitive /blah',
+ )
+
+ self.assertEqual(
+ self.get(url='/BLAH')['status'],
+ 404,
+ 'match wildcard middle case sensitive /BLAH',
+ )
+
+ def test_routes_match_wildcard_right_case_sensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "/bla*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard right case sensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 200,
+ 'match wildcard right case sensitive /blah',
+ )
+
+ self.assertEqual(
+ self.get(url='/BLAH')['status'],
+ 404,
+ 'match wildcard right case sensitive /BLAH',
+ )
+
+ def test_routes_match_wildcard_substring_case_sensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "*bla*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match wildcard substring case sensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 200,
+ 'match wildcard substring case sensitive /blah',
+ )
+
+ self.assertEqual(
+ self.get(url='/BLAH')['status'],
+ 404,
+ 'match wildcard substring case sensitive /BLAH',
+ )
+
def test_routes_absent(self):
- self.conf({
- "listeners": {
- "*:7081": {
- "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"
- }
+ self.conf(
+ {
+ "listeners": {"*:7081": {"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",
+ }
+ },
}
- })
+ )
self.assertEqual(self.get(port=7081)['status'], 200, 'routes absent')
def test_routes_pass_invalid(self):
- self.assertIn('error', self.conf({ "pass": "routes/blah" },
- 'listeners/*:7080'), 'routes invalid')
+ self.assertIn(
+ 'error',
+ self.conf({"pass": "routes/blah"}, 'listeners/*:7080'),
+ 'routes invalid',
+ )
def test_route_empty(self):
- self.assertIn('success', self.conf({
- "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"
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "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",
+ },
+ },
}
- }
- }), 'route empty configure')
+ ),
+ 'route empty configure',
+ )
self.assertEqual(self.get()['status'], 404, 'route empty')
def test_routes_route_empty(self):
- self.assertIn('success', self.conf({}, 'listeners'),
- 'routes empty listeners configure')
+ self.assertIn(
+ 'success',
+ self.conf({}, 'listeners'),
+ 'routes empty listeners configure',
+ )
- self.assertIn('success', self.conf({}, 'routes'),
- 'routes empty configure')
+ self.assertIn(
+ 'success', self.conf({}, 'routes'), 'routes empty configure'
+ )
def test_routes_route_match_absent(self):
- self.assertIn('success', self.conf([{
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'route match absent configure')
+ self.assertIn(
+ 'success',
+ self.conf([{"action": {"pass": "applications/empty"}}], 'routes'),
+ 'route match absent configure',
+ )
self.assertEqual(self.get()['status'], 200, 'route match absent')
def test_routes_route_action_absent(self):
self.skip_alerts.append(r'failed to apply new conf')
- self.assertIn('error', self.conf([{
- "match": { "method": "GET" }
- }], 'routes'), 'route pass absent configure')
+ self.assertIn(
+ 'error',
+ self.conf([{"match": {"method": "GET"}}], 'routes'),
+ 'route pass absent configure',
+ )
def test_routes_route_pass_absent(self):
self.skip_alerts.append(r'failed to apply new conf')
- self.assertIn('error', self.conf([{
- "match": { "method": "GET" },
- "action": {}
- }], 'routes'), 'route pass absent configure')
+ self.assertIn(
+ 'error',
+ self.conf([{"match": {"method": "GET"}, "action": {}}], 'routes'),
+ 'route pass absent configure',
+ )
def test_routes_rules_two(self):
- self.assertIn('success', self.conf([{
- "match": { "method": "GET" },
- "action": { "pass": "applications/empty" }
- },
- {
- "match": { "method": "POST" },
- "action": { "pass": "applications/mirror" }
- }], 'routes'), 'rules two configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "GET"},
+ "action": {"pass": "applications/empty"},
+ },
+ {
+ "match": {"method": "POST"},
+ "action": {"pass": "applications/mirror"},
+ },
+ ],
+ 'routes',
+ ),
+ 'rules two configure',
+ )
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(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Connection': 'close',
+ },
+ body='X',
+ )['status'],
+ 200,
+ 'rules two match second',
+ )
def test_routes_two(self):
- self.assertIn('success', self.conf({
- "listeners": {
- "*:7080": {
- "pass": "routes/first"
- }
- },
- "routes": {
- "first": [{
- "match": { "method": "GET" },
- "action": { "pass": "routes/second" }
- }],
- "second": [{
- "match": { "host": "localhost" },
- "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"
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes/first"}},
+ "routes": {
+ "first": [
+ {
+ "match": {"method": "GET"},
+ "action": {"pass": "routes/second"},
+ }
+ ],
+ "second": [
+ {
+ "match": {"host": "localhost"},
+ "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 two configure')
+ ),
+ 'routes two configure',
+ )
self.assertEqual(self.get()['status'], 200, 'routes two')
def test_routes_match_host_positive(self):
- self.assertIn('success', self.conf([{
- "match": { "host": "localhost" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host positive configure')
-
- self.assertEqual(self.get()['status'], 200,
- 'match host positive localhost')
-
- self.assertEqual(self.get(headers={'Connection': 'close'})['status'],
- 404, 'match host positive empty')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost.',
- 'Connection': 'close'
- })['status'], 200, 'match host positive trailing dot')
-
- self.assertEqual(self.get(headers={
- 'Host': 'www.localhost',
- 'Connection': 'close'
- })['status'], 404, 'match host positive www.localhost')
-
- self.assertEqual(self.get(headers={
- 'Host': 'localhost1',
- 'Connection': 'close'
- })['status'], 404, 'match host positive localhost1')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com',
- 'Connection': 'close'
- })['status'], 404, 'match host positive example.com')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "localhost"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host positive configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match host positive localhost'
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'localhost.', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host positive trailing dot',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'www.localhost', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'match host positive www.localhost',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'localhost1', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'match host positive localhost1',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 404,
+ 'match host positive example.com',
+ )
+
+ @unittest.skip('not yet')
+ def test_routes_match_host_absent(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "localhost"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host absent configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Connection': 'close'})['status'],
+ 400,
+ 'match host absent',
+ )
def test_routes_match_host_ipv4(self):
- self.assertIn('success', self.conf([{
- "match": { "host": "127.0.0.1" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host ipv4 configure')
-
- self.assertEqual(self.get(headers={
- 'Host': '127.0.0.1',
- 'Connection': 'close'
- })['status'], 200, 'match host ipv4')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "127.0.0.1"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host ipv4 configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host ipv4',
+ )
def test_routes_match_host_ipv6(self):
- self.assertIn('success', self.conf([{
- "match": { "host": "[::1]" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host ipv6 configure')
-
- self.assertEqual(self.get(headers={
- 'Host': '[::1]',
- 'Connection': 'close'
- })['status'], 200, 'match host ipv6')
-
- self.assertEqual(self.get(headers={
- 'Host': '[::1]:7080',
- 'Connection': 'close'
- })['status'], 200, 'match host ipv6 port')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "[::1]"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host ipv6 configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': '[::1]', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host ipv6',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host ipv6 port',
+ )
def test_routes_match_host_positive_many(self):
- self.assertIn('success', self.conf([{
- "match": { "host": ["localhost", "example.com"] },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host positive many configure')
-
- self.assertEqual(self.get()['status'], 200,
- 'match host positive many localhost')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com',
- 'Connection': 'close'
- })['status'], 200, 'match host positive many example.com')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": ["localhost", "example.com"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host positive many configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match host positive many localhost'
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host positive many example.com',
+ )
def test_routes_match_host_positive_and_negative(self):
- self.assertIn('success', self.conf([{
- "match": { "host": ["*example.com", "!www.example.com"] },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host positive and negative configure')
-
- self.assertEqual(self.get()['status'], 404,
- 'match host positive and negative localhost')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com',
- 'Connection': 'close'
- })['status'], 200, 'match host positive and negative example.com')
-
- self.assertEqual(self.get(headers={
- 'Host': 'www.example.com',
- 'Connection': 'close'
- })['status'], 404, 'match host positive and negative www.example.com')
-
- self.assertEqual(self.get(headers={
- 'Host': '!www.example.com',
- 'Connection': 'close'
- })['status'], 200, 'match host positive and negative !www.example.com')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "host": ["*example.com", "!www.example.com"]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host positive and negative configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'],
+ 404,
+ 'match host positive and negative localhost',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host positive and negative example.com',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={'Host': 'www.example.com', 'Connection': 'close'}
+ )['status'],
+ 404,
+ 'match host positive and negative www.example.com',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={'Host': '!www.example.com', 'Connection': 'close'}
+ )['status'],
+ 200,
+ 'match host positive and negative !www.example.com',
+ )
def test_routes_match_host_positive_and_negative_wildcard(self):
- self.assertIn('success', self.conf([{
- "match": { "host": ["*example*", "!www.example*"] },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host positive and negative wildcard configure')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com',
- 'Connection': 'close'
- })['status'], 200,
- 'match host positive and negative wildcard example.com')
-
- self.assertEqual(self.get(headers={
- 'Host': 'www.example.com',
- 'Connection': 'close'
- })['status'], 404,
- 'match host positive and negative wildcard www.example.com')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": ["*example*", "!www.example*"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host positive and negative wildcard configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'match host positive and negative wildcard example.com',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={'Host': 'www.example.com', 'Connection': 'close'}
+ )['status'],
+ 404,
+ 'match host positive and negative wildcard www.example.com',
+ )
def test_routes_match_host_case_insensitive(self):
- self.assertIn('success', self.conf([{
- "match": { "host": "Example.com" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'host case insensitive configure')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com',
- 'Connection': 'close'
- })['status'], 200, 'host case insensitive example.com')
-
- self.assertEqual(self.get(headers={
- 'Host': 'EXAMPLE.COM',
- 'Connection': 'close'
- })['status'], 200, 'host case insensitive EXAMPLE.COM')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "Example.com"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'host case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'example.com', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'host case insensitive example.com',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'})[
+ 'status'
+ ],
+ 200,
+ 'host case insensitive EXAMPLE.COM',
+ )
def test_routes_match_host_port(self):
- self.assertIn('success', self.conf([{
- "match": { "host": "example.com" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match host port configure')
-
- self.assertEqual(self.get(headers={
- 'Host': 'example.com:7080',
- 'Connection': 'close'
- })['status'], 200, 'match host port')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": "example.com"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host port configure',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={'Host': 'example.com:7080', 'Connection': 'close'}
+ )['status'],
+ 200,
+ 'match host port',
+ )
+
+ def test_routes_match_host_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"host": ""},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match host empty configure',
+ )
+
+ self.assertEqual(
+ self.get(headers={'Host': '', 'Connection': 'close'})['status'],
+ 200,
+ 'match host empty',
+ )
+ self.assertEqual(
+ self.get(http_10=True, headers={})['status'],
+ 200,
+ 'match host empty 2',
+ )
+ self.assertEqual(self.get()['status'], 404, 'match host empty 3')
def test_routes_match_uri_positive(self):
- self.assertIn('success', self.conf([{
- "match": { "uri": "/" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match uri positive configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "/"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match uri positive configure',
+ )
self.assertEqual(self.get()['status'], 200, 'match uri positive')
- self.assertEqual(self.get(url='/blah')['status'], 404,
- 'match uri positive blah')
- self.assertEqual(self.get(url='/#blah')['status'], 200,
- 'match uri positive #blah')
- self.assertEqual(self.get(url='/?var')['status'], 200,
- 'match uri params')
- self.assertEqual(self.get(url='//')['status'], 200,
- 'match uri adjacent slashes')
- self.assertEqual(self.get(url='/blah/../')['status'], 200,
- 'match uri relative path')
- self.assertEqual(self.get(url='/./')['status'], 200,
- 'match uri relative path')
+ self.assertEqual(
+ self.get(url='/blah')['status'], 404, 'match uri positive blah'
+ )
+ self.assertEqual(
+ self.get(url='/#blah')['status'], 200, 'match uri positive #blah'
+ )
+ self.assertEqual(
+ self.get(url='/?var')['status'], 200, 'match uri params'
+ )
+ self.assertEqual(
+ self.get(url='//')['status'], 200, 'match uri adjacent slashes'
+ )
+ self.assertEqual(
+ self.get(url='/blah/../')['status'], 200, 'match uri relative path'
+ )
+ self.assertEqual(
+ self.get(url='/./')['status'], 200, 'match uri relative path'
+ )
def test_routes_match_uri_case_sensitive(self):
- self.assertIn('success', self.conf([{
- "match": { "uri": "/BLAH" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match uri case sensitive configure')
-
- self.assertEqual(self.get(url='/blah')['status'], 404,
- 'match uri case sensitive blah')
- self.assertEqual(self.get(url='/BlaH')['status'], 404,
- 'match uri case sensitive BlaH')
- self.assertEqual(self.get(url='/BLAH')['status'], 200,
- 'match uri case sensitive BLAH')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "/BLAH"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match uri case sensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 404,
+ 'match uri case sensitive blah',
+ )
+ self.assertEqual(
+ self.get(url='/BlaH')['status'],
+ 404,
+ 'match uri case sensitive BlaH',
+ )
+ self.assertEqual(
+ self.get(url='/BLAH')['status'],
+ 200,
+ 'match uri case sensitive BLAH',
+ )
def test_routes_match_uri_normalize(self):
- self.assertIn('success', self.conf([{
- "match": { "uri": "/blah" },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'match uri normalize configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": "/blah"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match uri normalize configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/%62%6c%61%68')['status'], 200, 'match uri normalize'
+ )
+
+ def test_routes_match_empty_array(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"uri": []},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match empty array configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/blah')['status'],
+ 200,
+ 'match empty array',
+ )
+
+ def test_routes_reconfigure(self):
+ self.assertIn('success', self.conf([], 'routes'), 'routes redefine')
+ self.assertEqual(self.get()['status'], 404, 'routes redefine request')
+
+ self.assertIn(
+ 'success',
+ self.conf([{"action": {"pass": "applications/empty"}}], 'routes'),
+ 'routes redefine 2',
+ )
+ self.assertEqual(
+ self.get()['status'], 200, 'routes redefine request 2'
+ )
+
+ self.assertIn('success', self.conf([], 'routes'), 'routes redefine 3')
+ self.assertEqual(
+ self.get()['status'], 404, 'routes redefine request 3'
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "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 redefine 4',
+ )
+ self.assertEqual(
+ self.get()['status'], 200, 'routes redefine request 4'
+ )
+
+ self.assertIn(
+ 'success', self.conf_delete('routes/main/0'), 'routes redefine 5'
+ )
+ self.assertEqual(
+ self.get()['status'], 404, 'routes redefine request 5'
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_post(
+ {"action": {"pass": "applications/empty"}}, 'routes/main'
+ ),
+ 'routes redefine 6',
+ )
+ self.assertEqual(
+ self.get()['status'], 200, 'routes redefine request 6'
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"action": {"pass": "applications/empty"}}, 'routes/main/2'
+ ),
+ 'routes redefine 7',
+ )
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"action": {"pass": "applications/empty"}}, 'routes/main/1'
+ ),
+ 'routes redefine 8',
+ )
+
+ self.assertEqual(
+ len(self.conf_get('routes/main')), 2, 'routes redefine conf 8'
+ )
+ self.assertEqual(
+ self.get()['status'], 200, 'routes redefine request 8'
+ )
+
+ def test_routes_edit(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": "GET"},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'routes edit configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'routes edit GET')
+ self.assertEqual(self.post()['status'], 404, 'routes edit POST')
+
+ self.assertIn(
+ 'success',
+ self.conf_post(
+ {
+ "match": {"method": "POST"},
+ "action": {"pass": "applications/empty"},
+ },
+ 'routes',
+ ),
+ 'routes edit configure 2',
+ )
+ self.assertEqual(
+ 'GET',
+ self.conf_get('routes/0/match/method'),
+ 'routes edit configure 2 check',
+ )
+ self.assertEqual(
+ 'POST',
+ self.conf_get('routes/1/match/method'),
+ 'routes edit configure 2 check 2',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'routes edit GET 2')
+ self.assertEqual(self.post()['status'], 200, 'routes edit POST 2')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0'),
+ 'routes edit configure 3',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'routes edit GET 3')
+ self.assertEqual(self.post()['status'], 200, 'routes edit POST 3')
+
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/1'),
+ 'routes edit configure invalid',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/-1'),
+ 'routes edit configure invalid 2',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/blah'),
+ 'routes edit configure invalid 3',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'routes edit GET 4')
+ self.assertEqual(self.post()['status'], 200, 'routes edit POST 4')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0'),
+ 'routes edit configure 5',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'routes edit GET 5')
+ self.assertEqual(self.post()['status'], 404, 'routes edit POST 5')
+
+ self.assertIn(
+ 'success',
+ self.conf_post(
+ {
+ "match": {"method": "POST"},
+ "action": {"pass": "applications/empty"},
+ },
+ 'routes',
+ ),
+ 'routes edit configure 6',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'routes edit GET 6')
+ self.assertEqual(self.post()['status'], 200, 'routes edit POST 6')
+
+ self.assertIn(
+ 'success',
+ 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",
+ }
+ },
+ }
+ ),
+ 'route edit configure 7',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/0'),
+ 'routes edit configure invalid 4',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/main'),
+ 'routes edit configure invalid 5',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'routes edit GET 7')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('listeners/*:7080'),
+ 'route edit configure 8',
+ )
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/main'),
+ 'route edit configure 9',
+ )
+
+ def test_match_edit(self):
+ self.skip_alerts.append(r'failed to apply new conf')
- self.assertEqual(self.get(url='/%62%6c%61%68')['status'], 200,
- 'match uri normalize')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"method": ["GET", "POST"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match edit configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET')
+ self.assertEqual(self.post()['status'], 200, 'match edit POST')
+ self.assertEqual(self.put()['status'], 404, 'match edit PUT')
+
+ self.assertIn(
+ 'success',
+ self.conf_post('\"PUT\"', 'routes/0/match/method'),
+ 'match edit configure 2',
+ )
+ self.assertListEqual(
+ ['GET', 'POST', 'PUT'],
+ self.conf_get('routes/0/match/method'),
+ 'match edit configure 2 check',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 2')
+ self.assertEqual(self.post()['status'], 200, 'match edit POST 2')
+ self.assertEqual(self.put()['status'], 200, 'match edit PUT 2')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/method/1'),
+ 'match edit configure 3',
+ )
+ self.assertListEqual(
+ ['GET', 'PUT'],
+ self.conf_get('routes/0/match/method'),
+ 'match edit configure 3 check',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 3')
+ self.assertEqual(self.post()['status'], 404, 'match edit POST 3')
+ self.assertEqual(self.put()['status'], 200, 'match edit PUT 3')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/method/1'),
+ 'match edit configure 4',
+ )
+ self.assertListEqual(
+ ['GET'],
+ self.conf_get('routes/0/match/method'),
+ 'match edit configure 4 check',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 4')
+ self.assertEqual(self.post()['status'], 404, 'match edit POST 4')
+ self.assertEqual(self.put()['status'], 404, 'match edit PUT 4')
+
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/0/match/method/1'),
+ 'match edit configure invalid',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/0/match/method/-1'),
+ 'match edit configure invalid 2',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/0/match/method/blah'),
+ 'match edit configure invalid 3',
+ )
+ self.assertListEqual(
+ ['GET'],
+ self.conf_get('routes/0/match/method'),
+ 'match edit configure 5 check',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 5')
+ self.assertEqual(self.post()['status'], 404, 'match edit POST 5')
+ self.assertEqual(self.put()['status'], 404, 'match edit PUT 5')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/method/0'),
+ 'match edit configure 6',
+ )
+ self.assertListEqual(
+ [],
+ self.conf_get('routes/0/match/method'),
+ 'match edit configure 6 check',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 6')
+ self.assertEqual(self.post()['status'], 200, 'match edit POST 6')
+ self.assertEqual(self.put()['status'], 200, 'match edit PUT 6')
+
+ self.assertIn(
+ 'success',
+ self.conf('"GET"', 'routes/0/match/method'),
+ 'match edit configure 7',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 7')
+ self.assertEqual(self.post()['status'], 404, 'match edit POST 7')
+ self.assertEqual(self.put()['status'], 404, 'match edit PUT 7')
+
+ self.assertIn(
+ 'error',
+ self.conf_delete('routes/0/match/method/0'),
+ 'match edit configure invalid 5',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({}, 'routes/0/action'),
+ 'match edit configure invalid 6',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf({}, 'routes/0/match'),
+ 'match edit configure 8',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match edit GET 8')
def test_routes_match_rules(self):
- self.assertIn('success', self.conf([{
- "match": {
- "method": "GET",
- "host": "localhost",
- "uri": "/"
- },
- "action": { "pass": "applications/empty" }
- }], 'routes'), 'routes match rules configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "method": "GET",
+ "host": "localhost",
+ "uri": "/",
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'routes match rules configure',
+ )
self.assertEqual(self.get()['status'], 200, 'routes match rules')
def test_routes_loop(self):
- self.assertIn('success', self.conf([{
- "match": { "uri": "/" },
- "action": { "pass": "routes" }
- }], 'routes'), 'routes loop configure')
+ self.assertIn(
+ 'success',
+ self.conf(
+ [{"match": {"uri": "/"}, "action": {"pass": "routes"}}],
+ 'routes',
+ ),
+ 'routes loop configure',
+ )
self.assertEqual(self.get()['status'], 500, 'routes loop')
+ def test_routes_match_headers(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"host": "localhost"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match headers')
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "Localhost",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers case insensitive',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost.com",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers exact',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "llocalhost",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers exact 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "host",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers exact 3',
+ )
+
+ def test_routes_match_headers_multiple(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "headers": {"host": "localhost", "x-blah": "test"}
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers multiple configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match headers multiple')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": "test",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers multiple 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": "",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers multiple 3',
+ )
+
+ def test_routes_match_headers_multiple_values(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"x-blah": "test"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers multiple values configure',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": ["test", "test", "test"],
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers multiple values',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": ["test", "blah", "test"],
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers multiple values 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": ["test", "", "test"],
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers multiple values 3',
+ )
+
+ def test_routes_match_headers_multiple_rules(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"x-blah": ["test", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers multiple rules configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 404, 'match headers multiple rules'
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": "test",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers multiple rules 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": "blah",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers multiple rules 3',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": ["test", "blah", "test"],
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers multiple rules 4',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "X-blah": ["blah", ""],
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers multiple rules 5',
+ )
+
+ def test_routes_match_headers_case_insensitive(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"X-BLAH": "TEST"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers case insensitive configure',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-blah": "test",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers case insensitive',
+ )
+
+ def test_routes_match_headers_invalid(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"headers": ["blah"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers invalid',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"foo": ["bar", {}]}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers invalid 2',
+ )
+
+ def test_routes_match_headers_empty_rule(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"host": ""}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers empty rule configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match headers empty rule')
+
+ self.assertEqual(
+ self.get(headers={"Host": "", "Connection": "close"})['status'],
+ 200,
+ 'match headers empty rule 2',
+ )
+
+ def test_routes_match_headers_rule_field_empty(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"": "blah"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers rule field empty configure',
+ )
+
+ def test_routes_match_headers_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers empty configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match headers empty')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": []},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers array empty configure 2',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 200, 'match headers array empty 2'
+ )
+
+ def test_routes_match_headers_rule_array_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"headers": {"blah": []}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers rule array empty configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 404, 'match headers rule array empty'
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "blah": "foo",
+ "Connection": "close",
+ }
+ )['status'], 200, 'match headers rule array empty 2'
+ )
+
+ def test_routes_match_headers_array(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "headers": [
+ {"x-header1": "foo*"},
+ {"x-header2": "bar"},
+ {"x-header3": ["foo", "bar"]},
+ {"x-header1": "bar", "x-header4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match headers array configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match headers array')
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header1": "foo123",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers array 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header2": "bar",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers array 3',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header3": "bar",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers array 4',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header1": "bar",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers array 5',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header1": "bar",
+ "x-header4": "foo",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers array 6',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/headers/1'),
+ 'match headers array configure 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header2": "bar",
+ "Connection": "close",
+ }
+ )['status'],
+ 404,
+ 'match headers array 7',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ "Host": "localhost",
+ "x-header3": "foo",
+ "Connection": "close",
+ }
+ )['status'],
+ 200,
+ 'match headers array 8',
+ )
+
+ def test_routes_match_arguments(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"arguments": {"foo": "bar"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match arguments')
+ self.assertEqual(
+ self.get(url='/?foo=bar')['status'], 200, 'match arguments 2'
+ )
+
+ self.assertEqual(
+ self.get(url='/?Foo=bar')['status'],
+ 404,
+ 'match arguments case sensitive',
+ ) # FAIL
+ self.assertEqual(
+ self.get(url='/?foo=Bar')['status'],
+ 404,
+ 'match arguments case sensitive 2',
+ ) # FAIL
+ self.assertEqual(
+ self.get(url='/?foo=bar1')['status'],
+ 404,
+ 'match arguments exact',
+ )
+ self.assertEqual(
+ self.get(url='/?1foo=bar')['status'],
+ 404,
+ 'match arguments exact 2',
+ )
+
+ def test_routes_match_arguments_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"arguments": {}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments empty configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match arguments empty')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"arguments": []},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments empty configure 2',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match arguments empty 2')
+
+ def test_routes_match_arguments_invalid(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"arguments": ["var"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments invalid',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"arguments": [{"var1": {}}]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments invalid 2',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": {
+ "": "bar"
+ }
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments invalid 3',
+ )
+
+ @unittest.skip('not yet')
+ def test_routes_match_arguments_space(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": {
+ "foo": "bar "
+ }
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments space configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=bar &')['status'],
+ 200,
+ 'match arguments space',
+ )
+ self.assertEqual(
+ self.get(url='/?foo=bar+&')['status'],
+ 200,
+ 'match arguments space 2',
+ ) # FAIL
+ self.assertEqual(
+ self.get(url='/?foo=bar%20&')['status'],
+ 200,
+ 'match arguments space 3',
+ ) # FAIL
+
+ @unittest.skip('not yet')
+ def test_routes_match_arguments_plus(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": [
+ {"foo": "bar+"}
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments plus configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=bar+&')['status'],
+ 200,
+ 'match arguments plus',
+ )
+ self.assertEqual(
+ self.get(url='/?foo=bar%2B&')['status'],
+ 200,
+ 'match arguments plus 2',
+ ) # FAIL
+
+ @unittest.skip('not yet')
+ def test_routes_match_arguments_hex(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": [
+ {"foo": "bar"}
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments hex configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/?%66%6F%6f=%62%61%72&')['status'],
+ 200,
+ 'match arguments hex',
+ ) # FAIL
+
+ def test_routes_match_arguments_chars(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": {
+ "foo": "-._()[],;"
+ }
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments chars configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=-._()[],;')['status'],
+ 200,
+ 'match arguments chars',
+ )
+
+ def test_routes_match_arguments_complex(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": {
+ "foo": ""
+ }
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments complex configure',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo')['status'],
+ 200,
+ 'match arguments complex',
+ )
+ self.assertEqual(
+ self.get(url='/?blah=blah&foo=')['status'],
+ 200,
+ 'match arguments complex 2',
+ )
+ self.assertEqual(
+ self.get(url='/?&&&foo&&&')['status'],
+ 200,
+ 'match arguments complex 3',
+ )
+ self.assertEqual(
+ self.get(url='/?foo&foo=bar&foo')['status'],
+ 404,
+ 'match arguments complex 4',
+ )
+ self.assertEqual(
+ self.get(url='/?foo=&foo')['status'],
+ 200,
+ 'match arguments complex 5',
+ )
+ self.assertEqual(
+ self.get(url='/?&=&foo&==&')['status'],
+ 200,
+ 'match arguments complex 6',
+ )
+ self.assertEqual(
+ self.get(url='/?&=&bar&==&')['status'],
+ 404,
+ 'match arguments complex 7',
+ )
+
+ def test_routes_match_arguments_multiple(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": {"foo": "bar", "blah": "test"}
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments multiple configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match arguments multiple')
+
+ self.assertEqual(
+ self.get(url='/?foo=bar&blah=test')['status'],
+ 200,
+ 'match arguments multiple 2',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=bar&blah')['status'],
+ 404,
+ 'match arguments multiple 3',
+ )
+
+ def test_routes_match_arguments_multiple_rules(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"arguments": {"foo": ["bar", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments multiple rules configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 404, 'match arguments multiple rules'
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=bar')['status'],
+ 200,
+ 'match arguments multiple rules 2',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=blah')['status'],
+ 200,
+ 'match arguments multiple rules 3',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=blah&foo=bar&foo=blah')['status'],
+ 200,
+ 'match arguments multiple rules 4',
+ )
+
+ self.assertEqual(
+ self.get(url='/?foo=blah&foo=bar&foo=')['status'],
+ 404,
+ 'match arguments multiple rules 5',
+ )
+
+ def test_routes_match_arguments_array(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "arguments": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match arguments array configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match arguments array')
+ self.assertEqual(
+ self.get(url='/?var1=val123')['status'],
+ 200,
+ 'match arguments array 2',
+ )
+ self.assertEqual(
+ self.get(url='/?var2=val2')['status'],
+ 200,
+ 'match arguments array 3',
+ )
+ self.assertEqual(
+ self.get(url='/?var3=bar')['status'],
+ 200,
+ 'match arguments array 4',
+ )
+ self.assertEqual(
+ self.get(url='/?var1=bar')['status'],
+ 404,
+ 'match arguments array 5',
+ )
+ self.assertEqual(
+ self.get(url='/?var1=bar&var4=foo')['status'],
+ 200,
+ 'match arguments array 6',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/arguments/1'),
+ 'match arguments array configure 2',
+ )
+
+ self.assertEqual(
+ self.get(url='/?var2=val2')['status'],
+ 404,
+ 'match arguments array 7',
+ )
+ self.assertEqual(
+ self.get(url='/?var3=foo')['status'],
+ 200,
+ 'match arguments array 8',
+ )
+
+ def test_routes_match_cookies(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"cookies": {"foO": "bar"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookie configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match cookie')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=bar',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['foo=bar', 'blah=blah'],
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies 3',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=bar; blah=blah',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies 4',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'Foo=bar',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies case insensitive',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=Bar',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies case insensitive 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=bar1',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 404,
+ 'match cookies exact',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=bar;',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies exact 2',
+ )
+
+ def test_routes_match_cookies_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"cookies": {}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies empty configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match cookies empty')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"cookies": []},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies empty configure 2',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'match cookies empty 2')
+
+ def test_routes_match_cookies_invalid(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"cookies": ["var"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies invalid',
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ [
+ {
+ "match": {"cookies": [{"foo": {}}]},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies invalid 2',
+ )
+
+ def test_routes_match_cookies_multiple(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "cookies": {"foo": "bar", "blah": "blah"}
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies multiple configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match cookies multiple')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foo=bar; blah=blah',
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['foo=bar', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple 3',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['foo=bar; blah', 'blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 404,
+ 'match cookies multiple 4',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['foo=bar; blah=test', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 404,
+ 'match cookies multiple 5',
+ )
+
+ def test_routes_match_cookies_multiple_values(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"cookies": {"blah": "blah"}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies multiple values configure',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah', 'blah=blah', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match headers multiple values',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah', 'blah=test', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 404,
+ 'match cookies multiple values 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah; blah=', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 404,
+ 'match cookies multiple values 3',
+ )
+
+ def test_routes_match_cookies_multiple_rules(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {"cookies": {"blah": ["test", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies multiple rules configure',
+ )
+
+ self.assertEqual(
+ self.get()['status'], 404, 'match cookies multiple rules'
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'blah=test',
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple rules 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'blah=blah',
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple rules 3',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah', 'blah=test', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple rules 4',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah; blah=test', 'blah=blah'],
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple rules 5',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['blah=blah', 'blah'], # invalid cookie
+ 'Connection': 'close',
+ }
+ )['status'],
+ 200,
+ 'match cookies multiple rules 6',
+ )
+
+ def test_routes_match_cookies_array(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ [
+ {
+ "match": {
+ "cookies": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
+ ],
+ 'routes',
+ ),
+ 'match cookies array configure',
+ )
+
+ self.assertEqual(self.get()['status'], 404, 'match cookies array')
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var1=val123',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 2',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var2=val2',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 3',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var3=bar',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 4',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var3=bar;',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 5',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var1=bar',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 404,
+ 'match cookies array 6',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var1=bar; var4=foo;',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 7',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': ['var1=bar', 'var4=foo'],
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 8',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/cookies/1'),
+ 'match cookies array configure 2',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var2=val2',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 404,
+ 'match cookies array 9',
+ )
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'var3=foo',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies array 10',
+ )
+
if __name__ == '__main__':
- TestUnitRouting.main()
+ TestRouting.main()
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 262fc497..67db8a8e 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -1,53 +1,65 @@
import unittest
-import unit
+from unit.applications.lang.ruby import TestApplicationRuby
-class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
- def setUpClass():
- unit.TestUnit().check_modules('ruby')
+class TestRubyApplication(TestApplicationRuby):
+ prerequisites = ['ruby']
def test_ruby_application(self):
self.load('variables')
body = 'Test body string.'
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Custom-Header': 'blah',
- 'Connection': 'close'
- }, body=body)
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
self.assertEqual(resp['status'], 200, 'status')
headers = resp['headers']
header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header')
- self.assertEqual(headers.pop('Server-Software'), header_server,
- 'server software header')
+ self.assertEqual(
+ headers.pop('Server-Software'),
+ header_server,
+ 'server software header',
+ )
date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone')
- self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5,
- 'date header')
-
- self.assertDictEqual(headers, {
- 'Connection': 'close',
- 'Content-Length': str(len(body)),
- 'Content-Type': 'text/html',
- 'Request-Method': 'POST',
- 'Request-Uri': '/',
- 'Http-Host': 'localhost',
- 'Server-Protocol': 'HTTP/1.1',
- 'Custom-Header': 'blah',
- 'Rack-Version': '13',
- 'Rack-Url-Scheme': 'http',
- 'Rack-Multithread': 'false',
- 'Rack-Multiprocess': 'true',
- 'Rack-Run-Once': 'false',
- 'Rack-Hijack-Q': 'false',
- 'Rack-Hijack': '',
- 'Rack-Hijack-IO': ''
- }, 'headers')
+ self.assertLess(
+ abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
+ 5,
+ 'date header',
+ )
+
+ self.assertDictEqual(
+ headers,
+ {
+ 'Connection': 'close',
+ 'Content-Length': str(len(body)),
+ 'Content-Type': 'text/html',
+ 'Request-Method': 'POST',
+ 'Request-Uri': '/',
+ 'Http-Host': 'localhost',
+ 'Server-Protocol': 'HTTP/1.1',
+ 'Custom-Header': 'blah',
+ 'Rack-Version': '13',
+ 'Rack-Url-Scheme': 'http',
+ 'Rack-Multithread': 'false',
+ 'Rack-Multiprocess': 'true',
+ 'Rack-Run-Once': 'false',
+ 'Rack-Hijack-Q': 'false',
+ 'Rack-Hijack': '',
+ 'Rack-Hijack-IO': '',
+ },
+ 'headers',
+ )
self.assertEqual(resp['body'], body, 'body')
def test_ruby_application_query_string(self):
@@ -55,8 +67,11 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
resp = self.get(url='/?var1=val1&var2=val2')
- self.assertEqual(resp['headers']['Query-String'], 'var1=val1&var2=val2',
- 'Query-String header')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String header',
+ )
def test_ruby_application_query_string_empty(self):
self.load('query_string')
@@ -64,25 +79,27 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string empty')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string empty'
+ )
- @unittest.expectedFailure
def test_ruby_application_query_string_absent(self):
self.load('query_string')
resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status')
- self.assertEqual(resp['headers']['Query-String'], '',
- 'query string absent')
+ self.assertEqual(
+ resp['headers']['Query-String'], '', 'query string absent'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_server_port(self):
self.load('server_port')
- self.assertEqual(self.get()['headers']['Server-Port'], '7080',
- 'Server-Port header')
+ self.assertEqual(
+ self.get()['headers']['Server-Port'], '7080', 'Server-Port header'
+ )
def test_ruby_application_status_int(self):
self.load('status_int')
@@ -97,20 +114,29 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
def test_ruby_application_input_read_parts(self):
self.load('input_read_parts')
- self.assertEqual(self.post(body='0123456789')['body'], '012345678',
- 'input read parts')
+ self.assertEqual(
+ self.post(body='0123456789')['body'],
+ '012345678',
+ 'input read parts',
+ )
def test_ruby_application_input_read_buffer(self):
self.load('input_read_buffer')
- self.assertEqual(self.post(body='0123456789')['body'], '0123456789',
- 'input read buffer')
+ self.assertEqual(
+ self.post(body='0123456789')['body'],
+ '0123456789',
+ 'input read buffer',
+ )
def test_ruby_application_input_read_buffer_not_empty(self):
self.load('input_read_buffer_not_empty')
- self.assertEqual(self.post(body='0123456789')['body'], '0123456789',
- 'input read buffer not empty')
+ self.assertEqual(
+ self.post(body='0123456789')['body'],
+ '0123456789',
+ 'input read buffer not empty',
+ )
def test_ruby_application_input_gets(self):
self.load('input_gets')
@@ -122,8 +148,9 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
def test_ruby_application_input_gets_2(self):
self.load('input_gets')
- self.assertEqual(self.post(body='01234\n56789\n')['body'], '01234\n',
- 'input gets 2')
+ self.assertEqual(
+ self.post(body='01234\n56789\n')['body'], '01234\n', 'input gets 2'
+ )
def test_ruby_application_input_gets_all(self):
self.load('input_gets_all')
@@ -139,7 +166,7 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.assertEqual(self.post(body=body)['body'], body, 'input each')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_input_rewind(self):
self.load('input_rewind')
@@ -147,14 +174,16 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.assertEqual(self.post(body=body)['body'], body, 'input rewind')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_syntax_error(self):
- self.skip_alerts.extend([
- r'Failed to parse rack script',
- r'syntax error',
- r'new_from_string',
- r'parse_file'
- ])
+ self.skip_alerts.extend(
+ [
+ r'Failed to parse rack script',
+ r'syntax error',
+ r'new_from_string',
+ r'parse_file',
+ ]
+ )
self.load('syntax_error')
self.assertEqual(self.get()['status'], 500, 'syntax error')
@@ -167,8 +196,9 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+Error in application'),
- 'errors puts')
+ self.wait_for_record(r'\[error\].+Error in application'),
+ 'errors puts',
+ )
def test_ruby_application_errors_puts_int(self):
self.load('errors_puts_int')
@@ -178,8 +208,8 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+1234567890'),
- 'errors puts int')
+ self.wait_for_record(r'\[error\].+1234567890'), 'errors puts int'
+ )
def test_ruby_application_errors_write(self):
self.load('errors_write')
@@ -189,14 +219,14 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+Error in application'),
- 'errors write')
+ self.wait_for_record(r'\[error\].+Error in application'),
+ 'errors write',
+ )
def test_ruby_application_errors_write_to_s_custom(self):
self.load('errors_write_to_s_custom')
- self.assertEqual(self.get()['status'], 200,
- 'errors write to_s custom')
+ self.assertEqual(self.get()['status'], 200, 'errors write to_s custom')
def test_ruby_application_errors_write_int(self):
self.load('errors_write_int')
@@ -206,45 +236,47 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+1234567890'),
- 'errors write int')
+ self.wait_for_record(r'\[error\].+1234567890'), 'errors write int'
+ )
def test_ruby_application_at_exit(self):
self.load('at_exit')
self.get()
- self.conf({
- "listeners": {},
- "applications": {}
- })
+ self.conf({"listeners": {}, "applications": {}})
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+At exit called\.'), 'at exit')
+ self.wait_for_record(r'\[error\].+At exit called\.'), 'at exit'
+ )
def test_ruby_application_header_custom(self):
self.load('header_custom')
resp = self.post(body="\ntc=one,two\ntc=three,four,\n\n")
- self.assertEqual(resp['headers']['Custom-Header'],
- ['', 'tc=one,two', 'tc=three,four,', '', ''], 'header custom')
+ self.assertEqual(
+ resp['headers']['Custom-Header'],
+ ['', 'tc=one,two', 'tc=three,four,', '', ''],
+ 'header custom',
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_header_custom_non_printable(self):
self.load('header_custom')
- self.assertEqual(self.post(body='\b')['status'], 500,
- 'header custom non printable')
+ self.assertEqual(
+ self.post(body='\b')['status'], 500, 'header custom non printable'
+ )
def test_ruby_application_header_status(self):
self.load('header_status')
self.assertEqual(self.get()['status'], 200, 'header status')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_header_rack(self):
self.load('header_rack')
@@ -267,7 +299,7 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.assertEqual(self.post(body=body)['body'], body, 'body large')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_ruby_application_body_each_error(self):
self.load('body_each_error')
@@ -276,8 +308,9 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.stop()
self.assertIsNotNone(
- self.search_in_log(r'\[error\].+Failed to run ruby script'),
- 'body each error')
+ self.wait_for_record(r'\[error\].+Failed to run ruby script'),
+ 'body each error',
+ )
def test_ruby_application_body_file(self):
self.load('body_file')
@@ -287,21 +320,33 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
def test_ruby_keepalive_body(self):
self.load('mirror')
- (resp, sock) = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789' * 500)
+ self.assertEqual(self.get()['status'], 200, 'init')
+
+ (resp, sock) = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789' * 500,
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1')
- resp = self.post(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
+
if __name__ == '__main__':
- TestUnitRubyApplication.main()
+ TestRubyApplication.main()
diff --git a/test/test_settings.py b/test/test_settings.py
index 13bfad49..98063440 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -1,47 +1,72 @@
import time
import socket
import unittest
-import unit
+from unit.applications.lang.python import TestApplicationPython
-class TestUnitSettings(unit.TestUnitApplicationPython):
- def setUpClass():
- unit.TestUnit().check_modules('python')
+class TestSettings(TestApplicationPython):
+ prerequisites = ['python']
def test_settings_header_read_timeout(self):
self.load('empty')
- self.conf({'http': { 'header_read_timeout': 2 }}, 'settings')
+ self.conf({'http': {'header_read_timeout': 2}}, 'settings')
- (resp, sock) = self.http(b"""GET / HTTP/1.1
-""", start=True, read_timeout=1, raw=True)
+ (resp, sock) = self.http(
+ b"""GET / HTTP/1.1
+""",
+ start=True,
+ read_timeout=1,
+ raw=True,
+ )
time.sleep(3)
- resp = self.http(b"""Host: localhost
+ resp = self.http(
+ b"""Host: localhost
Connection: close
-""", sock=sock, raw=True)
+""",
+ sock=sock,
+ raw=True,
+ )
self.assertEqual(resp['status'], 408, 'status header read timeout')
def test_settings_header_read_timeout_update(self):
self.load('empty')
- self.conf({'http': { 'header_read_timeout': 4 }}, 'settings')
+ self.conf({'http': {'header_read_timeout': 4}}, 'settings')
- (resp, sock) = self.http(b"""GET / HTTP/1.1
-""", start=True, read_timeout=1, raw=True, no_recv=True)
+ (resp, sock) = self.http(
+ b"""GET / HTTP/1.1
+""",
+ start=True,
+ raw=True,
+ no_recv=True,
+ )
time.sleep(2)
- (resp, sock) = self.http(b"""Host: localhost
-""", start=True, sock=sock, read_timeout=1, raw=True, no_recv=True)
+ (resp, sock) = self.http(
+ b"""Host: localhost
+""",
+ start=True,
+ sock=sock,
+ raw=True,
+ no_recv=True,
+ )
time.sleep(2)
- (resp, sock) = self.http(b"""X-Blah: blah
-""", start=True, sock=sock, read_timeout=1, raw=True)
+ (resp, sock) = self.http(
+ b"""X-Blah: blah
+""",
+ start=True,
+ sock=sock,
+ read_timeout=1,
+ raw=True,
+ )
if len(resp) != 0:
sock.close()
@@ -49,24 +74,35 @@ Connection: close
else:
time.sleep(2)
- resp = self.http(b"""Connection: close
+ resp = self.http(
+ b"""Connection: close
-""", sock=sock, raw=True)
+""",
+ sock=sock,
+ raw=True,
+ )
- self.assertEqual(resp['status'], 408,
- 'status header read timeout update')
+ self.assertEqual(
+ resp['status'], 408, 'status header read timeout update'
+ )
def test_settings_body_read_timeout(self):
self.load('empty')
- self.conf({'http': { 'body_read_timeout': 2 }}, 'settings')
+ self.conf({'http': {'body_read_timeout': 2}}, 'settings')
- (resp, sock) = self.http(b"""POST / HTTP/1.1
+ (resp, sock) = self.http(
+ b"""POST / HTTP/1.1
Host: localhost
Content-Length: 10
Connection: close
-""", start=True, raw_resp=True, read_timeout=1, raw=True)
+""",
+ start=True,
+ raw_resp=True,
+ read_timeout=1,
+ raw=True,
+ )
time.sleep(3)
@@ -77,37 +113,46 @@ Connection: close
def test_settings_body_read_timeout_update(self):
self.load('empty')
- self.conf({'http': { 'body_read_timeout': 4 }}, 'settings')
+ self.conf({'http': {'body_read_timeout': 4}}, 'settings')
- (resp, sock) = self.http(b"""POST / HTTP/1.1
+ (resp, sock) = self.http(
+ b"""POST / HTTP/1.1
Host: localhost
Content-Length: 10
Connection: close
-""", start=True, read_timeout=1, raw=True)
+""",
+ start=True,
+ read_timeout=1,
+ raw=True,
+ )
time.sleep(2)
- (resp, sock) = self.http(b"""012""", start=True, sock=sock,
- read_timeout=1, raw=True)
+ (resp, sock) = self.http(
+ b"""012""", start=True, sock=sock, read_timeout=1, raw=True
+ )
time.sleep(2)
- (resp, sock) = self.http(b"""345""", start=True, sock=sock,
- read_timeout=1, raw=True)
+ (resp, sock) = self.http(
+ b"""345""", start=True, sock=sock, read_timeout=1, raw=True
+ )
time.sleep(2)
resp = self.http(b"""6789""", sock=sock, raw=True)
- self.assertEqual(resp['status'], 200, 'status body read timeout update')
+ self.assertEqual(
+ resp['status'], 200, 'status body read timeout update'
+ )
def test_settings_send_timeout(self):
self.load('mirror')
data_len = 1048576
- self.conf({'http': { 'send_timeout': 1 }}, 'settings')
+ self.conf({'http': {'send_timeout': 1}}, 'settings')
addr = self.testdir + '/sock'
@@ -122,7 +167,9 @@ Content-Type: text/html
Content-Length: %d
Connection: close
-""" % data_len + ('X' * data_len)
+""" % data_len + (
+ 'X' * data_len
+ )
sock.sendall(req.encode())
@@ -140,35 +187,42 @@ Connection: close
def test_settings_idle_timeout(self):
self.load('empty')
- self.conf({'http': { 'idle_timeout': 2 }}, 'settings')
+ self.assertEqual(self.get()['status'], 200, 'init')
- (resp, sock) = self.get(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive'
- }, start=True, read_timeout=1)
+ self.conf({'http': {'idle_timeout': 2}}, 'settings')
+
+ (resp, sock) = self.get(
+ headers={'Host': 'localhost', 'Connection': 'keep-alive'},
+ start=True,
+ read_timeout=1,
+ )
time.sleep(3)
- resp = self.get(headers={
- 'Host': 'localhost',
- 'Connection': 'close'
- }, sock=sock)
+ resp = self.get(
+ headers={'Host': 'localhost', 'Connection': 'close'}, sock=sock
+ )
self.assertEqual(resp['status'], 408, 'status idle timeout')
def test_settings_max_body_size(self):
self.load('empty')
- self.conf({'http': { 'max_body_size': 5 }}, 'settings')
+ self.conf({'http': {'max_body_size': 5}}, 'settings')
self.assertEqual(self.post(body='01234')['status'], 200, 'status size')
- self.assertEqual(self.post(body='012345')['status'], 413,
- 'status size max')
+ self.assertEqual(
+ self.post(body='012345')['status'], 413, 'status size max'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_settings_negative_value(self):
- self.assertIn('error', self.conf({'http': { 'max_body_size': -1 }},
- 'settings'), 'settings negative value')
+ self.assertIn(
+ 'error',
+ self.conf({'http': {'max_body_size': -1}}, 'settings'),
+ 'settings negative value',
+ )
+
if __name__ == '__main__':
- TestUnitSettings.main()
+ TestSettings.main()
diff --git a/test/test_tls.py b/test/test_tls.py
index 2131bf30..f055aa24 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -3,40 +3,32 @@ import ssl
import time
import subprocess
import unittest
-import unit
+from unit.applications.tls import TestApplicationTLS
-class TestUnitTLS(unit.TestUnitApplicationTLS):
- def setUpClass():
- unit.TestUnit().check_modules('python', 'openssl')
+class TestTLS(TestApplicationTLS):
+ prerequisites = ['python', 'openssl']
def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read())
- def wait_for_record(self, pattern):
- for i in range(50):
- with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
- if re.search(pattern, f.read()) is not None:
- break
-
- time.sleep(0.1)
-
def openssl_date_to_sec_epoch(self, date):
return self.date_to_sec_epoch(date, '%b %d %H:%M:%S %Y %Z')
def add_tls(self, application='empty', cert='default', port=7080):
- self.conf({
- "application": application,
- "tls": {
- "certificate": cert
- }
- }, 'listeners/*:' + str(port))
+ self.conf(
+ {
+ "pass": "applications/" + application,
+ "tls": {"certificate": cert}
+ },
+ 'listeners/*:' + str(port),
+ )
def remove_tls(self, application='empty', port=7080):
- self.conf({
- "application": application
- }, 'listeners/*:' + str(port))
+ self.conf(
+ {"pass": "applications/" + application}, 'listeners/*:' + str(port)
+ )
def test_tls_listener_option_add(self):
self.load('empty')
@@ -65,8 +57,11 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
self.certificate()
- self.assertIn('success', self.conf_delete('/certificates/default'),
- 'remove certificate')
+ self.assertIn(
+ 'success',
+ self.conf_delete('/certificates/default'),
+ 'remove certificate',
+ )
def test_tls_certificate_remove_used(self):
self.load('empty')
@@ -75,8 +70,11 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
self.add_tls()
- self.assertIn('error', self.conf_delete('/certificates/default'),
- 'remove certificate')
+ self.assertIn(
+ 'error',
+ self.conf_delete('/certificates/default'),
+ 'remove certificate',
+ )
def test_tls_certificate_remove_nonexisting(self):
self.load('empty')
@@ -85,10 +83,13 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
self.add_tls()
- self.assertIn('error', self.conf_delete('/certificates/blah'),
- 'remove nonexistings certificate')
+ self.assertIn(
+ 'error',
+ self.conf_delete('/certificates/blah'),
+ 'remove nonexistings certificate',
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_tls_certificate_update(self):
self.load('empty')
@@ -100,18 +101,20 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
self.certificate()
- self.assertNotEqual(cert_old, self.get_server_certificate(),
- 'update certificate')
+ self.assertNotEqual(
+ cert_old, self.get_server_certificate(), 'update certificate'
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_tls_certificate_key_incorrect(self):
self.load('empty')
self.certificate('first', False)
self.certificate('second', False)
- self.assertIn('error', self.certificate_load('first', 'second'),
- 'key incorrect')
+ self.assertIn(
+ 'error', self.certificate_load('first', 'second'), 'key incorrect'
+ )
def test_tls_certificate_change(self):
self.load('empty')
@@ -125,33 +128,53 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
self.add_tls(cert='new')
- self.assertNotEqual(cert_old, self.get_server_certificate(),
- 'change certificate')
+ self.assertNotEqual(
+ cert_old, self.get_server_certificate(), 'change certificate'
+ )
def test_tls_certificate_key_rsa(self):
self.load('empty')
self.certificate()
- self.assertEqual(self.conf_get('/certificates/default/key'),
- 'RSA (1024 bits)', 'certificate key rsa')
+ self.assertEqual(
+ self.conf_get('/certificates/default/key'),
+ 'RSA (1024 bits)',
+ 'certificate key rsa',
+ )
def test_tls_certificate_key_ec(self):
self.load('empty')
- subprocess.call(['openssl', 'ecparam', '-noout', '-genkey',
- '-out', self.testdir + '/ec.key',
- '-name', 'prime256v1'])
-
- subprocess.call(['openssl', 'req', '-x509', '-new',
- '-config', self.testdir + '/openssl.conf',
- '-key', self.testdir + '/ec.key', '-subj', '/CN=ec/',
- '-out', self.testdir + '/ec.crt'])
+ subprocess.call(
+ [
+ 'openssl',
+ 'ecparam',
+ '-noout',
+ '-genkey',
+ '-out', self.testdir + '/ec.key',
+ '-name', 'prime256v1',
+ ]
+ )
+
+ subprocess.call(
+ [
+ 'openssl',
+ 'req',
+ '-x509',
+ '-new',
+ '-subj', '/CN=ec/',
+ '-config', self.testdir + '/openssl.conf',
+ '-key', self.testdir + '/ec.key',
+ '-out', self.testdir + '/ec.crt',
+ ]
+ )
self.certificate_load('ec')
- self.assertEqual(self.conf_get('/certificates/ec/key'), 'ECDH',
- 'certificate key ec')
+ self.assertEqual(
+ self.conf_get('/certificates/ec/key'), 'ECDH', 'certificate key ec'
+ )
def test_tls_certificate_chain_options(self):
self.load('empty')
@@ -164,36 +187,64 @@ class TestUnitTLS(unit.TestUnitApplicationTLS):
cert = chain[0]
- self.assertEqual(cert['subject']['common_name'], 'default',
- 'certificate subject common name')
- self.assertEqual(cert['issuer']['common_name'], 'default',
- 'certificate issuer common name')
-
- self.assertLess(abs(self.sec_epoch() -
- self.openssl_date_to_sec_epoch(cert['validity']['since'])), 5,
- 'certificate validity since')
self.assertEqual(
- self.openssl_date_to_sec_epoch(cert['validity']['until']) -
- self.openssl_date_to_sec_epoch(cert['validity']['since']), 2592000,
- 'certificate validity until')
+ cert['subject']['common_name'],
+ 'default',
+ 'certificate subject common name',
+ )
+ self.assertEqual(
+ cert['issuer']['common_name'],
+ 'default',
+ 'certificate issuer common name',
+ )
+
+ self.assertLess(
+ abs(
+ self.sec_epoch()
+ - self.openssl_date_to_sec_epoch(cert['validity']['since'])
+ ),
+ 5,
+ 'certificate validity since',
+ )
+ self.assertEqual(
+ self.openssl_date_to_sec_epoch(cert['validity']['until'])
+ - self.openssl_date_to_sec_epoch(cert['validity']['since']),
+ 2592000,
+ 'certificate validity until',
+ )
def test_tls_certificate_chain(self):
self.load('empty')
self.certificate('root', False)
- subprocess.call(['openssl', 'req', '-new', '-config',
- self.testdir + '/openssl.conf', '-subj', '/CN=int/',
- '-out', self.testdir + '/int.csr',
- '-keyout', self.testdir + '/int.key'])
-
- subprocess.call(['openssl', 'req', '-new', '-config',
- self.testdir + '/openssl.conf', '-subj', '/CN=end/',
- '-out', self.testdir + '/end.csr',
- '-keyout', self.testdir + '/end.key'])
+ subprocess.call(
+ [
+ 'openssl',
+ 'req',
+ '-new',
+ '-subj', '/CN=int/',
+ '-config', self.testdir + '/openssl.conf',
+ '-out', self.testdir + '/int.csr',
+ '-keyout', self.testdir + '/int.key',
+ ]
+ )
+
+ subprocess.call(
+ [
+ 'openssl',
+ 'req',
+ '-new',
+ '-subj', '/CN=end/',
+ '-config', self.testdir + '/openssl.conf',
+ '-out', self.testdir + '/end.csr',
+ '-keyout', self.testdir + '/end.key',
+ ]
+ )
with open(self.testdir + '/ca.conf', 'w') as f:
- f.write("""[ ca ]
+ f.write(
+ """[ ca ]
default_ca = myca
[ myca ]
@@ -209,11 +260,13 @@ x509_extensions = myca_extensions
commonName = supplied
[ myca_extensions ]
-basicConstraints = critical,CA:TRUE""" % {
- 'dir': self.testdir,
- 'database': self.testdir + '/certindex',
- 'certserial': self.testdir + '/certserial'
- })
+basicConstraints = critical,CA:TRUE"""
+ % {
+ 'dir': self.testdir,
+ 'database': self.testdir + '/certindex',
+ 'certserial': self.testdir + '/certserial',
+ }
+ )
with open(self.testdir + '/certserial', 'w') as f:
f.write('1000')
@@ -221,26 +274,42 @@ basicConstraints = critical,CA:TRUE""" % {
with open(self.testdir + '/certindex', 'w') as f:
f.write('')
- subprocess.call(['openssl', 'ca', '-batch',
- '-config', self.testdir + '/ca.conf',
- '-keyfile', self.testdir + '/root.key',
- '-cert', self.testdir + '/root.crt',
- '-subj', '/CN=int/',
- '-in', self.testdir + '/int.csr',
- '-out', self.testdir + '/int.crt'])
-
- subprocess.call(['openssl', 'ca', '-batch',
- '-config', self.testdir + '/ca.conf',
- '-keyfile', self.testdir + '/int.key',
- '-cert', self.testdir + '/int.crt',
- '-subj', '/CN=end/',
- '-in', self.testdir + '/end.csr',
- '-out', self.testdir + '/end.crt'])
-
- with open(self.testdir + '/end-int.crt', 'wb') as crt, \
- open(self.testdir + '/end.crt', 'rb') as end, \
- open(self.testdir + '/int.crt', 'rb') as int:
- crt.write(end.read() + int.read())
+ subprocess.call(
+ [
+ 'openssl',
+ 'ca',
+ '-batch',
+ '-subj', '/CN=int/',
+ '-config', self.testdir + '/ca.conf',
+ '-keyfile', self.testdir + '/root.key',
+ '-cert', self.testdir + '/root.crt',
+ '-in', self.testdir + '/int.csr',
+ '-out', self.testdir + '/int.crt',
+ ]
+ )
+
+ subprocess.call(
+ [
+ 'openssl',
+ 'ca',
+ '-batch',
+ '-subj', '/CN=end/',
+ '-config', self.testdir + '/ca.conf',
+ '-keyfile', self.testdir + '/int.key',
+ '-cert', self.testdir + '/int.crt',
+ '-in', self.testdir + '/end.csr',
+ '-out', self.testdir + '/end.crt',
+ ]
+ )
+
+ crt_path = self.testdir + '/end-int.crt'
+ end_path = self.testdir + '/end.crt'
+ int_path = self.testdir + '/int.crt'
+
+ with open(crt_path, 'wb') as crt, \
+ open(end_path, 'rb') as end, \
+ open(int_path, 'rb') as int:
+ crt.write(end.read() + int.read())
self.context = ssl.create_default_context()
self.context.check_hostname = False
@@ -249,15 +318,24 @@ basicConstraints = critical,CA:TRUE""" % {
# incomplete chain
- self.assertIn('success', self.certificate_load('end', 'end'),
- 'certificate chain end upload')
+ self.assertIn(
+ 'success',
+ self.certificate_load('end', 'end'),
+ 'certificate chain end upload',
+ )
chain = self.conf_get('/certificates/end/chain')
self.assertEqual(len(chain), 1, 'certificate chain end length')
- self.assertEqual(chain[0]['subject']['common_name'], 'end',
- 'certificate chain end subject common name')
- self.assertEqual(chain[0]['issuer']['common_name'], 'int',
- 'certificate chain end issuer common name')
+ self.assertEqual(
+ chain[0]['subject']['common_name'],
+ 'end',
+ 'certificate chain end subject common name',
+ )
+ self.assertEqual(
+ chain[0]['issuer']['common_name'],
+ 'int',
+ 'certificate chain end issuer common name',
+ )
self.add_tls(cert='end')
@@ -270,153 +348,249 @@ basicConstraints = critical,CA:TRUE""" % {
# intermediate
- self.assertIn('success', self.certificate_load('int', 'int'),
- 'certificate chain int upload')
+ self.assertIn(
+ 'success',
+ self.certificate_load('int', 'int'),
+ 'certificate chain int upload',
+ )
chain = self.conf_get('/certificates/int/chain')
self.assertEqual(len(chain), 1, 'certificate chain int length')
- self.assertEqual(chain[0]['subject']['common_name'], 'int',
- 'certificate chain int subject common name')
- self.assertEqual(chain[0]['issuer']['common_name'], 'root',
- 'certificate chain int issuer common name')
+ self.assertEqual(
+ chain[0]['subject']['common_name'],
+ 'int',
+ 'certificate chain int subject common name',
+ )
+ self.assertEqual(
+ chain[0]['issuer']['common_name'],
+ 'root',
+ 'certificate chain int issuer common name',
+ )
self.add_tls(cert='int')
- self.assertEqual(self.get_ssl()['status'], 200,
- 'certificate chain intermediate')
+ self.assertEqual(
+ self.get_ssl()['status'], 200, 'certificate chain intermediate'
+ )
# intermediate server
- self.assertIn('success', self.certificate_load('end-int', 'end'),
- 'certificate chain end-int upload')
+ self.assertIn(
+ 'success',
+ self.certificate_load('end-int', 'end'),
+ 'certificate chain end-int upload',
+ )
chain = self.conf_get('/certificates/end-int/chain')
self.assertEqual(len(chain), 2, 'certificate chain end-int length')
- self.assertEqual(chain[0]['subject']['common_name'], 'end',
- 'certificate chain end-int int subject common name')
- self.assertEqual(chain[0]['issuer']['common_name'], 'int',
- 'certificate chain end-int int issuer common name')
- self.assertEqual(chain[1]['subject']['common_name'], 'int',
- 'certificate chain end-int end subject common name')
- self.assertEqual(chain[1]['issuer']['common_name'], 'root',
- 'certificate chain end-int end issuer common name')
+ self.assertEqual(
+ chain[0]['subject']['common_name'],
+ 'end',
+ 'certificate chain end-int int subject common name',
+ )
+ self.assertEqual(
+ chain[0]['issuer']['common_name'],
+ 'int',
+ 'certificate chain end-int int issuer common name',
+ )
+ self.assertEqual(
+ chain[1]['subject']['common_name'],
+ 'int',
+ 'certificate chain end-int end subject common name',
+ )
+ self.assertEqual(
+ chain[1]['issuer']['common_name'],
+ 'root',
+ 'certificate chain end-int end issuer common name',
+ )
self.add_tls(cert='end-int')
- self.assertEqual(self.get_ssl()['status'], 200,
- 'certificate chain intermediate server')
+ self.assertEqual(
+ self.get_ssl()['status'],
+ 200,
+ 'certificate chain intermediate server',
+ )
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_tls_reconfigure(self):
self.load('empty')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
self.certificate()
- (resp, sock) = self.get(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive'
- }, start=True)
+ (resp, sock) = self.get(
+ headers={'Host': 'localhost', 'Connection': 'keep-alive'},
+ start=True,
+ read_timeout=1,
+ )
self.assertEqual(resp['status'], 200, 'initial status')
self.add_tls()
- self.assertEqual(self.get(sock=sock)['status'], 200,
- 'reconfigure status')
- self.assertEqual(self.get_ssl()['status'], 200,
- 'reconfigure tls status')
+ self.assertEqual(
+ self.get(sock=sock)['status'], 200, 'reconfigure status'
+ )
+ self.assertEqual(
+ self.get_ssl()['status'], 200, 'reconfigure tls status'
+ )
def test_tls_keepalive(self):
self.load('mirror')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
self.certificate()
self.add_tls(application='mirror')
- (resp, sock) = self.post_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789')
+ (resp, sock) = self.post_ssl(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789',
+ read_timeout=1,
+ )
self.assertEqual(resp['body'], '0123456789', 'keepalive 1')
- resp = self.post_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post_ssl(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['body'], '0123456789', 'keepalive 2')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_tls_keepalive_certificate_remove(self):
self.load('empty')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
self.certificate()
self.add_tls()
- (resp, sock) = self.get_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive'
- }, start=True)
+ (resp, sock) = self.get_ssl(
+ headers={'Host': 'localhost', 'Connection': 'keep-alive'},
+ start=True,
+ read_timeout=1,
+ )
- self.conf({
- "application": "empty"
- }, 'listeners/*:7080')
+ self.conf({"pass": "applications/empty"}, 'listeners/*:7080')
self.conf_delete('/certificates/default')
try:
- resp = self.get_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'close'
- }, sock=sock)
+ resp = self.get_ssl(
+ headers={'Host': 'localhost', 'Connection': 'close'}, sock=sock
+ )
except:
resp = None
self.assertEqual(resp, None, 'keepalive remove certificate')
- @unittest.expectedFailure
+ @unittest.skip('not yet')
def test_tls_certificates_remove_all(self):
self.load('empty')
self.certificate()
- self.assertIn('success', self.conf_delete('/certificates'),
- 'remove all certificates')
+ self.assertIn(
+ 'success',
+ self.conf_delete('/certificates'),
+ 'remove all certificates',
+ )
def test_tls_application_respawn(self):
self.skip_alerts.append(r'process \d+ exited on signal 9')
self.load('mirror')
+ self.assertEqual(self.get()['status'], 200, 'init')
+
self.certificate()
self.conf('1', 'applications/mirror/processes')
self.add_tls(application='mirror')
- (resp, sock) = self.post_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/html'
- }, start=True, body='0123456789')
+ (resp, sock) = self.post_ssl(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ },
+ start=True,
+ body='0123456789',
+ read_timeout=1,
+ )
app_id = self.findall(r'(\d+)#\d+ "mirror" application started')[0]
subprocess.call(['kill', '-9', app_id])
- self.wait_for_record(re.compile(' (?!' + app_id +
- '#)(\d+)#\d+ "mirror" application started'))
+ self.wait_for_record(
+ re.compile(
+ ' (?!' + app_id + '#)(\d+)#\d+ "mirror" application started'
+ )
+ )
- resp = self.post_ssl(headers={
- 'Host': 'localhost',
- 'Connection': 'close',
- 'Content-Type': 'text/html'
- }, sock=sock, body='0123456789')
+ resp = self.post_ssl(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'Content-Type': 'text/html',
+ },
+ sock=sock,
+ body='0123456789',
+ )
self.assertEqual(resp['status'], 200, 'application respawn status')
- self.assertEqual(resp['body'], '0123456789', 'application respawn body')
+ self.assertEqual(
+ resp['body'], '0123456789', 'application respawn body'
+ )
+
+ def test_tls_url_scheme(self):
+ self.load('variables')
+
+ self.assertEqual(
+ self.post(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': '',
+ 'Connection': 'close',
+ }
+ )['headers']['Wsgi-Url-Scheme'],
+ 'http',
+ 'url scheme http',
+ )
+
+ self.certificate()
+
+ self.add_tls(application='variables')
+
+ self.assertEqual(
+ self.post_ssl(
+ headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': '',
+ 'Connection': 'close',
+ }
+ )['headers']['Wsgi-Url-Scheme'],
+ 'https',
+ 'url scheme https',
+ )
if __name__ == '__main__':
- TestUnitTLS.main()
+ TestTLS.main()
diff --git a/test/unit.py b/test/unit.py
deleted file mode 100644
index 6cca7f48..00000000
--- a/test/unit.py
+++ /dev/null
@@ -1,763 +0,0 @@
-import os
-import re
-import ssl
-import sys
-import json
-import time
-import shutil
-import socket
-import select
-import argparse
-import platform
-import tempfile
-import unittest
-import subprocess
-from multiprocessing import Process
-
-class TestUnit(unittest.TestCase):
-
- pardir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
- architecture = platform.architecture()[0]
- maxDiff = None
-
- detailed = False
- save_log = False
-
- def __init__(self, methodName='runTest'):
- super().__init__(methodName)
-
- if re.match(r'.*\/run\.py$', sys.argv[0]):
- args, rest = TestUnit._parse_args()
-
- TestUnit._set_args(args)
-
- @classmethod
- def main(cls):
- args, rest = TestUnit._parse_args()
-
- for i, arg in enumerate(rest):
- if arg[:5] == 'test_':
- rest[i] = cls.__name__ + '.' + arg
-
- sys.argv = sys.argv[:1] + rest
-
- TestUnit._set_args(args)
-
- unittest.main()
-
- 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
-
- with open(self.testdir + '/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_path_to_log()
-
- def check_modules(self, *modules):
- self._run()
-
- for i in range(50):
- with open(self.testdir + '/unit.log', 'r') as f:
- log = f.read()
- m = re.search('controller started', log)
-
- if m is None:
- time.sleep(0.1)
- else:
- break
-
- if m is None:
- self.stop()
- exit("Unit is writing log too long")
-
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- missed_module = ''
- for module in modules:
- if module == 'go':
- env = os.environ.copy()
- env['GOPATH'] = self.pardir + '/go'
-
- try:
- process = subprocess.Popen(['go', 'build', '-o',
- self.testdir + '/go/check_module',
- current_dir + '/go/empty/app.go'], env=env)
- process.communicate()
-
- m = module if process.returncode == 0 else None
-
- except:
- m = None
-
- elif module == 'node':
- if os.path.isdir(self.pardir + '/node/node_modules'):
- m = module
- else:
- m = None
-
- elif module == 'openssl':
- try:
- subprocess.check_output(['which', 'openssl'])
-
- output = subprocess.check_output([
- self.pardir + '/build/unitd', '--version'],
- stderr=subprocess.STDOUT)
-
- m = re.search('--openssl', output.decode())
-
- except:
- m = None
-
- else:
- m = re.search('module: ' + module, log)
-
- if m is None:
- missed_module = module
- break
-
- self.stop()
- self._check_alerts(log)
- shutil.rmtree(self.testdir)
-
- if missed_module:
- raise unittest.SkipTest('Unit has no ' + missed_module + ' module')
-
- def stop(self):
- if self._started:
- self._stop()
-
- def _run(self):
- self.testdir = tempfile.mkdtemp(prefix='unit-test-')
-
- os.mkdir(self.testdir + '/state')
-
- print()
-
- def _run_unit():
- subprocess.call([self.pardir + '/build/unitd',
- '--no-daemon',
- '--modules', self.pardir + '/build',
- '--state', self.testdir + '/state',
- '--pid', self.testdir + '/unit.pid',
- '--log', self.testdir + '/unit.log',
- '--control', 'unix:' + self.testdir + '/control.unit.sock'])
-
- self._p = Process(target=_run_unit)
- self._p.start()
-
- if not self.waitforfiles(self.testdir + '/unit.pid',
- self.testdir + '/unit.log', self.testdir + '/control.unit.sock'):
- exit("Could not start unit")
-
- self._started = True
-
- self.skip_alerts = [r'read signalfd\(4\) failed', r'sendmsg.+failed',
- r'recvmsg.+failed']
- self.skip_sanitizer = False
-
- def _stop(self):
- with open(self.testdir + '/unit.pid', 'r') as f:
- pid = f.read().rstrip()
-
- subprocess.call(['kill', '-s', 'QUIT', pid])
-
- for i in range(50):
- if not os.path.exists(self.testdir + '/unit.pid'):
- break
- time.sleep(0.1)
-
- if os.path.exists(self.testdir + '/unit.pid'):
- exit("Could not terminate unit")
-
- self._started = False
-
- self._p.join(timeout=1)
- self._terminate_process(self._p)
-
- def _terminate_process(self, process):
- if process.is_alive():
- process.terminate()
- process.join(timeout=5)
-
- if process.is_alive():
- exit("Could not terminate process " + process.pid)
-
- if process.exitcode:
- exit("Child process terminated with code " + str(process.exitcode))
-
- 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_path_to_log()
- self.assertFalse(alerts, 'alert(s)')
-
- if not self.skip_sanitizer:
- sanitizer_errors = re.findall('.+Sanitizer.+', log)
-
- if sanitizer_errors:
- self._print_path_to_log()
- self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
-
- if found:
- print('skipped.')
-
- def waitforfiles(self, *files):
- for i in range(50):
- wait = False
- ret = False
-
- for f in files:
- if not os.path.exists(f):
- wait = True
- break
-
- if wait:
- time.sleep(0.1)
-
- else:
- ret = True
- break
-
- return ret
-
- @staticmethod
- def _parse_args():
- parser = argparse.ArgumentParser(add_help=False)
-
- parser.add_argument('-d', '--detailed', dest='detailed',
- action='store_true', help='Detailed output for tests')
- parser.add_argument('-l', '--log', dest='save_log',
- action='store_true', help='Save unit.log after the test execution')
-
- return parser.parse_known_args()
-
- @staticmethod
- def _set_args(args):
- TestUnit.detailed = args.detailed
- TestUnit.save_log = args.save_log
-
- def _print_path_to_log(self):
- print('Path to unit.log:\n' + self.testdir + '/unit.log')
-
-class TestUnitHTTP(TestUnit):
-
- def http(self, start_str, **kwargs):
- sock_type = 'ipv4' if 'sock_type' not in kwargs else kwargs['sock_type']
- 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'
-
- headers = ({
- 'Host': 'localhost',
- 'Connection': 'close'
- } if 'headers' not in kwargs else kwargs['headers'])
-
- body = b'' if 'body' not in kwargs else kwargs['body']
- crlf = '\r\n'
-
- if 'addr' not in kwargs:
- addr = '::1' if sock_type == 'ipv6' else '127.0.0.1'
- else:
- addr = kwargs['addr']
-
- sock_types = {
- 'ipv4': socket.AF_INET,
- 'ipv6': socket.AF_INET6,
- 'unix': socket.AF_UNIX
- }
-
- if 'sock' not in kwargs:
- sock = socket.socket(sock_types[sock_type], socket.SOCK_STREAM)
-
- if sock_type == sock_types['ipv4'] or sock_type == sock_types['ipv6']:
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-
- if 'wrapper' in kwargs:
- sock = kwargs['wrapper'](sock)
-
- connect_args = addr if sock_type == 'unix' else (addr, port)
- try:
- sock.connect(connect_args)
- except ConnectionRefusedError:
- sock.close()
- return None
-
- else:
- sock = kwargs['sock']
-
- if 'raw' not in kwargs:
- req = ' '.join([start_str, url, http]) + crlf
-
- if body is not b'':
- if isinstance(body, str):
- body = body.encode()
-
- if 'Content-Length' not in headers:
- headers['Content-Length'] = len(body)
-
- for header, value in headers.items():
- if isinstance(value, list):
- for v in value:
- req += header + ': ' + str(v) + crlf
-
- else:
- req += header + ': ' + str(value) + crlf
-
- req = (req + crlf).encode() + body
-
- else:
- req = start_str
-
- sock.sendall(req)
-
- if TestUnit.detailed:
- print('>>>', req, sep='\n')
-
- resp = ''
-
- if 'no_recv' not in kwargs:
- enc = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
- read_timeout = 5 if 'read_timeout' not in kwargs else kwargs['read_timeout']
- resp = self.recvall(sock, read_timeout=read_timeout).decode(enc)
-
- if TestUnit.detailed:
- print('<<<', resp.encode('utf-8'), sep='\n')
-
- if 'raw_resp' not in kwargs:
- resp = self._resp_to_dict(resp)
-
- if 'start' not in kwargs:
- sock.close()
- return resp
-
- return (resp, sock)
-
- def delete(self, **kwargs):
- return self.http('DELETE', **kwargs)
-
- def get(self, **kwargs):
- return self.http('GET', **kwargs)
-
- def post(self, **kwargs):
- return self.http('POST', **kwargs)
-
- def put(self, **kwargs):
- return self.http('PUT', **kwargs)
-
- def recvall(self, sock, read_timeout=5, buff_size=4096):
- data = b''
- while select.select([sock], [], [], read_timeout)[0]:
- try:
- part = sock.recv(buff_size)
- except:
- break
-
- data += part
-
- if not len(part):
- break
-
- return data
-
- def _resp_to_dict(self, resp):
- m = re.search('(.*?\x0d\x0a?)\x0d\x0a?(.*)', resp, re.M | re.S)
-
- if not m:
- return {}
-
- headers_text, body = m.group(1), m.group(2)
-
- p = re.compile('(.*?)\x0d\x0a?', re.M | re.S)
- headers_lines = p.findall(headers_text)
-
- status = re.search('^HTTP\/\d\.\d\s(\d+)|$', headers_lines.pop(0)).group(1)
-
- headers = {}
- for line in headers_lines:
- m = re.search('(.*)\:\s(.*)', line)
-
- if m.group(1) not in headers:
- headers[m.group(1)] = m.group(2)
- elif isinstance(headers[m.group(1)], list):
- headers[m.group(1)].append(m.group(2))
- else:
- headers[m.group(1)] = [headers[m.group(1)], m.group(2)]
-
- return {
- 'status': int(status),
- 'headers': headers,
- 'body': body
- }
-
-class TestUnitControl(TestUnitHTTP):
-
- # TODO socket reuse
- # TODO http client
-
- def conf(self, conf, path='/config'):
- if isinstance(conf, dict) or isinstance(conf, list):
- conf = json.dumps(conf)
-
- if path[:1] != '/':
- path = '/config/' + path
-
- return json.loads(self.put(
- url=path,
- body=conf,
- sock_type='unix',
- addr=self.testdir + '/control.unit.sock'
- )['body'])
-
- def conf_get(self, path='/config'):
- if path[:1] != '/':
- path = '/config/' + path
-
- return json.loads(self.get(
- url=path,
- sock_type='unix',
- addr=self.testdir + '/control.unit.sock'
- )['body'])
-
- def conf_delete(self, path='/config'):
- if path[:1] != '/':
- path = '/config/' + path
-
- return json.loads(self.delete(
- url=path,
- sock_type='unix',
- addr=self.testdir + '/control.unit.sock'
- )['body'])
-
-class TestUnitApplicationProto(TestUnitControl):
-
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- def sec_epoch(self):
- return time.mktime(time.gmtime())
-
- def date_to_sec_epoch(self, date, template='%a, %d %b %Y %H:%M:%S %Z'):
- return time.mktime(time.strptime(date, template))
-
- def search_in_log(self, pattern):
- with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
- return re.search(pattern, f.read())
-
-class TestUnitApplicationPython(TestUnitApplicationProto):
- def load(self, script, name=None):
- if name is None:
- name = script
-
- self.conf({
- "listeners": {
- "*:7080": {
- "application": name
- }
- },
- "applications": {
- name: {
- "type": "python",
- "processes": { "spare": 0 },
- "path": self.current_dir + '/python/' + script,
- "working_directory": self.current_dir + '/python/' + script,
- "module": "wsgi"
- }
- }
- })
-
-class TestUnitApplicationRuby(TestUnitApplicationProto):
- def load(self, script, name='config.ru'):
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "type": "ruby",
- "processes": { "spare": 0 },
- "working_directory": self.current_dir + '/ruby/' + script,
- "script": self.current_dir + '/ruby/' + script + '/' + name
- }
- }
- })
-
-class TestUnitApplicationPHP(TestUnitApplicationProto):
- def load(self, script, name='index.php'):
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "type": "php",
- "processes": { "spare": 0 },
- "root": self.current_dir + '/php/' + script,
- "working_directory": self.current_dir + '/php/' + script,
- "index": name
- }
- }
- })
-
-class TestUnitApplicationGo(TestUnitApplicationProto):
- def load(self, script, name='app'):
-
- if not os.path.isdir(self.testdir + '/go'):
- os.mkdir(self.testdir + '/go')
-
- env = os.environ.copy()
- env['GOPATH'] = self.pardir + '/go'
- process = subprocess.Popen(['go', 'build', '-o',
- self.testdir + '/go/' + name,
- self.current_dir + '/go/' + script + '/' + name + '.go'],
- env=env)
- process.communicate()
-
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "type": "external",
- "processes": { "spare": 0 },
- "working_directory": self.current_dir + '/go/' + script,
- "executable": self.testdir + '/go/' + name
- }
- }
- })
-
-class TestUnitApplicationNode(TestUnitApplicationProto):
- def load(self, script, name='app.js'):
-
- # copy application
-
- shutil.copytree(self.current_dir + '/node/' + script,
- self.testdir + '/node')
-
- # link modules
-
- os.symlink(self.pardir + '/node/node_modules',
- self.testdir + '/node/node_modules')
-
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "type": "external",
- "processes": { "spare": 0 },
- "working_directory": self.testdir + '/node',
- "executable": name
- }
- }
- })
-
-class TestUnitApplicationJava(TestUnitApplicationProto):
- def load(self, script, name='app'):
-
- app_path = self.testdir + '/java'
- web_inf_path = app_path + '/WEB-INF/'
- classes_path = web_inf_path + 'classes/'
-
- script_path = self.current_dir + '/java/' + script + '/'
-
- if not os.path.isdir(app_path):
- os.makedirs(app_path)
-
- src = []
-
- for f in os.listdir(script_path):
- if f.endswith('.java'):
- src.append(script_path + f)
- continue
-
- if f.startswith('.') or f == 'Makefile':
- continue
-
- if os.path.isdir(script_path + f):
- if f == 'WEB-INF':
- continue
-
- shutil.copytree(script_path + f, app_path + '/' + f)
- continue
-
- if f == 'web.xml':
- if not os.path.isdir(web_inf_path):
- os.makedirs(web_inf_path)
-
- shutil.copy2(script_path + f, web_inf_path)
- else:
- shutil.copy2(script_path + f, app_path)
-
- if src:
- if not os.path.isdir(classes_path):
- os.makedirs(classes_path)
-
- javac = ['javac', '-encoding', 'utf-8', '-d', classes_path,
- '-classpath',
- self.pardir + '/build/tomcat-servlet-api-9.0.13.jar']
- javac.extend(src)
-
- process = subprocess.Popen(javac)
- process.communicate()
-
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "unit_jars": self.pardir + '/build',
- "type": "java",
- "processes": { "spare": 0 },
- "working_directory": script_path,
- "webapp": app_path
- }
- }
- })
-
-class TestUnitApplicationPerl(TestUnitApplicationProto):
- def load(self, script, name='psgi.pl'):
- self.conf({
- "listeners": {
- "*:7080": {
- "application": script
- }
- },
- "applications": {
- script: {
- "type": "perl",
- "processes": { "spare": 0 },
- "working_directory": self.current_dir + '/perl/' + script,
- "script": self.current_dir + '/perl/' + script + '/' + name
- }
- }
- })
-
-class TestUnitApplicationTLS(TestUnitApplicationProto):
- def __init__(self, test):
- super().__init__(test)
-
- self.context = ssl.create_default_context()
- self.context.check_hostname = False
- self.context.verify_mode = ssl.CERT_NONE
-
- def certificate(self, name='default', load=True):
- subprocess.call(['openssl', 'req', '-x509', '-new', '-config',
- self.testdir + '/openssl.conf', '-subj', '/CN=' + name + '/',
- '-out', self.testdir + '/' + name + '.crt',
- '-keyout', self.testdir + '/' + name + '.key'])
-
- if load:
- self.certificate_load(name)
-
- def certificate_load(self, crt, key=None):
- if key is None:
- key = crt
-
- with open(self.testdir + '/' + key + '.key', 'rb') as k, \
- open(self.testdir + '/' + crt + '.crt', 'rb') as c:
- return self.conf(k.read() + c.read(), '/certificates/' + crt)
-
- def get_ssl(self, **kwargs):
- return self.get(wrapper=self.context.wrap_socket,
- **kwargs)
-
- def post_ssl(self, **kwargs):
- return self.post(wrapper=self.context.wrap_socket,
- **kwargs)
-
- def get_server_certificate(self, addr=('127.0.0.1', 7080)):
-
- ssl_list = dir(ssl)
-
- if 'PROTOCOL_TLS' in ssl_list:
- ssl_version = ssl.PROTOCOL_TLS
-
- elif 'PROTOCOL_TLSv1_2' in ssl_list:
- ssl_version = ssl.PROTOCOL_TLSv1_2
-
- else:
- ssl_version = ssl.PROTOCOL_TLSv1_1
-
- return ssl.get_server_certificate(addr, ssl_version=ssl_version)
-
- def load(self, script, name=None):
- if name is None:
- name = script
-
- # create default openssl configuration
-
- with open(self.testdir + '/openssl.conf', 'w') as f:
- f.write("""[ req ]
-default_bits = 1024
-encrypt_key = no
-distinguished_name = req_distinguished_name
-[ req_distinguished_name ]""")
-
- self.conf({
- "listeners": {
- "*:7080": {
- "application": name
- }
- },
- "applications": {
- name: {
- "type": "python",
- "processes": { "spare": 0 },
- "path": self.current_dir + '/python/' + script,
- "working_directory": self.current_dir + '/python/' + script,
- "module": "wsgi"
- }
- }
- })
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/unit/__init__.py
diff --git a/test/unit/applications/__init__.py b/test/unit/applications/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/unit/applications/__init__.py
diff --git a/test/unit/applications/lang/__init__.py b/test/unit/applications/lang/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/unit/applications/lang/__init__.py
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
new file mode 100644
index 00000000..e4ab8ffa
--- /dev/null
+++ b/test/unit/applications/lang/go.py
@@ -0,0 +1,40 @@
+import os
+from subprocess import Popen
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationGo(TestApplicationProto):
+ def load(self, script, name='app'):
+
+ if not os.path.isdir(self.testdir + '/go'):
+ os.mkdir(self.testdir + '/go')
+
+ go_app_path = self.current_dir + '/go/'
+
+ env = os.environ.copy()
+ env['GOPATH'] = self.pardir + '/go'
+ process = Popen(
+ [
+ 'go',
+ 'build',
+ '-o',
+ self.testdir + '/go/' + name,
+ go_app_path + script + '/' + name + '.go',
+ ],
+ env=env,
+ )
+ process.communicate()
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "external",
+ "processes": {"spare": 0},
+ "working_directory": go_app_path + script,
+ "executable": self.testdir + '/go/' + name,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
new file mode 100644
index 00000000..c4390f15
--- /dev/null
+++ b/test/unit/applications/lang/java.py
@@ -0,0 +1,74 @@
+import os
+import shutil
+from subprocess import Popen
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationJava(TestApplicationProto):
+ def load(self, script, name='app'):
+
+ app_path = self.testdir + '/java'
+ web_inf_path = app_path + '/WEB-INF/'
+ classes_path = web_inf_path + 'classes/'
+
+ script_path = self.current_dir + '/java/' + script + '/'
+
+ if not os.path.isdir(app_path):
+ os.makedirs(app_path)
+
+ src = []
+
+ for f in os.listdir(script_path):
+ if f.endswith('.java'):
+ src.append(script_path + f)
+ continue
+
+ if f.startswith('.') or f == 'Makefile':
+ continue
+
+ if os.path.isdir(script_path + f):
+ if f == 'WEB-INF':
+ continue
+
+ shutil.copytree(script_path + f, app_path + '/' + f)
+ continue
+
+ if f == 'web.xml':
+ if not os.path.isdir(web_inf_path):
+ os.makedirs(web_inf_path)
+
+ shutil.copy2(script_path + f, web_inf_path)
+ else:
+ shutil.copy2(script_path + f, app_path)
+
+ if src:
+ if not os.path.isdir(classes_path):
+ os.makedirs(classes_path)
+
+ tomcat_jar = self.pardir + '/build/tomcat-servlet-api-9.0.13.jar'
+
+ javac = [
+ 'javac',
+ '-encoding', 'utf-8',
+ '-d', classes_path,
+ '-classpath', tomcat_jar,
+ ]
+ javac.extend(src)
+
+ process = Popen(javac)
+ process.communicate()
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "unit_jars": self.pardir + '/build',
+ "type": "java",
+ "processes": {"spare": 0},
+ "working_directory": script_path,
+ "webapp": app_path,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py
new file mode 100644
index 00000000..931c6596
--- /dev/null
+++ b/test/unit/applications/lang/node.py
@@ -0,0 +1,34 @@
+import os
+import shutil
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationNode(TestApplicationProto):
+ def load(self, script, name='app.js'):
+
+ # copy application
+
+ shutil.copytree(
+ self.current_dir + '/node/' + script, self.testdir + '/node'
+ )
+
+ # link modules
+
+ os.symlink(
+ self.pardir + '/node/node_modules',
+ self.testdir + '/node/node_modules',
+ )
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "external",
+ "processes": {"spare": 0},
+ "working_directory": self.testdir + '/node',
+ "executable": name,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/perl.py b/test/unit/applications/lang/perl.py
new file mode 100644
index 00000000..8aaf33a4
--- /dev/null
+++ b/test/unit/applications/lang/perl.py
@@ -0,0 +1,20 @@
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationPerl(TestApplicationProto):
+ def load(self, script, name='psgi.pl'):
+ script_path = self.current_dir + '/perl/' + script
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "perl",
+ "processes": {"spare": 0},
+ "working_directory": script_path,
+ "script": script_path + '/' + name,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py
new file mode 100644
index 00000000..99d84164
--- /dev/null
+++ b/test/unit/applications/lang/php.py
@@ -0,0 +1,21 @@
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationPHP(TestApplicationProto):
+ def load(self, script, name='index.php'):
+ script_path = self.current_dir + '/php/' + script
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "php",
+ "processes": {"spare": 0},
+ "root": script_path,
+ "working_directory": script_path,
+ "index": name,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
new file mode 100644
index 00000000..d1b5b839
--- /dev/null
+++ b/test/unit/applications/lang/python.py
@@ -0,0 +1,24 @@
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationPython(TestApplicationProto):
+ def load(self, script, name=None):
+ if name is None:
+ name = script
+
+ script_path = self.current_dir + '/python/' + script
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + name}},
+ "applications": {
+ name: {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": script_path,
+ "working_directory": script_path,
+ "module": "wsgi",
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
new file mode 100644
index 00000000..c2d8633e
--- /dev/null
+++ b/test/unit/applications/lang/ruby.py
@@ -0,0 +1,20 @@
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationRuby(TestApplicationProto):
+ def load(self, script, name='config.ru'):
+ script_path = self.current_dir + '/ruby/' + script
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "ruby",
+ "processes": {"spare": 0},
+ "working_directory": script_path,
+ "script": script_path + '/' + name,
+ }
+ },
+ }
+ )
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
new file mode 100644
index 00000000..4105473f
--- /dev/null
+++ b/test/unit/applications/proto.py
@@ -0,0 +1,31 @@
+import re
+import time
+from unit.control import TestControl
+
+
+class TestApplicationProto(TestControl):
+ def sec_epoch(self):
+ return time.mktime(time.gmtime())
+
+ def date_to_sec_epoch(self, date, template='%a, %d %b %Y %H:%M:%S %Z'):
+ return time.mktime(time.strptime(date, template))
+
+ def search_in_log(self, pattern, name='unit.log'):
+ with open(self.testdir + '/' + name, 'r', errors='ignore') as f:
+ return re.search(pattern, f.read())
+
+ def wait_for_record(self, pattern, name='unit.log'):
+ for i in range(50):
+ found = self.search_in_log(pattern, name)
+
+ if found is not None:
+ break
+
+ time.sleep(0.1)
+
+ return found
+
+ def _load_conf(self, conf):
+ self.assertIn(
+ 'success', self.conf(conf), 'load application configuration'
+ )
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
new file mode 100644
index 00000000..83cc1a03
--- /dev/null
+++ b/test/unit/applications/tls.py
@@ -0,0 +1,92 @@
+import ssl
+import subprocess
+from unit.applications.proto import TestApplicationProto
+
+
+class TestApplicationTLS(TestApplicationProto):
+ def __init__(self, test):
+ super().__init__(test)
+
+ self.context = ssl.create_default_context()
+ self.context.check_hostname = False
+ self.context.verify_mode = ssl.CERT_NONE
+
+ def certificate(self, name='default', load=True):
+ subprocess.call(
+ [
+ 'openssl',
+ 'req',
+ '-x509',
+ '-new',
+ '-subj', '/CN=' + name + '/',
+ '-config', self.testdir + '/openssl.conf',
+ '-out', self.testdir + '/' + name + '.crt',
+ '-keyout', self.testdir + '/' + name + '.key',
+ ]
+ )
+
+ if load:
+ self.certificate_load(name)
+
+ def certificate_load(self, crt, key=None):
+ if key is None:
+ key = crt
+
+ key_path = self.testdir + '/' + key + '.key'
+ crt_path = self.testdir + '/' + crt + '.crt'
+
+ with open(key_path, 'rb') as k, open(crt_path, 'rb') as c:
+ return self.conf(k.read() + c.read(), '/certificates/' + crt)
+
+ def get_ssl(self, **kwargs):
+ return self.get(wrapper=self.context.wrap_socket, **kwargs)
+
+ def post_ssl(self, **kwargs):
+ return self.post(wrapper=self.context.wrap_socket, **kwargs)
+
+ def get_server_certificate(self, addr=('127.0.0.1', 7080)):
+
+ ssl_list = dir(ssl)
+
+ if 'PROTOCOL_TLS' in ssl_list:
+ ssl_version = ssl.PROTOCOL_TLS
+
+ elif 'PROTOCOL_TLSv1_2' in ssl_list:
+ ssl_version = ssl.PROTOCOL_TLSv1_2
+
+ else:
+ ssl_version = ssl.PROTOCOL_TLSv1_1
+
+ return ssl.get_server_certificate(addr, ssl_version=ssl_version)
+
+ def load(self, script, name=None):
+ if name is None:
+ name = script
+
+ # create default openssl configuration
+
+ with open(self.testdir + '/openssl.conf', 'w') as f:
+ f.write(
+ """[ req ]
+default_bits = 1024
+encrypt_key = no
+distinguished_name = req_distinguished_name
+[ req_distinguished_name ]"""
+ )
+
+ script_path = self.current_dir + '/python/' + script
+
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/" + name}},
+ "applications": {
+ name: {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": script_path,
+ "working_directory": script_path,
+ "module": "wsgi",
+ }
+ },
+ }
+ )
diff --git a/test/unit/control.py b/test/unit/control.py
new file mode 100644
index 00000000..0b344ed5
--- /dev/null
+++ b/test/unit/control.py
@@ -0,0 +1,61 @@
+import json
+from unit.http import TestHTTP
+
+
+def args_handler(conf_func):
+ def args_wrapper(self, *args):
+ argcount = conf_func.__code__.co_argcount
+ url_default = '/config'
+ conf = None
+
+ if argcount == 2:
+ url = args[0] if len(args) == 1 else url_default
+
+ elif argcount == 3:
+ conf = args[0]
+
+ if isinstance(conf, dict) or isinstance(conf, list):
+ conf = json.dumps(conf)
+
+ url = args[1] if len(args) == 2 else url_default
+
+ url = url if url.startswith('/') else url_default + '/' + url
+ arguments = (self, url) if conf is None else (self, conf, url)
+
+ return json.loads(conf_func(*arguments))
+
+ return args_wrapper
+
+
+class TestControl(TestHTTP):
+
+ # TODO socket reuse
+ # TODO http client
+
+ @args_handler
+ def conf(self, conf, url):
+ return self.put(**self._get_args(url, conf))['body']
+
+ @args_handler
+ def conf_get(self, url):
+ return self.get(**self._get_args(url))['body']
+
+ @args_handler
+ def conf_delete(self, url):
+ return self.delete(**self._get_args(url))['body']
+
+ @args_handler
+ def conf_post(self, conf, url):
+ return self.post(**self._get_args(url, conf))['body']
+
+ def _get_args(self, url, conf=None):
+ args = {
+ 'url': url,
+ 'sock_type': 'unix',
+ 'addr': self.testdir + '/control.unit.sock',
+ }
+
+ if conf is not None:
+ args['body'] = conf
+
+ return args
diff --git a/test/unit/http.py b/test/unit/http.py
new file mode 100644
index 00000000..1ce86e5a
--- /dev/null
+++ b/test/unit/http.py
@@ -0,0 +1,162 @@
+import re
+import socket
+import select
+from unit.main import TestUnit
+
+
+class TestHTTP(TestUnit):
+ def http(self, start_str, **kwargs):
+ sock_type = (
+ 'ipv4' if 'sock_type' not in kwargs else kwargs['sock_type']
+ )
+ 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'
+
+ headers = (
+ {'Host': 'localhost', 'Connection': 'close'}
+ if 'headers' not in kwargs
+ else kwargs['headers']
+ )
+
+ body = b'' if 'body' not in kwargs else kwargs['body']
+ crlf = '\r\n'
+
+ if 'addr' not in kwargs:
+ addr = '::1' if sock_type == 'ipv6' else '127.0.0.1'
+ else:
+ addr = kwargs['addr']
+
+ sock_types = {
+ 'ipv4': socket.AF_INET,
+ 'ipv6': socket.AF_INET6,
+ 'unix': socket.AF_UNIX,
+ }
+
+ if 'sock' not in kwargs:
+ sock = socket.socket(sock_types[sock_type], socket.SOCK_STREAM)
+
+ if (
+ sock_type == sock_types['ipv4']
+ or sock_type == sock_types['ipv6']
+ ):
+ sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+
+ if 'wrapper' in kwargs:
+ sock = kwargs['wrapper'](sock)
+
+ connect_args = addr if sock_type == 'unix' else (addr, port)
+ try:
+ sock.connect(connect_args)
+ except ConnectionRefusedError:
+ sock.close()
+ return None
+
+ else:
+ sock = kwargs['sock']
+
+ if 'raw' not in kwargs:
+ req = ' '.join([start_str, url, http]) + crlf
+
+ if body is not b'':
+ if isinstance(body, str):
+ body = body.encode()
+
+ if 'Content-Length' not in headers:
+ headers['Content-Length'] = len(body)
+
+ for header, value in headers.items():
+ if isinstance(value, list):
+ for v in value:
+ req += header + ': ' + str(v) + crlf
+
+ else:
+ req += header + ': ' + str(value) + crlf
+
+ req = (req + crlf).encode() + body
+
+ else:
+ req = start_str
+
+ sock.sendall(req)
+
+ if TestUnit.detailed:
+ print('>>>', req, sep='\n')
+
+ resp = ''
+
+ if 'no_recv' not in kwargs:
+ enc = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
+ read_timeout = (
+ 30 if 'read_timeout' not in kwargs else kwargs['read_timeout']
+ )
+ resp = self.recvall(sock, read_timeout=read_timeout).decode(enc)
+
+ if TestUnit.detailed:
+ print('<<<', resp.encode('utf-8'), sep='\n')
+
+ if 'raw_resp' not in kwargs:
+ resp = self._resp_to_dict(resp)
+
+ if 'start' not in kwargs:
+ sock.close()
+ return resp
+
+ return (resp, sock)
+
+ def delete(self, **kwargs):
+ return self.http('DELETE', **kwargs)
+
+ def get(self, **kwargs):
+ return self.http('GET', **kwargs)
+
+ def post(self, **kwargs):
+ return self.http('POST', **kwargs)
+
+ def put(self, **kwargs):
+ return self.http('PUT', **kwargs)
+
+ def recvall(self, sock, read_timeout=30, buff_size=4096):
+ data = b''
+ while select.select([sock], [], [], read_timeout)[0]:
+ try:
+ part = sock.recv(buff_size)
+ except:
+ break
+
+ data += part
+
+ if not len(part):
+ break
+
+ return data
+
+ def _resp_to_dict(self, resp):
+ m = re.search('(.*?\x0d\x0a?)\x0d\x0a?(.*)', resp, re.M | re.S)
+
+ if not m:
+ return {}
+
+ headers_text, body = m.group(1), m.group(2)
+
+ p = re.compile('(.*?)\x0d\x0a?', re.M | re.S)
+ headers_lines = p.findall(headers_text)
+
+ status = re.search(
+ '^HTTP\/\d\.\d\s(\d+)|$', headers_lines.pop(0)
+ ).group(1)
+
+ headers = {}
+ for line in headers_lines:
+ m = re.search('(.*)\:\s(.*)', line)
+
+ if m.group(1) not in headers:
+ headers[m.group(1)] = m.group(2)
+
+ elif isinstance(headers[m.group(1)], list):
+ headers[m.group(1)].append(m.group(2))
+
+ else:
+ headers[m.group(1)] = [headers[m.group(1)], m.group(2)]
+
+ return {'status': int(status), 'headers': headers, 'body': body}
diff --git a/test/unit/main.py b/test/unit/main.py
new file mode 100644
index 00000000..49806fe7
--- /dev/null
+++ b/test/unit/main.py
@@ -0,0 +1,324 @@
+import os
+import re
+import sys
+import time
+import fcntl
+import shutil
+import argparse
+import platform
+import tempfile
+import unittest
+import subprocess
+from multiprocessing import Process
+
+
+class TestUnit(unittest.TestCase):
+
+ current_dir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), os.pardir)
+ )
+ pardir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
+ )
+ architecture = platform.architecture()[0]
+ maxDiff = None
+
+ detailed = False
+ save_log = False
+
+ def __init__(self, methodName='runTest'):
+ super().__init__(methodName)
+
+ if re.match(r'.*\/run\.py$', sys.argv[0]):
+ args, rest = TestUnit._parse_args()
+
+ TestUnit._set_args(args)
+
+ @classmethod
+ def main(cls):
+ args, rest = TestUnit._parse_args()
+
+ for i, arg in enumerate(rest):
+ if arg[:5] == 'test_':
+ rest[i] = cls.__name__ + '.' + arg
+
+ sys.argv = sys.argv[:1] + rest
+
+ TestUnit._set_args(args)
+
+ unittest.main()
+
+ @classmethod
+ def setUpClass(cls):
+ TestUnit().check_modules(*cls.prerequisites)
+
+ 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_path_to_log()
+
+ def check_modules(self, *modules):
+ self._run()
+
+ for i in range(50):
+ with open(self.testdir + '/unit.log', 'r') as f:
+ log = f.read()
+ m = re.search('controller started', log)
+
+ if m is None:
+ time.sleep(0.1)
+ else:
+ break
+
+ if m is None:
+ self.stop()
+ exit("Unit is writing log too long")
+
+ missed_module = ''
+ for module in modules:
+ if module == 'go':
+ env = os.environ.copy()
+ env['GOPATH'] = self.pardir + '/go'
+
+ try:
+ process = subprocess.Popen(
+ [
+ 'go',
+ 'build',
+ '-o',
+ self.testdir + '/go/check_module',
+ self.current_dir + '/go/empty/app.go',
+ ],
+ env=env,
+ )
+ process.communicate()
+
+ m = module if process.returncode == 0 else None
+
+ except:
+ m = None
+
+ elif module == 'node':
+ if os.path.isdir(self.pardir + '/node/node_modules'):
+ m = module
+ else:
+ m = None
+
+ elif module == 'openssl':
+ try:
+ subprocess.check_output(['which', 'openssl'])
+
+ output = subprocess.check_output(
+ [self.unitd, '--version'],
+ stderr=subprocess.STDOUT,
+ )
+
+ m = re.search('--openssl', output.decode())
+
+ except:
+ m = None
+
+ else:
+ m = re.search('module: ' + module, log)
+
+ if m is None:
+ missed_module = module
+ break
+
+ self.stop()
+ self._check_alerts(log)
+ shutil.rmtree(self.testdir)
+
+ if missed_module:
+ raise unittest.SkipTest('Unit has no ' + missed_module + ' module')
+
+ def stop(self):
+ if self._started:
+ self._stop()
+
+ def _run(self):
+ self.unitd = self.pardir + '/build/unitd'
+
+ if not os.path.isfile(self.unitd):
+ exit("Could not find unit")
+
+ self.testdir = tempfile.mkdtemp(prefix='unit-test-')
+
+ os.mkdir(self.testdir + '/state')
+
+ print()
+
+ def _run_unit():
+ subprocess.call(
+ [
+ self.unitd,
+ '--no-daemon',
+ '--modules', self.pardir + '/build',
+ '--state', self.testdir + '/state',
+ '--pid', self.testdir + '/unit.pid',
+ '--log', self.testdir + '/unit.log',
+ '--control', 'unix:' + self.testdir + '/control.unit.sock',
+ ]
+ )
+
+ self._p = Process(target=_run_unit)
+ self._p.start()
+
+ if not self.waitforfiles(
+ self.testdir + '/unit.pid',
+ self.testdir + '/unit.log',
+ self.testdir + '/control.unit.sock',
+ ):
+ exit("Could not start unit")
+
+ self._started = True
+
+ self.skip_alerts = [
+ r'read signalfd\(4\) failed',
+ r'sendmsg.+failed',
+ r'recvmsg.+failed',
+ ]
+ self.skip_sanitizer = False
+
+ def _stop(self):
+ with open(self.testdir + '/unit.pid', 'r') as f:
+ pid = f.read().rstrip()
+
+ subprocess.call(['kill', '-s', 'QUIT', pid])
+
+ for i in range(150):
+ if not os.path.exists(self.testdir + '/unit.pid'):
+ break
+ time.sleep(0.1)
+
+ if os.path.exists(self.testdir + '/unit.pid'):
+ exit("Could not terminate unit")
+
+ self._started = False
+
+ self._p.join(timeout=1)
+ self._terminate_process(self._p)
+
+ def _terminate_process(self, process):
+ if process.is_alive():
+ process.terminate()
+ process.join(timeout=5)
+
+ if process.is_alive():
+ exit("Could not terminate process " + process.pid)
+
+ if process.exitcode:
+ exit("Child process terminated with code " + str(process.exitcode))
+
+ 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_path_to_log()
+ self.assertFalse(alerts, 'alert(s)')
+
+ if not self.skip_sanitizer:
+ sanitizer_errors = re.findall('.+Sanitizer.+', log)
+
+ if sanitizer_errors:
+ self._print_path_to_log()
+ self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
+
+ if found:
+ print('skipped.')
+
+ def waitforfiles(self, *files):
+ for i in range(50):
+ wait = False
+ ret = False
+
+ for f in files:
+ if not os.path.exists(f):
+ wait = True
+ break
+
+ if wait:
+ time.sleep(0.1)
+
+ else:
+ ret = True
+ break
+
+ return ret
+
+ @staticmethod
+ def _parse_args():
+ parser = argparse.ArgumentParser(add_help=False)
+
+ parser.add_argument(
+ '-d',
+ '--detailed',
+ dest='detailed',
+ action='store_true',
+ help='Detailed output for tests',
+ )
+ parser.add_argument(
+ '-l',
+ '--log',
+ dest='save_log',
+ action='store_true',
+ help='Save unit.log after the test execution',
+ )
+
+ return parser.parse_known_args()
+
+ @staticmethod
+ def _set_args(args):
+ TestUnit.detailed = args.detailed
+ TestUnit.save_log = args.save_log
+
+ if TestUnit.detailed:
+ fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
+
+ def _print_path_to_log(self):
+ print('Path to unit.log:\n' + self.testdir + '/unit.log')
diff --git a/version b/version
index 07985001..540e9134 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.8.0
-NXT_VERNUM=10800
+NXT_VERSION=1.9.0
+NXT_VERNUM=10900