summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2021-02-04 18:40:45 +0300
committerAndrei Belov <defan@nginx.com>2021-02-04 18:40:45 +0300
commit0997fa324ca523ab282f595ac9f44b3e4daff86a (patch)
tree37424fff265780f34f9a9adb7ddd7501a67843f1
parent2bc99c614d5547e773bda73364efada47f0a37bf (diff)
parent774a6034d9daf32ac6c98da7e4c0ca9e820536b4 (diff)
downloadunit-0997fa324ca523ab282f595ac9f44b3e4daff86a.tar.gz
unit-0997fa324ca523ab282f595ac9f44b3e4daff86a.tar.bz2
Merged with the default branch.
-rw-r--r--.hgtags1
-rw-r--r--CHANGES39
-rw-r--r--NOTICE16
-rw-r--r--docs/Makefile9
-rw-r--r--docs/changes.xml110
-rw-r--r--docs/changes.xsls47
-rw-r--r--docs/changes.xslt47
-rw-r--r--pkg/deb/Makefile10
-rw-r--r--pkg/deb/debian.module/control.in4
-rw-r--r--pkg/deb/debian.module/unit.example-go-config1
-rw-r--r--pkg/deb/debian.module/unit.example-perl-config1
-rw-r--r--pkg/deb/debian.module/unit.example-php-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python2.7-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python3.4-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python3.5-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python3.6-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python3.7-config1
-rw-r--r--pkg/deb/debian.module/unit.example-python3.8-config1
-rw-r--r--pkg/deb/debian.module/unit.example-ruby-config1
-rw-r--r--pkg/deb/debian/control3
-rw-r--r--pkg/deb/debian/copyright16
-rw-r--r--pkg/deb/debian/rules.in8
-rw-r--r--pkg/deb/debian/unit.example.config4
-rwxr-xr-xpkg/deb/debian/unit.postinst34
-rw-r--r--pkg/deb/debian/unit.preinst1
-rw-r--r--pkg/docker/Dockerfile.full95
-rw-r--r--pkg/docker/Dockerfile.go1.11-dev95
-rw-r--r--pkg/docker/Dockerfile.go1.1575
-rw-r--r--pkg/docker/Dockerfile.jsc11146
-rw-r--r--pkg/docker/Dockerfile.minimal146
-rw-r--r--pkg/docker/Dockerfile.node1575
-rw-r--r--pkg/docker/Dockerfile.perl5.2895
-rw-r--r--pkg/docker/Dockerfile.perl5.3273
-rw-r--r--pkg/docker/Dockerfile.php7.395
-rw-r--r--pkg/docker/Dockerfile.php8.073
-rw-r--r--pkg/docker/Dockerfile.python2.795
-rw-r--r--pkg/docker/Dockerfile.python3.795
-rw-r--r--pkg/docker/Dockerfile.python3.973
-rw-r--r--pkg/docker/Dockerfile.ruby2.595
-rw-r--r--pkg/docker/Dockerfile.ruby2.773
-rw-r--r--pkg/docker/Dockerfile.tmpl95
-rw-r--r--pkg/docker/Makefile125
-rw-r--r--pkg/docker/template.Dockerfile73
-rw-r--r--pkg/rpm/Makefile13
-rw-r--r--pkg/rpm/Makefile.python3957
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-go-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-php-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python39-config16
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config1
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example.config4
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.init88
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.sysconf1
-rw-r--r--pkg/rpm/unit.module.spec.in11
-rw-r--r--pkg/rpm/unit.spec.in79
-rw-r--r--src/nodejs/unit-http/http_server.js50
-rw-r--r--src/nodejs/unit-http/unit.cpp271
-rw-r--r--src/nodejs/unit-http/unit.h6
-rw-r--r--src/nxt_app_queue.h18
-rw-r--r--src/nxt_application.h14
-rw-r--r--src/nxt_conf_validation.c35
-rw-r--r--src/nxt_controller.c37
-rw-r--r--src/nxt_h1proto.c39
-rw-r--r--src/nxt_http.h1
-rw-r--r--src/nxt_http_route.c6
-rw-r--r--src/nxt_http_static.c23
-rw-r--r--src/nxt_isolation.c6
-rw-r--r--src/nxt_main_process.c94
-rw-r--r--src/nxt_php_sapi.c11
-rw-r--r--src/nxt_port.c8
-rw-r--r--src/nxt_process.h2
-rw-r--r--src/nxt_router.c83
-rw-r--r--src/nxt_runtime.c2
-rw-r--r--src/nxt_unit.c68
-rw-r--r--src/nxt_unit_request.h1
-rw-r--r--src/python/nxt_python.c103
-rw-r--r--src/python/nxt_python_asgi.c41
-rw-r--r--src/python/nxt_python_wsgi.c65
-rw-r--r--src/ruby/nxt_ruby.c14
-rw-r--r--test/conftest.py170
-rw-r--r--test/php/auth/index.php7
-rw-r--r--test/pytest.ini2
-rw-r--r--test/python/path/wsgi.py8
-rw-r--r--test/test_access_log.py46
-rw-r--r--test/test_asgi_application.py6
-rw-r--r--test/test_asgi_lifespan.py4
-rw-r--r--test/test_asgi_websockets.py31
-rw-r--r--test/test_configuration.py4
-rw-r--r--test/test_go_application.py8
-rw-r--r--test/test_go_isolation.py42
-rw-r--r--test/test_go_isolation_rootfs.py1
-rw-r--r--test/test_http_header.py1
-rw-r--r--test/test_java_application.py7
-rw-r--r--test/test_java_isolation_rootfs.py3
-rw-r--r--test/test_java_websockets.py7
-rw-r--r--test/test_node_application.py48
-rw-r--r--test/test_node_websockets.py7
-rw-r--r--test/test_perl_application.py7
-rw-r--r--test/test_php_application.py107
-rw-r--r--test/test_php_basic.py26
-rw-r--r--test/test_php_isolation.py22
-rw-r--r--test/test_php_targets.py2
-rw-r--r--test/test_proxy.py14
-rw-r--r--test/test_proxy_chunked.py4
-rw-r--r--test/test_python_application.py64
-rw-r--r--test/test_python_basic.py24
-rw-r--r--test/test_python_isolation.py76
-rw-r--r--test/test_python_isolation_chroot.py2
-rw-r--r--test/test_python_procman.py5
-rw-r--r--test/test_respawn.py54
-rw-r--r--test/test_routing.py12
-rw-r--r--test/test_ruby_application.py18
-rw-r--r--test/test_ruby_isolation.py47
-rw-r--r--test/test_settings.py2
-rw-r--r--test/test_share_fallback.py5
-rw-r--r--test/test_static.py5
-rw-r--r--test/test_tls.py18
-rw-r--r--test/test_upstreams_rr.py2
-rw-r--r--test/test_usr1.py7
-rw-r--r--test/unit/applications/lang/go.py3
-rw-r--r--test/unit/applications/lang/java.py2
-rw-r--r--test/unit/applications/lang/node.py4
-rw-r--r--test/unit/applications/lang/perl.py2
-rw-r--r--test/unit/applications/lang/php.py2
-rw-r--r--test/unit/applications/lang/python.py2
-rw-r--r--test/unit/applications/lang/ruby.py2
-rw-r--r--test/unit/applications/proto.py2
-rw-r--r--test/unit/applications/tls.py2
-rw-r--r--test/unit/check/isolation.py158
-rw-r--r--test/unit/control.py2
-rw-r--r--test/unit/feature/isolation.py160
-rw-r--r--test/unit/http.py5
-rw-r--r--test/unit/main.py42
-rw-r--r--test/unit/option.py16
-rw-r--r--test/unit/utils.py94
-rw-r--r--version4
144 files changed, 2583 insertions, 2174 deletions
diff --git a/.hgtags b/.hgtags
index fc2201d3..66bd1338 100644
--- a/.hgtags
+++ b/.hgtags
@@ -49,3 +49,4 @@ de07e42484ecc595050fcbd3581a64cc6b1c1de5 1.18.0-1
29efab062b4e5d8a34b13724feb05c1890d738e8 1.20.0-1
f804aaf7eee10a7d8116820840d6312dd4914a41 1.21.0
e3f504b6082ee97ed0d6c8660890585ef6a5796f 1.21.0-1
+331bdadeca30a49dd11b86af99124c2ffeb22d05 1.22.0
diff --git a/CHANGES b/CHANGES
index b026fe5e..0f558c6f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,43 @@
+Changes with Unit 1.22.0 04 Feb 2021
+
+ *) Feature: the ServerRequest and ServerResponse objects of Node.js
+ module are now compliant with Stream API.
+
+ *) Feature: support for specifying multiple directories in the "path"
+ option of Python apps.
+
+ *) Bugfix: a memory leak occurred in the router process when serving
+ files larger than 128K; the bug had appeared in 1.13.0.
+
+ *) Bugfix: apps could stop processing new requests under high load; the
+ bug had appeared in 1.19.0.
+
+ *) Bugfix: app processes could terminate unexpectedly under high load;
+ the bug had appeared in 1.19.0.
+
+ *) Bugfix: invalid HTTP responses were generated for some unusual status
+ codes.
+
+ *) Bugfix: the PHP_AUTH_USER, PHP_AUTH_PW, and PHP_AUTH_DIGEST server
+ variables were missing in the PHP module.
+
+ *) Bugfix: the router process could crash with multithreaded apps under
+ high load.
+
+ *) Bugfix: Ruby apps with multithreading configured could crash on start
+ under load.
+
+ *) Bugfix: mount points weren't unmounted when the "mount" namespace
+ isolation was used; the bug had appeared in 1.21.0.
+
+ *) Bugfix: the router process could crash while removing or
+ reconfiguring an app that used WebSocket.
+
+ *) Bugfix: a memory leak occurring in the router process when removing
+ or reconfiguring an application; the bug had appeared in 1.19.0.
+
+
Changes with Unit 1.21.0 19 Nov 2020
*) Change: procfs is mounted by default for all languages when "rootfs"
diff --git a/NOTICE b/NOTICE
index 8d08b08b..ef97b24f 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,14 +1,14 @@
NGINX Unit.
- Copyright 2017-2020 NGINX, Inc.
- Copyright 2017-2020 Igor Sysoev
- Copyright 2017-2020 Valentin V. Bartenev
- Copyright 2017-2020 Max Romanov
- Copyright 2017-2020 Andrei Zeliankou
- Copyright 2017-2020 Andrei Belov
- Copyright 2018-2020 Konstantin Pavlov
- Copyright 2019-2020 Tiago Natel de Moura
+ Copyright 2017-2021 NGINX, Inc.
+ Copyright 2017-2021 Igor Sysoev
+ Copyright 2017-2021 Valentin V. Bartenev
+ Copyright 2017-2021 Max Romanov
+ Copyright 2017-2021 Andrei Zeliankou
+ Copyright 2017-2021 Andrei Belov
+ Copyright 2018-2021 Konstantin Pavlov
+ Copyright 2019-2021 Tiago Natel de Moura
Copyright 2019-2020 Axel Duch
Copyright 2018-2019 Alexander Borisov
diff --git a/docs/Makefile b/docs/Makefile
index aa8aeb9b..db63eec4 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -14,6 +14,9 @@ PACKAGES= unit \
unit-ruby \
unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11
+CURDATE:=$(shell date +"%Y-%m-%d")
+CURTIME:=$(shell date +"%H:%M:%S %z")
+
all: changes changelogs
@@ -31,6 +34,8 @@ $(DEST)/CHANGES: changes.dtd \
xmllint --noout --valid changes.xml
xsltproc --stringparam format generic \
+ --stringparam curdate '$(CURDATE)' \
+ --stringparam curtime '$(CURTIME)' \
-o $@ changes.xslt changes.xml
$(DEST)/%.rpm-changelog: changes.dtd \
@@ -40,6 +45,8 @@ $(DEST)/%.rpm-changelog: changes.dtd \
mkdir -p $(DEST)
xmllint --noout --valid changes.xml
xsltproc --stringparam pkgname $* --stringparam format rpm \
+ --stringparam curdate '$(CURDATE)' \
+ --stringparam curtime '$(CURTIME)' \
-o $@ changes.xslt changes.xml
$(DEST)/%.deb-changelog: changes.dtd \
@@ -49,6 +56,8 @@ $(DEST)/%.deb-changelog: changes.dtd \
mkdir -p $(DEST)
xmllint --noout --valid changes.xml
xsltproc --stringparam pkgname $* --stringparam format deb \
+ --stringparam curdate '$(CURDATE)' \
+ --stringparam curtime '$(CURTIME)' \
-o $@ changes.xslt changes.xml
changes.xslt: changes.xsls
diff --git a/docs/changes.xml b/docs/changes.xml
index 83bb4a56..70934d69 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -5,6 +5,116 @@
<change_log title="unit">
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7
+ unit-python3.8
+ unit-go
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13
+ unit-jsc14 unit-jsc15"
+ ver="1.22.0" rev="1"
+ date="2021-02-04" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.22.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.22.0" rev="1"
+ date="2021-02-04" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="feature">
+<para>
+the ServerRequest and ServerResponse objects of Node.js module are now
+compliant with Stream API.
+</para>
+</change>
+
+<change type="feature">
+<para>
+support for specifying multiple directories in the "path" option of Python
+apps.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+a memory leak occurred in the router process when serving files larger than
+128K; the bug had appeared in 1.13.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+apps could stop processing new requests under high load; the bug had
+appeared in 1.19.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+app processes could terminate unexpectedly under high load; the bug had
+appeared in 1.19.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+invalid HTTP responses were generated for some unusual status codes.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the PHP_AUTH_USER, PHP_AUTH_PW, and PHP_AUTH_DIGEST server variables were
+missing in the PHP module.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process could crash with multithreaded apps under high load.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+Ruby apps with multithreading configured could crash on start under load.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+mount points weren't unmounted when the "mount" namespace isolation was used;
+the bug had appeared in 1.21.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+the router process could crash while removing or reconfiguring an app that used
+WebSocket.
+</para>
+</change>
+
+<change type="bugfix">
+<para>
+a memory leak occurring in the router process when removing or reconfiguring
+an application; the bug had appeared in 1.19.0.
+</para>
+</change>
+
+</changes>
+
+
<changes apply="unit-jsc15" ver="1.21.0" rev="1"
date="2020-11-19" time="18:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/docs/changes.xsls b/docs/changes.xsls
index 2f3bcc46..9ff6a7e2 100644
--- a/docs/changes.xsls
+++ b/docs/changes.xsls
@@ -5,6 +5,8 @@ X:output method="text";
X:param format="'generic'";
X:param pkgname="'unit'";
X:param configuration="'change_log_conf.xml'";
+X:param curdate;
+X:param curtime;
X:var conf = "document($configuration)/configuration";
@@ -58,8 +60,10 @@ X:template = "change_log" { !! "changes"; }
X:template = "changes" {
- X:var pday = { !padded_day(date="@date") }
- X:var dow = { !day_of_week(date="@date") }
+ X:var date_ = { !getdate(date="@date", curdate="$curdate") }
+ X:var time_ = { !gettime(time="@time", curtime="$curtime") }
+ X:var pday = { !padded_day(date="$date_") }
+ X:var dow = { !day_of_week(date="$date_") }
X:var apply = { !string_in_list(list="@apply", string="$pkgname") }
X:var pkgname_ = { !beautify(pkgname="$pkgname") }
@@ -77,17 +81,16 @@ X:template = "changes" {
' '),
1, $conf/changes/length)}
- !{substring(@date, 9, 2)}
- !{$conf/changes/month[number(substring(current()/@date,
- 6, 2))]}
- !{substring(@date, 1, 4)}
+ !{substring($date_, 9, 2)}
+ !{$conf/changes/month[number(substring($date_, 6, 2))]}
+ !{substring($date_, 1, 4)}
}
X:if "$format='rpm'" {
!{concat('* ', $conf/changes/day[number($dow)],
- $conf/changes/month[number(substring(current()/@date, 6, 2))],
+ $conf/changes/month[number(substring($date_, 6, 2))],
$pday, ' ',
- substring(@date, 1, 4), ' ', @packager, ' - ',
+ substring($date_, 1, 4), ' ', @packager, ' - ',
@ver, '-', @rev, '%{?dist}.ngx')}
}
@@ -108,8 +111,8 @@ X:template = "changes" {
!{concat(' -- ', @packager, ' ',
$conf/changes/day[number($dow)], ', ',
$pday,
- $conf/changes/month[number(substring(current()/@date, 6, 2))],
- substring(@date, 1, 4), ' ', @time)}
+ $conf/changes/month[number(substring($date_, 6, 2))],
+ substring($date_, 1, 4), ' ', $time_)}
X:text {&#10;}
X:text {&#10;}
@@ -254,6 +257,30 @@ X:template beautify(pkgname) {
}
+X:template getdate(date, curdate) {
+ X:choose {
+ X:when "$date=''" {
+ !{$curdate}
+ }
+ X:otherwise {
+ !{$date}
+ }
+ }
+}
+
+
+X:template gettime(time, curtime) {
+ X:choose {
+ X:when "$time=''" {
+ !{$curtime}
+ }
+ X:otherwise {
+ !{$time}
+ }
+ }
+}
+
+
X:template = "at" {@}
X:template = "br" { !{$br} }
X:template = "nobr" { !{translate(., ' ', '&#xA0;')} }
diff --git a/docs/changes.xslt b/docs/changes.xslt
index 032d5c37..08f2f800 100644
--- a/docs/changes.xslt
+++ b/docs/changes.xslt
@@ -6,6 +6,8 @@
<xsl:param select="'generic'" name="format"/>
<xsl:param select="'unit'" name="pkgname"/>
<xsl:param select="'change_log_conf.xml'" name="configuration"/>
+<xsl:param name="curdate"/>
+<xsl:param name="curtime"/>
<xsl:variable select="document($configuration)/configuration" name="conf"/>
@@ -59,8 +61,10 @@
<xsl:template match="changes">
- <xsl:variable name="pday"> <xsl:call-template name="padded_day"><xsl:with-param select="@date" name="date"/></xsl:call-template></xsl:variable>
- <xsl:variable name="dow"> <xsl:call-template name="day_of_week"><xsl:with-param select="@date" name="date"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="date_"> <xsl:call-template name="getdate"><xsl:with-param select="@date" name="date"/><xsl:with-param select="$curdate" name="curdate"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="time_"> <xsl:call-template name="gettime"><xsl:with-param select="@time" name="time"/><xsl:with-param select="$curtime" name="curtime"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="pday"> <xsl:call-template name="padded_day"><xsl:with-param select="$date_" name="date"/></xsl:call-template></xsl:variable>
+ <xsl:variable name="dow"> <xsl:call-template name="day_of_week"><xsl:with-param select="$date_" name="date"/></xsl:call-template></xsl:variable>
<xsl:variable name="apply"> <xsl:call-template name="string_in_list"><xsl:with-param select="@apply" name="list"/><xsl:with-param select="$pkgname" name="string"/></xsl:call-template></xsl:variable>
<xsl:variable name="pkgname_"> <xsl:call-template name="beautify"><xsl:with-param select="$pkgname" name="pkgname"/></xsl:call-template></xsl:variable>
@@ -78,17 +82,16 @@
' '),
1, $conf/changes/length)"/>
- <xsl:value-of select="substring(@date, 9, 2)"/>
- <xsl:value-of select="$conf/changes/month[number(substring(current()/@date,
- 6, 2))]"/>
- <xsl:value-of select="substring(@date, 1, 4)"/>
+ <xsl:value-of select="substring($date_, 9, 2)"/>
+ <xsl:value-of select="$conf/changes/month[number(substring($date_, 6, 2))]"/>
+ <xsl:value-of select="substring($date_, 1, 4)"/>
</xsl:if>
<xsl:if test="$format='rpm'">
<xsl:value-of select="concat('* ', $conf/changes/day[number($dow)],
- $conf/changes/month[number(substring(current()/@date, 6, 2))],
+ $conf/changes/month[number(substring($date_, 6, 2))],
$pday, ' ',
- substring(@date, 1, 4), ' ', @packager, ' - ',
+ substring($date_, 1, 4), ' ', @packager, ' - ',
@ver, '-', @rev, '%{?dist}.ngx')"/>
</xsl:if>
@@ -109,8 +112,8 @@
<xsl:value-of select="concat(' -- ', @packager, ' ',
$conf/changes/day[number($dow)], ', ',
$pday,
- $conf/changes/month[number(substring(current()/@date, 6, 2))],
- substring(@date, 1, 4), ' ', @time)"/>
+ $conf/changes/month[number(substring($date_, 6, 2))],
+ substring($date_, 1, 4), ' ', $time_)"/>
<xsl:text>&#10;</xsl:text>
<xsl:text>&#10;</xsl:text>
@@ -246,6 +249,30 @@
</xsl:template>
+<xsl:template name="getdate"><xsl:param name="date"/><xsl:param name="curdate"/>
+ <xsl:choose>
+ <xsl:when test="$date=''">
+ <xsl:value-of select="$curdate"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$date"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="gettime"><xsl:param name="time"/><xsl:param name="curtime"/>
+ <xsl:choose>
+ <xsl:when test="$time=''">
+ <xsl:value-of select="$curtime"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$time"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
<xsl:template match="at">@</xsl:template>
<xsl:template match="br"> <xsl:value-of select="$br"/> </xsl:template>
<xsl:template match="nobr"> <xsl:value-of select="translate(., ' ', '&#xA0;')"/> </xsl:template>
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index 8a02105c..16e73b94 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -2,9 +2,10 @@
include ../../version
+DEFAULT_VERSION := $(NXT_VERSION)
DEFAULT_RELEASE := 1
-VERSION ?= $(NXT_VERSION)
+VERSION ?= $(DEFAULT_VERSION)
RELEASE ?= $(DEFAULT_RELEASE)
SRCDIR= unit-$(VERSION)
@@ -157,6 +158,8 @@ CONFIGURE_ARGS=\
--pid=/var/run/unit.pid \
--log=/var/log/unit.log \
--tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
--tests \
--openssl
@@ -205,6 +208,7 @@ debuild/$(SRCDIR)/debian:
cat debian/rules.in | sed \
-e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \
> debuild/$(SRCDIR)/debian/rules ; \
+ chmod +x debuild/$(SRCDIR)/debian/rules ; \
}
debuild/$(SRCDIR)/debian/changelog: ../../docs/changes.xml | debuild/$(SRCDIR)/debian
@@ -314,7 +318,7 @@ test: unit modules
test -h debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} || \
ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} ; \
done ; \
- ( cd debuild/unit-$(VERSION)/debian/build-unit && env python3 -m pytest ) ; \
+ ( cd debuild/unit-$(VERSION)/debian/build-unit && env python3 -m pytest --user=nobody $(PYTEST_ARGS) ) ; \
}
test-debug: unit modules
@@ -325,7 +329,7 @@ test-debug: unit modules
test -h debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} || \
ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} ; \
done ; \
- ( cd debuild/unit-$(VERSION)/debian/build-unit-debug && env python3 -m pytest ) ; \
+ ( cd debuild/unit-$(VERSION)/debian/build-unit-debug && env python3 -m pytest --user=nobody $(PYTEST_ARGS) ) ; \
}
clean:
diff --git a/pkg/deb/debian.module/control.in b/pkg/deb/debian.module/control.in
index e9b8b8e2..7e28f5e9 100644
--- a/pkg/deb/debian.module/control.in
+++ b/pkg/deb/debian.module/control.in
@@ -3,7 +3,9 @@ Section: admin
Priority: extra
Maintainer: Andrei Belov <defan@nginx.com>
Build-Depends: debhelper (>= 9),
- linux-libc-dev%%MODULE_BUILD_DEPENDS%%
+ linux-libc-dev,
+ libssl-dev,
+ libpcre2-dev%%MODULE_BUILD_DEPENDS%%
Standards-Version: 3.9.5
Homepage: https://unit.nginx.org
diff --git a/pkg/deb/debian.module/unit.example-go-config b/pkg/deb/debian.module/unit.example-go-config
index a2c91e80..8aa65939 100644
--- a/pkg/deb/debian.module/unit.example-go-config
+++ b/pkg/deb/debian.module/unit.example-go-config
@@ -2,7 +2,6 @@
"applications": {
"example_go": {
"type": "external",
- "user": "nobody",
"executable": "/tmp/go-app"
}
},
diff --git a/pkg/deb/debian.module/unit.example-perl-config b/pkg/deb/debian.module/unit.example-perl-config
index 031928ce..2182fc46 100644
--- a/pkg/deb/debian.module/unit.example-perl-config
+++ b/pkg/deb/debian.module/unit.example-perl-config
@@ -2,7 +2,6 @@
"applications": {
"example_perl": {
"type": "perl",
- "user": "nobody",
"processes": 1,
"working_directory": "/usr/share/doc/unit-perl/examples/perl-app",
"script": "/usr/share/doc/unit-perl/examples/perl-app/index.pl"
diff --git a/pkg/deb/debian.module/unit.example-php-config b/pkg/deb/debian.module/unit.example-php-config
index 8f23c984..9673385f 100644
--- a/pkg/deb/debian.module/unit.example-php-config
+++ b/pkg/deb/debian.module/unit.example-php-config
@@ -2,7 +2,6 @@
"applications": {
"example_php": {
"type": "php",
- "user": "nobody",
"processes": 2,
"root": "/usr/share/doc/unit-php/examples/phpinfo-app",
"index": "index.php"
diff --git a/pkg/deb/debian.module/unit.example-python-config b/pkg/deb/debian.module/unit.example-python-config
index d612c89d..b3d3a2e5 100644
--- a/pkg/deb/debian.module/unit.example-python-config
+++ b/pkg/deb/debian.module/unit.example-python-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python2.7-config b/pkg/deb/debian.module/unit.example-python2.7-config
index bede8899..4f1d16c9 100644
--- a/pkg/deb/debian.module/unit.example-python2.7-config
+++ b/pkg/deb/debian.module/unit.example-python2.7-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 2.7",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python2.7/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python3.4-config b/pkg/deb/debian.module/unit.example-python3.4-config
index dd496bd8..e6d90acf 100644
--- a/pkg/deb/debian.module/unit.example-python3.4-config
+++ b/pkg/deb/debian.module/unit.example-python3.4-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.4",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python3.4/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python3.5-config b/pkg/deb/debian.module/unit.example-python3.5-config
index 2be6de4a..480327ec 100644
--- a/pkg/deb/debian.module/unit.example-python3.5-config
+++ b/pkg/deb/debian.module/unit.example-python3.5-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.5",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python3.5/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python3.6-config b/pkg/deb/debian.module/unit.example-python3.6-config
index a77e8e07..543024ff 100644
--- a/pkg/deb/debian.module/unit.example-python3.6-config
+++ b/pkg/deb/debian.module/unit.example-python3.6-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.6",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python3.6/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python3.7-config b/pkg/deb/debian.module/unit.example-python3.7-config
index 9b13c058..e7b8dbc3 100644
--- a/pkg/deb/debian.module/unit.example-python3.7-config
+++ b/pkg/deb/debian.module/unit.example-python3.7-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.7",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python3.7/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-python3.8-config b/pkg/deb/debian.module/unit.example-python3.8-config
index 435e025f..dc649e30 100644
--- a/pkg/deb/debian.module/unit.example-python3.8-config
+++ b/pkg/deb/debian.module/unit.example-python3.8-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.8",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python3.8/examples/python-app",
"module": "wsgi"
diff --git a/pkg/deb/debian.module/unit.example-ruby-config b/pkg/deb/debian.module/unit.example-ruby-config
index 15a92735..930aa987 100644
--- a/pkg/deb/debian.module/unit.example-ruby-config
+++ b/pkg/deb/debian.module/unit.example-ruby-config
@@ -2,7 +2,6 @@
"applications": {
"example_ruby": {
"type": "ruby",
- "user": "nobody",
"processes": 2,
"script": "/usr/share/doc/unit-ruby/examples/ruby-app.ru"
}
diff --git a/pkg/deb/debian/control b/pkg/deb/debian/control
index 9828b6ab..a8e8cdc4 100644
--- a/pkg/deb/debian/control
+++ b/pkg/deb/debian/control
@@ -4,7 +4,8 @@ Priority: extra
Maintainer: Andrei Belov <defan@nginx.com>
Build-Depends: debhelper (>= 9),
linux-libc-dev,
- libssl-dev
+ libssl-dev,
+ libpcre2-dev
Standards-Version: 3.9.5
Homepage: https://unit.nginx.org
diff --git a/pkg/deb/debian/copyright b/pkg/deb/debian/copyright
index 2acb87b9..2a2c5fd0 100644
--- a/pkg/deb/debian/copyright
+++ b/pkg/deb/debian/copyright
@@ -1,14 +1,14 @@
NGINX Unit.
- Copyright 2017-2020 NGINX, Inc.
- Copyright 2017-2020 Igor Sysoev
- Copyright 2017-2020 Valentin V. Bartenev
- Copyright 2017-2020 Max Romanov
- Copyright 2017-2020 Andrei Zeliankou
- Copyright 2017-2020 Andrei Belov
- Copyright 2018-2020 Konstantin Pavlov
- Copyright 2019-2020 Tiago Natel de Moura
+ Copyright 2017-2021 NGINX, Inc.
+ Copyright 2017-2021 Igor Sysoev
+ Copyright 2017-2021 Valentin V. Bartenev
+ Copyright 2017-2021 Max Romanov
+ Copyright 2017-2021 Andrei Zeliankou
+ Copyright 2017-2021 Andrei Belov
+ Copyright 2018-2021 Konstantin Pavlov
+ Copyright 2019-2021 Tiago Natel de Moura
Copyright 2019-2020 Axel Duch
Copyright 2018-2019 Alexander Borisov
diff --git a/pkg/deb/debian/rules.in b/pkg/deb/debian/rules.in
index a4696793..aa7921d1 100644
--- a/pkg/deb/debian/rules.in
+++ b/pkg/deb/debian/rules.in
@@ -10,6 +10,7 @@ DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+CODENAME := $(shell lsb_release -cs)
BUILDDIR_unit = $(CURDIR)/debian/build-unit
BUILDDIR_unit_debug = $(CURDIR)/debian/build-unit-debug
@@ -96,7 +97,11 @@ install: build do.tests
dh_testroot
dh_prep
dh_installdirs
+ifeq ($(CODENAME), xenial)
dh_installinit
+else
+ dh_installsystemd
+endif
dh_installlogrotate
cd $(BUILDDIR_unit) && DESTDIR=$(INSTALLDIR) make install
cd $(BUILDDIR_unit) && DESTDIR=$(INSTALLDIR_dev) make libunit-install
@@ -104,8 +109,9 @@ install: build do.tests
install -m 644 $(BUILDDIR_unit_debug)/build/libunit.a $(INSTALLDIR_dev)/usr/lib/$(DEB_HOST_MULTIARCH)/libunit-debug.a
mkdir -p $(INSTALLDIR)/usr/share/doc/unit/examples
install -m 644 debian/unit.example.config $(INSTALLDIR)/usr/share/doc/unit/examples/example.config
- install -m 644 CHANGES $(INSTALLDIR)/usr/share/doc/unit/
+ install -m 644 CHANGES $(INSTALLDIR)/usr/share/doc/unit/changelog
install -m 644 README $(INSTALLDIR)/usr/share/doc/unit/
+ install -m 644 NOTICE $(INSTALLDIR)/usr/share/doc/unit/
binary-indep: build install
dh_testdir
diff --git a/pkg/deb/debian/unit.example.config b/pkg/deb/debian/unit.example.config
index 5610cb3a..66695327 100644
--- a/pkg/deb/debian/unit.example.config
+++ b/pkg/deb/debian/unit.example.config
@@ -2,7 +2,6 @@
"applications": {
"example_php": {
"type": "php",
- "user": "nobody",
"processes": 2,
"root": "/usr/share/doc/unit/examples/php-app",
"index": "index.php"
@@ -10,7 +9,6 @@
"example_python": {
"type": "python",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit/examples/python-app",
"module": "wsgi"
@@ -18,13 +16,11 @@
"example_go": {
"type": "external",
- "user": "nobody",
"executable": "/tmp/go-app"
},
"example_perl": {
"type": "perl",
- "user": "nobody",
"processes": 1,
"working_directory": "/usr/share/doc/unit-perl/examples/perl-app",
"script": "/usr/share/doc/unit-perl/examples/perl-app/index.pl"
diff --git a/pkg/deb/debian/unit.postinst b/pkg/deb/debian/unit.postinst
index 76375a2b..8aa476b3 100755
--- a/pkg/deb/debian/unit.postinst
+++ b/pkg/deb/debian/unit.postinst
@@ -6,6 +6,40 @@ if [ "$1" != "configure" ]; then
exit 0
fi
+if [ -n "$2" ]; then
+ if dpkg --compare-versions "${2%%-*}" le "1.21.0"; then
+ cat <<BANNER
+----------------------------------------------------------------------
+
+WARNING:
+
+Since version 1.22.0, Unit's non-privileged processes run as unit:unit by
+default. Review your system permissions and Unit configuration so apps and
+routes that relied on these processes running as nobody:nogroup stay working.
+
+More info: https://unit.nginx.org/installation/#official-packages
+
+----------------------------------------------------------------------
+BANNER
+ fi
+fi
+
+if ! getent group unit >/dev/null; then
+ addgroup --system unit >/dev/null
+fi
+
+if ! getent passwd unit >/dev/null; then
+ adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit >/dev/null
+fi
+
#DEBHELPER#
exit 0
diff --git a/pkg/deb/debian/unit.preinst b/pkg/deb/debian/unit.preinst
index d4be468d..bd513788 100644
--- a/pkg/deb/debian/unit.preinst
+++ b/pkg/deb/debian/unit.preinst
@@ -17,6 +17,7 @@ Online documentation is available at https://unit.nginx.org/
----------------------------------------------------------------------
BANNER
;;
+
upgrade)
;;
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
deleted file mode 100644
index a5f3e13f..00000000
--- a/pkg/docker/Dockerfile.full
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-php=${UNIT_VERSION} unit-python2.7=${UNIT_VERSION} unit-python3.7=${UNIT_VERSION} unit-perl=${UNIT_VERSION} unit-ruby=${UNIT_VERSION} unit-jsc11=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.go1.11-dev b/pkg/docker/Dockerfile.go1.11-dev
deleted file mode 100644
index cb1c1d85..00000000
--- a/pkg/docker/Dockerfile.go1.11-dev
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-go=${UNIT_VERSION} gcc" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.go1.15 b/pkg/docker/Dockerfile.go1.15
new file mode 100644
index 00000000..270dc428
--- /dev/null
+++ b/pkg/docker/Dockerfile.go1.15
@@ -0,0 +1,75 @@
+FROM golang:1.15 as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure go --go-path=$GOPATH \
+ && make -j $NCPU go-install-src libunit-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure go --go-path=$GOPATH \
+ && make -j $NCPU go-install-src libunit-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM golang:1.15
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+COPY --from=BUILDER /usr/lib/x86_64-linux-gnu/libunit.a /usr/lib/x86_64-linux-gnu/
+COPY --from=BUILDER /usr/include/nxt_* /usr/include/
+COPY --from=BUILDER /go/src/ /go/src/
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.jsc11 b/pkg/docker/Dockerfile.jsc11
index f3d1d22d..cde7c590 100644
--- a/pkg/docker/Dockerfile.jsc11
+++ b/pkg/docker/Dockerfile.jsc11
@@ -1,95 +1,73 @@
-FROM debian:buster-slim
+FROM openjdk:11-jdk as BUILDER
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.21.0-1~buster
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure java --jars=/usr/share/unit-jsc-common/ \
+ && make -j $NCPU java-shared-install java-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure java --jars=/usr/share/unit-jsc-common/ \
+ && make -j $NCPU java-shared-install java-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+FROM openjdk:11-jdk
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+COPY --from=BUILDER /usr/share/unit-jsc-common/ /usr/share/unit-jsc-common/
RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-jsc11=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
STOPSIGNAL SIGTERM
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index da9c8712..91fd79d0 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -1,95 +1,73 @@
-FROM debian:buster-slim
+FROM debian:buster-slim as BUILDER
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.21.0-1~buster
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure \
+ && make -j $NCPU version \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure \
+ && make -j $NCPU version \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
+FROM debian:buster-slim
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
STOPSIGNAL SIGTERM
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.node15 b/pkg/docker/Dockerfile.node15
new file mode 100644
index 00000000..59b12012
--- /dev/null
+++ b/pkg/docker/Dockerfile.node15
@@ -0,0 +1,75 @@
+FROM node:15 as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \
+ && make -j $NCPU node node-install libunit-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \
+ && make -j $NCPU node node-install libunit-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM node:15
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+COPY --from=BUILDER /usr/lib/x86_64-linux-gnu/libunit.a /usr/lib/x86_64-linux-gnu/
+COPY --from=BUILDER /usr/include/nxt_* /usr/include/
+COPY --from=BUILDER /usr/local/lib/node_modules/unit-http/ /usr/local/lib/node_modules/unit-http/
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.perl5.28 b/pkg/docker/Dockerfile.perl5.28
deleted file mode 100644
index 976fa7b0..00000000
--- a/pkg/docker/Dockerfile.perl5.28
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-perl=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.perl5.32 b/pkg/docker/Dockerfile.perl5.32
new file mode 100644
index 00000000..589eb989
--- /dev/null
+++ b/pkg/docker/Dockerfile.perl5.32
@@ -0,0 +1,73 @@
+FROM perl:5.32 as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure perl \
+ && make -j $NCPU perl-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure perl \
+ && make -j $NCPU perl-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM perl:5.32
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.php7.3 b/pkg/docker/Dockerfile.php7.3
deleted file mode 100644
index f1178551..00000000
--- a/pkg/docker/Dockerfile.php7.3
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-php=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.php8.0 b/pkg/docker/Dockerfile.php8.0
new file mode 100644
index 00000000..c31513d4
--- /dev/null
+++ b/pkg/docker/Dockerfile.php8.0
@@ -0,0 +1,73 @@
+FROM php:8.0-cli as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure php \
+ && make -j $NCPU php-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure php \
+ && make -j $NCPU php-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM php:8.0-cli
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+RUN ldconfig
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
deleted file mode 100644
index 95241af6..00000000
--- a/pkg/docker/Dockerfile.python2.7
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-python2.7=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.python3.7 b/pkg/docker/Dockerfile.python3.7
deleted file mode 100644
index 52a5a257..00000000
--- a/pkg/docker/Dockerfile.python3.7
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-python3.7=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.python3.9 b/pkg/docker/Dockerfile.python3.9
new file mode 100644
index 00000000..76f50733
--- /dev/null
+++ b/pkg/docker/Dockerfile.python3.9
@@ -0,0 +1,73 @@
+FROM python:3.9 as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure python --config=/usr/local/bin/python3-config \
+ && make -j $NCPU python3-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure python --config=/usr/local/bin/python3-config \
+ && make -j $NCPU python3-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM python:3.9
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.ruby2.5 b/pkg/docker/Dockerfile.ruby2.5
deleted file mode 100644
index 342ccc9a..00000000
--- a/pkg/docker/Dockerfile.ruby2.5
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION 1.21.0-1~buster
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages="unit=${UNIT_VERSION} unit-ruby=${UNIT_VERSION}" \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.ruby2.7 b/pkg/docker/Dockerfile.ruby2.7
new file mode 100644
index 00000000..aa823756
--- /dev/null
+++ b/pkg/docker/Dockerfile.ruby2.7
@@ -0,0 +1,73 @@
+FROM ruby:2.7 as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up 1.22.0 \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure ruby \
+ && make -j $NCPU ruby-install \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure ruby \
+ && make -j $NCPU ruby-install \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM ruby:2.7
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+RUN gem install rack
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Dockerfile.tmpl b/pkg/docker/Dockerfile.tmpl
deleted file mode 100644
index 8b0e35e4..00000000
--- a/pkg/docker/Dockerfile.tmpl
+++ /dev/null
@@ -1,95 +0,0 @@
-FROM debian:buster-slim
-
-LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-
-ENV UNIT_VERSION @@UNIT_VERSION@@
-
-RUN set -x \
- && apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \
- && \
- NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
- found=''; \
- for server in \
- ha.pool.sks-keyservers.net \
- hkp://keyserver.ubuntu.com:80 \
- hkp://p80.pool.sks-keyservers.net:80 \
- pgp.mit.edu \
- ; do \
- echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
- apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
- done; \
- test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
- apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
-# work-around debian bug 863199
- && mkdir -p /usr/share/man/man1 \
- && dpkgArch="$(dpkg --print-architecture)" \
- && unitPackages=@@UNITPACKAGES@@ \
- && case "$dpkgArch" in \
- amd64|i386) \
-# arches officialy built by upstream
- echo "deb https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- && apt-get update \
- ;; \
- *) \
-# we're on an architecture upstream doesn't officially build for
-# let's build binaries from the published source packages
- echo "deb-src https://packages.nginx.org/unit/debian/ buster unit" >> /etc/apt/sources.list.d/unit.list \
- \
-# new directory for storing sources and .deb files
- && tempDir="$(mktemp -d)" \
- && chmod 777 "$tempDir" \
-# (777 to ensure APT's "_apt" user can access it too)
- \
-# save list of currently-installed packages so build dependencies can be cleanly removed later
- && savedAptMark="$(apt-mark showmanual)" \
- \
-# build .deb files from upstream's source packages (which are verified by apt-get)
- && apt-get update \
- && apt-get build-dep -y $unitPackages \
- && ( \
- cd "$tempDir" \
- && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
- apt-get source --compile $unitPackages \
- ) \
-# we don't remove APT lists here because they get re-downloaded and removed later
- \
-# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
-# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
- && apt-mark showmanual | xargs apt-mark auto > /dev/null \
- && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
- \
-# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
- && ls -lAFh "$tempDir" \
- && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
- && grep '^Package: ' "$tempDir/Packages" \
- && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
-# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
-# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
-# ...
-# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
- && apt-get -o Acquire::GzipIndexes=false update \
- ;; \
- esac \
- \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- $unitPackages \
- curl \
- && apt-get remove --purge --auto-remove -y apt-transport-https && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unit.list \
- \
-# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
- && if [ -n "$tempDir" ]; then \
- apt-get purge -y --auto-remove \
- && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
- fi
-
-# forward log to docker log collector
-RUN ln -sf /dev/stdout /var/log/unit.log
-
-STOPSIGNAL SIGTERM
-
-COPY docker-entrypoint.sh /usr/local/bin/
-RUN mkdir /docker-entrypoint.d/
-ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-
-CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile
index aed5b8f7..00625526 100644
--- a/pkg/docker/Makefile
+++ b/pkg/docker/Makefile
@@ -3,54 +3,96 @@
include ../../version
include ../shasum.mak
-DEFAULT_RELEASE := 1
+DEFAULT_VERSION := $(NXT_VERSION)
-VERSION ?= $(NXT_VERSION)
-RELEASE ?= $(DEFAULT_RELEASE)
-CODENAME := buster
-
-UNIT_VERSION = $(VERSION)-$(RELEASE)~$(CODENAME)
-
-MODULES = python2.7 python3.7 php7.3 go1.11-dev perl5.28 ruby2.5 \
- jsc11 full minimal
-
-MODULE_php7.3="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION}"
-
-MODULE_python2.7="unit=$${UNIT_VERSION} unit-python2.7=$${UNIT_VERSION}"
-
-MODULE_python3.7="unit=$${UNIT_VERSION} unit-python3.7=$${UNIT_VERSION}"
-
-MODULE_go1.11-dev="unit=$${UNIT_VERSION} unit-go=$${UNIT_VERSION} gcc"
-
-MODULE_perl5.28="unit=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION}"
-
-MODULE_ruby2.5="unit=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION}"
-
-MODULE_jsc11="unit=$${UNIT_VERSION} unit-jsc11=$${UNIT_VERSION}"
-
-MODULE_full="unit=$${UNIT_VERSION} unit-php=$${UNIT_VERSION} unit-python2.7=$${UNIT_VERSION} unit-python3.7=$${UNIT_VERSION} unit-perl=$${UNIT_VERSION} unit-ruby=$${UNIT_VERSION} unit-jsc11=$${UNIT_VERSION}"
-
-MODULE_minimal="unit=$${UNIT_VERSION}"
+VERSION ?= $(DEFAULT_VERSION)
EXPORT_DIR := $(VERSION)
+MODULES ?= go jsc node perl php python ruby minimal
+
+VERSION_minimal ?=
+CONTAINER_minimal ?= debian:buster-slim
+CONFIGURE_minimal ?=
+INSTALL_minimal ?= version
+define COPY_minimal
+endef
+
+VERSION_go ?= 1.15
+CONTAINER_go ?= golang:$(VERSION_go)
+CONFIGURE_go ?= go --go-path=$$GOPATH
+INSTALL_go ?= go-install-src libunit-install
+define COPY_go
+COPY --from=BUILDER /usr/lib/x86_64-linux-gnu/libunit.a /usr/lib/x86_64-linux-gnu/\n\$
+COPY --from=BUILDER /usr/include/nxt_* /usr/include/\n\$
+COPY --from=BUILDER /go/src/ /go/src/
+endef
+
+VERSION_jsc ?= 11
+CONTAINER_jsc ?= openjdk:$(VERSION_jsc)-jdk
+CONFIGURE_jsc ?= java --jars=/usr/share/unit-jsc-common/
+INSTALL_jsc ?= java-shared-install java-install
+COPY_jsc = COPY --from=BUILDER /usr/share/unit-jsc-common/ /usr/share/unit-jsc-common/
+
+VERSION_node ?= 15
+CONTAINER_node ?= node:$(VERSION_node)
+CONFIGURE_node ?= nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp
+INSTALL_node ?= node node-install libunit-install
+define COPY_node
+COPY --from=BUILDER /usr/lib/x86_64-linux-gnu/libunit.a /usr/lib/x86_64-linux-gnu/\n\$
+COPY --from=BUILDER /usr/include/nxt_* /usr/include/\n\$
+COPY --from=BUILDER /usr/local/lib/node_modules/unit-http/ /usr/local/lib/node_modules/unit-http/
+endef
+
+VERSION_perl ?= 5.32
+CONTAINER_perl ?= perl:$(VERSION_perl)
+CONFIGURE_perl ?= perl
+INSTALL_perl ?= perl-install
+COPY_perl =
+
+VERSION_php ?= 8.0
+CONTAINER_php ?= php:$(VERSION_php)-cli
+CONFIGURE_php ?= php
+INSTALL_php ?= php-install
+COPY_php = RUN ldconfig
+
+VERSION_python ?= 3.9
+CONTAINER_python ?= python:$(VERSION_python)
+CONFIGURE_python ?= python --config=/usr/local/bin/python3-config
+INSTALL_python ?= python3-install
+COPY_python =
+
+VERSION_ruby ?= 2.7
+CONTAINER_ruby ?= ruby:$(VERSION_ruby)
+CONFIGURE_ruby ?= ruby
+INSTALL_ruby ?= ruby-install
+COPY_ruby = RUN gem install rack
+
default:
@echo "valid targets: all build dockerfiles push tag export clean"
-dockerfiles: $(addprefix Dockerfile., $(MODULES))
-build: refresh-base $(addprefix build-,$(MODULES))
-tag: $(addprefix tag-,$(MODULES))
-push: $(addprefix push-,$(MODULES)) latest
-export: $(addsuffix .tar.gz,$(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODULES))) $(addsuffix .tar.gz.sha512, $(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODULES)))
+MODVERSIONS = $(foreach module,$(MODULES),$(module)$(VERSION_$(module)))
+
+modname = $(shell echo $1 | /usr/bin/tr -d '.01234567890-')
+
+dockerfiles: $(addprefix Dockerfile., $(MODVERSIONS))
+build: $(addprefix build-,$(MODVERSIONS))
+tag: $(addprefix tag-,$(MODVERSIONS))
+push: $(addprefix push-,$(MODVERSIONS))
+export: $(addsuffix .tar.gz,$(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODVERSIONS))) $(addsuffix .tar.gz.sha512, $(addprefix $(EXPORT_DIR)/nginx-unit-$(VERSION)-,$(MODVERSIONS)))
Dockerfile.%: ../../version
@echo "===> Building $@"
- cat Dockerfile.tmpl | sed \
- -e 's,@@UNITPACKAGES@@,$(MODULE_$*),g' \
- -e 's,@@UNIT_VERSION@@,$(UNIT_VERSION),g' \
+ cat template.Dockerfile | sed \
+ -e 's,@@VERSION@@,$(VERSION),g' \
+ -e 's,@@CONTAINER@@,$(CONTAINER_$(call modname, $*)),g' \
+ -e 's,@@CONFIGURE@@,$(CONFIGURE_$(call modname, $*)),g' \
+ -e 's,@@INSTALL@@,$(INSTALL_$(call modname, $*)),g' \
+ -e 's,@@COPY@@,$(COPY_$(call modname, $*)),g' \
> $@
build-%: Dockerfile.%
+ docker pull $(CONTAINER_$(call modname, $*))
docker build --no-cache -t unit:$(VERSION)-$* -f Dockerfile.$* .
tag-%: build-%
@@ -59,13 +101,6 @@ tag-%: build-%
push-%: tag-%
docker push nginx/unit:$(VERSION)-$*
-latest:
- docker tag nginx/unit:$(VERSION)-full nginx/unit:latest
- docker push nginx/unit:latest
-
-refresh-base:
- docker pull $(shell head -n 1 Dockerfile.tmpl | cut -d' ' -f 2)
-
$(EXPORT_DIR):
mkdir -p $@
@@ -75,10 +110,10 @@ $(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz: $(EXPORT_DIR) tag-%
$(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz.sha512: $(EXPORT_DIR)/nginx-unit-$(VERSION)-%.tar.gz
$(SHA512SUM) $< | sed 's,$(EXPORT_DIR)/,,' > $@
-all: $(addprefix Dockerfile., $(MODULES))
+all: $(addprefix Dockerfile., $(MODVERSIONS))
clean:
- rm -f $(addprefix Dockerfile., $(MODULES))
+ rm -f $(addprefix Dockerfile., $(MODVERSIONS))
rm -rf $(EXPORT_DIR)
-.PHONY: default all build dockerfiles latest push tag export clean refresh-base
+.PHONY: default build dockerfiles push tag export clean
diff --git a/pkg/docker/template.Dockerfile b/pkg/docker/template.Dockerfile
new file mode 100644
index 00000000..d96d7982
--- /dev/null
+++ b/pkg/docker/template.Dockerfile
@@ -0,0 +1,73 @@
+FROM @@CONTAINER@@ as BUILDER
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+RUN set -ex \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \
+ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
+ && hg clone https://hg.nginx.org/unit \
+ && cd unit \
+ && hg up @@VERSION@@ \
+ && NCPU="$(getconf _NPROCESSORS_ONLN)" \
+ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
+ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
+ && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
+ && CONFIGURE_ARGS="--prefix=/usr \
+ --state=/var/lib/unit \
+ --control=unix:/var/run/control.unit.sock \
+ --pid=/var/run/unit.pid \
+ --log=/var/log/unit.log \
+ --tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
+ --openssl \
+ --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd-debug \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \
+ && make -j $NCPU unitd \
+ && install -pm755 build/unitd /usr/sbin/unitd \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \
+ && ./configure @@CONFIGURE@@ \
+ && make -j $NCPU @@INSTALL@@ \
+ && make clean \
+ && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \
+ && ./configure @@CONFIGURE@@ \
+ && make -j $NCPU @@INSTALL@@ \
+ && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt
+
+FROM @@CONTAINER@@
+COPY docker-entrypoint.sh /usr/local/bin/
+COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd
+COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug
+COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/
+COPY --from=BUILDER /requirements.apt /requirements.apt
+@@COPY@@
+RUN set -x \
+ && mkdir -p /var/lib/unit/ \
+ && mkdir /docker-entrypoint.d/ \
+ && addgroup --system unit \
+ && adduser \
+ --system \
+ --disabled-login \
+ --ingroup unit \
+ --no-create-home \
+ --home /nonexistent \
+ --gecos "unit user" \
+ --shell /bin/false \
+ unit \
+ && apt update \
+ && apt --no-install-recommends --no-install-suggests -y install $(cat /requirements.apt) \
+ && apt-get clean && rm -rf /var/lib/apt/lists/* \
+ && rm -f /requirements.apt \
+ && ln -sf /dev/stdout /var/log/unit.log
+
+STOPSIGNAL SIGTERM
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
+
+CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile
index 1944d58d..f0bd0cf9 100644
--- a/pkg/rpm/Makefile
+++ b/pkg/rpm/Makefile
@@ -2,9 +2,10 @@
include ../../version
+DEFAULT_VERSION := $(NXT_VERSION)
DEFAULT_RELEASE := 1
-VERSION ?= $(NXT_VERSION)
+VERSION ?= $(DEFAULT_VERSION)
RELEASE ?= $(DEFAULT_RELEASE)
ifeq ($(shell test `rpm --eval '0%{?rhel} -eq 6 -a 0%{?amzn} -eq 0'`; echo $$?), 0)
@@ -127,7 +128,9 @@ include Makefile.php
ifeq ($(shell test `rpm --eval '0%{?fedora} -lt 32'`; echo $$?),0)
include Makefile.python27
endif
-ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 32'`; echo $$?),0)
+ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 33'`; echo $$?),0)
+include Makefile.python39
+else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 32'`; echo $$?),0)
include Makefile.python38
else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 29'`; echo $$?),0)
include Makefile.python37
@@ -149,6 +152,8 @@ CONFIGURE_ARGS=\
--pid=/var/run/unit/unit.pid \
--log=/var/log/unit/unit.log \
--tmp=/var/tmp \
+ --user=unit \
+ --group=unit \
--tests \
--openssl
@@ -274,7 +279,7 @@ test: unit modules
test -h rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} || \
ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} ; \
done ; \
- ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-nodebug build && env python3 -m pytest ) ; \
+ ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-nodebug build && env python3 -m pytest --user=nobody $(PYTEST_ARGS) ) ; \
}
test-debug: unit modules
@@ -285,7 +290,7 @@ test-debug: unit modules
test -h rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} || \
ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} ; \
done ; \
- ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-debug build && env python3 -m pytest ) ; \
+ ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-debug build && env python3 -m pytest --user=nobody $(PYTEST_ARGS) ) ; \
}
clean:
diff --git a/pkg/rpm/Makefile.python39 b/pkg/rpm/Makefile.python39
new file mode 100644
index 00000000..8e444e56
--- /dev/null
+++ b/pkg/rpm/Makefile.python39
@@ -0,0 +1,57 @@
+MODULES+= python39
+MODULE_SUFFIX_python39= python3.9
+
+MODULE_SUMMARY_python39= Python 3.9 module for NGINX Unit
+
+MODULE_VERSION_python39= $(VERSION)
+MODULE_RELEASE_python39= 1
+
+MODULE_CONFARGS_python39= python --config=python3.9-config
+MODULE_MAKEARGS_python39= python3.9
+MODULE_INSTARGS_python39= python3.9-install
+
+MODULE_SOURCES_python39= unit.example-python-app \
+ unit.example-python39-config
+
+ifneq (,$(findstring $(OSVER),opensuse-tumbleweed sles fedora amazonlinux2))
+BUILD_DEPENDS_python39= python3-devel
+else
+BUILD_DEPENDS_python39= python39-devel
+endif
+
+BUILD_DEPENDS+= $(BUILD_DEPENDS_python39)
+
+define MODULE_PREINSTALL_python39
+%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python39/examples/python-app
+%{__install} -m 644 -p %{SOURCE100} \
+ %{buildroot}%{_datadir}/doc/unit-python39/examples/python-app/wsgi.py
+%{__install} -m 644 -p %{SOURCE101} \
+ %{buildroot}%{_datadir}/doc/unit-python39/examples/unit.config
+endef
+export MODULE_PREINSTALL_python39
+
+define MODULE_FILES_python39
+%{_libdir}/unit/modules/*
+%{_libdir}/unit/debug-modules/*
+endef
+export MODULE_FILES_python39
+
+define MODULE_POST_python39
+cat <<BANNER
+----------------------------------------------------------------------
+
+The $(MODULE_SUMMARY_python39) has been installed.
+
+To check the sample app, run these commands:
+
+ sudo service unit start
+ cd /usr/share/doc/%{name}/examples
+ sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/unit/control.sock http://localhost/config
+ curl http://localhost:8400/
+
+Online documentation is available at https://unit.nginx.org
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_python39
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
index a2c91e80..8aa65939 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-go-config
@@ -2,7 +2,6 @@
"applications": {
"example_go": {
"type": "external",
- "user": "nobody",
"executable": "/tmp/go-app"
}
},
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
index 031928ce..2182fc46 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-perl-config
@@ -2,7 +2,6 @@
"applications": {
"example_perl": {
"type": "perl",
- "user": "nobody",
"processes": 1,
"working_directory": "/usr/share/doc/unit-perl/examples/perl-app",
"script": "/usr/share/doc/unit-perl/examples/perl-app/index.pl"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
index 8f23c984..9673385f 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-php-config
@@ -2,7 +2,6 @@
"applications": {
"example_php": {
"type": "php",
- "user": "nobody",
"processes": 2,
"root": "/usr/share/doc/unit-php/examples/phpinfo-app",
"index": "index.php"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
index d612c89d..b3d3a2e5 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
index 7541fcb3..094e6621 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python27-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 2.7",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python27/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
index b64e570c..15063c5e 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python34-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.4",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python34/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
index 025f3428..f9923a49 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python35-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.5",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python35/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
index 825cabc4..ef31c781 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python36-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.6",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python36/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
index 7f5e52f1..904af440 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python37-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.7",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python37/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config
index 25003869..c98d1a52 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config
@@ -2,7 +2,6 @@
"applications": {
"example_python": {
"type": "python 3.8",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python38/examples/python-app",
"module": "wsgi"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python39-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python39-config
new file mode 100644
index 00000000..61ed8568
--- /dev/null
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python39-config
@@ -0,0 +1,16 @@
+{
+ "applications": {
+ "example_python": {
+ "type": "python 3.9",
+ "processes": 2,
+ "path": "/usr/share/doc/unit-python39/examples/python-app",
+ "module": "wsgi"
+ }
+ },
+
+ "listeners": {
+ "*:8400": {
+ "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 15a92735..930aa987 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-ruby-config
@@ -2,7 +2,6 @@
"applications": {
"example_ruby": {
"type": "ruby",
- "user": "nobody",
"processes": 2,
"script": "/usr/share/doc/unit-ruby/examples/ruby-app.ru"
}
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example.config b/pkg/rpm/rpmbuild/SOURCES/unit.example.config
index 6fe35e2f..4855a954 100644
--- a/pkg/rpm/rpmbuild/SOURCES/unit.example.config
+++ b/pkg/rpm/rpmbuild/SOURCES/unit.example.config
@@ -2,7 +2,6 @@
"applications": {
"example_php": {
"type": "php",
- "user": "nobody",
"processes": 2,
"root": "/usr/share/doc/unit-php/examples/phpinfo-app",
"index": "index.php"
@@ -10,7 +9,6 @@
"example_python": {
"type": "python",
- "user": "nobody",
"processes": 2,
"path": "/usr/share/doc/unit-python/examples/python-app",
"module": "wsgi"
@@ -18,13 +16,11 @@
"example_go": {
"type": "external",
- "user": "nobody",
"executable": "/tmp/go-app"
},
"example_perl": {
"type": "perl",
- "user": "nobody",
"processes": 1,
"working_directory": "/usr/share/doc/unit-perl/examples/perl-app",
"script": "/usr/share/doc/unit-perl/examples/perl-app/index.pl"
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.init b/pkg/rpm/rpmbuild/SOURCES/unit.init
deleted file mode 100644
index e1aacd81..00000000
--- a/pkg/rpm/rpmbuild/SOURCES/unit.init
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/sh
-#
-# unitd NGINX Unit
-#
-# chkconfig: - 86 14
-# description: NGINX Unit
-
-### BEGIN INIT INFO
-# Provides: unitd
-# Required-Start: $local_fs $network $named $syslog
-# Required-Stop: $local_fs $network $named $syslog
-# Default-Start:
-# Default-Stop: 0 1 2 3 4 5 6
-# Short-Description: NGINX Unit
-# Description: NGINX Unit
-### END INIT INFO
-
-# Source function library.
-. /etc/rc.d/init.d/functions
-
-exec="/usr/sbin/unitd"
-prog="unitd"
-
-[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
-
-lockfile=/var/lock/subsys/$prog
-
-start() {
- [ -x $exec ] || exit 5
- echo -n $"Starting $prog: "
- daemon $exec $UNITD_OPTIONS
- retval=$?
- echo
- [ $retval -eq 0 ] && touch $lockfile
- return $retval
-}
-
-stop() {
- echo -n $"Stopping $prog: "
- killproc $prog
- retval=$?
- echo
- [ $retval -eq 0 ] && rm -f $lockfile
- return $retval
-}
-
-restart() {
- stop
- start
-}
-
-rh_status() {
- status $prog
-}
-
-rh_status_q() {
- rh_status &>/dev/null
-}
-
-
-case "$1" in
- start)
- rh_status_q && exit 0
- $1
- ;;
- stop)
- rh_status_q || exit 0
- $1
- ;;
- restart)
- $1
- ;;
- reload|force-reload)
- echo "Not implemented." >&2
- exit 1
- ;;
- status)
- rh_status
- ;;
- condrestart|try-restart)
- rh_status_q || exit 0
- restart
- ;;
- *)
- echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart}"
- exit 2
-esac
-exit $?
diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.sysconf b/pkg/rpm/rpmbuild/SOURCES/unit.sysconf
deleted file mode 100644
index 9146bdac..00000000
--- a/pkg/rpm/rpmbuild/SOURCES/unit.sysconf
+++ /dev/null
@@ -1 +0,0 @@
-UNITD_OPTIONS="--log /var/log/unit/unit.log --pid /var/run/unit/unit.pid"
diff --git a/pkg/rpm/unit.module.spec.in b/pkg/rpm/unit.module.spec.in
index 2ef4ff1b..39083e66 100644
--- a/pkg/rpm/unit.module.spec.in
+++ b/pkg/rpm/unit.module.spec.in
@@ -7,6 +7,15 @@
%define dist .el7
%endif
+%if 0%{?rhel}%{?fedora}
+BuildRequires: gcc
+BuildRequires: openssl-devel
+%endif
+
+%if 0%{?suse_version} >= 1315
+BuildRequires: libopenssl-devel
+%endif
+
%define unit_version %%UNIT_VERSION%%
%define unit_release %%UNIT_RELEASE%%%{?dist}.ngx
@@ -29,6 +38,8 @@ Source0: unit-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: pcre2-devel
+
Requires: unit == %%UNIT_VERSION%%-%%UNIT_RELEASE%%%{?dist}.ngx
%description
diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in
index 7fc950ec..08db73e2 100644
--- a/pkg/rpm/unit.spec.in
+++ b/pkg/rpm/unit.spec.in
@@ -1,19 +1,7 @@
# distribution specific definitions
-%define use_systemd (0%{?rhel} >= 7 || 0%{?fedora} >= 19 || 0%{?suse_version} >= 1315)
%define bdir %{_builddir}/%{name}-%{version}
%define dotests 0
-%if ( 0%{?rhel} == 5 || 0%{?rhel} == 6 )
-Requires: initscripts >= 8.36
-%endif
-
-%if %{use_systemd}
-BuildRequires: systemd
-Requires(post): systemd
-Requires(preun): systemd
-Requires(postun): systemd
-%endif
-
%if 0%{?rhel}%{?fedora}
BuildRequires: gcc
BuildRequires: openssl-devel
@@ -48,12 +36,16 @@ Group: System Environment/Daemons
Source0: unit-%{version}.tar.gz
Source1: unit.service
-Source2: unit.init
-Source3: unit.sysconf
-Source4: unit.example.config
-Source5: unit.logrotate
+Source2: unit.example.config
+Source3: unit.logrotate
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: systemd
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+
+BuildRequires: pcre2-devel
%description
NGINX Unit is a runtime and delivery environment for modern distributed
@@ -116,17 +108,12 @@ DESTDIR=%{buildroot} make unitd-install libunit-install
%{__mkdir} -p %{buildroot}%{_sharedstatedir}/unit
%{__mkdir} -p %{buildroot}%{_localstatedir}/log/unit
%{__mkdir} -p %{buildroot}%{_localstatedir}/run/unit
-%if ! %{use_systemd}
-%{__mkdir} -p %{buildroot}%{_sysconfdir}/sysconfig
-%{__install} -m 644 -p %{SOURCE3} \
- %{buildroot}%{_sysconfdir}/sysconfig/unitd
-%endif
%{__mkdir} -p %{buildroot}%{_sysconfdir}/logrotate.d
-%{__install} -m 644 -p %{SOURCE5} \
+%{__install} -m 644 -p %{SOURCE3} \
%{buildroot}%{_sysconfdir}/logrotate.d/unit
%{__mkdir} -p %{buildroot}%{_sysconfdir}/unit
%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit/examples
-%{__install} -m 644 -p %{SOURCE4} \
+%{__install} -m 644 -p %{SOURCE2} \
%{buildroot}%{_datadir}/doc/unit/examples/example.config
%{__install} -m 644 -p CHANGES \
%{buildroot}%{_datadir}/doc/unit/
@@ -135,14 +122,8 @@ DESTDIR=%{buildroot} make unitd-install libunit-install
%{__install} -m 644 -p README \
%{buildroot}%{_datadir}/doc/unit/
-# init scripts
-%if %{use_systemd}
%{__rm} -rf %{buildroot}%{_initrddir}/
%{__install} -p -D -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/unit.service
-%else
-%{__mkdir} -p %{buildroot}%{_initrddir}
-%{__install} -p -D -m 0755 %{SOURCE2} %{buildroot}%{_initrddir}/unit
-%endif
QA_SKIP_BUILD_ROOT=1
export QA_SKIP_BUILD_ROOT
@@ -163,12 +144,12 @@ cat /dev/null > debugsourcefiles.list
%{__rm} -rf %{buildroot}
%post
+getent group unit >/dev/null || groupadd -r unit
+getent passwd unit >/dev/null || \
+ useradd -r -g unit -s /sbin/nologin \
+ -d /nonexistent -c "unit user" unit
if [ $1 -eq 1 ]; then
-%if %{use_systemd}
/usr/bin/systemctl preset unit.service >/dev/null 2>&1 ||:
-%else
- /sbin/chkconfig --add unit
-%endif
cat <<BANNER
----------------------------------------------------------------------
@@ -185,40 +166,38 @@ fi
%preun
if [ $1 -eq 0 ]; then
-%if %{use_systemd}
/usr/bin/systemctl --no-reload disable unit.service >/dev/null 2>&1 ||:
/usr/bin/systemctl stop unit.service >/dev/null 2>&1 ||:
-%else
- /sbin/service unit stop >/dev/null 2>&1
- /sbin/chkconfig --del unit
-%endif
fi
%postun
-%if %{use_systemd}
/usr/bin/systemctl daemon-reload >/dev/null 2>&1 ||:
-%endif
if [ $1 -ge 1 ]; then
-%if %{use_systemd}
/usr/bin/systemctl try-restart unit.service >/dev/null 2>&1 ||:
-%else
- /sbin/service unit condrestart >/dev/null 2>&1 ||:
-%endif
fi
+%triggerpostun -- unit < 1.22.0
+cat <<BANNER
+----------------------------------------------------------------------
+
+WARNING:
+
+Since version 1.22.0, Unit's non-privileged processes run as unit:unit by
+default. Review your system permissions and Unit configuration so apps and
+routes that relied on these processes running as nobody:nogroup stay working.
+
+More info: https://unit.nginx.org/installation/#official-packages
+
+----------------------------------------------------------------------
+BANNER
+
%files
%defattr(-,root,root,-)
%attr(0755,root,root) %{_sbindir}/unitd
%attr(0755,root,root) %{_sbindir}/unitd-debug
%dir %{_sysconfdir}/unit
-%if %{use_systemd}
%{_unitdir}/unit.service
%dir %attr(0755,root,root) %ghost %{_localstatedir}/run/unit
-%else
-%config(noreplace) %{_sysconfdir}/sysconfig/unitd
-%dir %attr(0755,root,root) %{_localstatedir}/run/unit
-%{_initrddir}/unit
-%endif
%dir %{_datadir}/doc/unit
%{_datadir}/doc/unit/*
%dir %{_libdir}/unit/modules
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index d378e410..e59296ae 100644
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -11,6 +11,7 @@ const util = require('util');
const unit_lib = require('./build/Release/unit-http');
const Socket = require('./socket');
const WebSocketFrame = require('./websocket_frame');
+const Readable = require('stream').Readable;
function ServerResponse(req) {
@@ -23,6 +24,7 @@ function ServerResponse(req) {
req._response = this;
this.socket = req.socket;
this.connection = req.connection;
+ this.writable = true;
}
util.inherits(ServerResponse, EventEmitter);
@@ -268,6 +270,7 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
res = this._write(chunk, 0, contentLength);
if (res < contentLength) {
this.socket.writable = false;
+ this.writable = false;
o = new BufferedOutput(this, res, chunk, encoding, callback);
this.server._output.push(o);
@@ -328,6 +331,8 @@ ServerResponse.prototype.end = function end(chunk, encoding, callback) {
if (typeof callback === 'function') {
callback();
}
+
+ this.emit("finish");
});
this.finished = true;
@@ -337,15 +342,14 @@ ServerResponse.prototype.end = function end(chunk, encoding, callback) {
};
function ServerRequest(server, socket) {
- EventEmitter.call(this);
+ Readable.call(this);
this.server = server;
this.socket = socket;
this.connection = socket;
+ this._pushed_eofchunk = false;
}
-util.inherits(ServerRequest, EventEmitter);
-
-ServerRequest.prototype.unpipe = undefined;
+util.inherits(ServerRequest, Readable);
ServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) {
this.timeout = msecs;
@@ -377,35 +381,21 @@ ServerRequest.prototype.STATUS_CODES = function STATUS_CODES() {
return http.STATUS_CODES;
};
-ServerRequest.prototype.listeners = function listeners() {
- return [];
-};
-
-ServerRequest.prototype.resume = function resume() {
- return [];
-};
+ServerRequest.prototype._request_read = unit_lib.request_read;
-/*
- * 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);
+ServerRequest.prototype._read = function _read(n) {
+ const b = this._request_read(n);
- if (ev === "data") {
- process.nextTick(function () {
- if (this._data.length !== 0) {
- this.emit("data", this._data);
- }
+ if (b != null) {
+ this.push(b);
+ }
- }.bind(this));
+ if (!this._pushed_eofchunk && (b == null || b.length < n)) {
+ this._pushed_eofchunk = true;
+ this.push(null);
}
};
-ServerRequest.prototype.addListener = ServerRequest.prototype.on;
function Server(requestListener) {
EventEmitter.call(this);
@@ -472,11 +462,6 @@ Server.prototype.emit_request = function (req, res) {
} else {
this.emit("request", req, res);
}
-
- process.nextTick(() => {
- req.emit("finish");
- req.emit("end");
- });
};
Server.prototype.emit_close = function () {
@@ -523,6 +508,7 @@ Server.prototype.emit_drain = function () {
}
resp.socket.writable = true;
+ resp.writable = true;
process.nextTick(() => {
resp.emit("drain");
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index c5bca49a..589eca3f 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -13,25 +13,140 @@
#include <nxt_unit_websocket.h>
-static void delete_port_data(uv_handle_t* handle);
-
napi_ref Unit::constructor_;
struct port_data_t {
- nxt_unit_ctx_t *ctx;
- nxt_unit_port_t *port;
- uv_poll_t poll;
+ port_data_t(nxt_unit_ctx_t *c, nxt_unit_port_t *p);
+
+ void process_port_msg();
+ void stop();
+
+ template<typename T>
+ static port_data_t *get(T *handle);
+
+ static void read_callback(uv_poll_t *handle, int status, int events);
+ static void timer_callback(uv_timer_t *handle);
+ static void delete_data(uv_handle_t* handle);
+
+ nxt_unit_ctx_t *ctx;
+ nxt_unit_port_t *port;
+ uv_poll_t poll;
+ uv_timer_t timer;
+ int ref_count;
+ bool scheduled;
+ bool stopped;
};
struct req_data_t {
napi_ref sock_ref;
+ napi_ref req_ref;
napi_ref resp_ref;
napi_ref conn_ref;
};
+port_data_t::port_data_t(nxt_unit_ctx_t *c, nxt_unit_port_t *p) :
+ ctx(c), port(p), ref_count(0), scheduled(false), stopped(false)
+{
+ timer.type = UV_UNKNOWN_HANDLE;
+}
+
+
+void
+port_data_t::process_port_msg()
+{
+ int rc, err;
+
+ rc = nxt_unit_process_port_msg(ctx, port);
+
+ if (rc != NXT_UNIT_OK) {
+ return;
+ }
+
+ if (timer.type == UV_UNKNOWN_HANDLE) {
+ err = uv_timer_init(poll.loop, &timer);
+ if (err < 0) {
+ nxt_unit_warn(ctx, "Failed to init uv.poll");
+ return;
+ }
+
+ ref_count++;
+ timer.data = this;
+ }
+
+ if (!scheduled && !stopped) {
+ uv_timer_start(&timer, timer_callback, 0, 0);
+
+ scheduled = true;
+ }
+}
+
+
+void
+port_data_t::stop()
+{
+ stopped = true;
+
+ uv_poll_stop(&poll);
+
+ uv_close((uv_handle_t *) &poll, delete_data);
+
+ if (timer.type == UV_UNKNOWN_HANDLE) {
+ return;
+ }
+
+ uv_timer_stop(&timer);
+
+ uv_close((uv_handle_t *) &timer, delete_data);
+}
+
+
+template<typename T>
+port_data_t *
+port_data_t::get(T *handle)
+{
+ return (port_data_t *) handle->data;
+}
+
+
+void
+port_data_t::read_callback(uv_poll_t *handle, int status, int events)
+{
+ get(handle)->process_port_msg();
+}
+
+
+void
+port_data_t::timer_callback(uv_timer_t *handle)
+{
+ port_data_t *data;
+
+ data = get(handle);
+
+ data->scheduled = false;
+ if (data->stopped) {
+ return;
+ }
+
+ data->process_port_msg();
+}
+
+
+void
+port_data_t::delete_data(uv_handle_t* handle)
+{
+ port_data_t *data;
+
+ data = get(handle);
+
+ if (--data->ref_count <= 0) {
+ delete data;
+ }
+}
+
+
Unit::Unit(napi_env env, napi_value jsthis):
nxt_napi(env),
wrapper_(wrap(jsthis, this, destroy)),
@@ -65,6 +180,7 @@ Unit::init(napi_env env, napi_value exports)
constructor_ = napi.create_reference(ctor);
napi.set_named_property(exports, "Unit", ctor);
+ napi.set_named_property(exports, "request_read", request_read);
napi.set_named_property(exports, "response_send_headers",
response_send_headers);
napi.set_named_property(exports, "response_write", response_write);
@@ -206,7 +322,7 @@ Unit::request_handler(nxt_unit_request_info_t *req)
server_obj = get_server_object();
socket = create_socket(server_obj, req);
- request = create_request(server_obj, socket);
+ request = create_request(server_obj, socket, req);
response = create_response(server_obj, request, req);
create_headers(req, request);
@@ -301,6 +417,7 @@ Unit::close_handler(nxt_unit_request_info_t *req)
nxt_napi::create(0));
remove_wrap(req_data->sock_ref);
+ remove_wrap(req_data->req_ref);
remove_wrap(req_data->resp_ref);
remove_wrap(req_data->conn_ref);
@@ -350,59 +467,50 @@ Unit::shm_ack_handler(nxt_unit_ctx_t *ctx)
}
-static void
-nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
-{
- port_data_t *data;
-
- data = (port_data_t *) handle->data;
-
- nxt_unit_process_port_msg(data->ctx, data->port);
-}
-
-
int
Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
{
- int err;
- Unit *obj;
- uv_loop_t *loop;
- port_data_t *data;
- napi_status status;
+ int err;
+ Unit *obj;
+ uv_loop_t *loop;
+ port_data_t *data;
+ napi_status status;
if (port->in_fd != -1) {
- obj = reinterpret_cast<Unit *>(ctx->unit->data);
-
if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
nxt_unit_warn(ctx, "fcntl(%d, O_NONBLOCK) failed: %s (%d)",
port->in_fd, strerror(errno), errno);
return -1;
}
+ obj = reinterpret_cast<Unit *>(ctx->unit->data);
+
status = napi_get_uv_event_loop(obj->env(), &loop);
if (status != napi_ok) {
nxt_unit_warn(ctx, "Failed to get uv.loop");
return NXT_UNIT_ERROR;
}
- data = new port_data_t;
+ data = new port_data_t(ctx, port);
err = uv_poll_init(loop, &data->poll, port->in_fd);
if (err < 0) {
nxt_unit_warn(ctx, "Failed to init uv.poll");
+ delete data;
return NXT_UNIT_ERROR;
}
- err = uv_poll_start(&data->poll, UV_READABLE, nxt_uv_read_callback);
+ err = uv_poll_start(&data->poll, UV_READABLE,
+ port_data_t::read_callback);
if (err < 0) {
nxt_unit_warn(ctx, "Failed to start uv.poll");
+ delete data;
return NXT_UNIT_ERROR;
}
port->data = data;
- data->ctx = ctx;
- data->port = port;
+ data->ref_count++;
data->poll.data = data;
}
@@ -418,26 +526,11 @@ Unit::remove_port(nxt_unit_t *unit, nxt_unit_port_t *port)
if (port->data != NULL) {
data = (port_data_t *) port->data;
- if (data->port == port) {
- uv_poll_stop(&data->poll);
-
- uv_close((uv_handle_t *) &data->poll, delete_port_data);
- }
+ data->stop();
}
}
-static void
-delete_port_data(uv_handle_t* handle)
-{
- port_data_t *data;
-
- data = (port_data_t *) handle->data;
-
- delete data;
-}
-
-
void
Unit::quit_cb(nxt_unit_ctx_t *ctx)
{
@@ -488,9 +581,8 @@ Unit::get_server_object()
void
Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
{
- void *data;
uint32_t i;
- napi_value headers, raw_headers, buffer;
+ napi_value headers, raw_headers;
napi_status status;
nxt_unit_request_t *r;
@@ -515,11 +607,6 @@ Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
set_named_property(request, "url", r->target, r->target_length);
set_named_property(request, "_websocket_handshake", r->websocket_handshake);
-
- buffer = create_buffer((size_t) req->content_length, &data);
- nxt_unit_request_read(req, data, req->content_length);
-
- set_named_property(request, "_data", buffer);
}
@@ -577,13 +664,20 @@ Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
napi_value
-Unit::create_request(napi_value server_obj, napi_value socket)
+Unit::create_request(napi_value server_obj, napi_value socket,
+ nxt_unit_request_info_t *req)
{
- napi_value constructor;
+ napi_value constructor, res;
+ req_data_t *req_data;
constructor = get_named_property(server_obj, "ServerRequest");
- return new_instance(constructor, server_obj, socket);
+ res = new_instance(constructor, server_obj, socket);
+
+ req_data = (req_data_t *) req->data;
+ req_data->req_ref = wrap(res, req, req_destroy);
+
+ return res;
}
@@ -643,6 +737,47 @@ Unit::create_websocket_frame(napi_value server_obj,
napi_value
+Unit::request_read(napi_env env, napi_callback_info info)
+{
+ void *data;
+ uint32_t wm;
+ nxt_napi napi(env);
+ napi_value this_arg, argv, buffer;
+ nxt_unit_request_info_t *req;
+
+ try {
+ this_arg = napi.get_cb_info(info, argv);
+
+ try {
+ req = napi.get_request_info(this_arg);
+
+ } catch (exception &e) {
+ return nullptr;
+ }
+
+ if (req->content_length == 0) {
+ return nullptr;
+ }
+
+ wm = napi.get_value_uint32(argv);
+
+ if (wm > req->content_length) {
+ wm = req->content_length;
+ }
+
+ buffer = napi.create_buffer((size_t) wm, &data);
+ nxt_unit_request_read(req, data, wm);
+
+ } catch (exception &e) {
+ napi.throw_error(e);
+ return nullptr;
+ }
+
+ return buffer;
+}
+
+
+napi_value
Unit::response_send_headers(napi_env env, napi_callback_info info)
{
int ret;
@@ -884,6 +1019,7 @@ Unit::response_end(napi_env env, napi_callback_info info)
req_data = (req_data_t *) req->data;
napi.remove_wrap(req_data->sock_ref);
+ napi.remove_wrap(req_data->req_ref);
napi.remove_wrap(req_data->resp_ref);
napi.remove_wrap(req_data->conn_ref);
@@ -998,33 +1134,28 @@ Unit::websocket_set_sock(napi_env env, napi_callback_info info)
void
-Unit::conn_destroy(napi_env env, void *nativeObject, void *finalize_hint)
+Unit::conn_destroy(napi_env env, void *r, void *finalize_hint)
{
- nxt_unit_request_info_t *req;
-
- req = (nxt_unit_request_info_t *) nativeObject;
-
- nxt_unit_warn(NULL, "conn_destroy: %p", req);
+ nxt_unit_req_debug(NULL, "conn_destroy: %p", r);
}
void
-Unit::sock_destroy(napi_env env, void *nativeObject, void *finalize_hint)
+Unit::sock_destroy(napi_env env, void *r, void *finalize_hint)
{
- nxt_unit_request_info_t *req;
-
- req = (nxt_unit_request_info_t *) nativeObject;
-
- nxt_unit_warn(NULL, "sock_destroy: %p", req);
+ nxt_unit_req_debug(NULL, "sock_destroy: %p", r);
}
void
-Unit::resp_destroy(napi_env env, void *nativeObject, void *finalize_hint)
+Unit::req_destroy(napi_env env, void *r, void *finalize_hint)
{
- nxt_unit_request_info_t *req;
+ nxt_unit_req_debug(NULL, "req_destroy: %p", r);
+}
- req = (nxt_unit_request_info_t *) nativeObject;
- nxt_unit_warn(NULL, "resp_destroy: %p", req);
+void
+Unit::resp_destroy(napi_env env, void *r, void *finalize_hint)
+{
+ nxt_unit_req_debug(NULL, "resp_destroy: %p", r);
}
diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h
index 07823c26..4ef40d45 100644
--- a/src/nodejs/unit-http/unit.h
+++ b/src/nodejs/unit-http/unit.h
@@ -21,6 +21,7 @@ private:
static void destroy(napi_env env, void *nativeObject, void *finalize_hint);
static void conn_destroy(napi_env env, void *nativeObject, void *finalize_hint);
static void sock_destroy(napi_env env, void *nativeObject, void *finalize_hint);
+ static void req_destroy(napi_env env, void *nativeObject, void *finalize_hint);
static void resp_destroy(napi_env env, void *nativeObject, void *finalize_hint);
static napi_value create_server(napi_env env, napi_callback_info info);
@@ -50,7 +51,8 @@ private:
napi_value create_socket(napi_value server_obj,
nxt_unit_request_info_t *req);
- napi_value create_request(napi_value server_obj, napi_value socket);
+ napi_value create_request(napi_value server_obj, napi_value socket,
+ nxt_unit_request_info_t *req);
napi_value create_response(napi_value server_obj, napi_value request,
nxt_unit_request_info_t *req);
@@ -58,6 +60,8 @@ private:
napi_value create_websocket_frame(napi_value server_obj,
nxt_unit_websocket_frame_t *ws);
+ static napi_value request_read(napi_env env, napi_callback_info info);
+
static napi_value response_send_headers(napi_env env,
napi_callback_info info);
diff --git a/src/nxt_app_queue.h b/src/nxt_app_queue.h
index 127cb8f3..a1cc2f11 100644
--- a/src/nxt_app_queue.h
+++ b/src/nxt_app_queue.h
@@ -23,7 +23,7 @@ typedef struct {
typedef struct {
- nxt_app_nncq_atomic_t nitems;
+ nxt_app_nncq_atomic_t notified;
nxt_app_nncq_t free_items;
nxt_app_nncq_t queue;
nxt_app_queue_item_t items[NXT_APP_QUEUE_SIZE];
@@ -42,7 +42,7 @@ nxt_app_queue_init(nxt_app_queue_t volatile *q)
nxt_app_nncq_enqueue(&q->free_items, i);
}
- q->nitems = 0;
+ q->notified = 0;
}
@@ -50,6 +50,7 @@ nxt_inline nxt_int_t
nxt_app_queue_send(nxt_app_queue_t volatile *q, const void *p,
uint8_t size, uint32_t tracking, int *notify, uint32_t *cookie)
{
+ int n;
nxt_app_queue_item_t *qi;
nxt_app_nncq_atomic_t i;
@@ -67,16 +68,23 @@ nxt_app_queue_send(nxt_app_queue_t volatile *q, const void *p,
nxt_app_nncq_enqueue(&q->queue, i);
- i = nxt_atomic_fetch_add(&q->nitems, 1);
+ n = nxt_atomic_cmp_set(&q->notified, 0, 1);
if (notify != NULL) {
- *notify = (i == 0);
+ *notify = n;
}
return NXT_OK;
}
+nxt_inline void
+nxt_app_queue_notification_received(nxt_app_queue_t volatile *q)
+{
+ q->notified = 0;
+}
+
+
nxt_inline nxt_bool_t
nxt_app_queue_cancel(nxt_app_queue_t volatile *q, uint32_t cookie,
uint32_t tracking)
@@ -110,8 +118,6 @@ nxt_app_queue_recv(nxt_app_queue_t volatile *q, void *p, uint32_t *cookie)
nxt_app_nncq_enqueue(&q->free_items, i);
- nxt_atomic_fetch_add(&q->nitems, -1);
-
return res;
}
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 5632f56f..632c5632 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -47,13 +47,13 @@ typedef struct {
typedef struct {
- char *home;
- nxt_str_t path;
- nxt_str_t module;
- char *callable;
- nxt_str_t protocol;
- uint32_t threads;
- uint32_t thread_stack_size;
+ char *home;
+ nxt_conf_value_t *path;
+ nxt_str_t module;
+ char *callable;
+ nxt_str_t protocol;
+ uint32_t threads;
+ uint32_t thread_stack_size;
} nxt_python_app_conf_t;
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index acb2e3de..67fa3095 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -96,6 +96,10 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt,
@@ -491,7 +495,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.type = NXT_CONF_VLDT_STRING,
}, {
.name = nxt_string("path"),
- .type = NXT_CONF_VLDT_STRING,
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_python_path,
}, {
.name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING,
@@ -1377,6 +1382,34 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
static nxt_int_t
+nxt_conf_vldt_python_path(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_python_path_element);
+ }
+
+ /* NXT_CONF_STRING */
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ if (nxt_conf_type(value) != NXT_CONF_STRING) {
+ return nxt_conf_vldt_error(vldt, "The \"path\" array must contain "
+ "only string values.");
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index 9a34a877..772d10c8 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -1686,7 +1686,10 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
static void
nxt_controller_conf_store(nxt_task_t *task, nxt_conf_value_t *conf)
{
+ void *mem;
+ u_char *end;
size_t size;
+ nxt_fd_t fd;
nxt_buf_t *b;
nxt_port_t *main_port;
nxt_runtime_t *rt;
@@ -1697,14 +1700,38 @@ nxt_controller_conf_store(nxt_task_t *task, nxt_conf_value_t *conf)
size = nxt_conf_json_length(conf, NULL);
- b = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, size);
+ fd = nxt_shm_open(task, size);
+ if (nxt_slow_path(fd == -1)) {
+ return;
+ }
+
+ mem = nxt_mem_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (nxt_slow_path(mem == MAP_FAILED)) {
+ goto fail;
+ }
+
+ end = nxt_conf_json_print(mem, conf, NULL);
+
+ nxt_mem_munmap(mem, size);
- if (nxt_fast_path(b != NULL)) {
- b->mem.free = nxt_conf_json_print(b->mem.free, conf, NULL);
+ size = end - (u_char *) mem;
- (void) nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CONF_STORE,
- -1, 0, -1, b);
+ b = nxt_buf_mem_alloc(task->thread->engine->mem_pool, sizeof(size_t), 0);
+ if (nxt_slow_path(b == NULL)) {
+ goto fail;
}
+
+ b->mem.free = nxt_cpymem(b->mem.pos, &size, sizeof(size_t));
+
+ (void) nxt_port_socket_write(task, main_port,
+ NXT_PORT_MSG_CONF_STORE | NXT_PORT_MSG_CLOSE_FD,
+ fd, 0, -1, b);
+
+ return;
+
+fail:
+
+ nxt_fd_close(fd);
}
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index dccbe56c..d3da6942 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -174,6 +174,8 @@ static nxt_http_field_proc_t nxt_h1p_fields[] = {
{ nxt_string("Content-Type"), &nxt_http_request_field,
offsetof(nxt_http_request_t, content_type) },
{ nxt_string("Content-Length"), &nxt_http_request_content_length, 0 },
+ { nxt_string("Authorization"), &nxt_http_request_field,
+ offsetof(nxt_http_request_t, authorization) },
};
@@ -1151,19 +1153,19 @@ static const nxt_str_t nxt_http_client_error[] = {
nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"),
nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"),
nxt_string("HTTP/1.1 417 Expectation Failed\r\n"),
- nxt_string("HTTP/1.1 418\r\n"),
- nxt_string("HTTP/1.1 419\r\n"),
- nxt_string("HTTP/1.1 420\r\n"),
- nxt_string("HTTP/1.1 421\r\n"),
- nxt_string("HTTP/1.1 422\r\n"),
- nxt_string("HTTP/1.1 423\r\n"),
- nxt_string("HTTP/1.1 424\r\n"),
- nxt_string("HTTP/1.1 425\r\n"),
+ nxt_string("HTTP/1.1 418 I'm a teapot\r\n"),
+ nxt_string("HTTP/1.1 419 \r\n"),
+ nxt_string("HTTP/1.1 420 \r\n"),
+ nxt_string("HTTP/1.1 421 Misdirected Request\r\n"),
+ nxt_string("HTTP/1.1 422 Unprocessable Entity\r\n"),
+ nxt_string("HTTP/1.1 423 Locked\r\n"),
+ nxt_string("HTTP/1.1 424 Failed Dependency\r\n"),
+ nxt_string("HTTP/1.1 425 \r\n"),
nxt_string("HTTP/1.1 426 Upgrade Required\r\n"),
- nxt_string("HTTP/1.1 427\r\n"),
- nxt_string("HTTP/1.1 428\r\n"),
- nxt_string("HTTP/1.1 429\r\n"),
- nxt_string("HTTP/1.1 430\r\n"),
+ nxt_string("HTTP/1.1 427 \r\n"),
+ nxt_string("HTTP/1.1 428 \r\n"),
+ nxt_string("HTTP/1.1 429 \r\n"),
+ nxt_string("HTTP/1.1 430 \r\n"),
nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"),
};
@@ -1190,7 +1192,7 @@ static const nxt_str_t nxt_http_server_error[] = {
};
-#define UNKNOWN_STATUS_LENGTH nxt_length("HTTP/1.1 65536\r\n")
+#define UNKNOWN_STATUS_LENGTH nxt_length("HTTP/1.1 999 \r\n")
static void
nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
@@ -1248,13 +1250,16 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
{
status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR];
- } else {
- p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH,
- "HTTP/1.1 %03d\r\n", n);
+ } else if (n <= NXT_HTTP_STATUS_MAX) {
+ (void) nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH,
+ "HTTP/1.1 %03d \r\n", n);
- unknown_status.length = p - buf;
+ unknown_status.length = UNKNOWN_STATUS_LENGTH;
unknown_status.start = buf;
status = &unknown_status;
+
+ } else {
+ status = &nxt_http_server_error[0];
}
size = status->length;
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 1418be95..e30bfeb4 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -156,6 +156,7 @@ struct nxt_http_request_s {
nxt_http_field_t *cookie;
nxt_http_field_t *referer;
nxt_http_field_t *user_agent;
+ nxt_http_field_t *authorization;
nxt_off_t content_length_n;
nxt_sockaddr_t *remote;
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 9aaa708e..28545fc9 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -813,6 +813,12 @@ nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
next = 0;
+ /*
+ * A workaround for GCC 10 with -flto -O2 flags that warns about "name"
+ * may be uninitialized in nxt_http_route_rule_name_create().
+ */
+ nxt_str_null(&name);
+
for (i = 0; i < n; i++) {
rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index 5687ef2c..df2655fc 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -395,12 +395,15 @@ static void
nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
{
ssize_t n, size;
- nxt_buf_t *b, *fb;
+ nxt_buf_t *b, *fb, *next;
nxt_off_t rest;
nxt_http_request_t *r;
b = obj;
r = data;
+
+complete_buf:
+
fb = r->out;
if (nxt_slow_path(fb == NULL || r->error)) {
@@ -424,6 +427,8 @@ nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
goto clean;
}
+ next = b->next;
+
if (n == rest) {
nxt_file_close(task, fb->file);
r->out = NULL;
@@ -439,12 +444,24 @@ nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
b->mem.free = b->mem.pos + n;
nxt_http_request_send(task, r, b);
+
+ if (next != NULL) {
+ b = next;
+ goto complete_buf;
+ }
+
return;
clean:
- nxt_mp_free(r->mem_pool, b);
- nxt_mp_release(r->mem_pool);
+ do {
+ next = b->next;
+
+ nxt_mp_free(r->mem_pool, b);
+ nxt_mp_release(r->mem_pool);
+
+ b = next;
+ } while (b != NULL);
if (fb != NULL) {
nxt_file_close(task, fb->file);
diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c
index 1e6323bc..cab0074b 100644
--- a/src/nxt_isolation.c
+++ b/src/nxt_isolation.c
@@ -676,12 +676,6 @@ nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process)
return;
}
-#if (NXT_HAVE_CLONE_NEWNS)
- if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) {
- return;
- }
-#endif
-
nxt_debug(task, "unmount all (%s)", process->name);
automount = &process->isolation.automount;
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 0cde435b..f20f2c2c 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -182,7 +182,7 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
{
nxt_string("path"),
- NXT_CONF_MAP_STR,
+ NXT_CONF_MAP_PTR,
offsetof(nxt_common_app_conf_t, u.python.path),
},
@@ -1001,21 +1001,22 @@ nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_listening_socket_t ls;
u_char message[2048];
+ port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
+ msg->port_msg.reply_port);
+ if (nxt_slow_path(port == NULL)) {
+ return;
+ }
+
b = msg->buf;
sa = (nxt_sockaddr_t *) b->mem.pos;
/* TODO check b size and make plain */
- out = NULL;
-
ls.socket = -1;
ls.error = NXT_SOCKET_ERROR_SYSTEM;
ls.start = message;
ls.end = message + sizeof(message);
- port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
- msg->port_msg.reply_port);
-
nxt_debug(task, "listening socket \"%*s\"",
(size_t) sa->length, nxt_sockaddr_start(sa));
@@ -1025,6 +1026,8 @@ nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_debug(task, "socket(\"%*s\"): %d",
(size_t) sa->length, nxt_sockaddr_start(sa), ls.socket);
+ out = NULL;
+
type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
} else {
@@ -1034,13 +1037,11 @@ nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
out = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool,
size + 1);
- if (nxt_slow_path(out == NULL)) {
- return;
- }
+ if (nxt_fast_path(out != NULL)) {
+ *out->mem.free++ = (uint8_t) ls.error;
- *out->mem.free++ = (uint8_t) ls.error;
-
- out->mem.free = nxt_cpymem(out->mem.free, ls.start, size);
+ out->mem.free = nxt_cpymem(out->mem.free, ls.start, size);
+ }
type = NXT_PORT_MSG_RPC_ERROR;
}
@@ -1408,12 +1409,45 @@ nxt_app_lang_compare(const void *v1, const void *v2)
static void
nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
{
- ssize_t n, size, offset;
- nxt_buf_t *b;
+ void *p;
+ size_t size;
+ ssize_t n;
nxt_int_t ret;
nxt_file_t file;
nxt_runtime_t *rt;
+ p = MAP_FAILED;
+
+ /*
+ * Ancient compilers like gcc 4.8.5 on CentOS 7 wants 'size' to be
+ * initialized in 'cleanup' section.
+ */
+ size = 0;
+
+ if (nxt_slow_path(msg->fd[0] == -1)) {
+ nxt_alert(task, "conf_store_handler: invalid shm fd");
+ goto error;
+ }
+
+ if (nxt_buf_mem_used_size(&msg->buf->mem) != sizeof(size_t)) {
+ nxt_alert(task, "conf_store_handler: unexpected buffer size (%d)",
+ (int) nxt_buf_mem_used_size(&msg->buf->mem));
+ goto error;
+ }
+
+ nxt_memcpy(&size, msg->buf->mem.pos, sizeof(size_t));
+
+ p = nxt_mem_mmap(NULL, size, PROT_READ, MAP_SHARED, msg->fd[0], 0);
+
+ nxt_fd_close(msg->fd[0]);
+ msg->fd[0] = -1;
+
+ if (nxt_slow_path(p == MAP_FAILED)) {
+ goto error;
+ }
+
+ nxt_debug(task, "conf_store_handler(%uz): %*s", size, size, p);
+
nxt_memzero(&file, sizeof(nxt_file_t));
rt = task->thread->runtime;
@@ -1427,33 +1461,35 @@ nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
goto error;
}
- offset = 0;
-
- for (b = msg->buf; b != NULL; b = b->next) {
- size = nxt_buf_mem_used_size(&b->mem);
-
- n = nxt_file_write(&file, b->mem.pos, size, offset);
+ n = nxt_file_write(&file, p, size, 0);
- if (nxt_slow_path(n != size)) {
- nxt_file_close(task, &file);
- (void) nxt_file_delete(file.name);
- goto error;
- }
+ nxt_file_close(task, &file);
- offset += n;
+ if (nxt_slow_path(n != (ssize_t) size)) {
+ (void) nxt_file_delete(file.name);
+ goto error;
}
- nxt_file_close(task, &file);
-
ret = nxt_file_rename(file.name, (nxt_file_name_t *) rt->conf);
if (nxt_fast_path(ret == NXT_OK)) {
- return;
+ goto cleanup;
}
error:
nxt_alert(task, "failed to store current configuration");
+
+cleanup:
+
+ if (p != MAP_FAILED) {
+ nxt_mem_munmap(p, size);
+ }
+
+ if (msg->fd[0] != -1) {
+ nxt_fd_close(msg->fd[0]);
+ msg->fd[0] = -1;
+ }
}
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index d2fbdd27..369e7f32 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -1038,6 +1038,17 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
ctx->cookie = nxt_unit_sptr_get(&f->value);
}
+ if (r->authorization_field != NXT_UNIT_NONE_FIELD) {
+ f = r->fields + r->authorization_field;
+
+ php_handle_auth_data(nxt_unit_sptr_get(&f->value));
+
+ } else {
+ SG(request_info).auth_digest = NULL;
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+ }
+
SG(sapi_headers).http_response_code = 200;
SG(request_info).path_translated = NULL;
diff --git a/src/nxt_port.c b/src/nxt_port.c
index dbcdec11..d4e46564 100644
--- a/src/nxt_port.c
+++ b/src/nxt_port.c
@@ -8,6 +8,7 @@
#include <nxt_runtime.h>
#include <nxt_port.h>
#include <nxt_router.h>
+#include <nxt_app_queue.h>
#include <nxt_port_queue.h>
@@ -84,6 +85,8 @@ nxt_port_new(nxt_task_t *task, nxt_port_id_t id, nxt_pid_t pid,
void
nxt_port_close(nxt_task_t *task, nxt_port_t *port)
{
+ size_t size;
+
nxt_debug(task, "port %p %d:%d close, type %d", port, port->pid,
port->id, port->type);
@@ -109,7 +112,10 @@ nxt_port_close(nxt_task_t *task, nxt_port_t *port)
}
if (port->queue != NULL) {
- nxt_mem_munmap(port->queue, sizeof(nxt_port_queue_t));
+ size = (port->id == (nxt_port_id_t) -1) ? sizeof(nxt_app_queue_t)
+ : sizeof(nxt_port_queue_t);
+ nxt_mem_munmap(port->queue, size);
+
port->queue = NULL;
}
}
diff --git a/src/nxt_process.h b/src/nxt_process.h
index 7afb8803..4f24b179 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -107,8 +107,6 @@ struct nxt_process_s {
nxt_port_mmaps_t incoming;
- nxt_thread_mutex_t cp_mutex;
-
uint32_t stream;
nxt_mp_t *mem_pool;
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 9dd5c30e..03fe2a6c 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -699,26 +699,39 @@ nxt_router_conf_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
void *p;
size_t size;
nxt_int_t ret;
+ nxt_port_t *port;
nxt_router_temp_conf_t *tmcf;
+ port = nxt_runtime_port_find(task->thread->runtime,
+ msg->port_msg.pid,
+ msg->port_msg.reply_port);
+ if (nxt_slow_path(port == NULL)) {
+ nxt_alert(task, "conf_data_handler: reply port not found");
+ return;
+ }
+
+ p = MAP_FAILED;
+
+ /*
+ * Ancient compilers like gcc 4.8.5 on CentOS 7 wants 'size' to be
+ * initialized in 'cleanup' section.
+ */
+ size = 0;
+
tmcf = nxt_router_temp_conf(task);
if (nxt_slow_path(tmcf == NULL)) {
- return;
+ goto fail;
}
if (nxt_slow_path(msg->fd[0] == -1)) {
- nxt_alert(task, "conf_data_handler: invalid file shm fd");
- return;
+ nxt_alert(task, "conf_data_handler: invalid shm fd");
+ goto fail;
}
if (nxt_buf_mem_used_size(&msg->buf->mem) != sizeof(size_t)) {
nxt_alert(task, "conf_data_handler: unexpected buffer size (%d)",
(int) nxt_buf_mem_used_size(&msg->buf->mem));
-
- nxt_fd_close(msg->fd[0]);
- msg->fd[0] = -1;
-
- return;
+ goto fail;
}
nxt_memcpy(&size, msg->buf->mem.pos, sizeof(size_t));
@@ -729,22 +742,14 @@ nxt_router_conf_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
msg->fd[0] = -1;
if (nxt_slow_path(p == MAP_FAILED)) {
- return;
+ goto fail;
}
nxt_debug(task, "conf_data_handler(%uz): %*s", size, size, p);
tmcf->router_conf->router = nxt_router;
tmcf->stream = msg->port_msg.stream;
- tmcf->port = nxt_runtime_port_find(task->thread->runtime,
- msg->port_msg.pid,
- msg->port_msg.reply_port);
-
- if (nxt_slow_path(tmcf->port == NULL)) {
- nxt_alert(task, "reply port not found");
-
- goto fail;
- }
+ tmcf->port = port;
nxt_port_use(task, tmcf->port, 1);
@@ -757,9 +762,27 @@ nxt_router_conf_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_router_conf_error(task, tmcf);
}
+ goto cleanup;
+
fail:
- nxt_mem_munmap(p, size);
+ nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1,
+ msg->port_msg.stream, 0, NULL);
+
+ if (tmcf != NULL) {
+ nxt_mp_destroy(tmcf->mem_pool);
+ }
+
+cleanup:
+
+ if (p != MAP_FAILED) {
+ nxt_mem_munmap(p, size);
+ }
+
+ if (msg->fd[0] != -1) {
+ nxt_fd_close(msg->fd[0]);
+ msg->fd[0] = -1;
+ }
}
@@ -1586,6 +1609,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
ret = nxt_router_app_queue_init(task, port);
if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_port_write_close(port);
+ nxt_port_read_close(port);
nxt_port_use(task, port, -1);
return NXT_ERROR;
}
@@ -3771,7 +3796,10 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_buf_chain_add(&b, nxt_http_buf_last(r));
req_rpc_data->rpc_cancel = 0;
- req_rpc_data->apr_action = NXT_APR_GOT_RESPONSE;
+
+ if (req_rpc_data->apr_action == NXT_APR_REQUEST_FAILED) {
+ req_rpc_data->apr_action = NXT_APR_GOT_RESPONSE;
+ }
nxt_request_rpc_data_unlink(task, req_rpc_data);
@@ -5169,6 +5197,7 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
req->content_length_field = NXT_UNIT_NONE_FIELD;
req->content_type_field = NXT_UNIT_NONE_FIELD;
req->cookie_field = NXT_UNIT_NONE_FIELD;
+ req->authorization_field = NXT_UNIT_NONE_FIELD;
dst_field = req->fields;
@@ -5193,6 +5222,9 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
} else if (field == r->cookie) {
req->cookie_field = dst_field - req->fields;
+
+ } else if (field == r->authorization) {
+ req->authorization_field = dst_field - req->fields;
}
nxt_debug(task, "add field 0x%04Xd, %d, %d, %p : %d %p",
@@ -5369,7 +5401,7 @@ nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_bool_t ack;
nxt_process_t *process;
nxt_free_map_t *m;
- nxt_port_mmap_header_t *hdr;
+ nxt_port_mmap_handler_t *mmap_handler;
nxt_debug(task, "oosm in %PI", msg->port_msg.pid);
@@ -5390,8 +5422,13 @@ nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_thread_mutex_lock(&process->incoming.mutex);
for (i = 0; i < process->incoming.size; i++) {
- hdr = process->incoming.elts[i].mmap_handler->hdr;
- m = hdr->free_map;
+ mmap_handler = process->incoming.elts[i].mmap_handler;
+
+ if (nxt_slow_path(mmap_handler == NULL)) {
+ continue;
+ }
+
+ m = mmap_handler->hdr->free_map;
for (mi = 0; mi < MAX_FREE_IDX; mi++) {
if (m[mi] != 0) {
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index d9d986da..8a86d38a 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -1387,7 +1387,6 @@ nxt_runtime_process_new(nxt_runtime_t *rt)
nxt_queue_init(&process->ports);
nxt_thread_mutex_create(&process->incoming.mutex);
- nxt_thread_mutex_create(&process->cp_mutex);
process->use_count = 1;
@@ -1408,7 +1407,6 @@ nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process)
nxt_port_mmaps_destroy(&process->incoming, 1);
nxt_thread_mutex_destroy(&process->incoming.mutex);
- nxt_thread_mutex_destroy(&process->cp_mutex);
/* processes from nxt_runtime_process_get() have no memory pool */
if (process->mem_pool != NULL) {
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index 097f50d6..2fef17c5 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -784,8 +784,8 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
{
int rc;
int ready_fd, router_fd, read_in_fd, read_out_fd;
- char *unit_init, *version_end;
- long version_length;
+ char *unit_init, *version_end, *vars;
+ size_t version_length;
int64_t ready_pid, router_pid, read_pid;
uint32_t ready_stream, router_id, ready_id, read_id;
@@ -797,21 +797,30 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
return NXT_UNIT_ERROR;
}
- nxt_unit_debug(NULL, "%s='%s'", NXT_UNIT_INIT_ENV, unit_init);
+ version_end = strchr(unit_init, ';');
+ if (nxt_slow_path(version_end == NULL)) {
+ nxt_unit_alert(NULL, "Unit version not found in %s=\"%s\"",
+ NXT_UNIT_INIT_ENV, unit_init);
- version_length = nxt_length(NXT_VERSION);
+ return NXT_UNIT_ERROR;
+ }
- version_end = strchr(unit_init, ';');
- if (version_end == NULL
- || version_end - unit_init != version_length
- || memcmp(unit_init, NXT_VERSION, version_length) != 0)
- {
- nxt_unit_alert(NULL, "version check error");
+ version_length = version_end - unit_init;
+
+ rc = version_length != nxt_length(NXT_VERSION)
+ || memcmp(unit_init, NXT_VERSION, nxt_length(NXT_VERSION));
+
+ if (nxt_slow_path(rc != 0)) {
+ nxt_unit_alert(NULL, "versions mismatch: the Unit daemon has version "
+ "%.*s, while the app was compiled with libunit %s",
+ (int) version_length, unit_init, NXT_VERSION);
return NXT_UNIT_ERROR;
}
- rc = sscanf(version_end + 1,
+ vars = version_end + 1;
+
+ rc = sscanf(vars,
"%"PRIu32";"
"%"PRId64",%"PRIu32",%d;"
"%"PRId64",%"PRIu32",%d;"
@@ -823,12 +832,22 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
&read_pid, &read_id, &read_in_fd, &read_out_fd,
log_fd, shm_limit);
+ if (nxt_slow_path(rc == EOF)) {
+ nxt_unit_alert(NULL, "sscanf(%s) failed: %s (%d) for %s env",
+ vars, strerror(errno), errno, NXT_UNIT_INIT_ENV);
+
+ return NXT_UNIT_ERROR;
+ }
+
if (nxt_slow_path(rc != 13)) {
- nxt_unit_alert(NULL, "failed to scan variables: %d", rc);
+ nxt_unit_alert(NULL, "invalid number of variables in %s env: "
+ "found %d of %d in %s", NXT_UNIT_INIT_ENV, rc, 13, vars);
return NXT_UNIT_ERROR;
}
+ nxt_unit_debug(NULL, "%s='%s'", NXT_UNIT_INIT_ENV, unit_init);
+
nxt_unit_port_id_init(&ready_port->id, (pid_t) ready_pid, ready_id);
ready_port->in_fd = -1;
@@ -3587,7 +3606,10 @@ nxt_unit_wait_shm_ack(nxt_unit_ctx_t *ctx)
return NXT_UNIT_ERROR;
}
- res = nxt_unit_ctx_port_recv(ctx, ctx_impl->read_port, rbuf);
+ do {
+ res = nxt_unit_ctx_port_recv(ctx, ctx_impl->read_port, rbuf);
+ } while (res == NXT_UNIT_AGAIN);
+
if (res == NXT_UNIT_ERROR) {
nxt_unit_read_buf_release(ctx, rbuf);
@@ -4994,7 +5016,6 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
int rc;
nxt_unit_impl_t *lib;
nxt_unit_read_buf_t *rbuf;
- nxt_unit_ctx_impl_t *ctx_impl;
rbuf = nxt_unit_read_buf_get(ctx);
if (nxt_slow_path(rbuf == NULL)) {
@@ -5002,9 +5023,6 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
}
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
- ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
-
-retry:
if (port == lib->shared_port) {
rc = nxt_unit_shared_port_recv(ctx, port, rbuf);
@@ -5030,15 +5048,6 @@ retry:
nxt_unit_process_ready_req(ctx);
- if (ctx_impl->online) {
- rbuf = nxt_unit_read_buf_get(ctx);
- if (nxt_slow_path(rbuf == NULL)) {
- return NXT_UNIT_ERROR;
- }
-
- goto retry;
- }
-
return rc;
}
@@ -6073,7 +6082,10 @@ static int
nxt_unit_shared_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port,
nxt_unit_read_buf_t *rbuf)
{
- int res;
+ int res;
+ nxt_unit_port_impl_t *port_impl;
+
+ port_impl = nxt_container_of(port, nxt_unit_port_impl_t, port);
retry:
@@ -6086,6 +6098,8 @@ retry:
}
if (nxt_unit_is_read_queue(rbuf)) {
+ nxt_app_queue_notification_received(port_impl->queue);
+
nxt_unit_debug(ctx, "port{%d,%d} recv %d read_queue",
(int) port->id.pid, (int) port->id.id, (int) rbuf->size);
diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h
index fede00d2..5dbf648d 100644
--- a/src/nxt_unit_request.h
+++ b/src/nxt_unit_request.h
@@ -31,6 +31,7 @@ struct nxt_unit_request_s {
uint32_t content_length_field;
uint32_t content_type_field;
uint32_t cookie_field;
+ uint32_t authorization_field;
uint64_t content_length;
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
index faf0c0e1..d8204937 100644
--- a/src/python/nxt_python.c
+++ b/src/python/nxt_python.c
@@ -24,6 +24,7 @@ typedef struct {
static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data);
+static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
static int nxt_python_init_threads(nxt_python_app_conf_t *c);
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
static void *nxt_python_thread_func(void *main_ctx);
@@ -67,7 +68,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
int rc;
char *nxt_py_module;
size_t len;
- PyObject *obj, *pypath, *module;
+ PyObject *obj, *module;
nxt_str_t proto;
const char *callable;
nxt_unit_ctx_t *unit_ctx;
@@ -162,38 +163,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
}
nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
+
+ /* obj is a Borrowed reference. */
+ obj = NULL;
+
if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
nxt_alert(task, "Python failed to get \"flush\" attribute of "
"\"sys.stderr\" object");
goto fail;
}
- /* obj is a Borrowed reference. */
-
- if (c->path.length > 0) {
- obj = PyString_FromStringAndSize((char *) c->path.start,
- c->path.length);
-
- if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task, "Python failed to create string object \"%V\"",
- &c->path);
- goto fail;
- }
-
- pypath = PySys_GetObject((char *) "path");
-
- if (nxt_slow_path(pypath == NULL)) {
- nxt_alert(task, "Python failed to get \"sys.path\" list");
- goto fail;
- }
-
- if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) {
- nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
- &c->path);
- goto fail;
- }
-
- Py_DECREF(obj);
+ if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) {
+ goto fail;
}
obj = Py_BuildValue("[s]", "unit");
@@ -317,6 +298,74 @@ fail:
}
+static nxt_int_t
+nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
+{
+ int ret;
+ PyObject *path, *sys;
+ nxt_str_t str;
+ nxt_uint_t n;
+ nxt_conf_value_t *array;
+
+ if (value == NULL) {
+ return NXT_OK;
+ }
+
+ sys = PySys_GetObject((char *) "path");
+ if (nxt_slow_path(sys == NULL)) {
+ nxt_alert(task, "Python failed to get \"sys.path\" list");
+ return NXT_ERROR;
+ }
+
+ /* sys is a Borrowed reference. */
+
+ if (nxt_conf_type(value) == NXT_CONF_STRING) {
+ n = 0;
+ goto value_is_string;
+ }
+
+ /* NXT_CONF_ARRAY */
+ array = value;
+
+ n = nxt_conf_array_elements_count(array);
+
+ while (n != 0) {
+ n--;
+
+ /*
+ * Insertion in front of existing paths starting from the last element
+ * to preserve original order while giving priority to the values
+ * specified in the "path" option.
+ */
+
+ value = nxt_conf_get_array_element(array, n);
+
+ value_is_string:
+
+ nxt_conf_get_string(value, &str);
+
+ path = PyString_FromStringAndSize((char *) str.start, str.length);
+ if (nxt_slow_path(path == NULL)) {
+ nxt_alert(task, "Python failed to create string object \"%V\"",
+ &str);
+ return NXT_ERROR;
+ }
+
+ ret = PyList_Insert(sys, 0, path);
+
+ Py_DECREF(path);
+
+ if (nxt_slow_path(ret != 0)) {
+ nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
+ &str);
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
static int
nxt_python_init_threads(nxt_python_app_conf_t *c)
{
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index 98aeedf4..a6f94507 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -1131,11 +1131,12 @@ nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx)
static PyObject *
nxt_py_asgi_port_read(PyObject *self, PyObject *args)
{
- int rc;
- PyObject *arg;
- Py_ssize_t n;
- nxt_unit_ctx_t *ctx;
- nxt_unit_port_t *port;
+ int rc;
+ PyObject *arg0, *arg1, *res;
+ Py_ssize_t n;
+ nxt_unit_ctx_t *ctx;
+ nxt_unit_port_t *port;
+ nxt_py_asgi_ctx_data_t *ctx_data;
n = PyTuple_GET_SIZE(args);
@@ -1147,31 +1148,45 @@ nxt_py_asgi_port_read(PyObject *self, PyObject *args)
return PyErr_Format(PyExc_TypeError, "invalid number of arguments");
}
- arg = PyTuple_GET_ITEM(args, 0);
- if (nxt_slow_path(arg == NULL || PyLong_Check(arg) == 0)) {
+ arg0 = PyTuple_GET_ITEM(args, 0);
+ if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) {
return PyErr_Format(PyExc_TypeError,
"the first argument is not a long");
}
- ctx = PyLong_AsVoidPtr(arg);
+ ctx = PyLong_AsVoidPtr(arg0);
- arg = PyTuple_GET_ITEM(args, 1);
- if (nxt_slow_path(arg == NULL || PyLong_Check(arg) == 0)) {
+ arg1 = PyTuple_GET_ITEM(args, 1);
+ if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) {
return PyErr_Format(PyExc_TypeError,
"the second argument is not a long");
}
- port = PyLong_AsVoidPtr(arg);
-
- nxt_unit_debug(ctx, "asgi_port_read %p %p", ctx, port);
+ port = PyLong_AsVoidPtr(arg1);
rc = nxt_unit_process_port_msg(ctx, port);
+ nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc);
+
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
return PyErr_Format(PyExc_RuntimeError,
"error processing port %d message", port->id.id);
}
+ if (rc == NXT_UNIT_OK) {
+ ctx_data = ctx->data;
+
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon,
+ nxt_py_port_read,
+ arg0, arg1, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'");
+ nxt_python_print_exception();
+ }
+
+ Py_XDECREF(res);
+ }
+
Py_RETURN_NONE;
}
diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c
index da7b183c..77c45af5 100644
--- a/src/python/nxt_python_wsgi.c
+++ b/src/python/nxt_python_wsgi.c
@@ -59,6 +59,7 @@ static void nxt_python_wsgi_done(void);
static void nxt_python_request_handler(nxt_unit_request_info_t *req);
static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c);
+static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req);
static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx);
static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size);
@@ -221,6 +222,7 @@ nxt_python_wsgi_ctx_data_alloc(void **pdata)
}
pctx->write = NULL;
+ pctx->environ = NULL;
pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method,
(PyObject *) pctx);
@@ -237,6 +239,11 @@ nxt_python_wsgi_ctx_data_alloc(void **pdata)
goto fail;
}
+ pctx->environ = nxt_python_copy_environ(NULL);
+ if (nxt_slow_path(pctx->environ == NULL)) {
+ goto fail;
+ }
+
*pdata = pctx;
return NXT_UNIT_OK;
@@ -258,6 +265,7 @@ nxt_python_wsgi_ctx_data_free(void *data)
Py_XDECREF(pctx->start_resp);
Py_XDECREF(pctx->write);
+ Py_XDECREF(pctx->environ);
Py_XDECREF(pctx);
}
@@ -295,6 +303,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
int rc;
PyObject *environ, *args, *response, *iterator, *item;
PyObject *close, *result;
+ nxt_bool_t prepare_environ;
nxt_python_ctx_t *pctx;
pctx = req->ctx->data;
@@ -305,6 +314,19 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
PyEval_RestoreThread(pctx->thread_state);
+ if (nxt_slow_path(pctx->environ == NULL)) {
+ pctx->environ = nxt_python_copy_environ(req);
+
+ if (pctx->environ == NULL) {
+ prepare_environ = 0;
+
+ rc = NXT_UNIT_ERROR;
+ goto done;
+ }
+ }
+
+ prepare_environ = 1;
+
environ = nxt_python_get_environ(pctx);
if (nxt_slow_path(environ == NULL)) {
rc = NXT_UNIT_ERROR;
@@ -418,6 +440,14 @@ done:
pctx->req = NULL;
nxt_unit_request_done(req, rc);
+
+ if (nxt_fast_path(prepare_environ)) {
+ PyEval_RestoreThread(pctx->thread_state);
+
+ pctx->environ = nxt_python_copy_environ(NULL);
+
+ pctx->thread_state = PyEval_SaveThread();
+ }
}
@@ -532,23 +562,30 @@ fail:
static PyObject *
-nxt_python_get_environ(nxt_python_ctx_t *pctx)
+nxt_python_copy_environ(nxt_unit_request_info_t *req)
{
- int rc;
- uint32_t i, j, vl;
- PyObject *environ;
- nxt_unit_field_t *f, *f2;
- nxt_unit_request_t *r;
+ PyObject *environ;
environ = PyDict_Copy(nxt_py_environ_ptyp);
+
if (nxt_slow_path(environ == NULL)) {
- nxt_unit_req_error(pctx->req,
+ nxt_unit_req_alert(req,
"Python failed to copy the \"environ\" dictionary");
-
- return NULL;
+ nxt_python_print_exception();
}
- pctx->environ = environ;
+ return environ;
+}
+
+
+static PyObject *
+nxt_python_get_environ(nxt_python_ctx_t *pctx)
+{
+ int rc;
+ uint32_t i, j, vl;
+ PyObject *environ;
+ nxt_unit_field_t *f, *f2;
+ nxt_unit_request_t *r;
r = pctx->req->request;
@@ -628,7 +665,7 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
#undef RC
- if (nxt_slow_path(PyDict_SetItem(environ, nxt_py_wsgi_input_str,
+ if (nxt_slow_path(PyDict_SetItem(pctx->environ, nxt_py_wsgi_input_str,
(PyObject *) pctx) != 0))
{
nxt_unit_req_error(pctx->req,
@@ -636,11 +673,15 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
goto fail;
}
+ environ = pctx->environ;
+ pctx->environ = NULL;
+
return environ;
fail:
- Py_DECREF(environ);
+ Py_DECREF(pctx->environ);
+ pctx->environ = NULL;
return NULL;
}
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index 698d4a43..0aad887d 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -38,6 +38,7 @@ static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx);
static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
static void *nxt_ruby_request_handler_gvl(void *req);
static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx);
+static void *nxt_ruby_thread_create_gvl(void *rctx);
static VALUE nxt_ruby_thread_func(VALUE arg);
static void *nxt_ruby_unit_run(void *ctx);
static void nxt_ruby_ubf(void *ctx);
@@ -1141,7 +1142,7 @@ nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx)
rctx->ctx = ctx;
- res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx);
+ res = (VALUE) rb_thread_call_with_gvl(nxt_ruby_thread_create_gvl, rctx);
if (nxt_fast_path(res != Qnil)) {
nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
@@ -1159,6 +1160,17 @@ nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx)
}
+static void *
+nxt_ruby_thread_create_gvl(void *rctx)
+{
+ VALUE res;
+
+ res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx);
+
+ return (void *) (uintptr_t) res;
+}
+
+
static VALUE
nxt_ruby_thread_func(VALUE arg)
{
diff --git a/test/conftest.py b/test/conftest.py
index 3edc471d..b36aabad 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -4,7 +4,6 @@ import platform
import re
import shutil
import signal
-import socket
import stat
import subprocess
import sys
@@ -13,10 +12,13 @@ import time
from multiprocessing import Process
import pytest
-
from unit.check.go import check_go
+from unit.check.isolation import check_isolation
from unit.check.node import check_node
from unit.check.tls import check_openssl
+from unit.option import option
+from unit.utils import public_dir
+from unit.utils import waitforfiles
def pytest_addoption(parser):
@@ -27,13 +29,13 @@ def pytest_addoption(parser):
help="Detailed output for tests",
)
parser.addoption(
- "--print_log",
+ "--print-log",
default=False,
action="store_true",
help="Print unit.log to stdout in case of errors",
)
parser.addoption(
- "--save_log",
+ "--save-log",
default=False,
action="store_true",
help="Save unit.log after the test execution",
@@ -44,16 +46,24 @@ def pytest_addoption(parser):
action="store_true",
help="Run unsafe tests",
)
+ parser.addoption(
+ "--user",
+ type=str,
+ help="Default user for non-privileged processes of unitd",
+ )
unit_instance = {}
_processes = []
-option = None
-
def pytest_configure(config):
- global option
- option = config.option
+ option.config = config.option
+
+ option.detailed = config.option.detailed
+ option.print_log = config.option.print_log
+ option.save_log = config.option.save_log
+ option.unsafe = config.option.unsafe
+ option.user = config.option.user
option.generated_tests = {}
option.current_dir = os.path.abspath(
@@ -63,16 +73,15 @@ def pytest_configure(config):
option.architecture = platform.architecture()[0]
option.system = platform.system()
+ option.cache_dir = tempfile.mkdtemp(prefix='unit-test-cache-')
+ public_dir(option.cache_dir)
+
# set stdout to non-blocking
if option.detailed or option.print_log:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
-def skip_alert(*alerts):
- option.skip_alerts.extend(alerts)
-
-
def pytest_generate_tests(metafunc):
cls = metafunc.cls
if (not hasattr(cls, 'application_type')
@@ -122,6 +131,7 @@ def pytest_sessionstart(session):
option.available = {'modules': {}, 'features': {}}
unit = unit_run()
+ option.temp_dir = unit['temp_dir']
# read unit.log
@@ -160,8 +170,12 @@ def pytest_sessionstart(session):
k: v for k, v in option.available['modules'].items() if v is not None
}
+ check_isolation()
+
unit_stop()
+ _check_alerts()
+
shutil.rmtree(unit_instance['temp_dir'])
@@ -177,6 +191,40 @@ def pytest_runtest_makereport(item, call):
setattr(item, "rep_" + rep.when, rep)
+@pytest.fixture(scope='class', autouse=True)
+def check_prerequisites(request):
+ cls = request.cls
+ missed = []
+
+ # check modules
+
+ if 'modules' in cls.prerequisites:
+ available_modules = list(option.available['modules'].keys())
+
+ for module in cls.prerequisites['modules']:
+ if module in available_modules:
+ continue
+
+ missed.append(module)
+
+ if missed:
+ pytest.skip('Unit has no ' + ', '.join(missed) + ' module(s)')
+
+ # check features
+
+ if 'features' in cls.prerequisites:
+ available_features = list(option.available['features'].keys())
+
+ for feature in cls.prerequisites['features']:
+ if feature in available_features:
+ continue
+
+ missed.append(feature)
+
+ if missed:
+ pytest.skip(', '.join(missed) + ' feature(s) not supported')
+
+
@pytest.fixture(autouse=True)
def run(request):
unit = unit_run()
@@ -239,26 +287,28 @@ def unit_run():
os.mkdir(temp_dir + '/state')
+ unitd_args = [
+ unitd,
+ '--no-daemon',
+ '--modules',
+ build_dir,
+ '--state',
+ temp_dir + '/state',
+ '--pid',
+ temp_dir + '/unit.pid',
+ '--log',
+ temp_dir + '/unit.log',
+ '--control',
+ 'unix:' + temp_dir + '/control.unit.sock',
+ '--tmp',
+ temp_dir,
+ ]
+
+ if option.user:
+ unitd_args.extend(['--user', option.user])
+
with open(temp_dir + '/unit.log', 'w') as log:
- unit_instance['process'] = subprocess.Popen(
- [
- unitd,
- '--no-daemon',
- '--modules',
- build_dir,
- '--state',
- temp_dir + '/state',
- '--pid',
- temp_dir + '/unit.pid',
- '--log',
- temp_dir + '/unit.log',
- '--control',
- 'unix:' + temp_dir + '/control.unit.sock',
- '--tmp',
- temp_dir,
- ],
- stderr=log,
- )
+ unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log)
if not waitforfiles(temp_dir + '/control.unit.sock'):
_print_log()
@@ -293,34 +343,6 @@ def unit_stop():
p.kill()
return 'Could not terminate unit'
-def public_dir(path):
- os.chmod(path, 0o777)
-
- for root, dirs, files in os.walk(path):
- for d in dirs:
- os.chmod(os.path.join(root, d), 0o777)
- for f in files:
- os.chmod(os.path.join(root, f), 0o777)
-
-def waitforfiles(*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
-
def _check_alerts(path=None):
@@ -335,7 +357,7 @@ def _check_alerts(path=None):
alerts = re.findall(r'.+\[alert\].+', log)
if alerts:
- print('All alerts/sanitizer errors found in log:')
+ print('\nAll alerts/sanitizer errors found in log:')
[print(alert) for alert in alerts]
found = True
@@ -399,26 +421,13 @@ def stop_processes():
return 'Fail to stop process(es)'
-def waitforsocket(port):
- ret = False
-
- for i in range(50):
- try:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect(('127.0.0.1', port))
- ret = True
- break
-
- except KeyboardInterrupt:
- raise
-
- except:
- sock.close()
- time.sleep(0.1)
+@pytest.fixture()
+def skip_alert():
+ def _skip(*alerts):
+ option.skip_alerts.extend(alerts)
- sock.close()
+ return _skip
- assert ret, 'socket connected'
@pytest.fixture
def temp_dir(request):
@@ -432,5 +441,10 @@ def is_unsafe(request):
def is_su(request):
return os.geteuid() == 0
+@pytest.fixture
+def unit_pid(request):
+ return unit_instance['process'].pid
+
def pytest_sessionfinish(session):
unit_stop()
+ shutil.rmtree(option.cache_dir)
diff --git a/test/php/auth/index.php b/test/php/auth/index.php
new file mode 100644
index 00000000..d77076d8
--- /dev/null
+++ b/test/php/auth/index.php
@@ -0,0 +1,7 @@
+<?php
+
+header('X-Digest: ' . (isset($_SERVER['PHP_AUTH_DIGEST']) ? $_SERVER['PHP_AUTH_DIGEST'] : 'not set'));
+header('X-User: ' . (isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : 'not set'));
+header('X-Password: ' . (isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : 'not set'));
+
+?>
diff --git a/test/pytest.ini b/test/pytest.ini
index fe86cef2..8952e8c6 100644
--- a/test/pytest.ini
+++ b/test/pytest.ini
@@ -1,3 +1,3 @@
[pytest]
-addopts = -vvv -s --print_log
+addopts = -vvv -s --print-log
python_functions = test_*
diff --git a/test/python/path/wsgi.py b/test/python/path/wsgi.py
new file mode 100644
index 00000000..2807f6ef
--- /dev/null
+++ b/test/python/path/wsgi.py
@@ -0,0 +1,8 @@
+import os
+import sys
+
+def application(environ, start_response):
+ body = os.pathsep.join(sys.path).encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/test_access_log.py b/test/test_access_log.py
index 511ce6c5..65d5e50a 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -1,10 +1,8 @@
import time
import pytest
-
-from conftest import option
-from conftest import unit_stop
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestAccessLog(TestApplicationPython):
@@ -50,8 +48,6 @@ class TestAccessLog(TestApplicationPython):
body='0123456789',
)
- unit_stop()
-
assert (
self.wait_for_record(r'"POST / HTTP/1.1" 200 10') is not None
), 'keepalive 2'
@@ -78,8 +74,6 @@ Connection: close
raw=True,
)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-1" "-"')
is not None
@@ -96,12 +90,12 @@ Connection: close
def test_access_log_ipv6(self):
self.load('empty')
- self.conf({"[::1]:7080": {"pass": "applications/empty"}}, 'listeners')
+ assert 'success' in self.conf(
+ {"[::1]:7080": {"pass": "applications/empty"}}, 'listeners'
+ )
self.get(sock_type='ipv6')
- unit_stop()
-
assert (
self.wait_for_record(
r'::1 - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
@@ -114,14 +108,12 @@ Connection: close
addr = option.temp_dir + '/sock'
- self.conf(
+ assert 'success' in self.conf(
{"unix:" + addr: {"pass": "applications/empty"}}, 'listeners'
)
self.get(sock_type='unix', addr=addr)
- unit_stop()
-
assert (
self.wait_for_record(
r'unix: - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
@@ -140,8 +132,6 @@ Connection: close
}
)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "referer-value" "-"')
is not None
@@ -158,8 +148,6 @@ Connection: close
}
)
- unit_stop()
-
assert (
self.wait_for_record(
r'"GET / HTTP/1.1" 200 0 "-" "user-agent-value"'
@@ -172,8 +160,6 @@ Connection: close
self.get(http_10=True)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.0" 200 0 "-" "-"') is not None
), 'http 1.0'
@@ -187,8 +173,6 @@ Connection: close
time.sleep(1)
- unit_stop()
-
assert (
self.wait_for_record(r'"GE" 400 0 "-" "-"') is not None
), 'partial'
@@ -200,8 +184,6 @@ Connection: close
self.http(b"""GET /\n""", raw=True)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET /" 400 \d+ "-" "-"') is not None
), 'partial 2'
@@ -215,8 +197,6 @@ Connection: close
time.sleep(1)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET /" 400 0 "-" "-"') is not None
), 'partial 3'
@@ -230,8 +210,6 @@ Connection: close
time.sleep(1)
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.1" 400 0 "-" "-"') is not None
), 'partial 4'
@@ -244,8 +222,6 @@ Connection: close
self.get(headers={'Connection': 'close'})
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.1" 400 \d+ "-" "-"')
is not None
@@ -256,8 +232,6 @@ Connection: close
self.get(url='/?blah&var=val')
- unit_stop()
-
assert (
self.wait_for_record(
r'"GET /\?blah&var=val HTTP/1.1" 200 0 "-" "-"'
@@ -268,12 +242,10 @@ Connection: close
def test_access_log_delete(self):
self.load('empty')
- self.conf_delete('access_log')
+ assert 'success' in self.conf_delete('access_log')
self.get(url='/delete')
- unit_stop()
-
assert self.search_in_log(r'/delete', 'access.log') is None, 'delete'
def test_access_log_change(self, temp_dir):
@@ -281,12 +253,12 @@ Connection: close
self.get()
- self.conf('"' + option.temp_dir + '/new.log"', 'access_log')
+ assert 'success' in self.conf(
+ '"' + option.temp_dir + '/new.log"', 'access_log'
+ )
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log')
is not None
diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py
index e90d78bc..5770265d 100644
--- a/test/test_asgi_application.py
+++ b/test/test_asgi_application.py
@@ -3,10 +3,8 @@ import time
from distutils.version import LooseVersion
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestASGIApplication(TestApplicationPython):
@@ -361,7 +359,7 @@ Connection: close
self.get(headers=headers_delay_1)
- def test_asgi_application_loading_error(self):
+ def test_asgi_application_loading_error(self, skip_alert):
skip_alert(r'Python failed to import module "blah"')
self.load('empty', module="blah")
diff --git a/test/test_asgi_lifespan.py b/test/test_asgi_lifespan.py
index 3f29c7e7..3ecece43 100644
--- a/test/test_asgi_lifespan.py
+++ b/test/test_asgi_lifespan.py
@@ -2,11 +2,9 @@ import os
from distutils.version import LooseVersion
import pytest
-
-from conftest import option
-from conftest import public_dir
from conftest import unit_stop
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestASGILifespan(TestApplicationPython):
diff --git a/test/test_asgi_websockets.py b/test/test_asgi_websockets.py
index 54984526..6121fcc5 100644
--- a/test/test_asgi_websockets.py
+++ b/test/test_asgi_websockets.py
@@ -3,11 +3,9 @@ import time
from distutils.version import LooseVersion
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.lang.python import TestApplicationPython
from unit.applications.websockets import TestApplicationWebsocket
+from unit.option import option
class TestASGIWebsockets(TestApplicationPython):
@@ -17,7 +15,8 @@ class TestASGIWebsockets(TestApplicationPython):
ws = TestApplicationWebsocket()
- def setup_method(self):
+ @pytest.fixture(autouse=True)
+ def setup_method_fixture(self, request, skip_alert):
assert 'success' in self.conf(
{'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
), 'clear keepalive_interval'
@@ -64,6 +63,9 @@ class TestASGIWebsockets(TestApplicationPython):
key
), 'key'
+ # remove "mirror" application
+ self.load('websockets/subprotocol')
+
def test_asgi_websockets_subprotocol(self):
self.load('websockets/subprotocol')
@@ -93,6 +95,27 @@ class TestASGIWebsockets(TestApplicationPython):
sock.close()
+ def test_asgi_websockets_mirror_app_change(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ assert message == frame['data'].decode('utf-8'), 'mirror'
+
+ self.load('websockets/subprotocol')
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ assert message == frame['data'].decode('utf-8'), 'mirror 2'
+
+ sock.close()
+
def test_asgi_websockets_no_mask(self):
self.load('websockets/mirror')
diff --git a/test/test_configuration.py b/test/test_configuration.py
index d1e6f000..b7417264 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -1,6 +1,4 @@
import pytest
-
-from conftest import skip_alert
from unit.control import TestControl
@@ -337,7 +335,7 @@ class TestConfiguration(TestControl):
assert 'success' in self.conf(conf)
- def test_unprivileged_user_error(self, is_su):
+ def test_unprivileged_user_error(self, is_su, skip_alert):
skip_alert(r'cannot set user "root"', r'failed to apply new conf')
if is_su:
pytest.skip('unprivileged tests')
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 8c77dfc5..e833d190 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -149,7 +149,7 @@ class TestGoApplication(TestApplicationGo):
arg2 = '--cc-opt=\'-O0 -DNXT_DEBUG_MEMORY=1 -fsanitize=address\''
arg3 = '--debug'
- self.conf(
+ assert 'success' in self.conf(
'["' + arg1 + '", "' + arg2 + '", "' + arg3 + '"]',
'applications/command_line_arguments/arguments',
)
@@ -163,15 +163,15 @@ class TestGoApplication(TestApplicationGo):
args_path = 'applications/command_line_arguments/arguments'
- self.conf('["0", "a", "$", ""]', args_path)
+ assert 'success' in self.conf('["0", "a", "$", ""]', args_path)
assert self.get()['body'] == '0,a,$,', 'arguments'
- self.conf('["-1", "b", "%"]', args_path)
+ assert 'success' in self.conf('["-1", "b", "%"]', args_path)
assert self.get()['body'] == '-1,b,%', 'arguments change'
- self.conf('[]', args_path)
+ assert 'success' in self.conf('[]', args_path)
assert (
self.get()['headers']['Content-Length'] == '0'
diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py
index 8c4a6b9c..e8fa26c6 100644
--- a/test/test_go_isolation.py
+++ b/test/test_go_isolation.py
@@ -1,35 +1,15 @@
import grp
import os
import pwd
-import shutil
import pytest
-
-from conftest import option
-from conftest import unit_run
-from conftest import unit_stop
from unit.applications.lang.go import TestApplicationGo
-from unit.feature.isolation import TestFeatureIsolation
+from unit.option import option
+from unit.utils import getns
class TestGoIsolation(TestApplicationGo):
prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
- isolation = TestFeatureIsolation()
-
- @classmethod
- def setup_class(cls, complete_check=True):
- check = super().setup_class(complete_check=False)
-
- unit = unit_run()
- option.temp_dir = unit['temp_dir']
-
- TestFeatureIsolation().check(option.available, unit['temp_dir'])
-
- assert unit_stop() is None
- shutil.rmtree(unit['temp_dir'])
-
- return check if not complete_check else check()
-
def unpriv_creds(self):
nobody_uid = pwd.getpwnam('nobody').pw_uid
@@ -219,8 +199,8 @@ class TestGoIsolation(TestApplicationGo):
== option.available['features']['isolation'][ns]
), ('%s match' % ns)
- assert obj['NS']['MNT'] != self.isolation.getns('mnt'), 'mnt set'
- assert obj['NS']['USER'] != self.isolation.getns('user'), 'user set'
+ assert obj['NS']['MNT'] != getns('mnt'), 'mnt set'
+ assert obj['NS']['USER'] != getns('user'), 'user set'
def test_isolation_pid(self, is_su):
if not self.isolation_key('pid'):
@@ -360,16 +340,20 @@ class TestGoIsolation(TestApplicationGo):
'pid': True
}
+ isolation['automount'] = {
+ 'tmpfs': False
+ }
+
self.load('ns_inspect', isolation=isolation)
obj = self.getjson(url='/?mounts=true')['body']
assert (
- "/ /tmp" in obj['Mounts'] and "tmpfs" in obj['Mounts']
- ), 'app has /tmp mounted on /'
+ "/ /tmp" not in obj['Mounts'] and "tmpfs" not in obj['Mounts']
+ ), 'app has no /tmp mounted'
isolation['automount'] = {
- 'tmpfs': False
+ 'tmpfs': True
}
self.load('ns_inspect', isolation=isolation)
@@ -377,5 +361,5 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson(url='/?mounts=true')['body']
assert (
- "/ /tmp" not in obj['Mounts'] and "tmpfs" not in obj['Mounts']
- ), 'app has no /tmp mounted'
+ "/ /tmp" in obj['Mounts'] and "tmpfs" in obj['Mounts']
+ ), 'app has /tmp mounted on /'
diff --git a/test/test_go_isolation_rootfs.py b/test/test_go_isolation_rootfs.py
index 1cc59c67..2bded5ec 100644
--- a/test/test_go_isolation_rootfs.py
+++ b/test/test_go_isolation_rootfs.py
@@ -1,7 +1,6 @@
import os
import pytest
-
from unit.applications.lang.go import TestApplicationGo
diff --git a/test/test_http_header.py b/test/test_http_header.py
index fdb557cf..ca355eb7 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -1,5 +1,4 @@
import pytest
-
from unit.applications.lang.python import TestApplicationPython
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 41345e87..4a67f291 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -3,15 +3,14 @@ import os
import re
import time
-from conftest import option
-from conftest import public_dir
-from conftest import skip_alert
from unit.applications.lang.java import TestApplicationJava
+from unit.option import option
+from unit.utils import public_dir
class TestJavaApplication(TestApplicationJava):
prerequisites = {'modules': {'java': 'all'}}
- def test_java_conf_error(self, temp_dir):
+ def test_java_conf_error(self, temp_dir, skip_alert):
skip_alert(
r'realpath.*failed',
r'failed to apply new conf',
diff --git a/test/test_java_isolation_rootfs.py b/test/test_java_isolation_rootfs.py
index 02d35a62..91773981 100644
--- a/test/test_java_isolation_rootfs.py
+++ b/test/test_java_isolation_rootfs.py
@@ -2,9 +2,8 @@ import os
import subprocess
import pytest
-
-from conftest import option
from unit.applications.lang.java import TestApplicationJava
+from unit.option import option
class TestJavaIsolationRootfs(TestApplicationJava):
diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py
index 7586d4aa..315c496d 100644
--- a/test/test_java_websockets.py
+++ b/test/test_java_websockets.py
@@ -2,11 +2,9 @@ import struct
import time
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.lang.java import TestApplicationJava
from unit.applications.websockets import TestApplicationWebsocket
+from unit.option import option
class TestJavaWebsockets(TestApplicationJava):
@@ -14,7 +12,8 @@ class TestJavaWebsockets(TestApplicationJava):
ws = TestApplicationWebsocket()
- def setup_method(self):
+ @pytest.fixture(autouse=True)
+ def setup_method_fixture(self, request, skip_alert):
assert 'success' in self.conf(
{'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
), 'clear keepalive_interval'
diff --git a/test/test_node_application.py b/test/test_node_application.py
index c8c3a444..b277dc3a 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -1,9 +1,8 @@
import re
import pytest
-
-from conftest import waitforfiles
from unit.applications.lang.node import TestApplicationNode
+from unit.utils import waitforfiles
class TestNodeApplication(TestApplicationNode):
@@ -222,22 +221,6 @@ class TestNodeApplication(TestApplicationNode):
assert 'X-Header' not in headers, 'insensitive'
assert 'X-header' not in headers, 'insensitive 2'
- def test_node_application_promise_handler(self, temp_dir):
- self.load('promise_handler')
-
- assert (
- self.post(
- headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close',
- },
- body='callback',
- )['status']
- == 200
- ), 'promise handler request'
- assert waitforfiles(temp_dir + '/node/callback'), 'promise handler'
-
def test_node_application_promise_handler_write_after_end(self):
self.load('promise_handler')
@@ -270,35 +253,6 @@ class TestNodeApplication(TestApplicationNode):
), 'promise end request'
assert waitforfiles(temp_dir + '/node/callback'), 'promise end'
- def test_node_application_promise_multiple_calls(self, temp_dir):
- self.load('promise_handler')
-
- self.post(
- headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close',
- },
- body='callback1',
- )
-
- assert waitforfiles(
- temp_dir + '/node/callback1'
- ), 'promise first call'
-
- self.post(
- headers={
- 'Host': 'localhost',
- 'Content-Type': 'text/html',
- 'Connection': 'close',
- },
- body='callback2',
- )
-
- assert waitforfiles(
- temp_dir + '/node/callback2'
- ), 'promise second call'
-
@pytest.mark.skip('not yet')
def test_node_application_header_name_valid(self):
self.load('header_name_valid')
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
index 7b65b5c1..4f1e5906 100644
--- a/test/test_node_websockets.py
+++ b/test/test_node_websockets.py
@@ -2,11 +2,9 @@ import struct
import time
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
+from unit.option import option
class TestNodeWebsockets(TestApplicationNode):
@@ -14,7 +12,8 @@ class TestNodeWebsockets(TestApplicationNode):
ws = TestApplicationWebsocket()
- def setup_method(self):
+ @pytest.fixture(autouse=True)
+ def setup_method_fixture(self, request, skip_alert):
assert 'success' in self.conf(
{'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
), 'clear keepalive_interval'
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index 78f2dd90..dfd8be6c 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -1,9 +1,6 @@
import re
import pytest
-
-from conftest import skip_alert
-from conftest import unit_stop
from unit.applications.lang.perl import TestApplicationPerl
@@ -120,8 +117,6 @@ class TestPerlApplication(TestApplicationPerl):
assert self.get()['body'] == '1', 'errors result'
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+Error in application')
is not None
@@ -170,7 +165,7 @@ class TestPerlApplication(TestApplicationPerl):
assert self.get()['body'] == 'body\n', 'body io file'
@pytest.mark.skip('not yet')
- def test_perl_application_syntax_error(self):
+ def test_perl_application_syntax_error(self, skip_alert):
skip_alert(r'PSGI: Failed to parse script')
self.load('syntax_error')
diff --git a/test/test_php_application.py b/test/test_php_application.py
index 578de0b7..e73c67ba 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -2,12 +2,11 @@ import os
import re
import shutil
import time
+from subprocess import call
import pytest
-
-from conftest import option
-from conftest import unit_stop
from unit.applications.lang.php import TestApplicationPHP
+from unit.option import option
class TestPHPApplication(TestApplicationPHP):
prerequisites = {'modules': {'php': 'all'}}
@@ -20,7 +19,7 @@ class TestPHPApplication(TestApplicationPHP):
def set_opcache(self, app, val):
assert 'success' in self.conf(
- {"admin": {"opcache.enable": val, "opcache.enable_cli": val,},},
+ {"admin": {"opcache.enable": val, "opcache.enable_cli": val}},
'applications/' + app + '/options',
)
@@ -99,7 +98,10 @@ class TestPHPApplication(TestApplicationPHP):
assert self.get()['body'] == '0123'
- unit_stop()
+ with open(temp_dir + '/unit.pid', 'r') as f:
+ pid = f.read().rstrip()
+
+ call(['kill', '-s', 'USR1', pid])
with open(temp_dir + '/unit.log', 'r', errors='ignore') as f:
errs = re.findall(r'Error in fastcgi_finish_request', f.read())
@@ -113,7 +115,10 @@ class TestPHPApplication(TestApplicationPHP):
assert resp['status'] == 200
assert resp['body'] == ''
- unit_stop()
+ with open(temp_dir + '/unit.pid', 'r') as f:
+ pid = f.read().rstrip()
+
+ call(['kill', '-s', 'USR1', pid])
with open(temp_dir + '/unit.log', 'r', errors='ignore') as f:
errs = re.findall(r'Error in fastcgi_finish_request', f.read())
@@ -263,7 +268,7 @@ class TestPHPApplication(TestApplicationPHP):
assert self.get()['headers']['X-Precision'] != '4', 'ini value default'
- self.conf(
+ assert 'success' in self.conf(
{"file": "ini/php.ini"}, 'applications/ini_precision/options'
)
@@ -285,7 +290,7 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_admin(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"file": "php.ini", "admin": {"precision": "5"}},
'applications/ini_precision/options',
)
@@ -295,7 +300,7 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_user(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"file": "php.ini", "user": {"precision": "5"}},
'applications/ini_precision/options',
)
@@ -305,13 +310,13 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_user_2(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"file": "ini/php.ini"}, 'applications/ini_precision/options'
)
assert self.get()['headers']['X-Precision'] == '4', 'ini user file'
- self.conf(
+ assert 'success' in self.conf(
{"precision": "5"}, 'applications/ini_precision/options/user'
)
@@ -320,7 +325,7 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_set_admin(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"precision": "5"}}, 'applications/ini_precision/options'
)
@@ -331,7 +336,7 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_set_user(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"user": {"precision": "5"}}, 'applications/ini_precision/options'
)
@@ -342,7 +347,7 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_ini_repeat(self):
self.load('ini_precision')
- self.conf(
+ assert 'success' in self.conf(
{"user": {"precision": "5"}}, 'applications/ini_precision/options'
)
@@ -355,7 +360,7 @@ class TestPHPApplication(TestApplicationPHP):
self.before_disable_functions()
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"disable_functions": "exec"}},
'applications/time_exec/options',
)
@@ -370,7 +375,7 @@ class TestPHPApplication(TestApplicationPHP):
self.before_disable_functions()
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"disable_functions": "exec,time"}},
'applications/time_exec/options',
)
@@ -384,12 +389,70 @@ class TestPHPApplication(TestApplicationPHP):
r'exec: \/\w+', body
), 'disable_functions comma exec'
+ def test_php_application_auth(self):
+ self.load('auth')
+
+ resp = self.get()
+ assert resp['status'] == 200, 'status'
+ assert resp['headers']['X-Digest'] == 'not set', 'digest'
+ assert resp['headers']['X-User'] == 'not set', 'user'
+ assert resp['headers']['X-Password'] == 'not set', 'password'
+
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Authorization': 'Basic dXNlcjpwYXNzd29yZA==',
+ 'Connection': 'close',
+ }
+ )
+ assert resp['status'] == 200, 'basic status'
+ assert resp['headers']['X-Digest'] == 'not set', 'basic digest'
+ assert resp['headers']['X-User'] == 'user', 'basic user'
+ assert resp['headers']['X-Password'] == 'password', 'basic password'
+
+ resp = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Authorization': 'Digest username="blah", realm="", uri="/"',
+ 'Connection': 'close',
+ }
+ )
+ assert resp['status'] == 200, 'digest status'
+ assert (
+ resp['headers']['X-Digest'] == 'username="blah", realm="", uri="/"'
+ ), 'digest digest'
+ assert resp['headers']['X-User'] == 'not set', 'digest user'
+ assert resp['headers']['X-Password'] == 'not set', 'digest password'
+
+ def test_php_application_auth_invalid(self):
+ self.load('auth')
+
+ def check_auth(auth):
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Authorization': auth,
+ 'Connection': 'close',
+ })
+
+ assert resp['status'] == 200, 'status'
+ assert resp['headers']['X-Digest'] == 'not set', 'Digest'
+ assert resp['headers']['X-User'] == 'not set', 'User'
+ assert resp['headers']['X-Password'] == 'not set', 'Password'
+
+ check_auth('Basic dXN%cjpwYXNzd29yZA==')
+ check_auth('Basic XNlcjpwYXNzd29yZA==')
+ check_auth('Basic DdXNlcjpwYXNzd29yZA==')
+ check_auth('Basic blah')
+ check_auth('Basic')
+ check_auth('Digest')
+ check_auth('blah')
+
def test_php_application_disable_functions_space(self):
self.load('time_exec')
self.before_disable_functions()
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"disable_functions": "exec time"}},
'applications/time_exec/options',
)
@@ -408,7 +471,7 @@ class TestPHPApplication(TestApplicationPHP):
self.before_disable_functions()
- self.conf(
+ assert 'success' in self.conf(
{"user": {"disable_functions": "exec"}},
'applications/time_exec/options',
)
@@ -425,7 +488,7 @@ class TestPHPApplication(TestApplicationPHP):
self.before_disable_functions()
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"disable_functions": "blah"}},
'applications/time_exec/options',
)
@@ -446,7 +509,7 @@ class TestPHPApplication(TestApplicationPHP):
r'012345', self.get()['body']
), 'disable_classes before'
- self.conf(
+ assert 'success' in self.conf(
{"admin": {"disable_classes": "DateTime"}},
'applications/date_time/options',
)
@@ -462,7 +525,7 @@ class TestPHPApplication(TestApplicationPHP):
r'012345', self.get()['body']
), 'disable_classes before'
- self.conf(
+ assert 'success' in self.conf(
{"user": {"disable_classes": "DateTime"}},
'applications/date_time/options',
)
@@ -480,8 +543,6 @@ class TestPHPApplication(TestApplicationPHP):
assert self.get()['status'] == 200, 'status 2'
- unit_stop()
-
pattern = r'\d{4}\/\d\d\/\d\d\s\d\d:.+\[notice\].+Error in application'
assert self.wait_for_record(pattern) is not None, 'errors print'
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 1420ec21..bcd66173 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -19,7 +19,7 @@ class TestPHPBasic(TestControl):
}
def test_php_get_applications(self):
- self.conf(self.conf_app, 'applications')
+ assert 'success' in self.conf(self.conf_app, 'applications')
conf = self.conf_get()
@@ -55,7 +55,7 @@ class TestPHPBasic(TestControl):
), 'spare processes'
def test_php_get_listeners(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert self.conf_get()['listeners'] == {
"*:7080": {"pass": "applications/app"}
@@ -70,16 +70,20 @@ class TestPHPBasic(TestControl):
}, 'listeners prefix 2'
def test_php_change_listener(self):
- self.conf(self.conf_basic)
- self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
+ assert 'success' in self.conf(self.conf_basic)
+ assert 'success' in self.conf(
+ {"*:7081": {"pass": "applications/app"}}, 'listeners'
+ )
assert self.conf_get('listeners') == {
"*:7081": {"pass": "applications/app"}
}, 'change listener'
def test_php_add_listener(self):
- self.conf(self.conf_basic)
- self.conf({"pass": "applications/app"}, 'listeners/*:7082')
+ assert 'success' in self.conf(self.conf_basic)
+ assert 'success' in self.conf(
+ {"pass": "applications/app"}, 'listeners/*:7082'
+ )
assert self.conf_get('listeners') == {
"*:7080": {"pass": "applications/app"},
@@ -87,20 +91,20 @@ class TestPHPBasic(TestControl):
}, 'add listener'
def test_php_change_application(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
- self.conf('30', 'applications/app/processes/max')
+ assert 'success' in self.conf('30', 'applications/app/processes/max')
assert (
self.conf_get('applications/app/processes/max') == 30
), 'change application max'
- self.conf('"/www"', 'applications/app/root')
+ assert 'success' in self.conf('"/www"', 'applications/app/root')
assert (
self.conf_get('applications/app/root') == '/www'
), 'change application root'
def test_php_delete(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert 'error' in self.conf_delete('applications/app')
assert 'success' in self.conf_delete('listeners/*:7080')
@@ -108,7 +112,7 @@ class TestPHPBasic(TestControl):
assert 'error' in self.conf_delete('applications/app')
def test_php_delete_blocks(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert 'success' in self.conf_delete('listeners')
assert 'success' in self.conf_delete('applications')
diff --git a/test/test_php_isolation.py b/test/test_php_isolation.py
index cc660e04..2e458023 100644
--- a/test/test_php_isolation.py
+++ b/test/test_php_isolation.py
@@ -1,31 +1,11 @@
-import shutil
-
import pytest
-
-from conftest import option
-from conftest import unit_run
-from conftest import unit_stop
from unit.applications.lang.php import TestApplicationPHP
-from unit.feature.isolation import TestFeatureIsolation
+from unit.option import option
class TestPHPIsolation(TestApplicationPHP):
prerequisites = {'modules': {'php': 'any'}, 'features': ['isolation']}
- @classmethod
- def setup_class(cls, complete_check=True):
- check = super().setup_class(complete_check=False)
-
- unit = unit_run()
- option.temp_dir = unit['temp_dir']
-
- TestFeatureIsolation().check(option.available, unit['temp_dir'])
-
- assert unit_stop() is None
- shutil.rmtree(unit['temp_dir'])
-
- return check if not complete_check else check()
-
def test_php_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
diff --git a/test/test_php_targets.py b/test/test_php_targets.py
index e64cd6b6..76326131 100644
--- a/test/test_php_targets.py
+++ b/test/test_php_targets.py
@@ -1,5 +1,5 @@
-from conftest import option
from unit.applications.lang.php import TestApplicationPHP
+from unit.option import option
class TestPHPTargets(TestApplicationPHP):
diff --git a/test/test_proxy.py b/test/test_proxy.py
index be3e93fd..2d305e98 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -3,12 +3,10 @@ import socket
import time
import pytest
-
-from conftest import option
from conftest import run_process
-from conftest import skip_alert
-from conftest import waitforsocket
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
+from unit.utils import waitforsocket
class TestProxy(TestApplicationPython):
@@ -174,7 +172,9 @@ Content-Length: 10
assert resp['status'] == 200, 'status'
assert resp['body'] == payload, 'body'
- self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
+ assert 'success' in self.conf(
+ {'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings'
+ )
payload = '0123456789abcdef' * 32 * 64 * 1024
resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024)
@@ -482,13 +482,13 @@ Content-Length: 10
check_proxy('http://[:]:7080')
check_proxy('http://[::7080')
- def test_proxy_loop(self):
+ def test_proxy_loop(self, skip_alert):
skip_alert(
r'socket.*failed',
r'accept.*failed',
r'new connections are not accepted',
)
- self.conf(
+ assert 'success' in self.conf(
{
"listeners": {
"*:7080": {"pass": "routes"},
diff --git a/test/test_proxy_chunked.py b/test/test_proxy_chunked.py
index ae2228fa..73d94332 100644
--- a/test/test_proxy_chunked.py
+++ b/test/test_proxy_chunked.py
@@ -3,10 +3,10 @@ import select
import socket
import time
-from conftest import option
from conftest import run_process
-from conftest import waitforsocket
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
+from unit.utils import waitforsocket
class TestProxyChunked(TestApplicationPython):
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 83b0c8f4..709df3ff 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -1,14 +1,12 @@
import grp
+import os
import pwd
import re
import time
import pytest
-
-from conftest import option
-from conftest import skip_alert
-from conftest import unit_stop
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestPythonApplication(TestApplicationPython):
@@ -156,9 +154,7 @@ custom-header: BLAH
assert resp['status'] == 200, 'ctx iter status'
assert resp['body'] == '0123456789', 'ctx iter body'
- self.conf({"listeners": {}, "applications": {}})
-
- unit_stop()
+ assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert (
self.wait_for_record(r'RuntimeError') is not None
@@ -336,9 +332,7 @@ Connection: close
self.get()
- self.conf({"listeners": {}, "applications": {}})
-
- unit_stop()
+ assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert self.wait_for_record(r'At exit called\.') is not None, 'atexit'
@@ -497,8 +491,6 @@ last line: 987654321
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+Error in application\.')
is not None
@@ -520,13 +512,13 @@ last line: 987654321
assert self.get()['body'] == 'body\n', 'body io file'
@pytest.mark.skip('not yet')
- def test_python_application_syntax_error(self):
+ def test_python_application_syntax_error(self, skip_alert):
skip_alert(r'Python failed to import module "wsgi"')
self.load('syntax_error')
assert self.get()['status'] == 500, 'syntax error'
- def test_python_application_loading_error(self):
+ def test_python_application_loading_error(self, skip_alert):
skip_alert(r'Python failed to import module "blah"')
self.load('empty', module="blah")
@@ -538,8 +530,6 @@ last line: 987654321
self.get()
- unit_stop()
-
assert self.wait_for_record(r'Close called\.') is not None, 'close'
def test_python_application_close_error(self):
@@ -547,8 +537,6 @@ last line: 987654321
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'Close called\.') is not None
), 'close error'
@@ -558,8 +546,6 @@ last line: 987654321
self.get()
- unit_stop()
-
assert (
self.wait_for_record(
r'\[error\].+the application returned not an iterable object'
@@ -791,7 +777,7 @@ last line: 987654321
assert obj['UID'] == nobody_uid, 'root uid group=root'
assert obj['GID'] == 0, 'root gid group=root'
- def test_python_application_callable(self):
+ def test_python_application_callable(self, skip_alert):
skip_alert(r'Python failed to get "blah" from module')
self.load('callable')
@@ -805,6 +791,42 @@ last line: 987654321
assert self.get()['status'] not in [200, 204], 'callable response inv'
+ def test_python_application_path(self):
+ self.load('path')
+
+ def set_path(path):
+ assert 'success' in self.conf(path, 'applications/path/path')
+
+ def get_path():
+ return self.get()['body'].split(os.pathsep)
+
+ default_path = self.conf_get('/config/applications/path/path')
+ assert 'success' in self.conf(
+ {"PYTHONPATH": default_path},
+ '/config/applications/path/environment',
+ )
+
+ self.conf_delete('/config/applications/path/path')
+ sys_path = get_path()
+
+ set_path('"/blah"')
+ assert ['/blah', *sys_path] == get_path(), 'check path'
+
+ set_path('"/new"')
+ assert ['/new', *sys_path] == get_path(), 'check path update'
+
+ set_path('["/blah1", "/blah2"]')
+ assert ['/blah1', '/blah2', *sys_path] == get_path(), 'check path array'
+
+ def test_python_application_path_invalid(self):
+ self.load('path')
+
+ def check_path(path):
+ assert 'error' in self.conf(path, 'applications/path/path')
+
+ check_path('{}')
+ check_path('["/blah", []]')
+
def test_python_application_threads(self):
self.load('threads', threads=4)
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index 0cc70e51..e661a89c 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -58,7 +58,7 @@ class TestPythonBasic(TestControl):
assert self.conf_get('applications/app/processes/spare') == 0, 'spare'
def test_python_get_listeners(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert self.conf_get()['listeners'] == {
"*:7080": {"pass": "applications/app"}
@@ -73,16 +73,20 @@ class TestPythonBasic(TestControl):
}, 'listeners prefix 2'
def test_python_change_listener(self):
- self.conf(self.conf_basic)
- self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
+ assert 'success' in self.conf(self.conf_basic)
+ assert 'success' in self.conf(
+ {"*:7081": {"pass": "applications/app"}}, 'listeners'
+ )
assert self.conf_get('listeners') == {
"*:7081": {"pass": "applications/app"}
}, 'change listener'
def test_python_add_listener(self):
- self.conf(self.conf_basic)
- self.conf({"pass": "applications/app"}, 'listeners/*:7082')
+ assert 'success' in self.conf(self.conf_basic)
+ assert 'success' in self.conf(
+ {"pass": "applications/app"}, 'listeners/*:7082'
+ )
assert self.conf_get('listeners') == {
"*:7080": {"pass": "applications/app"},
@@ -90,20 +94,20 @@ class TestPythonBasic(TestControl):
}, 'add listener'
def test_python_change_application(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
- self.conf('30', 'applications/app/processes/max')
+ assert 'success' in self.conf('30', 'applications/app/processes/max')
assert (
self.conf_get('applications/app/processes/max') == 30
), 'change application max'
- self.conf('"/www"', 'applications/app/path')
+ assert 'success' in self.conf('"/www"', 'applications/app/path')
assert (
self.conf_get('applications/app/path') == '/www'
), 'change application path'
def test_python_delete(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert 'error' in self.conf_delete('applications/app')
assert 'success' in self.conf_delete('listeners/*:7080')
@@ -111,7 +115,7 @@ class TestPythonBasic(TestControl):
assert 'error' in self.conf_delete('applications/app')
def test_python_delete_blocks(self):
- self.conf(self.conf_basic)
+ assert 'success' in self.conf(self.conf_basic)
assert 'success' in self.conf_delete('listeners')
assert 'success' in self.conf_delete('applications')
diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py
index 1a157528..680f2c03 100644
--- a/test/test_python_isolation.py
+++ b/test/test_python_isolation.py
@@ -1,31 +1,15 @@
-import shutil
import pytest
-
-from conftest import option
-from conftest import unit_run
-from conftest import unit_stop
from unit.applications.lang.python import TestApplicationPython
-from unit.feature.isolation import TestFeatureIsolation
+from unit.option import option
+from unit.utils import findmnt
+from unit.utils import waitformount
+from unit.utils import waitforunmount
class TestPythonIsolation(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']}
- @classmethod
- def setup_class(cls, complete_check=True):
- check = super().setup_class(complete_check=False)
-
- unit = unit_run()
- option.temp_dir = unit['temp_dir']
-
- TestFeatureIsolation().check(option.available, unit['temp_dir'])
-
- assert unit_stop() is None
- shutil.rmtree(unit['temp_dir'])
-
- return check if not complete_check else check()
-
def test_python_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
@@ -79,39 +63,51 @@ class TestPythonIsolation(TestApplicationPython):
), 'application exists in rootfs'
def test_python_isolation_rootfs_no_language_deps(self, is_su, temp_dir):
- isolation_features = option.available['features']['isolation'].keys()
-
if not is_su:
- if not 'unprivileged_userns_clone' in isolation_features:
- pytest.skip('requires unprivileged userns or root')
-
- if 'user' not in isolation_features:
- pytest.skip('user namespace is not supported')
-
- if 'mnt' not in isolation_features:
- pytest.skip('mnt namespace is not supported')
-
- if 'pid' not in isolation_features:
- pytest.skip('pid namespace is not supported')
+ pytest.skip('requires root')
isolation = {
'rootfs': temp_dir,
'automount': {'language_deps': False}
}
- if not is_su:
- isolation['namespaces'] = {
- 'mount': True,
- 'credential': True,
- 'pid': True
- }
-
self.load('empty', isolation=isolation)
+ assert findmnt().find(temp_dir) == -1
assert (self.get()['status'] != 200), 'disabled language_deps'
+ assert findmnt().find(temp_dir) == -1
isolation['automount']['language_deps'] = True
self.load('empty', isolation=isolation)
+ assert findmnt().find(temp_dir) == -1
assert (self.get()['status'] == 200), 'enabled language_deps'
+ assert waitformount(temp_dir), 'language_deps mount'
+
+ self.conf({"listeners": {}, "applications": {}})
+
+ assert waitforunmount(temp_dir), 'language_deps unmount'
+
+ def test_python_isolation_procfs(self, is_su, temp_dir):
+ isolation_features = option.available['features']['isolation'].keys()
+
+ if not is_su:
+ pytest.skip('requires root')
+
+ isolation = {'rootfs': temp_dir, 'automount': {'procfs': False}}
+
+ self.load('ns_inspect', isolation=isolation)
+
+ assert (
+ self.getjson(url='/?path=/proc/self')['body']['FileExists']
+ == False
+ ), 'no /proc/self'
+
+ isolation['automount']['procfs'] = True
+
+ self.load('ns_inspect', isolation=isolation)
+
+ assert (
+ self.getjson(url='/?path=/proc/self')['body']['FileExists'] == True
+ ), '/proc/self'
diff --git a/test/test_python_isolation_chroot.py b/test/test_python_isolation_chroot.py
index 8018d5b9..7281f5f6 100644
--- a/test/test_python_isolation_chroot.py
+++ b/test/test_python_isolation_chroot.py
@@ -1,7 +1,5 @@
import pytest
-
from unit.applications.lang.python import TestApplicationPython
-from unit.feature.isolation import TestFeatureIsolation
class TestPythonIsolation(TestApplicationPython):
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index ff914fc8..ac403ce4 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -3,9 +3,8 @@ import subprocess
import time
import pytest
-
-from conftest import option
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestPythonProcman(TestApplicationPython):
@@ -198,6 +197,6 @@ class TestPythonProcman(TestApplicationPython):
), 'max zero'
def stop_all(self):
- self.conf({"listeners": {}, "applications": {}})
+ assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert len(self.pids_for_process()) == 0, 'stop all'
diff --git a/test/test_respawn.py b/test/test_respawn.py
index 09a806d4..ed85ee95 100644
--- a/test/test_respawn.py
+++ b/test/test_respawn.py
@@ -2,9 +2,8 @@ import re
import subprocess
import time
-from conftest import option
-from conftest import skip_alert
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestRespawn(TestApplicationPython):
@@ -22,17 +21,17 @@ class TestRespawn(TestApplicationPython):
'1', 'applications/' + self.app_name + '/processes'
)
- def pid_by_name(self, name):
- output = subprocess.check_output(['ps', 'ax']).decode()
- m = re.search(r'\s*(\d+).*' + name, output)
- return m if m is None else m.group(1)
+ def pid_by_name(self, name, ppid):
+ output = subprocess.check_output(['ps', 'ax', '-O', 'ppid']).decode()
+ m = re.search(r'\s*(\d+)\s*' + str(ppid) + r'.*' + name, output)
+ return None if m is None else m.group(1)
def kill_pids(self, *pids):
subprocess.call(['kill', '-9'] + list(pids))
- def wait_for_process(self, process):
+ def wait_for_process(self, process, unit_pid):
for i in range(50):
- found = self.pid_by_name(process)
+ found = self.pid_by_name(process, unit_pid)
if found is not None:
break
@@ -41,7 +40,10 @@ class TestRespawn(TestApplicationPython):
return found
- def smoke_test(self):
+ def find_proc(self, name, ppid, ps_output):
+ return re.findall(str(ppid) + r'.*' + name, ps_output)
+
+ def smoke_test(self, unit_pid):
for _ in range(5):
assert 'success' in self.conf(
'1', 'applications/' + self.app_name + '/processes'
@@ -51,39 +53,41 @@ class TestRespawn(TestApplicationPython):
# Check if the only one router, controller,
# and application processes running.
- output = subprocess.check_output(['ps', 'ax']).decode()
- assert len(re.findall(self.PATTERN_ROUTER, output)) == 1
- assert len(re.findall(self.PATTERN_CONTROLLER, output)) == 1
- assert len(re.findall(self.app_name, output)) == 1
+ out = subprocess.check_output(['ps', 'ax', '-O', 'ppid']).decode()
+ assert len(self.find_proc(self.PATTERN_ROUTER, unit_pid, out)) == 1
+ assert len(self.find_proc(self.PATTERN_CONTROLLER, unit_pid, out)) == 1
+ assert len(self.find_proc(self.app_name, unit_pid, out)) == 1
- def test_respawn_router(self):
- pid = self.pid_by_name(self.PATTERN_ROUTER)
+ def test_respawn_router(self, skip_alert, unit_pid):
+ pid = self.pid_by_name(self.PATTERN_ROUTER, unit_pid)
self.kill_pids(pid)
skip_alert(r'process %s exited on signal 9' % pid)
- assert self.wait_for_process(self.PATTERN_ROUTER) is not None
+ assert self.wait_for_process(self.PATTERN_ROUTER, unit_pid) is not None
- self.smoke_test()
+ self.smoke_test(unit_pid)
- def test_respawn_controller(self):
- pid = self.pid_by_name(self.PATTERN_CONTROLLER)
+ def test_respawn_controller(self, skip_alert, unit_pid):
+ pid = self.pid_by_name(self.PATTERN_CONTROLLER, unit_pid)
self.kill_pids(pid)
skip_alert(r'process %s exited on signal 9' % pid)
- assert self.wait_for_process(self.PATTERN_CONTROLLER) is not None
+ assert self.wait_for_process(
+ self.PATTERN_CONTROLLER, unit_pid
+ ) is not None
assert self.get()['status'] == 200
- self.smoke_test()
+ self.smoke_test(unit_pid)
- def test_respawn_application(self):
- pid = self.pid_by_name(self.app_name)
+ def test_respawn_application(self, skip_alert, unit_pid):
+ pid = self.pid_by_name(self.app_name, unit_pid)
self.kill_pids(pid)
skip_alert(r'process %s exited on signal 9' % pid)
- assert self.wait_for_process(self.app_name) is not None
+ assert self.wait_for_process(self.app_name, unit_pid) is not None
- self.smoke_test()
+ self.smoke_test(unit_pid)
diff --git a/test/test_routing.py b/test/test_routing.py
index 83852273..4d27cb61 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestRouting(TestApplicationProto):
@@ -318,7 +316,7 @@ class TestRouting(TestApplicationProto):
check_pass_error("%1", "%1")
def test_routes_absent(self):
- self.conf(
+ assert 'success' in self.conf(
{
"listeners": {"*:7081": {"pass": "applications/empty"}},
"applications": {
@@ -366,7 +364,7 @@ class TestRouting(TestApplicationProto):
assert self.get()['status'] == 200, 'route match absent'
- def test_routes_route_action_absent(self):
+ def test_routes_route_action_absent(self, skip_alert):
skip_alert(r'failed to apply new conf')
assert 'error' in self.conf(
@@ -755,7 +753,7 @@ class TestRouting(TestApplicationProto):
'routes/main'
), 'route edit configure 9'
- def test_match_edit(self):
+ def test_match_edit(self, skip_alert):
skip_alert(r'failed to apply new conf')
self.route_match({"method": ["GET", "POST"]})
@@ -1352,7 +1350,7 @@ class TestRouting(TestApplicationProto):
assert self.get(url='/?var2=val2')['status'] == 404, 'arr 7'
assert self.get(url='/?var3=foo')['status'] == 200, 'arr 8'
- def test_routes_match_arguments_invalid(self):
+ def test_routes_match_arguments_invalid(self, skip_alert):
# TODO remove it after controller fixed
skip_alert(r'failed to apply new conf')
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index e42fb97f..b18a6cee 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -1,9 +1,6 @@
import re
import pytest
-
-from conftest import skip_alert
-from conftest import unit_stop
from unit.applications.lang.ruby import TestApplicationRuby
@@ -160,7 +157,7 @@ class TestRubyApplication(TestApplicationRuby):
assert self.post(body=body)['body'] == body, 'input rewind'
@pytest.mark.skip('not yet')
- def test_ruby_application_syntax_error(self):
+ def test_ruby_application_syntax_error(self, skip_alert):
skip_alert(
r'Failed to parse rack script',
r'syntax error',
@@ -176,8 +173,6 @@ class TestRubyApplication(TestApplicationRuby):
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+Error in application')
is not None
@@ -188,8 +183,6 @@ class TestRubyApplication(TestApplicationRuby):
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+1234567890') is not None
), 'errors puts int'
@@ -199,8 +192,6 @@ class TestRubyApplication(TestApplicationRuby):
self.get()
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+Error in application')
is not None
@@ -216,7 +207,6 @@ class TestRubyApplication(TestApplicationRuby):
self.get()
- unit_stop()
assert (
self.wait_for_record(r'\[error\].+1234567890') is not None
@@ -227,9 +217,7 @@ class TestRubyApplication(TestApplicationRuby):
self.get()
- self.conf({"listeners": {}, "applications": {}})
-
- unit_stop()
+ assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert (
self.wait_for_record(r'\[error\].+At exit called\.') is not None
@@ -290,8 +278,6 @@ class TestRubyApplication(TestApplicationRuby):
assert self.get()['status'] == 500, 'body each error status'
- unit_stop()
-
assert (
self.wait_for_record(r'\[error\].+Failed to run ruby script')
is not None
diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py
index 69e25de9..c49b6732 100644
--- a/test/test_ruby_isolation.py
+++ b/test/test_ruby_isolation.py
@@ -1,32 +1,15 @@
+import os
import shutil
import pytest
-
-from conftest import option
-from conftest import unit_run
-from conftest import unit_stop
from unit.applications.lang.ruby import TestApplicationRuby
-from unit.feature.isolation import TestFeatureIsolation
+from unit.option import option
class TestRubyIsolation(TestApplicationRuby):
prerequisites = {'modules': {'ruby': 'any'}, 'features': ['isolation']}
- @classmethod
- def setup_class(cls, complete_check=True):
- check = super().setup_class(complete_check=False)
-
- unit = unit_run()
- option.temp_dir = unit['temp_dir']
-
- TestFeatureIsolation().check(option.available, unit['temp_dir'])
-
- assert unit_stop() is None
- shutil.rmtree(unit['temp_dir'])
-
- return check if not complete_check else check()
-
- def test_ruby_isolation_rootfs_mount_namespace(self, is_su):
+ def test_ruby_isolation_rootfs(self, is_su):
isolation_features = option.available['features']['isolation'].keys()
if not is_su:
@@ -42,34 +25,22 @@ class TestRubyIsolation(TestApplicationRuby):
if 'pid' not in isolation_features:
pytest.skip('pid namespace is not supported')
- isolation = {'rootfs': option.test_dir}
+ isolation = {'rootfs': option.temp_dir}
if not is_su:
isolation['namespaces'] = {
'mount': True,
'credential': True,
- 'pid': True
+ 'pid': True,
}
- self.load('status_int', isolation=isolation)
-
- assert 'success' in self.conf(
- '"/ruby/status_int/config.ru"', 'applications/status_int/script',
- )
+ os.mkdir(option.temp_dir + '/ruby')
- assert 'success' in self.conf(
- '"/ruby/status_int"', 'applications/status_int/working_directory',
+ shutil.copytree(
+ option.test_dir + '/ruby/status_int',
+ option.temp_dir + '/ruby/status_int',
)
- assert self.get()['status'] == 200, 'status int'
-
- def test_ruby_isolation_rootfs(self, is_su):
- if not is_su:
- pytest.skip('requires root')
- return
-
- isolation = {'rootfs': option.test_dir}
-
self.load('status_int', isolation=isolation)
assert 'success' in self.conf(
diff --git a/test/test_settings.py b/test/test_settings.py
index 22830a3b..c59ca8b9 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -3,7 +3,6 @@ import socket
import time
import pytest
-
from unit.applications.lang.python import TestApplicationPython
@@ -297,4 +296,3 @@ Connection: close
assert bool(resp), 'response from application 4'
assert resp['status'] == 200, 'status 4'
assert resp['body'] == body, 'body 4'
-
diff --git a/test/test_share_fallback.py b/test/test_share_fallback.py
index 462da9de..a02cb1a3 100644
--- a/test/test_share_fallback.py
+++ b/test/test_share_fallback.py
@@ -1,8 +1,7 @@
import os
-from conftest import option
-from conftest import skip_alert
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestStatic(TestApplicationProto):
@@ -117,7 +116,7 @@ class TestStatic(TestApplicationProto):
assert resp['status'] == 200, 'fallback proxy status'
assert resp['body'] == '', 'fallback proxy'
- def test_fallback_proxy_loop(self):
+ def test_fallback_proxy_loop(self, skip_alert):
skip_alert(
r'open.*/blah/index.html.*failed',
r'accept.*failed',
diff --git a/test/test_static.py b/test/test_static.py
index a65928ca..3e85b435 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -2,10 +2,9 @@ import os
import socket
import pytest
-
-from conftest import option
-from conftest import waitforfiles
from unit.applications.proto import TestApplicationProto
+from unit.option import option
+from unit.utils import waitforfiles
class TestStatic(TestApplicationProto):
diff --git a/test/test_tls.py b/test/test_tls.py
index 4cf8d22c..89c57d07 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -4,10 +4,8 @@ import ssl
import subprocess
import pytest
-
-from conftest import option
-from conftest import skip_alert
from unit.applications.tls import TestApplicationTLS
+from unit.option import option
class TestTLS(TestApplicationTLS):
@@ -21,7 +19,7 @@ class TestTLS(TestApplicationTLS):
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(
+ assert 'success' in self.conf(
{
"pass": "applications/" + application,
"tls": {"certificate": cert}
@@ -30,7 +28,7 @@ class TestTLS(TestApplicationTLS):
)
def remove_tls(self, application='empty', port=7080):
- self.conf(
+ assert 'success' in self.conf(
{"pass": "applications/" + application}, 'listeners/*:' + str(port)
)
@@ -479,8 +477,10 @@ basicConstraints = critical,CA:TRUE"""
read_timeout=1,
)
- self.conf({"pass": "applications/empty"}, 'listeners/*:7080')
- self.conf_delete('/certificates/default')
+ assert 'success' in self.conf(
+ {"pass": "applications/empty"}, 'listeners/*:7080'
+ )
+ assert 'success' in self.conf_delete('/certificates/default')
try:
resp = self.get_ssl(
@@ -505,12 +505,12 @@ basicConstraints = critical,CA:TRUE"""
'/certificates'
), 'remove all certificates'
- def test_tls_application_respawn(self):
+ def test_tls_application_respawn(self, skip_alert):
self.load('mirror')
self.certificate()
- self.conf('1', 'applications/mirror/processes')
+ assert 'success' in self.conf('1', 'applications/mirror/processes')
self.add_tls(application='mirror')
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
index c20d6054..163eb646 100644
--- a/test/test_upstreams_rr.py
+++ b/test/test_upstreams_rr.py
@@ -1,8 +1,8 @@
import os
import re
-from conftest import option
from unit.applications.lang.python import TestApplicationPython
+from unit.option import option
class TestUpstreamsRR(TestApplicationPython):
diff --git a/test/test_usr1.py b/test/test_usr1.py
index 3e44e4c5..dbb5265c 100644
--- a/test/test_usr1.py
+++ b/test/test_usr1.py
@@ -1,9 +1,8 @@
import os
from subprocess import call
-from conftest import unit_stop
-from conftest import waitforfiles
from unit.applications.lang.python import TestApplicationPython
+from unit.utils import waitforfiles
class TestUSR1(TestApplicationPython):
@@ -41,8 +40,6 @@ class TestUSR1(TestApplicationPython):
assert self.get(url='/usr1')['status'] == 200
- unit_stop()
-
assert (
self.wait_for_record(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"', log)
is not None
@@ -74,8 +71,6 @@ class TestUSR1(TestApplicationPython):
body = 'body_for_a_log_unit'
assert self.post(body=body)['status'] == 200
- unit_stop()
-
assert self.wait_for_record(body) is not None, 'rename new'
assert self.search_in_log(body, log_new) is None, 'rename new 2'
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index 866dec47..a17b1af4 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -1,8 +1,8 @@
import os
import subprocess
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationGo(TestApplicationProto):
@@ -12,6 +12,7 @@ class TestApplicationGo(TestApplicationProto):
env = os.environ.copy()
env['GOPATH'] = option.current_dir + '/build/go'
+ env['GOCACHE'] = option.cache_dir + '/go'
if static:
args = [
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index 0ff85187..b2e17f23 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -4,8 +4,8 @@ import shutil
import subprocess
import pytest
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationJava(TestApplicationProto):
diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py
index 98fd9ffc..cc6d06ef 100644
--- a/test/unit/applications/lang/node.py
+++ b/test/unit/applications/lang/node.py
@@ -1,9 +1,9 @@
import shutil
from urllib.parse import quote
-from conftest import option
-from conftest import public_dir
from unit.applications.proto import TestApplicationProto
+from unit.option import option
+from unit.utils import public_dir
class TestApplicationNode(TestApplicationProto):
diff --git a/test/unit/applications/lang/perl.py b/test/unit/applications/lang/perl.py
index 9dc24ace..58b867f0 100644
--- a/test/unit/applications/lang/perl.py
+++ b/test/unit/applications/lang/perl.py
@@ -1,5 +1,5 @@
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationPerl(TestApplicationProto):
diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py
index 3dbb32f5..90c0078c 100644
--- a/test/unit/applications/lang/php.py
+++ b/test/unit/applications/lang/php.py
@@ -1,8 +1,8 @@
-from conftest import option
import os
import shutil
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationPHP(TestApplicationProto):
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index 792a86fa..287d23f0 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -3,8 +3,8 @@ import shutil
from urllib.parse import quote
import pytest
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationPython(TestApplicationProto):
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
index 82d66e65..02644584 100644
--- a/test/unit/applications/lang/ruby.py
+++ b/test/unit/applications/lang/ruby.py
@@ -1,5 +1,5 @@
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationRuby(TestApplicationProto):
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index 6e760c70..af05d071 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -2,8 +2,8 @@ import os
import re
import time
-from conftest import option
from unit.control import TestControl
+from unit.option import option
class TestApplicationProto(TestControl):
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
index fb1b112c..b0cd5abb 100644
--- a/test/unit/applications/tls.py
+++ b/test/unit/applications/tls.py
@@ -2,8 +2,8 @@ import os
import ssl
import subprocess
-from conftest import option
from unit.applications.proto import TestApplicationProto
+from unit.option import option
class TestApplicationTLS(TestApplicationProto):
diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py
new file mode 100644
index 00000000..fe5a41f8
--- /dev/null
+++ b/test/unit/check/isolation.py
@@ -0,0 +1,158 @@
+import json
+import os
+
+from unit.applications.lang.go import TestApplicationGo
+from unit.applications.lang.java import TestApplicationJava
+from unit.applications.lang.node import TestApplicationNode
+from unit.applications.proto import TestApplicationProto
+from unit.http import TestHTTP
+from unit.option import option
+from unit.utils import getns
+
+allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
+http = TestHTTP()
+
+def check_isolation():
+ test_conf = {"namespaces": {"credential": True}}
+ available = option.available
+
+ conf = ''
+ if 'go' in available['modules']:
+ TestApplicationGo().prepare_env('empty', 'app')
+
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/empty"}},
+ "applications": {
+ "empty": {
+ "type": "external",
+ "processes": {"spare": 0},
+ "working_directory": option.test_dir + "/go/empty",
+ "executable": option.temp_dir + "/go/app",
+ "isolation": {"namespaces": {"credential": True}},
+ },
+ },
+ }
+
+ elif 'python' in available['modules']:
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/empty"}},
+ "applications": {
+ "empty": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": option.test_dir + "/python/empty",
+ "working_directory": option.test_dir + "/python/empty",
+ "module": "wsgi",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ elif 'php' in available['modules']:
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
+ "applications": {
+ "phpinfo": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "root": option.test_dir + "/php/phpinfo",
+ "working_directory": option.test_dir + "/php/phpinfo",
+ "index": "index.php",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ elif 'ruby' in available['modules']:
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/empty"}},
+ "applications": {
+ "empty": {
+ "type": "ruby",
+ "processes": {"spare": 0},
+ "working_directory": option.test_dir + "/ruby/empty",
+ "script": option.test_dir + "/ruby/empty/config.ru",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ elif 'java' in available['modules']:
+ TestApplicationJava().prepare_env('empty')
+
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/empty"}},
+ "applications": {
+ "empty": {
+ "unit_jars": option.current_dir + "/build",
+ "type": "java",
+ "processes": {"spare": 0},
+ "working_directory": option.test_dir + "/java/empty/",
+ "webapp": option.temp_dir + "/java",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ elif 'node' in available['modules']:
+ TestApplicationNode().prepare_env('basic')
+
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/basic"}},
+ "applications": {
+ "basic": {
+ "type": "external",
+ "processes": {"spare": 0},
+ "working_directory": option.temp_dir + "/node",
+ "executable": "app.js",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ elif 'perl' in available['modules']:
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/body_empty"}},
+ "applications": {
+ "body_empty": {
+ "type": "perl",
+ "processes": {"spare": 0},
+ "working_directory": option.test_dir
+ + "/perl/body_empty",
+ "script": option.test_dir + "/perl/body_empty/psgi.pl",
+ "isolation": {"namespaces": {"credential": True}},
+ }
+ },
+ }
+
+ else:
+ return
+
+ resp = http.put(
+ url='/config',
+ sock_type='unix',
+ addr=option.temp_dir + '/control.unit.sock',
+ body=json.dumps(conf),
+ )
+
+ if 'success' not in resp['body']:
+ return
+
+ userns = getns('user')
+ if not userns:
+ return
+
+ available['features']['isolation'] = {'user': userns}
+
+ unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
+ if os.path.exists(unp_clone_path):
+ with open(unp_clone_path, 'r') as f:
+ if str(f.read()).rstrip() == '1':
+ available['features']['isolation'][
+ 'unprivileged_userns_clone'
+ ] = True
+
+ for ns in allns:
+ ns_value = getns(ns)
+ if ns_value:
+ available['features']['isolation'][ns] = ns_value
diff --git a/test/unit/control.py b/test/unit/control.py
index f05aa827..3008a64b 100644
--- a/test/unit/control.py
+++ b/test/unit/control.py
@@ -1,7 +1,7 @@
import json
-from conftest import option
from unit.http import TestHTTP
+from unit.option import option
def args_handler(conf_func):
diff --git a/test/unit/feature/isolation.py b/test/unit/feature/isolation.py
deleted file mode 100644
index 7877c03a..00000000
--- a/test/unit/feature/isolation.py
+++ /dev/null
@@ -1,160 +0,0 @@
-import os
-
-from unit.applications.lang.go import TestApplicationGo
-from unit.applications.lang.java import TestApplicationJava
-from unit.applications.lang.node import TestApplicationNode
-from unit.applications.proto import TestApplicationProto
-from conftest import option
-
-
-class TestFeatureIsolation(TestApplicationProto):
- allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
-
- def check(self, available, temp_dir):
- test_conf = {"namespaces": {"credential": True}}
-
- conf = ''
- if 'go' in available['modules']:
- TestApplicationGo().prepare_env('empty', 'app')
-
- conf = {
- "listeners": {"*:7080": {"pass": "applications/empty"}},
- "applications": {
- "empty": {
- "type": "external",
- "processes": {"spare": 0},
- "working_directory": option.test_dir + "/go/empty",
- "executable": option.temp_dir + "/go/app",
- "isolation": {"namespaces": {"credential": True}},
- },
- },
- }
-
- elif 'python' in available['modules']:
- conf = {
- "listeners": {"*:7080": {"pass": "applications/empty"}},
- "applications": {
- "empty": {
- "type": "python",
- "processes": {"spare": 0},
- "path": option.test_dir + "/python/empty",
- "working_directory": option.test_dir + "/python/empty",
- "module": "wsgi",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- elif 'php' in available['modules']:
- conf = {
- "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
- "applications": {
- "phpinfo": {
- "type": "php",
- "processes": {"spare": 0},
- "root": option.test_dir + "/php/phpinfo",
- "working_directory": option.test_dir + "/php/phpinfo",
- "index": "index.php",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- elif 'ruby' in available['modules']:
- conf = {
- "listeners": {"*:7080": {"pass": "applications/empty"}},
- "applications": {
- "empty": {
- "type": "ruby",
- "processes": {"spare": 0},
- "working_directory": option.test_dir + "/ruby/empty",
- "script": option.test_dir + "/ruby/empty/config.ru",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- elif 'java' in available['modules']:
- TestApplicationJava().prepare_env('empty')
-
- conf = {
- "listeners": {"*:7080": {"pass": "applications/empty"}},
- "applications": {
- "empty": {
- "unit_jars": option.current_dir + "/build",
- "type": "java",
- "processes": {"spare": 0},
- "working_directory": option.test_dir + "/java/empty/",
- "webapp": option.temp_dir + "/java",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- elif 'node' in available['modules']:
- TestApplicationNode().prepare_env('basic')
-
- conf = {
- "listeners": {"*:7080": {"pass": "applications/basic"}},
- "applications": {
- "basic": {
- "type": "external",
- "processes": {"spare": 0},
- "working_directory": option.temp_dir + "/node",
- "executable": "app.js",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- elif 'perl' in available['modules']:
- conf = {
- "listeners": {"*:7080": {"pass": "applications/body_empty"}},
- "applications": {
- "body_empty": {
- "type": "perl",
- "processes": {"spare": 0},
- "working_directory": option.test_dir
- + "/perl/body_empty",
- "script": option.test_dir + "/perl/body_empty/psgi.pl",
- "isolation": {"namespaces": {"credential": True}},
- }
- },
- }
-
- else:
- return
-
- if 'success' not in self.conf(conf):
- return
-
- userns = self.getns('user')
- if not userns:
- return
-
- available['features']['isolation'] = {'user': userns}
-
- unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
- if os.path.exists(unp_clone_path):
- with open(unp_clone_path, 'r') as f:
- if str(f.read()).rstrip() == '1':
- available['features']['isolation'][
- 'unprivileged_userns_clone'
- ] = True
-
- for ns in self.allns:
- ns_value = self.getns(ns)
- if ns_value:
- available['features']['isolation'][ns] = ns_value
-
- def getns(self, nstype):
- # read namespace id from symlink file:
- # it points to: '<nstype>:[<ns id>]'
- # # eg.: 'pid:[4026531836]'
- nspath = '/proc/self/ns/' + nstype
- data = None
-
- if os.path.exists(nspath):
- data = int(os.readlink(nspath)[len(nstype) + 2 : -1])
-
- return data
diff --git a/test/unit/http.py b/test/unit/http.py
index 8d964978..57e6ed3a 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -7,11 +7,10 @@ import select
import socket
import pytest
-from conftest import option
-from unit.main import TestUnit
+from unit.option import option
-class TestHTTP(TestUnit):
+class TestHTTP():
def http(self, start_str, **kwargs):
sock_type = kwargs.get('sock_type', 'ipv4')
port = kwargs.get('port', 7080)
diff --git a/test/unit/main.py b/test/unit/main.py
deleted file mode 100644
index 488b3f4d..00000000
--- a/test/unit/main.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import pytest
-from conftest import option
-
-
-class TestUnit():
- @classmethod
- def setup_class(cls, complete_check=True):
- def check():
- missed = []
-
- # check modules
-
- if 'modules' in cls.prerequisites:
- available_modules = list(option.available['modules'].keys())
-
- for module in cls.prerequisites['modules']:
- if module in available_modules:
- continue
-
- missed.append(module)
-
- if missed:
- pytest.skip('Unit has no ' + ', '.join(missed) + ' module(s)')
-
- # check features
-
- if 'features' in cls.prerequisites:
- available_features = list(option.available['features'].keys())
-
- for feature in cls.prerequisites['features']:
- if feature in available_features:
- continue
-
- missed.append(feature)
-
- if missed:
- pytest.skip(', '.join(missed) + ' feature(s) not supported')
-
- if complete_check:
- check()
- else:
- return check
diff --git a/test/unit/option.py b/test/unit/option.py
new file mode 100644
index 00000000..677d806e
--- /dev/null
+++ b/test/unit/option.py
@@ -0,0 +1,16 @@
+class Options():
+ _options = {
+ 'skip_alerts': [],
+ 'skip_sanitizer': False,
+ }
+
+ def __setattr__(self, name, value):
+ Options._options[name] = value
+
+ def __getattr__(self, name):
+ if name in Options._options:
+ return Options._options[name]
+
+ raise AttributeError
+
+option = Options()
diff --git a/test/unit/utils.py b/test/unit/utils.py
new file mode 100644
index 00000000..7a0a3fe5
--- /dev/null
+++ b/test/unit/utils.py
@@ -0,0 +1,94 @@
+import os
+import socket
+import subprocess
+import time
+
+import pytest
+
+
+def public_dir(path):
+ os.chmod(path, 0o777)
+
+ for root, dirs, files in os.walk(path):
+ for d in dirs:
+ os.chmod(os.path.join(root, d), 0o777)
+ for f in files:
+ os.chmod(os.path.join(root, f), 0o777)
+
+
+def waitforfiles(*files):
+ for i in range(50):
+ wait = False
+
+ for f in files:
+ if not os.path.exists(f):
+ wait = True
+ break
+
+ if not wait:
+ return True
+
+ time.sleep(0.1)
+
+ return False
+
+
+def waitforsocket(port):
+ for i in range(50):
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ try:
+ sock.settimeout(5)
+ sock.connect(('127.0.0.1', port))
+ return
+
+ except ConnectionRefusedError:
+ time.sleep(0.1)
+
+ except KeyboardInterrupt:
+ raise
+
+ pytest.fail('Can\'t connect to the 127.0.0.1:' + port)
+
+
+def findmnt():
+ try:
+ out = subprocess.check_output(
+ ['findmnt', '--raw'], stderr=subprocess.STDOUT
+ ).decode()
+ except FileNotFoundError:
+ pytest.skip('requires findmnt')
+
+ return out
+
+
+def waitformount(template, wait=50):
+ for i in range(wait):
+ if findmnt().find(template) != -1:
+ return True
+
+ time.sleep(0.1)
+
+ return False
+
+
+def waitforunmount(template, wait=50):
+ for i in range(wait):
+ if findmnt().find(template) == -1:
+ return True
+
+ time.sleep(0.1)
+
+ return False
+
+
+def getns(nstype):
+ # read namespace id from symlink file:
+ # it points to: '<nstype>:[<ns id>]'
+ # # eg.: 'pid:[4026531836]'
+ nspath = '/proc/self/ns/' + nstype
+ data = None
+
+ if os.path.exists(nspath):
+ data = int(os.readlink(nspath)[len(nstype) + 2 : -1])
+
+ return data
diff --git a/version b/version
index 2ad4b832..55e5da42 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.21.0
-NXT_VERNUM=12100
+NXT_VERSION=1.22.0
+NXT_VERNUM=12200