summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2018-12-20 20:25:50 +0300
committerAndrei Belov <defan@nginx.com>2018-12-20 20:25:50 +0300
commit82e12d0cdeeb707ad4b1aef91c5e90b4347b0831 (patch)
tree19ef4cf3dea340fa365af022c76543f92d1e2e0a
parentb140ac29e5571e9abaafce006932f15d86b78803 (diff)
parent4195a29fabfe65f5a28baf2405c2077e2ba3c09a (diff)
downloadunit-82e12d0cdeeb707ad4b1aef91c5e90b4347b0831.tar.gz
unit-82e12d0cdeeb707ad4b1aef91c5e90b4347b0831.tar.bz2
Merged with the default branch.
Diffstat (limited to '')
-rw-r--r--.hgtags1
-rw-r--r--CHANGES39
-rw-r--r--auto/make14
-rw-r--r--auto/modules/nodejs18
-rw-r--r--auto/modules/php29
-rw-r--r--auto/modules/ruby25
-rw-r--r--auto/summary32
-rw-r--r--docs/changes.xml104
-rw-r--r--pkg/deb/Makefile4
-rw-r--r--pkg/deb/Makefile.go2
-rw-r--r--pkg/deb/Makefile.go1102
-rw-r--r--pkg/deb/Makefile.go172
-rw-r--r--pkg/deb/Makefile.go182
-rw-r--r--pkg/deb/Makefile.go192
-rw-r--r--pkg/deb/debian.module/control-noarch.in23
-rwxr-xr-xpkg/deb/debian.module/rules-noarch.in100
-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/npm/Makefile5
-rw-r--r--pkg/rpm/Makefile.go2
-rw-r--r--src/nodejs/unit-http/binding.gyp2
-rwxr-xr-xsrc/nodejs/unit-http/http_server.js185
-rw-r--r--src/nodejs/unit-http/package.json14
-rwxr-xr-xsrc/nodejs/unit-http/socket.js15
-rw-r--r--src/nodejs/unit-http/unit.cpp104
-rw-r--r--src/nodejs/unit-http/unit.h9
-rw-r--r--src/nxt_main.h4
-rw-r--r--src/nxt_php_sapi.c132
-rw-r--r--src/nxt_python_wsgi.c44
-rw-r--r--src/nxt_unit.h2
-rw-r--r--test/node/404/404.html6
-rwxr-xr-xtest/node/404/app.js8
-rwxr-xr-xtest/node/basic/app.js6
-rwxr-xr-xtest/node/double_end/app.js6
-rwxr-xr-xtest/node/get_header_names/app.js8
-rwxr-xr-xtest/node/get_header_type/app.js7
-rwxr-xr-xtest/node/get_variables/app.js9
-rwxr-xr-xtest/node/has_header/app.js6
-rwxr-xr-xtest/node/header_name_case/app.js8
-rwxr-xr-xtest/node/header_name_valid/app.js7
-rwxr-xr-xtest/node/header_value_object/app.js6
-rwxr-xr-xtest/node/mirror/app.js12
-rwxr-xr-xtest/node/post_variables/app.js15
-rwxr-xr-xtest/node/promise_end/app.js16
-rwxr-xr-xtest/node/promise_handler/app.js18
-rwxr-xr-xtest/node/remove_header/app.js11
-rwxr-xr-xtest/node/set_header_array/app.js6
-rwxr-xr-xtest/node/status_message/app.js6
-rwxr-xr-xtest/node/update_header/app.js7
-rwxr-xr-xtest/node/variables/app.js20
-rwxr-xr-xtest/node/write_before_write_head/app.js6
-rwxr-xr-xtest/node/write_buffer/app.js6
-rwxr-xr-xtest/node/write_callback/app.js13
-rwxr-xr-xtest/node/write_multiple/app.js8
-rwxr-xr-xtest/node/write_return/app.js6
-rw-r--r--test/php/date_time/index.php4
-rw-r--r--test/php/highlight_file_exec/index.php4
-rw-r--r--test/test_access_log.py2
-rw-r--r--test/test_configuration.py2
-rw-r--r--test/test_go_application.py2
-rw-r--r--test/test_http_header.py2
-rw-r--r--test/test_node_application.py284
-rw-r--r--test/test_perl_application.py2
-rw-r--r--test/test_php_application.py112
-rw-r--r--test/test_php_basic.py2
-rw-r--r--test/test_python_application.py2
-rw-r--r--test/test_python_basic.py2
-rw-r--r--test/test_python_environment.py2
-rw-r--r--test/test_python_procman.py2
-rw-r--r--test/test_ruby_application.py2
-rw-r--r--test/test_settings.py2
-rw-r--r--test/test_tls.py2
-rw-r--r--test/unit.py83
80 files changed, 1334 insertions, 341 deletions
diff --git a/.hgtags b/.hgtags
index abd17808..e25955ff 100644
--- a/.hgtags
+++ b/.hgtags
@@ -15,3 +15,4 @@ b3dee0cc5a4edd046345511769b5cfec49044f1c 1.5
e507438883ef0044c278f1accfc7bc7f90c0ffb6 1.5-1
d411e7fdee9e03036adb652f8d9f4c45a420bdd5 1.6
01160bbced577121cb14d0b86ec1f8bb764cfab2 1.6-1
+784b45adb0fe8bdd707510f59ed18309087e5c21 1.7
diff --git a/CHANGES b/CHANGES
index 69761f50..674120ac 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,39 @@
+Changes with Unit 1.7 20 Dec 2018
+
+ *) Change: now rpath is set in Ruby module only if the library was not
+ found in default search paths; this allows to meet packaging
+ restrictions on some systems.
+
+ *) Bugfix: "disable_functions" and "disable_classes" PHP options set via
+ Control API did not work.
+
+ *) Bugfix: Promises on request data in Node.js were not triggered.
+
+ *) Bugfix: various compatibility issues with Node.js applications.
+
+ *) Bugfix: a segmentation fault occurred in Node.js module if
+ application tried to read request body after request.end() was
+ called.
+
+ *) Bugfix: a segmentation fault occurred in Node.js module if
+ application attempted to send header twice.
+
+ *) Bugfix: names of response header fields in Node.js module were
+ erroneously treated as case-sensitive.
+
+ *) Bugfix: uncatched exceptions in Node.js were not logged.
+
+ *) Bugfix: global install of Node.js module from sources was broken on
+ some systems; the bug had appeared in 1.6.
+
+ *) Bugfix: traceback for exceptions during initialization of Python
+ applications might not be logged.
+
+ *) Bugfix: PHP module build failed if PHP interpreter was built with
+ thread safety enabled.
+
+
Changes with Unit 1.6 15 Nov 2018
*) Change: "make install" now installs Node.js module as well if it was
@@ -17,7 +52,7 @@ Changes with Unit 1.6 15 Nov 2018
*) Bugfix: "freed pointer is out of pool" alerts might have appeared in
log.
- *) Bugfix: module discovery didn't work on 64-bit big-endian systems
+ *) Bugfix: module discovery did not work on 64-bit big-endian systems
like IBM/S390x.
@@ -41,7 +76,7 @@ Changes with Unit 1.5 25 Oct 2018
producing "last message send failed: Resource temporarily
unavailable" alerts in log; the bug had appeared in 1.4.
- *) Bugfix: Go applications didn't work when Unit was built with musl C
+ *) Bugfix: Go applications did not work when Unit was built with musl C
library.
diff --git a/auto/make b/auto/make
index 5bbca9e4..4f716b93 100644
--- a/auto/make
+++ b/auto/make
@@ -82,6 +82,14 @@ $NXT_BUILD_DIR/$NXT_LIB_STATIC: \$(NXT_LIB_OBJS)
$NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_STATIC \\
\$(NXT_LIB_OBJS)
+$NXT_BUILD_DIR/nxt_unit_version.h: src/nxt_main.h
+ $echo -n '#define NXT_UNIT_VERNUM ' > $NXT_BUILD_DIR/nxt_unit_version.h
+ grep 'define NXT_VERNUM' src/nxt_main.h \\
+ | sed -e 's/[^0-9]//g' >> $NXT_BUILD_DIR/nxt_unit_version.h
+
+$NXT_BUILD_DIR/src/nxt_unit.o: $NXT_BUILD_DIR/nxt_unit_version.h
+$NXT_BUILD_DIR/src/nxt_lib.o: $NXT_BUILD_DIR/nxt_unit_version.h
+
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC: \$(NXT_LIB_UNIT_OBJS)
$NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \\
\$(NXT_LIB_UNIT_OBJS)
@@ -288,7 +296,8 @@ libunit-install: $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
install -d \$(DESTDIR)$NXT_INCDIR
install -p -m u=rw,go=r src/nxt_unit.h src/nxt_unit_field.h \
src/nxt_unit_request.h src/nxt_unit_response.h src/nxt_unit_sptr.h \
- src/nxt_unit_typedefs.h \$(DESTDIR)$NXT_INCDIR/
+ src/nxt_unit_typedefs.h $NXT_BUILD_DIR/nxt_unit_version.h \
+ \$(DESTDIR)$NXT_INCDIR/
libunit-uninstall:
rm -f \$(DESTDIR)$NXT_LIBDIR/$NXT_LIB_UNIT_STATIC
@@ -298,7 +307,8 @@ libunit-uninstall:
\$(DESTDIR)$NXT_INCDIR/nxt_unit_request.h \
\$(DESTDIR)$NXT_INCDIR/nxt_unit_response.h \
\$(DESTDIR)$NXT_INCDIR/nxt_unit_sptr.h \
- \$(DESTDIR)$NXT_INCDIR/nxt_unit_typedefs.h
+ \$(DESTDIR)$NXT_INCDIR/nxt_unit_typedefs.h \
+ \$(DESTDIR)$NXT_INCDIR/nxt_unit_version.h
@rmdir -p \$(DESTDIR)$NXT_INCDIR 2>/dev/null || true
END
diff --git a/auto/modules/nodejs b/auto/modules/nodejs
index 443ee9d5..e0208f5d 100644
--- a/auto/modules/nodejs
+++ b/auto/modules/nodejs
@@ -123,8 +123,10 @@ fi
NXT_NODE_TMP=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http
NXT_NODE_TARBALL=${NXT_BUILD_DIR}/${NXT_NODE}-unit-http.tar.gz
-NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src && \
- export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a"
+NXT_NODE_VERSION_FILE=${NXT_NODE_TMP}/version.h
+NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src \
+ && export UNIT_BUILD_PATH=${PWD}/${NXT_BUILD_DIR} \
+ && export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a"
if [ -n "$NXT_NODE_LOCAL" ]; then
NXT_NODE_INSTALL=local-install
@@ -148,9 +150,15 @@ ${NXT_NODE}: ${NXT_NODE}-copy $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
${NXT_NODE_EXPORTS} && \\
cd ${NXT_NODE_TMP} && ${NXT_NODE_GYP} configure build clean
-${NXT_NODE}-copy:
+${NXT_NODE}-copy: ${NXT_NODE_VERSION_FILE}
mkdir -p ${NXT_BUILD_DIR}/src/
- cp -rp src/nodejs/ ${NXT_BUILD_DIR}/src/${NXT_NODE}
+ cp -rp src/nodejs/* ${NXT_BUILD_DIR}/src/${NXT_NODE}
+
+${NXT_NODE_VERSION_FILE}: src/nxt_main.h
+ mkdir -p ${NXT_NODE_TMP}
+ $echo -n '#define NXT_NODE_VERNUM ' > $NXT_NODE_VERSION_FILE
+ grep 'define NXT_VERNUM' src/nxt_main.h \\
+ | sed -e 's/[^0-9]//g' >> $NXT_NODE_VERSION_FILE
${NXT_NODE_TARBALL}: ${NXT_NODE}-copy
tar -zcvf ${NXT_NODE_TARBALL} -C ${NXT_NODE_TMP} .
@@ -161,7 +169,7 @@ install: ${NXT_NODE}-$NXT_NODE_INSTALL
${NXT_NODE}-install: ${NXT_NODE_TARBALL} \
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
${NXT_NODE_EXPORTS} && \\
- ${NXT_NPM} install -g ${PWD}/${NXT_NODE_TARBALL}
+ ${NXT_NPM} install -g --unsafe-perm ${PWD}/${NXT_NODE_TARBALL}
${NXT_NODE}-uninstall:
${NXT_NPM} uninstall -g unit-http
diff --git a/auto/modules/php b/auto/modules/php
index 762c1621..362bbc69 100644
--- a/auto/modules/php
+++ b/auto/modules/php
@@ -111,7 +111,7 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then
#include <php_main.h>
int main() {
- php_request_startup();
+ php_module_startup(NULL, NULL, 0);
return 0;
}"
@@ -124,6 +124,30 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then
exit 1;
fi
+ # Bug #71041 (https://bugs.php.net/bug.php?id=71041).
+
+ nxt_feature="PHP zend_signal_startup()"
+ nxt_feature_name=""
+ nxt_feature_run=no
+ nxt_feature_incs="${NXT_PHP_INCLUDE}"
+ nxt_feature_libs="${NXT_PHP_LIB} ${NXT_PHP_LDFLAGS}"
+ nxt_feature_test="
+ #include <php.h>
+ #include <php_main.h>
+
+ int main() {
+ zend_signal_startup();
+ return 0;
+ }"
+
+ . auto/feature
+
+ if [ $nxt_found = yes ]; then
+ NXT_ZEND_SIGNAL_STARTUP=1
+ else
+ NXT_ZEND_SIGNAL_STARTUP=0
+ fi
+
else
$echo
$echo $0: error: no PHP found.
@@ -181,6 +205,7 @@ for nxt_src in $NXT_PHP_MODULE_SRCS; do
$NXT_BUILD_DIR/$nxt_obj: $nxt_src
\$(CC) -c \$(CFLAGS) \$(NXT_INCS) $NXT_PHP_INCLUDE \\
+ -DNXT_ZEND_SIGNAL_STARTUP=$NXT_ZEND_SIGNAL_STARTUP \\
$nxt_dep_flags \\
-o $NXT_BUILD_DIR/$nxt_obj $nxt_src
$nxt_dep_post
@@ -191,7 +216,7 @@ END
done
-
+
cat << END >> $NXT_MAKEFILE
.PHONY: ${NXT_PHP_MODULE}
diff --git a/auto/modules/ruby b/auto/modules/ruby
index 05072353..7d379f2f 100644
--- a/auto/modules/ruby
+++ b/auto/modules/ruby
@@ -62,10 +62,9 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then
NXT_RUBY_LIBNAME=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["RUBY_SO_NAME"])'`
NXT_RUBY_LIBSCONF=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["LIBS"])'`
- NXT_RUBY_LIBPATH=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["libdir"])'`
- NXT_RUBY_LIBS="-L$NXT_RUBY_LIBPATH -Wl,-rpath,${NXT_RUBY_LIBPATH} -l$NXT_RUBY_LIBNAME $NXT_RUBY_LIBSCONF"
+ NXT_RUBY_LIBS="-l$NXT_RUBY_LIBNAME $NXT_RUBY_LIBSCONF"
- nxt_feature="Ruby"
+ nxt_feature="Ruby library"
nxt_feature_name=""
nxt_feature_run=no
nxt_feature_incs="${NXT_RUBY_INCPATH}"
@@ -80,6 +79,26 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then
. auto/feature
+ if [ $nxt_found = no ]; then
+ NXT_RUBY_LIBPATH=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["libdir"])'`
+ NXT_RUBY_LIBS="-L$NXT_RUBY_LIBPATH -Wl,-rpath,${NXT_RUBY_LIBPATH} $NXT_RUBY_LIBS"
+
+ nxt_feature="Ruby library in $NXT_RUBY_LIBPATH"
+ nxt_feature_name=""
+ nxt_feature_run=no
+ nxt_feature_incs="${NXT_RUBY_INCPATH}"
+ nxt_feature_libs="${NXT_RUBY_LIBS}"
+ nxt_feature_test="
+ #include <ruby.h>
+
+ int main() {
+ ruby_init();
+ return ruby_cleanup(0);
+ }"
+
+ . auto/feature
+ fi
+
else
$echo "checking for Ruby ... not found"
fi
diff --git a/auto/summary b/auto/summary
index ce6b42db..1c9df4b1 100644
--- a/auto/summary
+++ b/auto/summary
@@ -5,25 +5,27 @@
cat << END
-Configuration summary:
+Unit configuration summary:
- unit bin directory: "$NXT_BINDIR"
- unit sbin directory: "$NXT_SBINDIR"
- unit lib directory: "$NXT_LIBDIR"
- unit include directory: "$NXT_INCDIR"
- unit modules directory: "$NXT_MODULES"
- unit state directory: "$NXT_STATE"
+ bin directory: ............. "$NXT_BINDIR"
+ sbin directory: ............ "$NXT_SBINDIR"
+ lib directory: ............. "$NXT_LIBDIR"
+ include directory: ......... "$NXT_INCDIR"
+ modules directory: ......... "$NXT_MODULES"
+ state directory: ........... "$NXT_STATE"
- unit pid file: "$NXT_PID"
- unit log file: "$NXT_LOG"
+ pid file: .................. "$NXT_PID"
+ log file: .................. "$NXT_LOG"
- unit control API socket: "$NXT_CONTROL"
+ control API socket: ........ "$NXT_CONTROL"
- non-privileged user: "$NXT_USER"
- non-privileged group: "$NXT_GROUP"
+ non-privileged user: ....... "$NXT_USER"
+ non-privileged group: ...... "$NXT_GROUP"
- IPv6 support: $NXT_INET6
- Unix domain sockets support: $NXT_UNIX_DOMAIN
- debug logging: $NXT_DEBUG
+ IPv6 support: .............. $NXT_INET6
+ Unix domain sockets support: $NXT_UNIX_DOMAIN
+ TLS support: ............... $NXT_OPENSSL
+
+ debug logging: ............. $NXT_DEBUG
END
diff --git a/docs/changes.xml b/docs/changes.xml
index b3bc33ee..7443795b 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -11,6 +11,106 @@
unit-go unit-go1.7 unit-go1.8 unit-go1.9 unit-go1.10
unit-perl
unit-ruby"
+ ver="1.7" rev="1"
+ date="2018-12-20" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.7.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.7" rev="1"
+ date="2018-12-20" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="change">
+<para>
+now rpath is set in Ruby module only if the library was not found in default
+search paths; this allows to meet packaging restrictions on some systems.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+"disable_functions" and "disable_classes" PHP options set via Control API
+did not work.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+Promises on request data in Node.js were not triggered.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+various compatibility issues with Node.js applications.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+a segmentation fault occurred in Node.js module if application tried to read
+request body after request.end() was called.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+a segmentation fault occurred in Node.js module if application attempted to
+send header twice.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+names of response header fields in Node.js module were erroneously treated as
+case-sensitive.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+uncatched exceptions in Node.js were not logged.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+global install of Node.js module from sources was broken on some systems;
+the bug had appeared in 1.6.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+traceback for exceptions during initialization of Python applications might not
+be logged.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+PHP module build failed if PHP interpreter was built with thread safety
+enabled.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6
+ unit-go unit-go1.7 unit-go1.8 unit-go1.9 unit-go1.10
+ unit-perl
+ unit-ruby"
ver="1.6" rev="1"
date="2018-11-15" time="18:00:00 +0300"
packager="Konstantin Pavlov &lt;thresh@nginx.com&gt;">
@@ -66,7 +166,7 @@ various compatibility issues with Node.js applications.
<change type="bugfix">
<para>
-module discovery didn't work on 64-bit big-endian systems like IBM/S390x.
+module discovery did not work on 64-bit big-endian systems like IBM/S390x.
</para>
</change>
@@ -138,7 +238,7 @@ the bug had appeared in 1.4.
<change type="bugfix">
<para>
-Go applications didn't work when Unit was built with musl C library.
+Go applications did not work when Unit was built with musl C library.
</para>
</change>
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index 735f283b..2dcf6f50 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -199,7 +199,7 @@ endif
prebuild=`echo "$$MODULE_PREBUILD_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \
preinstall=`echo "$$MODULE_PREINSTALL_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \
post=`echo "$$MODULE_POST_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \
- cat debian.module/$(if $(MODULE_NOARCH_$*),control-noarch.in,control.in) | sed \
+ cat debian.module/control.in | sed \
-e "s#%%NAME%%#unit-$(MODULE_SUFFIX_$*)#g" \
-e "s#%%SUMMARY%%#$(MODULE_SUMMARY_$*)#g" \
-e "s#%%CODENAME%%#$(CODENAME)#g" \
@@ -210,7 +210,7 @@ endif
-e "s#%%MODULE_BUILD_DEPENDS%%#$(MODULE_BUILD_DEPENDS_$*)#g" \
-e "s#%%MODULE_DEPENDS%%#$(MODULE_DEPENDS_$*)#g" \
> $@/$(SRCDIR)/debian/control ; \
- cat debian.module/$(if $(MODULE_NOARCH_$*),rules-noarch.in,rules.in) | sed \
+ cat debian.module/rules.in | sed \
-e "s#%%NAME%%#unit-$(MODULE_SUFFIX_$*)#g" \
-e "s#%%CODENAME%%#$(CODENAME)#g" \
-e "s#%%UNIT_VERSION%%#$(VERSION)#g" \
diff --git a/pkg/deb/Makefile.go b/pkg/deb/Makefile.go
index 2d7d6537..bbade7a3 100644
--- a/pkg/deb/Makefile.go
+++ b/pkg/deb/Makefile.go
@@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go)
MODULE_BUILD_DEPENDS_go=,golang
MODULE_DEPENDS_go=,golang
-MODULE_NOARCH_go= true
-
define MODULE_PREINSTALL_go
mkdir -p debian/unit-go/usr/share/doc/unit-go/examples/go-app
install -m 644 -p debian/unit.example-go-app debian/unit-go/usr/share/doc/unit-go/examples/go-app/let-my-people.go
diff --git a/pkg/deb/Makefile.go110 b/pkg/deb/Makefile.go110
index 863f7c90..9fb3da9d 100644
--- a/pkg/deb/Makefile.go110
+++ b/pkg/deb/Makefile.go110
@@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go110)
MODULE_BUILD_DEPENDS_go110=,golang-1.10
MODULE_DEPENDS_go110=,golang-1.10
-MODULE_NOARCH_go110= true
-
define MODULE_PREINSTALL_go110
mkdir -p debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app
install -m 644 -p debian/unit.example-go-app debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app/let-my-people.go
diff --git a/pkg/deb/Makefile.go17 b/pkg/deb/Makefile.go17
index 201b32b2..4c3cc73f 100644
--- a/pkg/deb/Makefile.go17
+++ b/pkg/deb/Makefile.go17
@@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go17)
MODULE_BUILD_DEPENDS_go17=,golang-1.7
MODULE_DEPENDS_go17=,golang-1.7
-MODULE_NOARCH_go17= true
-
define MODULE_PREINSTALL_go17
mkdir -p debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app
install -m 644 -p debian/unit.example-go-app debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app/let-my-people.go
diff --git a/pkg/deb/Makefile.go18 b/pkg/deb/Makefile.go18
index 70b155d4..a9db87e0 100644
--- a/pkg/deb/Makefile.go18
+++ b/pkg/deb/Makefile.go18
@@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go18)
MODULE_BUILD_DEPENDS_go18=,golang-1.8
MODULE_DEPENDS_go18=,golang-1.8
-MODULE_NOARCH_go18= true
-
define MODULE_PREINSTALL_go18
mkdir -p debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app
install -m 644 -p debian/unit.example-go-app debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app/let-my-people.go
diff --git a/pkg/deb/Makefile.go19 b/pkg/deb/Makefile.go19
index 9ddcc493..43611560 100644
--- a/pkg/deb/Makefile.go19
+++ b/pkg/deb/Makefile.go19
@@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go19)
MODULE_BUILD_DEPENDS_go19=,golang-1.9
MODULE_DEPENDS_go19=,golang-1.9
-MODULE_NOARCH_go19= true
-
define MODULE_PREINSTALL_go19
mkdir -p debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app
install -m 644 -p debian/unit.example-go-app debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app/let-my-people.go
diff --git a/pkg/deb/debian.module/control-noarch.in b/pkg/deb/debian.module/control-noarch.in
deleted file mode 100644
index e22bb49a..00000000
--- a/pkg/deb/debian.module/control-noarch.in
+++ /dev/null
@@ -1,23 +0,0 @@
-Source: %%NAME%%
-Section: admin
-Priority: extra
-Maintainer: Andrei Belov <defan@nginx.com>
-Build-Depends: debhelper (>= 9),
- linux-libc-dev%%MODULE_BUILD_DEPENDS%%
-Standards-Version: 3.9.5
-Homepage: https://unit.nginx.org
-
-Package: %%NAME%%
-Section: admin
-Architecture: all
-Depends: lsb-base,
- ${misc:Depends},
- unit (= %%UNIT_VERSION%%-%%UNIT_RELEASE%%~%%CODENAME%%)%%MODULE_DEPENDS%%
-Description: %%SUMMARY%%
- NGINX Unit is a runtime and delivery environment for modern distributed
- applications. It runs the application code in multiple languages
- (PHP, Python, Go, etc.), and tightly couples it with traffic delivery
- in and out of the application. Take this application server and proxy
- directly in the cloud / container environments and fully control your app
- dynamically via an API.
- This package contains %%SUMMARY%%.
diff --git a/pkg/deb/debian.module/rules-noarch.in b/pkg/deb/debian.module/rules-noarch.in
deleted file mode 100755
index d75134db..00000000
--- a/pkg/deb/debian.module/rules-noarch.in
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/make -f
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie
-export DEB_CFLAGS_MAINT_APPEND=-Wp,-D_FORTIFY_SOURCE=2 -fPIC
-DPKG_EXPORT_BUILDFLAGS = 1
-include /usr/share/dpkg/buildflags.mk
-
-BUILDDIR_unit = $(CURDIR)/debian/build-unit
-BUILDDIR_unit_debug = $(CURDIR)/debian/build-unit-debug
-INSTALLDIR = $(CURDIR)/debian/%%NAME%%
-BASEDIR = $(CURDIR)
-
-%%MODULE_DEFINITIONS%%
-
-config.env.%:
- dh_testdir
- mkdir -p $(BUILDDIR_$*)
- cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/
- cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/
- cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/
- cp -Pa $(CURDIR)/test $(BUILDDIR_$*)/
- touch $@
-
-configure.unit: config.env.unit
- cd $(BUILDDIR_unit) && \
- CFLAGS= ./configure \
- %%CONFIGURE_ARGS%% \
- --modules=/usr/lib/unit/modules \
- --cc-opt="$(CFLAGS)" && \
- ./configure %%MODULE_CONFARGS%%
- touch $@
-
-configure.unit_debug: config.env.unit_debug
- cd $(BUILDDIR_unit_debug) && \
- CFLAGS= ./configure \
- %%CONFIGURE_ARGS%% \
- --modules=/usr/lib/unit/debug-modules \
- --cc-opt="$(CFLAGS)" \
- --debug && \
- ./configure %%MODULE_CONFARGS%%
- touch $@
-
-build-arch.%: configure.%
- dh_testdir
- $(MAKE) -C $(BUILDDIR_$*) %%MODULE_MAKEARGS%%
- touch $@
-
-build-indep:
- dh_testdir
- touch $@
-
-build-arch: build-arch.unit build-arch.unit_debug
- dh_testdir
- touch $@
-
-build: build-arch build-indep
- dh_testdir
- touch $@
-
-clean:
- dh_testdir
- dh_testroot
- dh_clean
- find $(CURDIR) -maxdepth 1 -size 0 -delete
-
-install: build
- dh_testdir
- dh_testroot
- dh_prep
- dh_installdirs
- dh_installinit
- dh_installlogrotate
-%%MODULE_PREINSTALL%%
- cd $(BUILDDIR_unit) && \
- DESTDIR=$(INSTALLDIR) make %%MODULE_INSTARGS%%
- cd $(BUILDDIR_unit_debug) && \
- DESTDIR=$(INSTALLDIR) make %%MODULE_INSTARGS%%
-
-binary-indep: build install
- dh_testdir
- dh_testroot
- dh_installdocs
- dh_installchangelogs
- dh_link
- dh_compress
- dh_fixperms
- dh_installdeb
- dh_perl
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary-arch: install
-
-binary: binary-indep binary-arch
-
-.PHONY: clean binary-indep binary-arch binary install build
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
index f3663eb4..c513bb1e 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.6-1~stretch
+ENV UNIT_VERSION 1.7-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 2c6975e2..7c8b2af2 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.6-1~stretch
+ENV UNIT_VERSION 1.7-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 1cd1acfc..aecdeb41 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index 0ffff5c9..0c42a942 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.perl5.24 b/pkg/docker/Dockerfile.perl5.24
index 9cbbc645..a7c8c9dc 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.php7.0 b/pkg/docker/Dockerfile.php7.0
index 70ea8bbc..48aa472a 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
index 4cb4d5f3..fdd0bc7a 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python3.5 b/pkg/docker/Dockerfile.python3.5
index 26c54174..da6f825a 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.ruby2.3 b/pkg/docker/Dockerfile.ruby2.3
index b0d9de49..0672a8c1 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.6-1~stretch
+ENV UNIT_VERSION 1.7-1~stretch
RUN set -x \
&& apt-get update \
diff --git a/pkg/npm/Makefile b/pkg/npm/Makefile
index 2696c226..dfa9ccdc 100644
--- a/pkg/npm/Makefile
+++ b/pkg/npm/Makefile
@@ -3,7 +3,11 @@
DEFAULT_VERSION := $(shell grep 'define NXT_VERSION' ../../src/nxt_main.h \
| sed -e 's/^.*"\(.*\)".*/\1/')
+DEFAULT_VERNUM := $(shell grep 'define NXT_VERNUM' ../../src/nxt_main.h \
+ | sed -e 's/[^0-9]//g')
+
VERSION ?= $(DEFAULT_VERSION)
+VERNUM ?= $(DEFAULT_VERNUM)
NPM ?= npm
default:
@@ -11,6 +15,7 @@ default:
copy:
cp -rp ../../src/nodejs/unit-http .
+ echo '#define NXT_NODE_VERNUM ${VERNUM}' > unit-http/version.h
mv unit-http/binding_pub.gyp unit-http/binding.gyp
sed -e 's/"version"\s*:.*/"version": "${VERSION}.0",/' \
unit-http/package.json > unit-http/package.json.tmp
diff --git a/pkg/rpm/Makefile.go b/pkg/rpm/Makefile.go
index 40ebf8fb..d13e8d1a 100644
--- a/pkg/rpm/Makefile.go
+++ b/pkg/rpm/Makefile.go
@@ -26,13 +26,11 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go)
ifneq (,$(findstring $(OSVER),opensuse-leap opensuse-tumbleweed))
define MODULE_DEFINITIONS_go
BuildRequires: $(BUILD_DEPENDS_go)
-BuildArch: noarch
%define gopath /usr/share/go/contrib
endef
else
define MODULE_DEFINITIONS_go
BuildRequires: $(BUILD_DEPENDS_go)
-BuildArch: noarch
endef
endif
export MODULE_DEFINITIONS_go
diff --git a/src/nodejs/unit-http/binding.gyp b/src/nodejs/unit-http/binding.gyp
index 171c2eb7..ee09bfed 100644
--- a/src/nodejs/unit-http/binding.gyp
+++ b/src/nodejs/unit-http/binding.gyp
@@ -3,7 +3,7 @@
'target_name': "unit-http",
'sources': ["unit.cpp", "addon.cpp"],
'include_dirs': [
- "<!(echo $UNIT_SRC_PATH)"
+ "<!(echo $UNIT_SRC_PATH)", "<!(echo $UNIT_BUILD_PATH)"
],
'libraries': [
"<!(echo $UNIT_LIB_STATIC_PATH)"
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index 57163c0b..057a1f26 100755
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -47,44 +47,43 @@ ServerResponse.prototype.writeContinue = function writeContinue(cb) {
ServerResponse.prototype.writeProcessing = function writeProcessing(cb) {
};
-ServerResponse.prototype.setHeader = function setHeader(key, value) {
- if (typeof key !== 'string') {
- throw new TypeError('Key argument must be a string');
+ServerResponse.prototype.setHeader = function setHeader(name, value) {
+ if (typeof name !== 'string') {
+ throw new TypeError('Name argument must be a string');
}
- let header_key_len = Buffer.byteLength(key, 'latin1');
- let header_len = 0
- let header_count = 0;
+ let value_len = 0
+ let count = 0;
if (Array.isArray(value)) {
- header_count = value.length;
+ count = value.length;
value.forEach(function(val) {
- if (typeof val !== 'string' && typeof val !== 'number') {
- throw new TypeError('Array entries must be string or number');
- }
-
- header_len += Buffer.byteLength(val + "", 'latin1');
+ value_len += Buffer.byteLength(val + "", 'latin1');
});
} else {
- if (typeof value !== 'string' && typeof value !== 'number') {
- throw new TypeError('Value argument must be string, number, or array');
- }
+ count = 1;
+ value_len = Buffer.byteLength(value + "", 'latin1');
+ }
- header_count = 1;
- header_len = Buffer.byteLength(value + "", 'latin1');
+ let lc_name = name.toLowerCase();
+
+ if (lc_name in this.headers) {
+ this._removeHeader(lc_name);
}
- this.removeHeader(key);
+ let name_len = Buffer.byteLength(name, 'latin1');
- this.headers[key] = value;
- this.headers_len += header_len + (header_key_len * header_count);
- this.headers_count += header_count;
+ this.headers[lc_name] = [name, value];
+ this.headers_len += value_len + (name_len * count);
+ this.headers_count += count;
};
ServerResponse.prototype.getHeader = function getHeader(name) {
- return this.headers[name];
+ const entry = this.headers[name.toLowerCase()];
+
+ return entry && entry[1];
};
ServerResponse.prototype.getHeaderNames = function getHeaderNames() {
@@ -92,34 +91,57 @@ ServerResponse.prototype.getHeaderNames = function getHeaderNames() {
};
ServerResponse.prototype.getHeaders = function getHeaders() {
- return this.headers;
+ const ret = Object.create(null);
+
+ if (this.headers) {
+ const keys = Object.keys(this.headers);
+
+ for (var i = 0; i < keys.length; i++) {
+ const key = keys[i];
+
+ ret[key] = this.headers[key][1];
+ }
+ }
+
+ return ret;
};
ServerResponse.prototype.hasHeader = function hasHeader(name) {
- return name in this.headers;
+ return name.toLowerCase() in this.headers;
};
ServerResponse.prototype.removeHeader = function removeHeader(name) {
- if (!(name in this.headers)) {
- return;
+ if (typeof name !== 'string') {
+ throw new TypeError('Name argument must be a string');
}
- let name_len = Buffer.byteLength(name + "", 'latin1');
+ let lc_name = name.toLowerCase();
+
+ if (lc_name in this.headers) {
+ this._removeHeader(lc_name);
+ }
+};
- if (Array.isArray(this.headers[name])) {
- this.headers_count -= this.headers[name].length;
- this.headers_len -= this.headers[name].length * name_len;
+ServerResponse.prototype._removeHeader = function _removeHeader(lc_name) {
+ let entry = this.headers[lc_name];
+ let name_len = Buffer.byteLength(entry[0] + "", 'latin1');
+ let value = entry[1];
+
+ delete this.headers[lc_name];
+
+ if (Array.isArray(value)) {
+ this.headers_count -= value.length;
+ this.headers_len -= value.length * name_len;
- this.headers[name].forEach(function(val) {
+ value.forEach(function(val) {
this.headers_len -= Buffer.byteLength(val + "", 'latin1');
});
- } else {
- this.headers_count--;
- this.headers_len -= name_len + Buffer.byteLength(this.headers[name] + "", 'latin1');
+ return;
}
- delete this.headers[name];
+ this.headers_count--;
+ this.headers_len -= name_len + Buffer.byteLength(value + "", 'latin1');
};
ServerResponse.prototype.sendDate = function sendDate() {
@@ -136,11 +158,6 @@ ServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) {
return this;
};
-// for Express
-ServerResponse.prototype._implicitHeader = function _implicitHeader() {
- this.writeHead(this.statusCode);
-};
-
ServerResponse.prototype.writeHead = writeHead;
ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead;
@@ -178,21 +195,16 @@ function writeHead(statusCode, reason, obj) {
}
}
}
-
- unit_lib.unit_response_headers(this, statusCode, this.headers, this.headers_count, this.headers_len);
-
- this.headersSent = true;
};
ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
var contentLength = 0;
if (!this.headersSent) {
- this.writeHead(this.statusCode);
- }
+ unit_lib.unit_response_headers(this, this.statusCode, this.headers,
+ this.headers_count, this.headers_len);
- if (this.finished) {
- return this;
+ this.headersSent = true;
}
if (typeof chunk === 'function') {
@@ -220,20 +232,40 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
}
if (typeof callback === 'function') {
- callback(this);
+ /*
+ * The callback must be called only when response.write() caller
+ * completes. process.nextTick() postpones the callback execution.
+ *
+ * process.nextTick() is not technically part of the event loop.
+ * Instead, the nextTickQueue will be processed after the current
+ * operation completes, regardless of the current phase of
+ * the event loop. All callbacks passed to process.nextTick()
+ * will be resolved before the event loop continues.
+ */
+ process.nextTick(function () {
+ callback(this);
+ }.bind(this));
}
};
ServerResponse.prototype.write = function write(chunk, encoding, callback) {
+ if (this.finished) {
+ throw new Error("Write after end");
+ }
+
this._writeBody(chunk, encoding, callback);
return true;
};
ServerResponse.prototype.end = function end(chunk, encoding, callback) {
- this._writeBody(chunk, encoding, callback);
+ if (!this.finished) {
+ this._writeBody(chunk, encoding, callback);
- this.finished = true;
+ unit_lib.unit_response_end(this);
+
+ this.finished = true;
+ }
return this;
};
@@ -285,6 +317,28 @@ ServerRequest.prototype.resume = function resume() {
return [];
};
+/*
+ * The "on" method is overridden to defer reading data until user code is
+ * ready, that is (ev === "data"). This can occur after req.emit("end") is
+ * executed, since the user code can be scheduled asynchronously by Promises
+ * and so on. Passing the data is postponed by process.nextTick() until
+ * the "on" method caller completes.
+ */
+ServerRequest.prototype.on = function on(ev, fn) {
+ Server.prototype.on.call(this, ev, fn);
+
+ if (ev === "data") {
+ process.nextTick(function () {
+ if (this.server.buffer.length !== 0) {
+ this.emit("data", this.server.buffer);
+ }
+
+ }.bind(this));
+ }
+};
+
+ServerRequest.prototype.addListener = ServerRequest.prototype.on;
+
function Server(requestListener) {
EventEmitter.call(this);
@@ -317,28 +371,19 @@ Server.prototype.listen = function () {
this.unit.listen();
};
-Server.prototype.run_events = function (server, req, res) {
- /* Important!!! setImmediate starts the next iteration in Node.js loop. */
- setImmediate(function () {
- server.emit("request", req, res);
+Server.prototype.emit_events = function (server, req, res) {
+ req.server = server;
+ res.server = server;
+ req.res = res;
+ res.req = req;
- Promise.resolve().then(() => {
- let buf = server.unit._read(req.socket.req_pointer);
+ server.buffer = server.unit._read(req.socket.req_pointer);
- if (buf.length != 0) {
- req.emit("data", buf);
- }
-
- req.emit("end");
- });
+ server.emit("request", req, res);
- Promise.resolve().then(() => {
- req.emit("finish");
-
- if (res.finished) {
- unit_lib.unit_response_end(res);
- }
- });
+ process.nextTick(() => {
+ req.emit("finish");
+ req.emit("end");
});
};
diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json
index 3a15d573..13c91018 100644
--- a/src/nodejs/unit-http/package.json
+++ b/src/nodejs/unit-http/package.json
@@ -4,14 +4,15 @@
"description": "HTTP module for NGINX Unit",
"main": "http.js",
"files": [
+ "unit.h",
+ "version.h",
"addon.cpp",
- "binding.gyp",
- "http_server.js",
+ "unit.cpp",
"http.js",
+ "http_server.js",
"package.json",
"socket.js",
- "unit.cpp",
- "unit.h",
+ "binding.gyp",
"README.md"
],
"scripts": {
@@ -22,8 +23,5 @@
},
"author": "Alexander Borisov",
"license": "Apache-2.0",
- "gypfile": true,
- "dependencies": {
- "node-addon-api": "1.2.0"
- }
+ "gypfile": true
}
diff --git a/src/nodejs/unit-http/socket.js b/src/nodejs/unit-http/socket.js
index aef065bf..6e836949 100755
--- a/src/nodejs/unit-http/socket.js
+++ b/src/nodejs/unit-http/socket.js
@@ -18,10 +18,16 @@ function Socket(options) {
throw new TypeError('Options must be object');
}
- this.readable = (typeof options.readable === 'boolean' ? options.readable
- : false);
- this.writable = (typeof options.writable === 'boolean' ? options.writable
- : false);
+ if ("fd" in options) {
+ throw new TypeError('Working with file descriptors not supported');
+ }
+
+ /*
+ * For HTTP TCP socket 'readable' and 'writable' are always true.
+ * These options are required by Express and Koa frameworks.
+ */
+ this.readable = true;
+ this.writable = true;
}
util.inherits(Socket, EventEmitter);
@@ -43,7 +49,6 @@ Socket.prototype.connect = function connect(options, connectListener) {
this.once('connect', connectListener);
this.connecting = true;
- this.writable = true;
return this;
};
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index be64a59b..60b0412a 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -276,12 +276,13 @@ 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;
- napi_value global, server_obj;
- napi_value run_events, events_res;
- napi_status status;
- napi_value events_args[3];
+ 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];
obj = reinterpret_cast<Unit *>(req->unit->data);
@@ -328,11 +329,11 @@ Unit::request_handler(nxt_unit_request_info_t *req)
return;
}
- status = napi_get_named_property(obj->env_, server_obj, "run_events",
- &run_events);
+ 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"
- " 'run_events' function");
+ napi_throw_error(obj->env_, NULL, "Failed to get "
+ "'emit_events' function");
return;
}
@@ -340,15 +341,74 @@ Unit::request_handler(nxt_unit_request_info_t *req)
events_args[1] = request;
events_args[2] = response;
- status = napi_call_function(obj->env_, server_obj, run_events, 3,
- events_args, &events_res);
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ status = napi_async_destroy(obj->env_, async_context);
if (status != napi_ok) {
- napi_throw_error(obj->env_, NULL, "Failed to call"
- " 'run_events' function");
+ napi_throw_error(obj->env_, NULL, "Failed to destroy async object");
return;
}
- napi_close_handle_scope(obj->env_, scope);
+ status = napi_close_handle_scope(obj->env_, scope);
+ if (status != napi_ok) {
+ napi_throw_error(obj->env_, NULL, "Failed to close handle scope");
+ }
}
@@ -694,7 +754,7 @@ Unit::response_send_headers(napi_env env, napi_callback_info info)
uint32_t keys_count, i, j;
uint16_t hash;
napi_value this_arg, headers, keys, name, value, array_val;
- napi_value req_num;
+ napi_value req_num, array_entry;
napi_status status;
napi_valuetype val_type;
nxt_unit_field_t *f;
@@ -771,7 +831,17 @@ Unit::response_send_headers(napi_env env, napi_callback_info info)
goto failed;
}
- status = napi_get_property(env, headers, name, &value);
+ status = napi_get_property(env, headers, name, &array_entry);
+ if (status != napi_ok) {
+ goto failed;
+ }
+
+ status = napi_get_element(env, array_entry, 0, &name);
+ if (status != napi_ok) {
+ goto failed;
+ }
+
+ status = napi_get_element(env, array_entry, 1, &value);
if (status != napi_ok) {
goto failed;
}
diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h
index 5f541cc4..8baeb967 100644
--- a/src/nodejs/unit-http/unit.h
+++ b/src/nodejs/unit-http/unit.h
@@ -6,18 +6,23 @@
#ifndef _NXT_NODEJS_UNIT_H_INCLUDED_
#define _NXT_NODEJS_UNIT_H_INCLUDED_
-
#include <node_api.h>
-
#ifdef __cplusplus
extern "C" {
#endif
+#include "version.h"
#include <nxt_unit.h>
+
+#if NXT_UNIT_VERNUM != NXT_NODE_VERNUM
+#error "libunit version mismatch."
+#endif
+
#include <nxt_unit_response.h>
#include <nxt_unit_request.h>
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/nxt_main.h b/src/nxt_main.h
index 12c0ce6d..71ee6599 100644
--- a/src/nxt_main.h
+++ b/src/nxt_main.h
@@ -11,8 +11,8 @@
#include <nxt_auto_config.h>
-#define NXT_VERSION "1.6"
-#define NXT_VERNUM 10600
+#define NXT_VERSION "1.7"
+#define NXT_VERNUM 10700
#define NXT_SERVER "Unit/" NXT_VERSION
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 413764f1..8c25f82a 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -15,16 +15,6 @@
#include <nxt_unit_request.h>
-typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t;
-
-static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
-
-static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
-static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
-nxt_inline u_char *nxt_realpath(const void *c);
-
-static void nxt_php_request_handler(nxt_unit_request_info_t *req);
-
#if PHP_MAJOR_VERSION >= 7
# define NXT_PHP7 1
# if PHP_MINOR_VERSION >= 1
@@ -40,24 +30,44 @@ static void nxt_php_request_handler(nxt_unit_request_info_t *req);
# endif
#endif
+
+typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t;
+
+#ifdef NXT_PHP7
+typedef int (*nxt_php_disable_t)(char *p, size_t size);
+#else
+typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
+#endif
+
+
+static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+
+static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
+static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
+nxt_inline u_char *nxt_realpath(const void *c);
+
+static void nxt_php_request_handler(nxt_unit_request_info_t *req);
+
static int nxt_php_startup(sapi_module_struct *sapi_module);
static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
int type);
static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
int type);
-static int nxt_php_send_headers(sapi_headers_struct *sapi_headers);
-static char *nxt_php_read_cookies(void);
+static void nxt_php_disable(nxt_task_t *task, const char *type,
+ nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
+static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
+static char *nxt_php_read_cookies(TSRMLS_D);
static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
-static void nxt_php_register_variables(zval *track_vars_array);
+static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC);
#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
static void nxt_php_log_message(char *message, int syslog_type_int);
#else
-static void nxt_php_log_message(char *message);
+static void nxt_php_log_message(char *message TSRMLS_DC);
#endif
#ifdef NXT_PHP7
@@ -159,6 +169,9 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
static nxt_task_t *nxt_php_task;
+#ifdef ZTS
+static void ***tsrm_ls;
+#endif
static nxt_int_t
@@ -262,6 +275,21 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_memcpy(index->start, c->index.start, c->index.length);
}
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+#endif
+
+#if defined(NXT_PHP7) && defined(ZEND_SIGNALS)
+
+#if (NXT_ZEND_SIGNAL_STARTUP)
+ zend_signal_startup();
+#elif defined(ZTS)
+#error PHP is built with thread safety and broken signals.
+#endif
+
+#endif
+
sapi_startup(&nxt_php_sapi_module);
if (c->options != NULL) {
@@ -359,6 +387,21 @@ nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
nxt_log(task, NXT_LOG_ERR,
"setting PHP option \"%V: %V\" failed", &name, &value);
+ continue;
+ }
+
+ if (nxt_str_eq(&name, "disable_functions", 17)) {
+ nxt_php_disable(task, "function", &value,
+ &PG(disable_functions),
+ zend_disable_function);
+ continue;
+ }
+
+ if (nxt_str_eq(&name, "disable_classes", 15)) {
+ nxt_php_disable(task, "class", &value,
+ &PG(disable_classes),
+ zend_disable_class);
+ continue;
}
}
}
@@ -433,7 +476,8 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
if (ini_entry->on_modify
&& ini_entry->on_modify(ini_entry, cstr, value->length,
ini_entry->mh_arg1, ini_entry->mh_arg2,
- ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE)
+ ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE
+ TSRMLS_CC)
!= SUCCESS)
{
nxt_free(cstr);
@@ -451,6 +495,58 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
static void
+nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value,
+ char **ptr, nxt_php_disable_t disable)
+{
+ char c, *p, *start;
+
+ p = nxt_malloc(value->length + 1);
+ if (nxt_slow_path(p == NULL)) {
+ return;
+ }
+
+ /*
+ * PHP frees this memory on module shutdown.
+ * See core_globals_dtor() for details.
+ */
+ *ptr = p;
+
+ nxt_memcpy(p, value->start, value->length);
+ p[value->length] = '\0';
+
+ start = p;
+
+ do {
+ c = *p;
+
+ if (c == ' ' || c == ',' || c == '\0') {
+
+ if (p != start) {
+ *p = '\0';
+
+#ifdef NXT_PHP7
+ if (disable(start, p - start)
+#else
+ if (disable(start, p - start TSRMLS_CC)
+#endif
+ != SUCCESS)
+ {
+ nxt_log(task, NXT_LOG_ERR,
+ "PHP: failed to disable \"%s\": no such %s",
+ start, type);
+ }
+ }
+
+ start = p + 1;
+ }
+
+ p++;
+
+ } while (c != '\0');
+}
+
+
+static void
nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
{
while (str->length > 0 && str->start[str->length - 1] == t) {
@@ -573,7 +669,11 @@ nxt_php_request_handler(nxt_unit_request_info_t *req)
(char *) ctx->script.start);
}
+#if (NXT_PHP7)
if (nxt_slow_path(php_request_startup() == FAILURE)) {
+#else
+ if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) {
+#endif
nxt_unit_req_debug(req, "php_request_startup() failed");
rc = NXT_UNIT_ERROR;
@@ -915,7 +1015,7 @@ static void
nxt_php_log_message(char *message, int syslog_type_int)
#else
static void
-nxt_php_log_message(char *message)
+nxt_php_log_message(char *message TSRMLS_DC)
#endif
{
nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message);
diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c
index 3a5f1913..bd3a2cb2 100644
--- a/src/nxt_python_wsgi.c
+++ b/src/nxt_python_wsgi.c
@@ -276,7 +276,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
Py_InitializeEx(0);
- obj = NULL;
module = NULL;
if (c->path.length > 0) {
@@ -284,7 +283,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
c->path.length);
if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task, "Python failed create string object \"%V\"",
+ nxt_alert(task, "Python failed to create string object \"%V\"",
&c->path);
goto fail;
}
@@ -303,11 +302,9 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
}
Py_DECREF(obj);
- obj = NULL;
}
obj = PyCFunction_New(nxt_py_start_resp_method, NULL);
-
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task,
"Python failed to initialize the \"start_response\" function");
@@ -317,7 +314,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_py_start_resp_obj = obj;
obj = PyCFunction_New(nxt_py_write_method, NULL);
-
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to initialize the \"write\" function");
goto fail;
@@ -326,40 +322,37 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_py_write_obj = obj;
obj = nxt_python_create_environ(task);
-
- if (obj == NULL) {
+ if (nxt_slow_path(obj == NULL)) {
goto fail;
}
nxt_py_environ_ptyp = obj;
obj = Py_BuildValue("[s]", "unit");
- if (obj == NULL) {
+ if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to create the \"sys.argv\" list");
goto fail;
}
- if (PySys_SetObject((char *) "argv", obj) != 0) {
+ if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
nxt_alert(task, "Python failed to set the \"sys.argv\" list");
goto fail;
}
- Py_DECREF(obj);
+ Py_CLEAR(obj);
nxt_py_module = nxt_alloca(c->module.length + 1);
nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
nxt_py_module[c->module.length] = '\0';
module = PyImport_ImportModule(nxt_py_module);
-
if (nxt_slow_path(module == NULL)) {
nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
- PyErr_PrintEx(1);
- return NXT_ERROR;
+ PyErr_Print();
+ goto fail;
}
obj = PyDict_GetItemString(PyModule_GetDict(module), "application");
-
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to get \"application\" "
"from module \"%s\"", nxt_py_module);
@@ -369,14 +362,15 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"application\" in module \"%s\" "
"is not a callable object", nxt_py_module);
- PyErr_PrintEx(1);
+ PyErr_Print();
goto fail;
}
Py_INCREF(obj);
- Py_DECREF(module);
+ Py_CLEAR(module);
nxt_py_application = obj;
+ obj = NULL;
nxt_unit_default_init(task, &python_init);
@@ -384,7 +378,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
unit_ctx = nxt_unit_init(&python_init);
if (nxt_slow_path(unit_ctx == NULL)) {
- return NXT_ERROR;
+ goto fail;
}
rc = nxt_unit_run(unit_ctx);
@@ -402,9 +396,7 @@ fail:
Py_XDECREF(obj);
Py_XDECREF(module);
- if (nxt_py_home != NULL) {
- nxt_free(nxt_py_home);
- }
+ nxt_python_atexit();
return NXT_ERROR;
}
@@ -536,10 +528,10 @@ fail:
static void
nxt_python_atexit(void)
{
- Py_DECREF(nxt_py_application);
- Py_DECREF(nxt_py_start_resp_obj);
- Py_DECREF(nxt_py_write_obj);
- Py_DECREF(nxt_py_environ_ptyp);
+ Py_XDECREF(nxt_py_application);
+ Py_XDECREF(nxt_py_start_resp_obj);
+ Py_XDECREF(nxt_py_write_obj);
+ Py_XDECREF(nxt_py_environ_ptyp);
Py_Finalize();
@@ -804,7 +796,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
nxt_unit_req_error(ctx->req,
"Python failed to create value string \"%.*s\"",
(int) size, src);
- PyErr_PrintEx(1);
+ PyErr_Print();
return NXT_UNIT_ERROR;
}
@@ -839,7 +831,7 @@ nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name,
nxt_unit_req_error(ctx->req,
"Python failed to create value string \"%.*s\"",
(int) size, str);
- PyErr_PrintEx(1);
+ PyErr_Print();
return NXT_UNIT_ERROR;
}
diff --git a/src/nxt_unit.h b/src/nxt_unit.h
index 2806d035..a3fcc541 100644
--- a/src/nxt_unit.h
+++ b/src/nxt_unit.h
@@ -11,8 +11,10 @@
#include <sys/types.h>
#include <string.h>
+#include "nxt_unit_version.h"
#include "nxt_unit_typedefs.h"
+
enum {
NXT_UNIT_OK = 0,
NXT_UNIT_ERROR = 1,
diff --git a/test/node/404/404.html b/test/node/404/404.html
new file mode 100644
index 00000000..6d0c635a
--- /dev/null
+++ b/test/node/404/404.html
@@ -0,0 +1,6 @@
+<html>
+<head><title>404 Not Found</title></head>
+<body bgcolor="white">
+<center><h1>404 Not Found</h1></center>
+</body>
+</html>
diff --git a/test/node/404/app.js b/test/node/404/app.js
new file mode 100755
index 00000000..9600d486
--- /dev/null
+++ b/test/node/404/app.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(404, {});
+ res.end(fs.readFileSync('404.html'));
+}).listen(7080);
diff --git a/test/node/basic/app.js b/test/node/basic/app.js
new file mode 100755
index 00000000..bc8d570a
--- /dev/null
+++ b/test/node/basic/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'});
+ res.end('Hello World\n');
+}).listen(7080);
diff --git a/test/node/double_end/app.js b/test/node/double_end/app.js
new file mode 100755
index 00000000..d8280917
--- /dev/null
+++ b/test/node/double_end/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.end();
+ res.end();
+}).listen(7080);
diff --git a/test/node/get_header_names/app.js b/test/node/get_header_names/app.js
new file mode 100755
index 00000000..4cbccc16
--- /dev/null
+++ b/test/node/get_header_names/app.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('DATE', ['date1', 'date2']);
+ res.setHeader('X-Header', 'blah');
+ res.setHeader('X-Names', res.getHeaderNames());
+ res.end();
+}).listen(7080);
diff --git a/test/node/get_header_type/app.js b/test/node/get_header_type/app.js
new file mode 100755
index 00000000..b606f142
--- /dev/null
+++ b/test/node/get_header_type/app.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('X-Number', 100);
+ res.setHeader('X-Type', typeof(res.getHeader('X-Number')));
+ res.end();
+}).listen(7080);
diff --git a/test/node/get_variables/app.js b/test/node/get_variables/app.js
new file mode 100755
index 00000000..5c1faf41
--- /dev/null
+++ b/test/node/get_variables/app.js
@@ -0,0 +1,9 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ let query = require('url').parse(req.url, true).query;
+ res.setHeader('X-Var-1', query.var1);
+ res.setHeader('X-Var-2', query.var2);
+ res.setHeader('X-Var-3', query.var3);
+ res.end();
+}).listen(7080);
diff --git a/test/node/has_header/app.js b/test/node/has_header/app.js
new file mode 100755
index 00000000..040f551e
--- /dev/null
+++ b/test/node/has_header/app.js
@@ -0,0 +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.end();
+}).listen(7080);
diff --git a/test/node/header_name_case/app.js b/test/node/header_name_case/app.js
new file mode 100755
index 00000000..490bd4d5
--- /dev/null
+++ b/test/node/header_name_case/app.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('X-Header', '1');
+ res.setHeader('X-header', '2');
+ res.setHeader('X-HEADER', '3');
+ res.end();
+}).listen(7080);
diff --git a/test/node/header_name_valid/app.js b/test/node/header_name_valid/app.js
new file mode 100755
index 00000000..425f026f
--- /dev/null
+++ b/test/node/header_name_valid/app.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {});
+ res.setHeader('@$', 'test');
+ res.end();
+}).listen(7080);
diff --git a/test/node/header_value_object/app.js b/test/node/header_value_object/app.js
new file mode 100755
index 00000000..ff4e2bb0
--- /dev/null
+++ b/test/node/header_value_object/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('X-Header', {});
+ res.end();
+}).listen(7080);
diff --git a/test/node/mirror/app.js b/test/node/mirror/app.js
new file mode 100755
index 00000000..abcb87cb
--- /dev/null
+++ b/test/node/mirror/app.js
@@ -0,0 +1,12 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ let body = '';
+ req.on('data', chunk => {
+ body += chunk.toString();
+ });
+ req.on('end', () => {
+ res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
+ res.end(body);
+ });
+}).listen(7080);
diff --git a/test/node/post_variables/app.js b/test/node/post_variables/app.js
new file mode 100755
index 00000000..928a38cf
--- /dev/null
+++ b/test/node/post_variables/app.js
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ let body = '';
+ req.on('data', chunk => {
+ body += chunk.toString();
+ });
+ req.on('end', () => {
+ let query = require('querystring').parse(body);
+ res.setHeader('X-Var-1', query.var1);
+ res.setHeader('X-Var-2', query.var2);
+ res.setHeader('X-Var-3', query.var3);
+ res.end();
+ });
+}).listen(7080);
diff --git a/test/node/promise_end/app.js b/test/node/promise_end/app.js
new file mode 100755
index 00000000..ed22464c
--- /dev/null
+++ b/test/node/promise_end/app.js
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+
+require('unit-http').createServer(function (req, res) {
+ res.write('blah');
+
+ Promise.resolve().then(() => {
+ res.end();
+ });
+
+ req.on('data', (data) => {
+ fs.appendFile('callback', '', function() {});
+ });
+
+}).listen(7080);
diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js
new file mode 100755
index 00000000..54df09d1
--- /dev/null
+++ b/test/node/promise_handler/app.js
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+
+require('unit-http').createServer(function (req, res) {
+ res.end();
+
+ if (req.headers['X-Write-Call']) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.write('blah');
+ }
+
+ Promise.resolve().then(() => {
+ req.on('data', (data) => {
+ fs.appendFile(data.toString(), '', function() {});
+ });
+ });
+}).listen(7080);
diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js
new file mode 100755
index 00000000..578b72a7
--- /dev/null
+++ b/test/node/remove_header/app.js
@@ -0,0 +1,11 @@
+#!/usr/bin/env node
+
+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.setHeader('Has-Header', res.hasHeader('X-Header').toString());
+
+ res.end();
+}).listen(7080);
diff --git a/test/node/set_header_array/app.js b/test/node/set_header_array/app.js
new file mode 100755
index 00000000..faac45c7
--- /dev/null
+++ b/test/node/set_header_array/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('Set-Cookie', ['tc=one,two,three', 'tc=four,five,six']);
+ res.end();
+}).listen(7080);
diff --git a/test/node/status_message/app.js b/test/node/status_message/app.js
new file mode 100755
index 00000000..4f3b064a
--- /dev/null
+++ b/test/node/status_message/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, 'blah', {'Content-Type': 'text/plain'});
+ res.end();
+}).listen(7080);
diff --git a/test/node/update_header/app.js b/test/node/update_header/app.js
new file mode 100755
index 00000000..0c5cd237
--- /dev/null
+++ b/test/node/update_header/app.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.setHeader('X-Header', 'test');
+ res.setHeader('X-Header', 'new');
+ res.end();
+}).listen(7080);
diff --git a/test/node/variables/app.js b/test/node/variables/app.js
new file mode 100755
index 00000000..968afba5
--- /dev/null
+++ b/test/node/variables/app.js
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ let body = '';
+ req.on('data', chunk => {
+ body += chunk.toString();
+ });
+ req.on('end', () => {
+ res.setHeader('Request-Method', req.method);
+ res.setHeader('Request-Uri', req.url);
+ 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.writeHead(200, {});
+ res.end(body);
+ });
+}).listen(7080);
diff --git a/test/node/write_before_write_head/app.js b/test/node/write_before_write_head/app.js
new file mode 100755
index 00000000..9fe3a58d
--- /dev/null
+++ b/test/node/write_before_write_head/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.write('blah');
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+}).listen(7080);
diff --git a/test/node/write_buffer/app.js b/test/node/write_buffer/app.js
new file mode 100755
index 00000000..f41de2a1
--- /dev/null
+++ b/test/node/write_buffer/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]));
+}).listen(7080);
diff --git a/test/node/write_callback/app.js b/test/node/write_callback/app.js
new file mode 100755
index 00000000..3a9e51e8
--- /dev/null
+++ b/test/node/write_callback/app.js
@@ -0,0 +1,13 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ var a = 'world';
+ res.write('hello', 'utf8', function() {
+ a = 'blah';
+ fs.appendFile('callback', '', function() {});
+ });
+ res.end(a);
+}).listen(7080);
diff --git a/test/node/write_multiple/app.js b/test/node/write_multiple/app.js
new file mode 100755
index 00000000..3cbb3b86
--- /dev/null
+++ b/test/node/write_multiple/app.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain', 'Content-Length': 14});
+ res.write('write');
+ res.write('write2');
+ res.end('end');
+}).listen(7080);
diff --git a/test/node/write_return/app.js b/test/node/write_return/app.js
new file mode 100755
index 00000000..3ae967c6
--- /dev/null
+++ b/test/node/write_return/app.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+
+require('unit-http').createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(res.write('body').toString());
+}).listen(7080);
diff --git a/test/php/date_time/index.php b/test/php/date_time/index.php
new file mode 100644
index 00000000..4e06fdf9
--- /dev/null
+++ b/test/php/date_time/index.php
@@ -0,0 +1,4 @@
+<?php
+$d = new DateTime('2011-01-01T15:03:01.012345Z');
+echo $d->format('u');
+?>
diff --git a/test/php/highlight_file_exec/index.php b/test/php/highlight_file_exec/index.php
new file mode 100644
index 00000000..adcd5ed8
--- /dev/null
+++ b/test/php/highlight_file_exec/index.php
@@ -0,0 +1,4 @@
+<?php
+highlight_file('index.php');
+exec('pwd');
+?>
diff --git a/test/test_access_log.py b/test/test_access_log.py
index 05f5f54a..c8464796 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -305,4 +305,4 @@ Connection: close
'reopen 2')
if __name__ == '__main__':
- unittest.main()
+ TestUnitAccessLog.main()
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 6db65bb3..02705afe 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -218,4 +218,4 @@ class TestUnitConfiguration(unit.TestUnitControl):
}), 'no port')
if __name__ == '__main__':
- unittest.main()
+ TestUnitConfiguration.main()
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 650d1c27..fd80bf5b 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -151,4 +151,4 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo):
'arguments empty')
if __name__ == '__main__':
- unittest.main()
+ TestUnitGoApplication.main()
diff --git a/test/test_http_header.py b/test/test_http_header.py
index 1ca0920d..b850831d 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -163,4 +163,4 @@ a
self.assertEqual(resp['status'], 200, 'transfer encoding chunked')
if __name__ == '__main__':
- unittest.main()
+ TestUnitHTTPHeader.main()
diff --git a/test/test_node_application.py b/test/test_node_application.py
new file mode 100644
index 00000000..5dedb5a3
--- /dev/null
+++ b/test/test_node_application.py
@@ -0,0 +1,284 @@
+import unittest
+import unit
+
+class TestUnitNodeApplication(unit.TestUnitApplicationNode):
+
+ def setUpClass():
+ u = unit.TestUnit().check_modules('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['body'], 'Hello World\n', 'basic body')
+
+ def test_node_application_seq(self):
+ self.load('basic')
+
+ self.assertEqual(self.get()['status'], 200, 'seq')
+ self.assertEqual(self.get()['status'], 200, 'seq 2')
+
+ def test_node_application_variables(self):
+ self.load('variables')
+
+ body = 'Test body string.'
+
+ resp = self.post(headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'Custom-Header': 'blah'
+ }, 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')
+
+ 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')
+
+ raw_headers = headers.pop('Request-Raw-Headers')
+ self.assertRegex(raw_headers, r'^(?:Host|localhost|Content-Type|' \
+ 'text\/html|Custom-Header|blah|Content-Length|17|,)+$',
+ 'raw headers')
+
+ 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',
+ 'Custom-Header': 'blah'
+ }, 'headers')
+ self.assertEqual(resp['body'], body, 'body')
+
+ def test_node_application_get_variables(self):
+ self.load('get_variables')
+
+ resp = self.get(url='/?var1=val1&var2=&var3')
+ self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables')
+ self.assertEqual(resp['headers']['X-Var-2'], '', 'GET variables 2')
+ self.assertEqual(resp['headers']['X-Var-3'], '', 'GET variables 3')
+
+ 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')
+
+ self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables')
+ self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2')
+ self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3')
+
+ def test_node_application_404(self):
+ self.load('404')
+
+ resp = self.get()
+
+ self.assertEqual(resp['status'], 404, '404 status')
+ 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={
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'text/html',
+ 'Host': 'localhost'
+ }, start=True, body='0123456789' * 500)
+
+ 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')
+
+ 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')
+
+ @unittest.expectedFailure
+ 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')
+
+ def test_node_application_write_before_writeHead(self):
+ self.skip_alerts.append(r'process \d+ exited on signal')
+ self.load('write_before_write_head')
+
+ self.get()
+
+ def test_node_application_double_end(self):
+ self.load('double_end')
+
+ self.assertEqual(self.get()['status'], 200, 'double end')
+ self.assertEqual(self.get()['status'], 200, 'double end 2')
+
+ 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')
+
+ def test_node_application_remove_header(self):
+ self.load('remove_header')
+
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'X-Remove': 'X-Header'
+ })
+ 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')
+
+ def test_node_application_remove_header_nonexisting(self):
+ self.load('remove_header')
+
+ self.assertEqual(self.get(headers={
+ 'Host': 'localhost',
+ 'X-Remove': 'blah'
+ })['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')
+
+ 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')
+
+ @unittest.expectedFailure
+ def test_node_application_status_message(self):
+ self.load('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')
+
+ @unittest.expectedFailure
+ def test_node_application_header_name_case(self):
+ self.load('header_name_case')
+
+ headers = self.get()['headers']
+
+ self.assertEqual(headers['X-HEADER'], '3', 'header value')
+ self.assertNotIn('X-Header', headers, 'insensitive')
+ self.assertNotIn('X-header', headers, 'insensitive 2')
+
+ def test_node_application_promise_handler(self):
+ self.load('promise_handler')
+
+ self.assertEqual(self.post(headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html'
+ }, body='callback')['status'], 200, 'promise handler request')
+ self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'),
+ 'promise handler')
+
+ @unittest.expectedFailure
+ def test_node_application_promise_handler_write_after_end(self):
+ self.skip_alerts.append(r'process \d+ exited on signal')
+ self.load('promise_handler')
+
+ self.assertEqual(self.post(headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html',
+ 'X-Write-Call': '1'
+ }, 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'
+ }, 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'
+ }, body='callback1')
+
+ self.assertTrue(self.waitforfiles(self.testdir + '/node/callback1'),
+ 'promise first call')
+
+ self.post(headers={
+ 'Host': 'localhost',
+ 'Content-Type': 'text/html'
+ }, body='callback2')
+
+ self.assertTrue(self.waitforfiles(self.testdir + '/node/callback2'),
+ 'promise second call')
+
+ @unittest.expectedFailure
+ def test_node_application_header_name_valid(self):
+ self.load('header_name_valid')
+
+ self.assertNotIn('status', self.get(), 'header name valid')
+
+ @unittest.expectedFailure
+ def test_node_application_header_value_object(self):
+ self.load('header_value_object')
+
+ self.assertIn('X-Header', self.get()['headers'], 'header value object')
+
+ @unittest.expectedFailure
+ 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')
+
+ def test_node_application_has_header(self):
+ self.load('has_header')
+
+ self.assertEqual(self.get(headers={
+ 'Host': 'localhost',
+ 'X-Header': 'length'
+ })['headers']['X-Has-Header'], 'false', 'has header length')
+
+ self.assertEqual(self.get(headers={
+ 'Host': 'localhost',
+ 'X-Header': 'Date'
+ })['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')
+
+if __name__ == '__main__':
+ TestUnitNodeApplication.main()
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index 09e3d576..c9cb3f0c 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -167,4 +167,4 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPerlApplication.main()
diff --git a/test/test_php_application.py b/test/test_php_application.py
index 0dbc743d..e0058d9a 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -1,11 +1,16 @@
import unittest
import unit
+import re
class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
def setUpClass():
unit.TestUnit().check_modules('php')
+ def search_disabled(self, name):
+ p = re.compile(name + '\(\) has been disabled')
+ return self.search_in_log(p)
+
def test_php_application_variables(self):
self.load('variables')
@@ -204,5 +209,110 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP):
self.assertEqual(self.get()['headers']['X-Precision'], '5',
'ini value repeat')
+ def test_php_application_disable_functions_exec(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"admin": { "disable_functions": "exec" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_functions_highlight_file(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"admin": { "disable_functions": "highlight_file" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNotNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_functions_comma(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"admin": { "disable_functions": "exec,highlight_file" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNotNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_functions_space(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"admin": { "disable_functions": "exec highlight_file" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNotNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_functions_user(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"user": { "disable_functions": "exec" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_functions_nonexistent(self):
+ self.load('highlight_file_exec')
+
+ self.conf({"admin": { "disable_functions": "blah" }},
+ 'applications/highlight_file_exec/options')
+
+ self.get()
+
+ self.assertIsNone(self.search_disabled('exec'),
+ 'disable_functions exec')
+ self.assertIsNone(self.search_disabled('highlight_file'),
+ 'disable_functions highlight_file')
+
+ def test_php_application_disable_classes(self):
+ self.load('date_time')
+
+ self.get()
+
+ self.assertIsNone(self.search_disabled('DateTime'),
+ 'disable_classes before')
+
+ self.conf({"admin": { "disable_classes": "DateTime" }},
+ 'applications/date_time/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('DateTime'),
+ 'disable_classes')
+
+ def test_php_application_disable_classes_user(self):
+ self.load('date_time')
+
+ self.conf({"user": { "disable_classes": "DateTime" }},
+ 'applications/date_time/options')
+
+ self.get()
+
+ self.assertIsNotNone(self.search_disabled('DateTime'),
+ 'disable_classes user')
+
if __name__ == '__main__':
- unittest.main()
+ TestUnitPHPApplication.main()
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 9e0ce822..1ea46c91 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -139,4 +139,4 @@ class TestUnitPHPBasic(unit.TestUnitControl):
'delete app again')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPHPBasic.main()
diff --git a/test/test_python_application.py b/test/test_python_application.py
index e71b6432..667047bc 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -349,4 +349,4 @@ Connection: close
self.assertEqual(self.get()['body'], '0123456789', 'write')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPythonApplication.main()
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index 4cb194f6..b5179dea 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -149,4 +149,4 @@ class TestUnitPythonBasic(unit.TestUnitControl):
'delete app again')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPythonBasic.main()
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index 907ad57c..71e4d5b7 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -125,4 +125,4 @@ class TestUnitPythonEnvironment(unit.TestUnitApplicationPython):
})['body'], pwd_default, 'restore default')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPythonEnvironment.main()
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index 297484eb..65268d49 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -245,4 +245,4 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython):
self.assertEqual(len(self.pids_for_process()), 0, 'stop all')
if __name__ == '__main__':
- unittest.main()
+ TestUnitPythonProcman.main()
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 77040127..57ab88cd 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -284,4 +284,4 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby):
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
if __name__ == '__main__':
- unittest.main()
+ TestUnitRubyApplication.main()
diff --git a/test/test_settings.py b/test/test_settings.py
index 816dcb5e..b4ac33dc 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -170,4 +170,4 @@ Content-Length: %d
'settings'), 'settings negative value')
if __name__ == '__main__':
- unittest.main()
+ TestUnitSettings.main()
diff --git a/test/test_tls.py b/test/test_tls.py
index aaf939ec..fa5c9754 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -417,4 +417,4 @@ Connection: close
self.assertEqual(resp['body'], '0123456789', 'application respawn body')
if __name__ == '__main__':
- unittest.main()
+ TestUnitTLS.main()
diff --git a/test/unit.py b/test/unit.py
index a5f96968..e88ed684 100644
--- a/test/unit.py
+++ b/test/unit.py
@@ -7,6 +7,7 @@ import time
import shutil
import socket
import select
+import argparse
import platform
import tempfile
import unittest
@@ -19,6 +20,31 @@ class TestUnit(unittest.TestCase):
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()
@@ -49,7 +75,7 @@ class TestUnit(unittest.TestCase):
# remove unit.log
- if '--leave' not in sys.argv and success:
+ if not TestUnit.save_log and success:
shutil.rmtree(self.testdir)
else:
@@ -91,6 +117,12 @@ class TestUnit(unittest.TestCase):
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'])
@@ -227,6 +259,22 @@ class TestUnit(unittest.TestCase):
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')
@@ -296,7 +344,7 @@ class TestUnitHTTP(TestUnit):
sock.sendall(req)
- if '--verbose' in sys.argv:
+ if TestUnit.detailed:
print('>>>', req, sep='\n')
resp = ''
@@ -305,7 +353,7 @@ class TestUnitHTTP(TestUnit):
enc = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
resp = self.recvall(sock).decode(enc)
- if '--verbose' in sys.argv:
+ if TestUnit.detailed:
print('<<<', resp.encode('utf-8'), sep='\n')
if 'raw_resp' not in kwargs:
@@ -516,6 +564,35 @@ class TestUnitApplicationGo(TestUnitApplicationProto):
}
})
+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 TestUnitApplicationPerl(TestUnitApplicationProto):
def load(self, script, name='psgi.pl'):
self.conf({