From e4d9cffedfda046134ad7b152aa751c6e6404e06 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 15 Nov 2018 20:08:43 +0300 Subject: Version bump. --- src/nxt_main.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nxt_main.h b/src/nxt_main.h index 12c0ce6d..71ee6599 100644 --- a/src/nxt_main.h +++ b/src/nxt_main.h @@ -11,8 +11,8 @@ #include -#define NXT_VERSION "1.6" -#define NXT_VERNUM 10600 +#define NXT_VERSION "1.7" +#define NXT_VERNUM 10700 #define NXT_SERVER "Unit/" NXT_VERSION -- cgit From c1fd6cb589d08a5feae5399d176e6e02599c823d Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 15 Nov 2018 20:08:46 +0300 Subject: Added TLS information to ./configure summary. --- auto/summary | 1 + 1 file changed, 1 insertion(+) diff --git a/auto/summary b/auto/summary index ce6b42db..eba49b49 100644 --- a/auto/summary +++ b/auto/summary @@ -24,6 +24,7 @@ Configuration summary: IPv6 support: $NXT_INET6 Unix domain sockets support: $NXT_UNIX_DOMAIN + TLS support: $NXT_OPENSSL debug logging: $NXT_DEBUG END -- cgit From eff760bd2b79f6db1de00a9d871e45ada673c8fc Mon Sep 17 00:00:00 2001 From: Andrey Zelenkov Date: Thu, 15 Nov 2018 21:26:15 +0300 Subject: Tests: added command line arguments parsing in tests. Added the following command line arguments: -d, --detailed: Show detailed output for tests Usage examples: ./test/run.py --detailed python3 test/test_access_log.py --detailed python3 test/test_access_log.py -d TestUnitAccessLog.test_access_log_ipv6 -l, --log: Save unit.log after the test execution Usage examples: ./test/run.py -l python3 test/test_access_log.py -l python3 test/test_access_log.py --log TestUnitAccessLog.test_access_log_ipv6 --- test/test_access_log.py | 2 +- test/test_configuration.py | 2 +- test/test_go_application.py | 2 +- test/test_http_header.py | 2 +- test/test_perl_application.py | 2 +- test/test_php_application.py | 2 +- test/test_php_basic.py | 2 +- test/test_python_application.py | 2 +- test/test_python_basic.py | 2 +- test/test_python_environment.py | 2 +- test/test_python_procman.py | 2 +- test/test_ruby_application.py | 2 +- test/test_settings.py | 2 +- test/test_tls.py | 2 +- test/unit.py | 44 ++++++++++++++++++++++++++++++++++++++--- 15 files changed, 55 insertions(+), 17 deletions(-) diff --git a/test/test_access_log.py b/test/test_access_log.py index 05f5f54a..c8464796 100644 --- a/test/test_access_log.py +++ b/test/test_access_log.py @@ -305,4 +305,4 @@ Connection: close 'reopen 2') if __name__ == '__main__': - unittest.main() + TestUnitAccessLog.main() diff --git a/test/test_configuration.py b/test/test_configuration.py index 6db65bb3..02705afe 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -218,4 +218,4 @@ class TestUnitConfiguration(unit.TestUnitControl): }), 'no port') if __name__ == '__main__': - unittest.main() + TestUnitConfiguration.main() diff --git a/test/test_go_application.py b/test/test_go_application.py index 650d1c27..fd80bf5b 100644 --- a/test/test_go_application.py +++ b/test/test_go_application.py @@ -151,4 +151,4 @@ class TestUnitGoApplication(unit.TestUnitApplicationGo): 'arguments empty') if __name__ == '__main__': - unittest.main() + TestUnitGoApplication.main() diff --git a/test/test_http_header.py b/test/test_http_header.py index 1ca0920d..b850831d 100644 --- a/test/test_http_header.py +++ b/test/test_http_header.py @@ -163,4 +163,4 @@ a self.assertEqual(resp['status'], 200, 'transfer encoding chunked') if __name__ == '__main__': - unittest.main() + TestUnitHTTPHeader.main() diff --git a/test/test_perl_application.py b/test/test_perl_application.py index 09e3d576..c9cb3f0c 100644 --- a/test/test_perl_application.py +++ b/test/test_perl_application.py @@ -167,4 +167,4 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl): self.assertEqual(resp['body'], '0123456789', 'keep-alive 2') if __name__ == '__main__': - unittest.main() + TestUnitPerlApplication.main() diff --git a/test/test_php_application.py b/test/test_php_application.py index 0dbc743d..1b6dd497 100644 --- a/test/test_php_application.py +++ b/test/test_php_application.py @@ -205,4 +205,4 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP): 'ini value repeat') if __name__ == '__main__': - unittest.main() + TestUnitPHPApplication.main() diff --git a/test/test_php_basic.py b/test/test_php_basic.py index 9e0ce822..1ea46c91 100644 --- a/test/test_php_basic.py +++ b/test/test_php_basic.py @@ -139,4 +139,4 @@ class TestUnitPHPBasic(unit.TestUnitControl): 'delete app again') if __name__ == '__main__': - unittest.main() + TestUnitPHPBasic.main() diff --git a/test/test_python_application.py b/test/test_python_application.py index e71b6432..667047bc 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -349,4 +349,4 @@ Connection: close self.assertEqual(self.get()['body'], '0123456789', 'write') if __name__ == '__main__': - unittest.main() + TestUnitPythonApplication.main() diff --git a/test/test_python_basic.py b/test/test_python_basic.py index 4cb194f6..b5179dea 100644 --- a/test/test_python_basic.py +++ b/test/test_python_basic.py @@ -149,4 +149,4 @@ class TestUnitPythonBasic(unit.TestUnitControl): 'delete app again') if __name__ == '__main__': - unittest.main() + TestUnitPythonBasic.main() diff --git a/test/test_python_environment.py b/test/test_python_environment.py index 907ad57c..71e4d5b7 100644 --- a/test/test_python_environment.py +++ b/test/test_python_environment.py @@ -125,4 +125,4 @@ class TestUnitPythonEnvironment(unit.TestUnitApplicationPython): })['body'], pwd_default, 'restore default') if __name__ == '__main__': - unittest.main() + TestUnitPythonEnvironment.main() diff --git a/test/test_python_procman.py b/test/test_python_procman.py index 297484eb..65268d49 100644 --- a/test/test_python_procman.py +++ b/test/test_python_procman.py @@ -245,4 +245,4 @@ class TestUnitPythonProcman(unit.TestUnitApplicationPython): self.assertEqual(len(self.pids_for_process()), 0, 'stop all') if __name__ == '__main__': - unittest.main() + TestUnitPythonProcman.main() diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py index 77040127..57ab88cd 100644 --- a/test/test_ruby_application.py +++ b/test/test_ruby_application.py @@ -284,4 +284,4 @@ class TestUnitRubyApplication(unit.TestUnitApplicationRuby): self.assertEqual(resp['body'], '0123456789', 'keep-alive 2') if __name__ == '__main__': - unittest.main() + TestUnitRubyApplication.main() diff --git a/test/test_settings.py b/test/test_settings.py index 816dcb5e..b4ac33dc 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -170,4 +170,4 @@ Content-Length: %d 'settings'), 'settings negative value') if __name__ == '__main__': - unittest.main() + TestUnitSettings.main() diff --git a/test/test_tls.py b/test/test_tls.py index aaf939ec..fa5c9754 100644 --- a/test/test_tls.py +++ b/test/test_tls.py @@ -417,4 +417,4 @@ Connection: close self.assertEqual(resp['body'], '0123456789', 'application respawn body') if __name__ == '__main__': - unittest.main() + TestUnitTLS.main() diff --git a/test/unit.py b/test/unit.py index a5f96968..0415d83a 100644 --- a/test/unit.py +++ b/test/unit.py @@ -7,6 +7,7 @@ import time import shutil import socket import select +import argparse import platform import tempfile import unittest @@ -19,6 +20,27 @@ class TestUnit(unittest.TestCase): architecture = platform.architecture()[0] maxDiff = None + detailed = False + save_log = False + + def __init__(self, methodName='runTest'): + super().__init__(methodName) + + if re.match(r'.*\/run\.py$', sys.argv[0]): + args, rest = TestUnit._parse_args() + + TestUnit._set_args(args) + + @staticmethod + def main(): + args, rest = TestUnit._parse_args() + + sys.argv = sys.argv[:1] + rest + + TestUnit._set_args(args) + + unittest.main() + def setUp(self): self._run() @@ -49,7 +71,7 @@ class TestUnit(unittest.TestCase): # remove unit.log - if '--leave' not in sys.argv and success: + if not TestUnit.save_log and success: shutil.rmtree(self.testdir) else: @@ -227,6 +249,22 @@ class TestUnit(unittest.TestCase): return ret + @staticmethod + def _parse_args(): + parser = argparse.ArgumentParser(add_help=False) + + parser.add_argument('-d', '--detailed', dest='detailed', + action='store_true', help='Detailed output for tests') + parser.add_argument('-l', '--log', dest='save_log', + action='store_true', help='Save unit.log after the test execution') + + return parser.parse_known_args() + + @staticmethod + def _set_args(args): + TestUnit.detailed = args.detailed + TestUnit.save_log = args.save_log + def _print_path_to_log(self): print('Path to unit.log:\n' + self.testdir + '/unit.log') @@ -296,7 +334,7 @@ class TestUnitHTTP(TestUnit): sock.sendall(req) - if '--verbose' in sys.argv: + if TestUnit.detailed: print('>>>', req, sep='\n') resp = '' @@ -305,7 +343,7 @@ class TestUnitHTTP(TestUnit): enc = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding'] resp = self.recvall(sock).decode(enc) - if '--verbose' in sys.argv: + if TestUnit.detailed: print('<<<', resp.encode('utf-8'), sep='\n') if 'raw_resp' not in kwargs: -- cgit From 41d3d63758fc3846d5a09afd3b33aac19231942a Mon Sep 17 00:00:00 2001 From: Andrey Zelenkov Date: Thu, 15 Nov 2018 21:26:15 +0300 Subject: Tests: class prefix made optional. --- test/unit.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/unit.py b/test/unit.py index 0415d83a..c3efef26 100644 --- a/test/unit.py +++ b/test/unit.py @@ -31,10 +31,14 @@ class TestUnit(unittest.TestCase): TestUnit._set_args(args) - @staticmethod - def main(): + @classmethod + def main(cls): args, rest = TestUnit._parse_args() + for i, arg in enumerate(rest): + if arg[:5] == 'test_': + rest[i] = cls.__name__ + '.' + arg + sys.argv = sys.argv[:1] + rest TestUnit._set_args(args) -- cgit From 0fdc7c3a55daceb54c034a51b30f06a932236965 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 25 Oct 2018 15:43:48 +0300 Subject: Tests: Node.js application tests. --- test/node/404/404.html | 6 ++ test/node/404/app.js | 8 ++ test/node/basic/app.js | 6 ++ test/node/get_header_type/app.js | 7 ++ test/node/get_variables/app.js | 9 ++ test/node/mirror/app.js | 12 +++ test/node/post_variables/app.js | 15 +++ test/node/remove_header/app.js | 11 ++ test/node/set_header_array/app.js | 6 ++ test/node/status_message/app.js | 6 ++ test/node/update_header/app.js | 7 ++ test/node/variables/app.js | 20 ++++ test/node/write_before_write_head/app.js | 6 ++ test/node/write_buffer/app.js | 6 ++ test/node/write_callback/app.js | 10 ++ test/node/write_return/app.js | 6 ++ test/test_node_application.py | 166 +++++++++++++++++++++++++++++++ test/unit.py | 35 +++++++ 18 files changed, 342 insertions(+) create mode 100644 test/node/404/404.html create mode 100755 test/node/404/app.js create mode 100755 test/node/basic/app.js create mode 100755 test/node/get_header_type/app.js create mode 100755 test/node/get_variables/app.js create mode 100755 test/node/mirror/app.js create mode 100755 test/node/post_variables/app.js create mode 100755 test/node/remove_header/app.js create mode 100755 test/node/set_header_array/app.js create mode 100755 test/node/status_message/app.js create mode 100755 test/node/update_header/app.js create mode 100755 test/node/variables/app.js create mode 100755 test/node/write_before_write_head/app.js create mode 100755 test/node/write_buffer/app.js create mode 100755 test/node/write_callback/app.js create mode 100755 test/node/write_return/app.js create mode 100644 test/test_node_application.py diff --git a/test/node/404/404.html b/test/node/404/404.html new file mode 100644 index 00000000..6d0c635a --- /dev/null +++ b/test/node/404/404.html @@ -0,0 +1,6 @@ + +404 Not Found + +

404 Not Found

+ + diff --git a/test/node/404/app.js b/test/node/404/app.js new file mode 100755 index 00000000..9600d486 --- /dev/null +++ b/test/node/404/app.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var fs = require('fs'); + +require('unit-http').createServer(function (req, res) { + res.writeHead(404, {}); + res.end(fs.readFileSync('404.html')); +}).listen(7080); diff --git a/test/node/basic/app.js b/test/node/basic/app.js new file mode 100755 index 00000000..bc8d570a --- /dev/null +++ b/test/node/basic/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}); + res.end('Hello World\n'); +}).listen(7080); diff --git a/test/node/get_header_type/app.js b/test/node/get_header_type/app.js new file mode 100755 index 00000000..b606f142 --- /dev/null +++ b/test/node/get_header_type/app.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Number', 100); + res.setHeader('X-Type', typeof(res.getHeader('X-Number'))); + res.end(); +}).listen(7080); diff --git a/test/node/get_variables/app.js b/test/node/get_variables/app.js new file mode 100755 index 00000000..5c1faf41 --- /dev/null +++ b/test/node/get_variables/app.js @@ -0,0 +1,9 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + let query = require('url').parse(req.url, true).query; + res.setHeader('X-Var-1', query.var1); + res.setHeader('X-Var-2', query.var2); + res.setHeader('X-Var-3', query.var3); + res.end(); +}).listen(7080); diff --git a/test/node/mirror/app.js b/test/node/mirror/app.js new file mode 100755 index 00000000..abcb87cb --- /dev/null +++ b/test/node/mirror/app.js @@ -0,0 +1,12 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + let body = ''; + req.on('data', chunk => { + body += chunk.toString(); + }); + req.on('end', () => { + res.writeHead(200, {'Content-Length': Buffer.byteLength(body)}); + res.end(body); + }); +}).listen(7080); diff --git a/test/node/post_variables/app.js b/test/node/post_variables/app.js new file mode 100755 index 00000000..928a38cf --- /dev/null +++ b/test/node/post_variables/app.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + let body = ''; + req.on('data', chunk => { + body += chunk.toString(); + }); + req.on('end', () => { + let query = require('querystring').parse(body); + res.setHeader('X-Var-1', query.var1); + res.setHeader('X-Var-2', query.var2); + res.setHeader('X-Var-3', query.var3); + res.end(); + }); +}).listen(7080); diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js new file mode 100755 index 00000000..28fee16d --- /dev/null +++ b/test/node/remove_header/app.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Header', 'test'); + res.setHeader('Was-Header', res.hasHeader('X-Header').toString()); + + res.removeHeader('X-Header'); + res.setHeader('Has-Header', res.hasHeader('X-Header').toString()); + + res.end(); +}).listen(7080); diff --git a/test/node/set_header_array/app.js b/test/node/set_header_array/app.js new file mode 100755 index 00000000..faac45c7 --- /dev/null +++ b/test/node/set_header_array/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('Set-Cookie', ['tc=one,two,three', 'tc=four,five,six']); + res.end(); +}).listen(7080); diff --git a/test/node/status_message/app.js b/test/node/status_message/app.js new file mode 100755 index 00000000..4f3b064a --- /dev/null +++ b/test/node/status_message/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, 'blah', {'Content-Type': 'text/plain'}); + res.end(); +}).listen(7080); diff --git a/test/node/update_header/app.js b/test/node/update_header/app.js new file mode 100755 index 00000000..0c5cd237 --- /dev/null +++ b/test/node/update_header/app.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Header', 'test'); + res.setHeader('X-Header', 'new'); + res.end(); +}).listen(7080); diff --git a/test/node/variables/app.js b/test/node/variables/app.js new file mode 100755 index 00000000..968afba5 --- /dev/null +++ b/test/node/variables/app.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + let body = ''; + req.on('data', chunk => { + body += chunk.toString(); + }); + req.on('end', () => { + res.setHeader('Request-Method', req.method); + res.setHeader('Request-Uri', req.url); + res.setHeader('Server-Protocol', req.httpVersion); + res.setHeader('Request-Raw-Headers', req.rawHeaders.join()); + res.setHeader('Content-Length', Buffer.byteLength(body)); + res.setHeader('Content-Type', req.headers['Content-Type']); + res.setHeader('Custom-Header', req.headers['Custom-Header']); + res.setHeader('Http-Host', req.headers['Host']); + res.writeHead(200, {}); + res.end(body); + }); +}).listen(7080); diff --git a/test/node/write_before_write_head/app.js b/test/node/write_before_write_head/app.js new file mode 100755 index 00000000..9fe3a58d --- /dev/null +++ b/test/node/write_before_write_head/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.write('blah'); + res.writeHead(200, {'Content-Type': 'text/plain'}); +}).listen(7080); diff --git a/test/node/write_buffer/app.js b/test/node/write_buffer/app.js new file mode 100755 index 00000000..f41de2a1 --- /dev/null +++ b/test/node/write_buffer/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end(new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])); +}).listen(7080); diff --git a/test/node/write_callback/app.js b/test/node/write_callback/app.js new file mode 100755 index 00000000..9d4bc1c5 --- /dev/null +++ b/test/node/write_callback/app.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + var a = 'blah'; + res.write('hello', 'utf8', function() { + a = 'world'; + }); + res.end(a); +}).listen(7080); diff --git a/test/node/write_return/app.js b/test/node/write_return/app.js new file mode 100755 index 00000000..3ae967c6 --- /dev/null +++ b/test/node/write_return/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end(res.write('body').toString()); +}).listen(7080); diff --git a/test/test_node_application.py b/test/test_node_application.py new file mode 100644 index 00000000..715173e6 --- /dev/null +++ b/test/test_node_application.py @@ -0,0 +1,166 @@ +import unittest +import unit + +class TestUnitNodeApplication(unit.TestUnitApplicationNode): + + def setUpClass(): + u = unit.TestUnit().check_modules('node') + + def test_node_application_basic(self): + self.load('basic') + + resp = self.get() + self.assertEqual(resp['headers']['Content-Type'], 'text/plain', + 'basic header') + self.assertEqual(resp['body'], 'Hello World\n', 'basic body') + + def test_node_application_seq(self): + self.load('basic') + + self.assertEqual(self.get()['status'], 200, 'seq') + self.assertEqual(self.get()['status'], 200, 'seq 2') + + def test_node_application_variables(self): + self.load('variables') + + body = 'Test body string.' + + resp = self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html', + 'Custom-Header': 'blah' + }, body=body) + + self.assertEqual(resp['status'], 200, 'status') + headers = resp['headers'] + header_server = headers.pop('Server') + self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') + + date = headers.pop('Date') + self.assertEqual(date[-4:], ' GMT', 'date header timezone') + self.assertLess(abs(self.date_to_sec_epoch(date) - self.sec_epoch()), 5, + 'date header') + + raw_headers = headers.pop('Request-Raw-Headers') + self.assertRegex(raw_headers, r'^(?:Host|localhost|Content-Type|' \ + 'text\/html|Custom-Header|blah|Content-Length|17|,)+$', + 'raw headers') + + self.assertDictEqual(headers, { + 'Content-Length': str(len(body)), + 'Content-Type': 'text/html', + 'Request-Method': 'POST', + 'Request-Uri': '/', + 'Http-Host': 'localhost', + 'Server-Protocol': 'HTTP/1.1', + 'Custom-Header': 'blah' + }, 'headers') + self.assertEqual(resp['body'], body, 'body') + + def test_node_application_get_variables(self): + self.load('get_variables') + + resp = self.get(url='/?var1=val1&var2=&var3') + self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables') + self.assertEqual(resp['headers']['X-Var-2'], '', 'GET variables 2') + self.assertEqual(resp['headers']['X-Var-3'], '', 'GET variables 3') + + def test_node_application_post_variables(self): + self.load('post_variables') + + resp = self.post(headers={ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Host': 'localhost', + 'Connection': 'close' + }, body='var1=val1&var2=&var3') + + self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables') + self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2') + self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3') + + def test_node_application_404(self): + self.load('404') + + resp = self.get() + + self.assertEqual(resp['status'], 404, '404 status') + self.assertRegex(resp['body'], r'404 Not Found', + '404 body') + + def test_node_keepalive_body(self): + self.load('mirror') + + (resp, sock) = self.post(headers={ + 'Connection': 'keep-alive', + 'Content-Type': 'text/html', + 'Host': 'localhost' + }, start=True, body='0123456789' * 500) + + self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1') + + resp = self.post(headers={ + 'Connection': 'close', + 'Content-Type': 'text/html', + 'Host': 'localhost' + }, sock=sock, body='0123456789') + + self.assertEqual(resp['body'], '0123456789', 'keep-alive 2') + + def test_node_application_write_buffer(self): + self.load('write_buffer') + + self.assertEqual(self.get()['body'], '6\r\nbuffer\r\n0\r\n\r\n', + 'write buffer') + + def test_node_application_write_callback(self): + self.load('write_callback') + + self.assertEqual(self.get()['body'], + '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n', 'write callback') + + def test_node_application_write_before_writeHead(self): + self.skip_alerts.append(r'process \d+ exited on signal') + self.load('write_before_write_head') + + self.get() + + def test_node_application_write_return(self): + self.load('write_return') + + self.assertEqual(self.get()['body'], + '4\r\nbody\r\n4\r\ntrue\r\n0\r\n\r\n', 'write return') + + def test_node_application_remove_header(self): + self.load('remove_header') + + resp = self.get() + self.assertEqual(resp['headers']['Was-Header'], 'true', 'was header') + self.assertEqual(resp['headers']['Has-Header'], 'false', 'has header') + self.assertFalse('X-Header' in resp['headers'], 'remove header') + + def test_node_application_update_header(self): + self.load('update_header') + + self.assertEqual(self.get()['headers']['X-Header'], 'new', + 'update header') + + def test_node_application_set_header_array(self): + self.load('set_header_array') + + self.assertListEqual(self.get()['headers']['Set-Cookie'], + ['tc=one,two,three', 'tc=four,five,six'], 'set header array') + + @unittest.expectedFailure + def test_node_application_status_message(self): + self.load('status_message') + + self.assertRegex(self.get(raw_resp=True), r'200 blah', 'status message') + + def test_node_application_get_header_type(self): + self.load('get_header_type') + + self.assertEqual(self.get()['headers']['X-Type'], 'number', + 'get header type') + +if __name__ == '__main__': + TestUnitNodeApplication.main() diff --git a/test/unit.py b/test/unit.py index c3efef26..e88ed684 100644 --- a/test/unit.py +++ b/test/unit.py @@ -117,6 +117,12 @@ class TestUnit(unittest.TestCase): except: m = None + elif module == 'node': + if os.path.isdir(self.pardir + '/node/node_modules'): + m = module + else: + m = None + elif module == 'openssl': try: subprocess.check_output(['which', 'openssl']) @@ -558,6 +564,35 @@ class TestUnitApplicationGo(TestUnitApplicationProto): } }) +class TestUnitApplicationNode(TestUnitApplicationProto): + def load(self, script, name='app.js'): + + # copy application + + shutil.copytree(self.current_dir + '/node/' + script, + self.testdir + '/node') + + # link modules + + os.symlink(self.pardir + '/node/node_modules', + self.testdir + '/node/node_modules') + + self.conf({ + "listeners": { + "*:7080": { + "application": script + } + }, + "applications": { + script: { + "type": "external", + "processes": { "spare": 0 }, + "working_directory": self.testdir + '/node', + "executable": name + } + } + }) + class TestUnitApplicationPerl(TestUnitApplicationProto): def load(self, script, name='psgi.pl'): self.conf({ -- cgit From 39cd4a9dee9a8ea61af0efd6d62ed8d692375ab7 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 15 Nov 2018 21:50:00 +0300 Subject: Prettier ./configure summary. --- auto/summary | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/auto/summary b/auto/summary index eba49b49..1c9df4b1 100644 --- a/auto/summary +++ b/auto/summary @@ -5,26 +5,27 @@ cat << END -Configuration summary: +Unit configuration summary: - unit bin directory: "$NXT_BINDIR" - unit sbin directory: "$NXT_SBINDIR" - unit lib directory: "$NXT_LIBDIR" - unit include directory: "$NXT_INCDIR" - unit modules directory: "$NXT_MODULES" - unit state directory: "$NXT_STATE" + bin directory: ............. "$NXT_BINDIR" + sbin directory: ............ "$NXT_SBINDIR" + lib directory: ............. "$NXT_LIBDIR" + include directory: ......... "$NXT_INCDIR" + modules directory: ......... "$NXT_MODULES" + state directory: ........... "$NXT_STATE" - unit pid file: "$NXT_PID" - unit log file: "$NXT_LOG" + pid file: .................. "$NXT_PID" + log file: .................. "$NXT_LOG" - unit control API socket: "$NXT_CONTROL" + control API socket: ........ "$NXT_CONTROL" - non-privileged user: "$NXT_USER" - non-privileged group: "$NXT_GROUP" + non-privileged user: ....... "$NXT_USER" + non-privileged group: ...... "$NXT_GROUP" - IPv6 support: $NXT_INET6 - Unix domain sockets support: $NXT_UNIX_DOMAIN - TLS support: $NXT_OPENSSL - debug logging: $NXT_DEBUG + IPv6 support: .............. $NXT_INET6 + Unix domain sockets support: $NXT_UNIX_DOMAIN + TLS support: ............... $NXT_OPENSSL + + debug logging: ............. $NXT_DEBUG END -- cgit From adf22b6a0d3481f7fc4d38ade08a2a0dd4ea6f19 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 21 Nov 2018 18:22:19 +0300 Subject: PHP: fixed compatibility with ZTS. This closes #184 issue on GitHub. --- auto/modules/php | 2 +- src/nxt_php_sapi.c | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/auto/modules/php b/auto/modules/php index 762c1621..5d54c119 100644 --- a/auto/modules/php +++ b/auto/modules/php @@ -111,7 +111,7 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then #include int main() { - php_request_startup(); + php_module_startup(NULL, NULL, 0); return 0; }" diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 413764f1..fbddd779 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -45,19 +45,19 @@ static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type); static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type); -static int nxt_php_send_headers(sapi_headers_struct *sapi_headers); -static char *nxt_php_read_cookies(void); +static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); +static char *nxt_php_read_cookies(TSRMLS_D); static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC); nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, nxt_str_t *s, zval *track_vars_array TSRMLS_DC); static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, char *str, uint32_t len, zval *track_vars_array TSRMLS_DC); -static void nxt_php_register_variables(zval *track_vars_array); +static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC); #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE static void nxt_php_log_message(char *message, int syslog_type_int); #else -static void nxt_php_log_message(char *message); +static void nxt_php_log_message(char *message TSRMLS_DC); #endif #ifdef NXT_PHP7 @@ -159,6 +159,9 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { static nxt_task_t *nxt_php_task; +#ifdef ZTS +static void ***tsrm_ls; +#endif static nxt_int_t @@ -262,6 +265,17 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_memcpy(index->start, c->index.start, c->index.length); } +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); + tsrm_ls = ts_resource(0); +#endif + +#ifdef NXT_PHP7 +#if defined(ZEND_SIGNALS) || PHP_MINOR_VERSION > 0 + zend_signal_startup(); +#endif +#endif + sapi_startup(&nxt_php_sapi_module); if (c->options != NULL) { @@ -433,7 +447,8 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) if (ini_entry->on_modify && ini_entry->on_modify(ini_entry, cstr, value->length, ini_entry->mh_arg1, ini_entry->mh_arg2, - ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE) + ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE + TSRMLS_CC) != SUCCESS) { nxt_free(cstr); @@ -573,7 +588,11 @@ nxt_php_request_handler(nxt_unit_request_info_t *req) (char *) ctx->script.start); } +#if (NXT_PHP7) if (nxt_slow_path(php_request_startup() == FAILURE)) { +#else + if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) { +#endif nxt_unit_req_debug(req, "php_request_startup() failed"); rc = NXT_UNIT_ERROR; @@ -915,7 +934,7 @@ static void nxt_php_log_message(char *message, int syslog_type_int) #else static void -nxt_php_log_message(char *message) +nxt_php_log_message(char *message TSRMLS_DC) #endif { nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message); -- cgit From 262578dc71e4c1aaad01656a9b0c78539b1e7371 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 22 Nov 2018 20:23:43 +0300 Subject: PHP: workaround for bug #71041. Since PHP 7, a zend_signal_startup() call is required if the interpreter was built with ZEND_SIGNALS defined; such a call was added in 3fd76e4ce70a. However, the zend_signal_startup() export is missing from the PHP library; as the result, dlopen() fails with the 'Undefined symbol "zend_signal_startup"' error while loading the PHP module. Meanwhile, if PHP is built without ZTS, the zend_signal_startup() call can be omitted; otherwise, the missing call causes segmentation fault. The PHP fix already was committed to upstream, but we still have to deal with numerous unpatched versions remaining at large. See the related PHP bug: https://bugs.php.net/bug.php?id=71041 --- auto/modules/php | 27 ++++++++++++++++++++++++++- src/nxt_php_sapi.c | 8 ++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/auto/modules/php b/auto/modules/php index 5d54c119..362bbc69 100644 --- a/auto/modules/php +++ b/auto/modules/php @@ -124,6 +124,30 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then exit 1; fi + # Bug #71041 (https://bugs.php.net/bug.php?id=71041). + + nxt_feature="PHP zend_signal_startup()" + nxt_feature_name="" + nxt_feature_run=no + nxt_feature_incs="${NXT_PHP_INCLUDE}" + nxt_feature_libs="${NXT_PHP_LIB} ${NXT_PHP_LDFLAGS}" + nxt_feature_test=" + #include + #include + + int main() { + zend_signal_startup(); + return 0; + }" + + . auto/feature + + if [ $nxt_found = yes ]; then + NXT_ZEND_SIGNAL_STARTUP=1 + else + NXT_ZEND_SIGNAL_STARTUP=0 + fi + else $echo $echo $0: error: no PHP found. @@ -181,6 +205,7 @@ for nxt_src in $NXT_PHP_MODULE_SRCS; do $NXT_BUILD_DIR/$nxt_obj: $nxt_src \$(CC) -c \$(CFLAGS) \$(NXT_INCS) $NXT_PHP_INCLUDE \\ + -DNXT_ZEND_SIGNAL_STARTUP=$NXT_ZEND_SIGNAL_STARTUP \\ $nxt_dep_flags \\ -o $NXT_BUILD_DIR/$nxt_obj $nxt_src $nxt_dep_post @@ -191,7 +216,7 @@ END done - + cat << END >> $NXT_MAKEFILE .PHONY: ${NXT_PHP_MODULE} diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index fbddd779..47b5ff04 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -270,10 +270,14 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) tsrm_ls = ts_resource(0); #endif -#ifdef NXT_PHP7 -#if defined(ZEND_SIGNALS) || PHP_MINOR_VERSION > 0 +#if defined(NXT_PHP7) && defined(ZEND_SIGNALS) + +#if (NXT_ZEND_SIGNAL_STARTUP) zend_signal_startup(); +#elif defined(ZTS) +#error PHP is built with thread safety and broken signals. #endif + #endif sapi_startup(&nxt_php_sapi_module); -- cgit From 7c09387156790e579ff9a821ecb05224a9c7b2be Mon Sep 17 00:00:00 2001 From: Andrei Belov Date: Mon, 26 Nov 2018 13:05:37 +0300 Subject: Packages: added strict version dependency for unit-dev/unit-devel. --- pkg/deb/debian/control | 3 ++- pkg/rpm/unit.spec.in | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/deb/debian/control b/pkg/deb/debian/control index c0873a1c..9828b6ab 100644 --- a/pkg/deb/debian/control +++ b/pkg/deb/debian/control @@ -34,6 +34,7 @@ Package: unit-dev Section: libdevel Priority: optional Architecture: any -Depends: ${misc:Depends} +Depends: unit (= ${binary:Version}), + ${misc:Depends} Description: NGINX Unit (development files) Library and include files required for NGINX Unit modules development. diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in index e3523cb3..1ef02881 100644 --- a/pkg/rpm/unit.spec.in +++ b/pkg/rpm/unit.spec.in @@ -65,6 +65,7 @@ Summary: NGINX Unit (development files) Version: %%VERSION%% Release: %%RELEASE%%%{?dist}.ngx Group: Development/Libraries +Requires: unit == %%VERSION%%-%%RELEASE%%%{?dist}.ngx %description devel Library and include files required for NGINX Unit modules development. -- cgit From db631917190c44b3b55a15e4e5e88aa92e6b5334 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 27 Nov 2018 22:06:39 +0300 Subject: PHP: fixed "disable_functions" and "disable_classes" options. It turned out they need additional processing to work. This closes #183 issue on GitHub. --- src/nxt_php_sapi.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 47b5ff04..8c25f82a 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -15,16 +15,6 @@ #include -typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t; - -static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); - -static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); -static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); -nxt_inline u_char *nxt_realpath(const void *c); - -static void nxt_php_request_handler(nxt_unit_request_info_t *req); - #if PHP_MAJOR_VERSION >= 7 # define NXT_PHP7 1 # if PHP_MINOR_VERSION >= 1 @@ -40,11 +30,31 @@ static void nxt_php_request_handler(nxt_unit_request_info_t *req); # endif #endif + +typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t; + +#ifdef NXT_PHP7 +typedef int (*nxt_php_disable_t)(char *p, size_t size); +#else +typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC); +#endif + + +static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); + +static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); +static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); +nxt_inline u_char *nxt_realpath(const void *c); + +static void nxt_php_request_handler(nxt_unit_request_info_t *req); + static int nxt_php_startup(sapi_module_struct *sapi_module); static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type); static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type); +static void nxt_php_disable(nxt_task_t *task, const char *type, + nxt_str_t *value, char **ptr, nxt_php_disable_t disable); static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); static char *nxt_php_read_cookies(TSRMLS_D); static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, @@ -377,6 +387,21 @@ nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) if (nxt_php_alter_option(&name, &value, type) != NXT_OK) { nxt_log(task, NXT_LOG_ERR, "setting PHP option \"%V: %V\" failed", &name, &value); + continue; + } + + if (nxt_str_eq(&name, "disable_functions", 17)) { + nxt_php_disable(task, "function", &value, + &PG(disable_functions), + zend_disable_function); + continue; + } + + if (nxt_str_eq(&name, "disable_classes", 15)) { + nxt_php_disable(task, "class", &value, + &PG(disable_classes), + zend_disable_class); + continue; } } } @@ -469,6 +494,58 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) #endif +static void +nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value, + char **ptr, nxt_php_disable_t disable) +{ + char c, *p, *start; + + p = nxt_malloc(value->length + 1); + if (nxt_slow_path(p == NULL)) { + return; + } + + /* + * PHP frees this memory on module shutdown. + * See core_globals_dtor() for details. + */ + *ptr = p; + + nxt_memcpy(p, value->start, value->length); + p[value->length] = '\0'; + + start = p; + + do { + c = *p; + + if (c == ' ' || c == ',' || c == '\0') { + + if (p != start) { + *p = '\0'; + +#ifdef NXT_PHP7 + if (disable(start, p - start) +#else + if (disable(start, p - start TSRMLS_CC) +#endif + != SUCCESS) + { + nxt_log(task, NXT_LOG_ERR, + "PHP: failed to disable \"%s\": no such %s", + start, type); + } + } + + start = p + 1; + } + + p++; + + } while (c != '\0'); +} + + static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t) { -- cgit From 63d16aa5f6faf6c96188188c58b4a369ec4f3a4c Mon Sep 17 00:00:00 2001 From: Andrei Belov Date: Thu, 29 Nov 2018 12:10:16 +0300 Subject: Packages: made unit-go architecture-dependent. This closes #172 issue on GitHub. --- pkg/deb/Makefile | 4 +- pkg/deb/Makefile.go | 2 - pkg/deb/Makefile.go110 | 2 - pkg/deb/Makefile.go17 | 2 - pkg/deb/Makefile.go18 | 2 - pkg/deb/Makefile.go19 | 2 - pkg/deb/debian.module/control-noarch.in | 23 -------- pkg/deb/debian.module/rules-noarch.in | 100 -------------------------------- pkg/rpm/Makefile.go | 2 - 9 files changed, 2 insertions(+), 137 deletions(-) delete mode 100644 pkg/deb/debian.module/control-noarch.in delete mode 100755 pkg/deb/debian.module/rules-noarch.in diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index 735f283b..2dcf6f50 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -199,7 +199,7 @@ endif prebuild=`echo "$$MODULE_PREBUILD_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \ preinstall=`echo "$$MODULE_PREINSTALL_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \ post=`echo "$$MODULE_POST_$*" | sed -e ':a' -e 'N' -e '$$!ba' -e "s/\n/\$$CR/g"` ; \ - cat debian.module/$(if $(MODULE_NOARCH_$*),control-noarch.in,control.in) | sed \ + cat debian.module/control.in | sed \ -e "s#%%NAME%%#unit-$(MODULE_SUFFIX_$*)#g" \ -e "s#%%SUMMARY%%#$(MODULE_SUMMARY_$*)#g" \ -e "s#%%CODENAME%%#$(CODENAME)#g" \ @@ -210,7 +210,7 @@ endif -e "s#%%MODULE_BUILD_DEPENDS%%#$(MODULE_BUILD_DEPENDS_$*)#g" \ -e "s#%%MODULE_DEPENDS%%#$(MODULE_DEPENDS_$*)#g" \ > $@/$(SRCDIR)/debian/control ; \ - cat debian.module/$(if $(MODULE_NOARCH_$*),rules-noarch.in,rules.in) | sed \ + cat debian.module/rules.in | sed \ -e "s#%%NAME%%#unit-$(MODULE_SUFFIX_$*)#g" \ -e "s#%%CODENAME%%#$(CODENAME)#g" \ -e "s#%%UNIT_VERSION%%#$(VERSION)#g" \ diff --git a/pkg/deb/Makefile.go b/pkg/deb/Makefile.go index 2d7d6537..bbade7a3 100644 --- a/pkg/deb/Makefile.go +++ b/pkg/deb/Makefile.go @@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go) MODULE_BUILD_DEPENDS_go=,golang MODULE_DEPENDS_go=,golang -MODULE_NOARCH_go= true - define MODULE_PREINSTALL_go mkdir -p debian/unit-go/usr/share/doc/unit-go/examples/go-app install -m 644 -p debian/unit.example-go-app debian/unit-go/usr/share/doc/unit-go/examples/go-app/let-my-people.go diff --git a/pkg/deb/Makefile.go110 b/pkg/deb/Makefile.go110 index 863f7c90..9fb3da9d 100644 --- a/pkg/deb/Makefile.go110 +++ b/pkg/deb/Makefile.go110 @@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go110) MODULE_BUILD_DEPENDS_go110=,golang-1.10 MODULE_DEPENDS_go110=,golang-1.10 -MODULE_NOARCH_go110= true - define MODULE_PREINSTALL_go110 mkdir -p debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app install -m 644 -p debian/unit.example-go-app debian/unit-go1.10/usr/share/doc/unit-go1.10/examples/go-app/let-my-people.go diff --git a/pkg/deb/Makefile.go17 b/pkg/deb/Makefile.go17 index 201b32b2..4c3cc73f 100644 --- a/pkg/deb/Makefile.go17 +++ b/pkg/deb/Makefile.go17 @@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go17) MODULE_BUILD_DEPENDS_go17=,golang-1.7 MODULE_DEPENDS_go17=,golang-1.7 -MODULE_NOARCH_go17= true - define MODULE_PREINSTALL_go17 mkdir -p debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app install -m 644 -p debian/unit.example-go-app debian/unit-go1.7/usr/share/doc/unit-go1.7/examples/go-app/let-my-people.go diff --git a/pkg/deb/Makefile.go18 b/pkg/deb/Makefile.go18 index 70b155d4..a9db87e0 100644 --- a/pkg/deb/Makefile.go18 +++ b/pkg/deb/Makefile.go18 @@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go18) MODULE_BUILD_DEPENDS_go18=,golang-1.8 MODULE_DEPENDS_go18=,golang-1.8 -MODULE_NOARCH_go18= true - define MODULE_PREINSTALL_go18 mkdir -p debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app install -m 644 -p debian/unit.example-go-app debian/unit-go1.8/usr/share/doc/unit-go1.8/examples/go-app/let-my-people.go diff --git a/pkg/deb/Makefile.go19 b/pkg/deb/Makefile.go19 index 9ddcc493..43611560 100644 --- a/pkg/deb/Makefile.go19 +++ b/pkg/deb/Makefile.go19 @@ -19,8 +19,6 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go19) MODULE_BUILD_DEPENDS_go19=,golang-1.9 MODULE_DEPENDS_go19=,golang-1.9 -MODULE_NOARCH_go19= true - define MODULE_PREINSTALL_go19 mkdir -p debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app install -m 644 -p debian/unit.example-go-app debian/unit-go1.9/usr/share/doc/unit-go1.9/examples/go-app/let-my-people.go diff --git a/pkg/deb/debian.module/control-noarch.in b/pkg/deb/debian.module/control-noarch.in deleted file mode 100644 index e22bb49a..00000000 --- a/pkg/deb/debian.module/control-noarch.in +++ /dev/null @@ -1,23 +0,0 @@ -Source: %%NAME%% -Section: admin -Priority: extra -Maintainer: Andrei Belov -Build-Depends: debhelper (>= 9), - linux-libc-dev%%MODULE_BUILD_DEPENDS%% -Standards-Version: 3.9.5 -Homepage: https://unit.nginx.org - -Package: %%NAME%% -Section: admin -Architecture: all -Depends: lsb-base, - ${misc:Depends}, - unit (= %%UNIT_VERSION%%-%%UNIT_RELEASE%%~%%CODENAME%%)%%MODULE_DEPENDS%% -Description: %%SUMMARY%% - NGINX Unit is a runtime and delivery environment for modern distributed - applications. It runs the application code in multiple languages - (PHP, Python, Go, etc.), and tightly couples it with traffic delivery - in and out of the application. Take this application server and proxy - directly in the cloud / container environments and fully control your app - dynamically via an API. - This package contains %%SUMMARY%%. diff --git a/pkg/deb/debian.module/rules-noarch.in b/pkg/deb/debian.module/rules-noarch.in deleted file mode 100755 index d75134db..00000000 --- a/pkg/deb/debian.module/rules-noarch.in +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/make -f - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie -export DEB_CFLAGS_MAINT_APPEND=-Wp,-D_FORTIFY_SOURCE=2 -fPIC -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/buildflags.mk - -BUILDDIR_unit = $(CURDIR)/debian/build-unit -BUILDDIR_unit_debug = $(CURDIR)/debian/build-unit-debug -INSTALLDIR = $(CURDIR)/debian/%%NAME%% -BASEDIR = $(CURDIR) - -%%MODULE_DEFINITIONS%% - -config.env.%: - dh_testdir - mkdir -p $(BUILDDIR_$*) - cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/ - cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/ - cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/ - cp -Pa $(CURDIR)/test $(BUILDDIR_$*)/ - touch $@ - -configure.unit: config.env.unit - cd $(BUILDDIR_unit) && \ - CFLAGS= ./configure \ - %%CONFIGURE_ARGS%% \ - --modules=/usr/lib/unit/modules \ - --cc-opt="$(CFLAGS)" && \ - ./configure %%MODULE_CONFARGS%% - touch $@ - -configure.unit_debug: config.env.unit_debug - cd $(BUILDDIR_unit_debug) && \ - CFLAGS= ./configure \ - %%CONFIGURE_ARGS%% \ - --modules=/usr/lib/unit/debug-modules \ - --cc-opt="$(CFLAGS)" \ - --debug && \ - ./configure %%MODULE_CONFARGS%% - touch $@ - -build-arch.%: configure.% - dh_testdir - $(MAKE) -C $(BUILDDIR_$*) %%MODULE_MAKEARGS%% - touch $@ - -build-indep: - dh_testdir - touch $@ - -build-arch: build-arch.unit build-arch.unit_debug - dh_testdir - touch $@ - -build: build-arch build-indep - dh_testdir - touch $@ - -clean: - dh_testdir - dh_testroot - dh_clean - find $(CURDIR) -maxdepth 1 -size 0 -delete - -install: build - dh_testdir - dh_testroot - dh_prep - dh_installdirs - dh_installinit - dh_installlogrotate -%%MODULE_PREINSTALL%% - cd $(BUILDDIR_unit) && \ - DESTDIR=$(INSTALLDIR) make %%MODULE_INSTARGS%% - cd $(BUILDDIR_unit_debug) && \ - DESTDIR=$(INSTALLDIR) make %%MODULE_INSTARGS%% - -binary-indep: build install - dh_testdir - dh_testroot - dh_installdocs - dh_installchangelogs - dh_link - dh_compress - dh_fixperms - dh_installdeb - dh_perl - dh_gencontrol - dh_md5sums - dh_builddeb - -binary-arch: install - -binary: binary-indep binary-arch - -.PHONY: clean binary-indep binary-arch binary install build diff --git a/pkg/rpm/Makefile.go b/pkg/rpm/Makefile.go index 40ebf8fb..d13e8d1a 100644 --- a/pkg/rpm/Makefile.go +++ b/pkg/rpm/Makefile.go @@ -26,13 +26,11 @@ BUILD_DEPENDS+= $(BUILD_DEPENDS_go) ifneq (,$(findstring $(OSVER),opensuse-leap opensuse-tumbleweed)) define MODULE_DEFINITIONS_go BuildRequires: $(BUILD_DEPENDS_go) -BuildArch: noarch %define gopath /usr/share/go/contrib endef else define MODULE_DEFINITIONS_go BuildRequires: $(BUILD_DEPENDS_go) -BuildArch: noarch endef endif export MODULE_DEFINITIONS_go -- cgit From d500e29f8818247c1eb6fe38c0d802405aa2c283 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 4 Dec 2018 17:18:00 +0300 Subject: Ruby: rpath made optional. In most cases it is not needed because Ruby libraries are in the default path. At the same time, rpath pointing to the default path is prohibited by rpmbuild on Fedora. This is related to issue #87 on GitHub. --- auto/modules/ruby | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/auto/modules/ruby b/auto/modules/ruby index 05072353..7d379f2f 100644 --- a/auto/modules/ruby +++ b/auto/modules/ruby @@ -62,10 +62,9 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then NXT_RUBY_LIBNAME=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["RUBY_SO_NAME"])'` NXT_RUBY_LIBSCONF=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["LIBS"])'` - NXT_RUBY_LIBPATH=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["libdir"])'` - NXT_RUBY_LIBS="-L$NXT_RUBY_LIBPATH -Wl,-rpath,${NXT_RUBY_LIBPATH} -l$NXT_RUBY_LIBNAME $NXT_RUBY_LIBSCONF" + NXT_RUBY_LIBS="-l$NXT_RUBY_LIBNAME $NXT_RUBY_LIBSCONF" - nxt_feature="Ruby" + nxt_feature="Ruby library" nxt_feature_name="" nxt_feature_run=no nxt_feature_incs="${NXT_RUBY_INCPATH}" @@ -80,6 +79,26 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then . auto/feature + if [ $nxt_found = no ]; then + NXT_RUBY_LIBPATH=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["libdir"])'` + NXT_RUBY_LIBS="-L$NXT_RUBY_LIBPATH -Wl,-rpath,${NXT_RUBY_LIBPATH} $NXT_RUBY_LIBS" + + nxt_feature="Ruby library in $NXT_RUBY_LIBPATH" + nxt_feature_name="" + nxt_feature_run=no + nxt_feature_incs="${NXT_RUBY_INCPATH}" + nxt_feature_libs="${NXT_RUBY_LIBS}" + nxt_feature_test=" + #include + + int main() { + ruby_init(); + return ruby_cleanup(0); + }" + + . auto/feature + fi + else $echo "checking for Ruby ... not found" fi -- cgit From 2ef5011236b1088c2f8b460c2c13c07e9b727477 Mon Sep 17 00:00:00 2001 From: Andrey Zelenkov Date: Tue, 4 Dec 2018 19:05:10 +0300 Subject: Tests: fixed test_node_application_write_callback. Fixed the write() callback order test. Also introduced a separate test to verify the callback call itself. --- test/node/write_callback/app.js | 7 +++++-- test/test_node_application.py | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/test/node/write_callback/app.js b/test/node/write_callback/app.js index 9d4bc1c5..3a9e51e8 100755 --- a/test/node/write_callback/app.js +++ b/test/node/write_callback/app.js @@ -1,10 +1,13 @@ #!/usr/bin/env node +var fs = require('fs'); + require('unit-http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); - var a = 'blah'; + var a = 'world'; res.write('hello', 'utf8', function() { - a = 'world'; + a = 'blah'; + fs.appendFile('callback', '', function() {}); }); res.end(a); }).listen(7080); diff --git a/test/test_node_application.py b/test/test_node_application.py index 715173e6..b736058c 100644 --- a/test/test_node_application.py +++ b/test/test_node_application.py @@ -112,11 +112,14 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode): self.assertEqual(self.get()['body'], '6\r\nbuffer\r\n0\r\n\r\n', 'write buffer') + @unittest.expectedFailure def test_node_application_write_callback(self): self.load('write_callback') self.assertEqual(self.get()['body'], - '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n', 'write callback') + '5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n', 'write callback order') + self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'), + 'write callback') def test_node_application_write_before_writeHead(self): self.skip_alerts.append(r'process \d+ exited on signal') -- cgit From 37cd6d23b6106f24b1fbaf8c0c898725ef4e6c58 Mon Sep 17 00:00:00 2001 From: Andrey Zelenkov Date: Wed, 28 Nov 2018 03:14:38 +0300 Subject: Tests: PHP directives "disable_classes" and "disable_functions". --- test/php/date_time/index.php | 4 ++ test/php/highlight_file_exec/index.php | 4 ++ test/test_php_application.py | 110 +++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 test/php/date_time/index.php create mode 100644 test/php/highlight_file_exec/index.php diff --git a/test/php/date_time/index.php b/test/php/date_time/index.php new file mode 100644 index 00000000..4e06fdf9 --- /dev/null +++ b/test/php/date_time/index.php @@ -0,0 +1,4 @@ +format('u'); +?> diff --git a/test/php/highlight_file_exec/index.php b/test/php/highlight_file_exec/index.php new file mode 100644 index 00000000..adcd5ed8 --- /dev/null +++ b/test/php/highlight_file_exec/index.php @@ -0,0 +1,4 @@ + diff --git a/test/test_php_application.py b/test/test_php_application.py index 1b6dd497..e0058d9a 100644 --- a/test/test_php_application.py +++ b/test/test_php_application.py @@ -1,11 +1,16 @@ import unittest import unit +import re class TestUnitPHPApplication(unit.TestUnitApplicationPHP): def setUpClass(): unit.TestUnit().check_modules('php') + def search_disabled(self, name): + p = re.compile(name + '\(\) has been disabled') + return self.search_in_log(p) + def test_php_application_variables(self): self.load('variables') @@ -204,5 +209,110 @@ class TestUnitPHPApplication(unit.TestUnitApplicationPHP): self.assertEqual(self.get()['headers']['X-Precision'], '5', 'ini value repeat') + def test_php_application_disable_functions_exec(self): + self.load('highlight_file_exec') + + self.conf({"admin": { "disable_functions": "exec" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_functions_highlight_file(self): + self.load('highlight_file_exec') + + self.conf({"admin": { "disable_functions": "highlight_file" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNotNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_functions_comma(self): + self.load('highlight_file_exec') + + self.conf({"admin": { "disable_functions": "exec,highlight_file" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNotNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_functions_space(self): + self.load('highlight_file_exec') + + self.conf({"admin": { "disable_functions": "exec highlight_file" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNotNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_functions_user(self): + self.load('highlight_file_exec') + + self.conf({"user": { "disable_functions": "exec" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_functions_nonexistent(self): + self.load('highlight_file_exec') + + self.conf({"admin": { "disable_functions": "blah" }}, + 'applications/highlight_file_exec/options') + + self.get() + + self.assertIsNone(self.search_disabled('exec'), + 'disable_functions exec') + self.assertIsNone(self.search_disabled('highlight_file'), + 'disable_functions highlight_file') + + def test_php_application_disable_classes(self): + self.load('date_time') + + self.get() + + self.assertIsNone(self.search_disabled('DateTime'), + 'disable_classes before') + + self.conf({"admin": { "disable_classes": "DateTime" }}, + 'applications/date_time/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('DateTime'), + 'disable_classes') + + def test_php_application_disable_classes_user(self): + self.load('date_time') + + self.conf({"user": { "disable_classes": "DateTime" }}, + 'applications/date_time/options') + + self.get() + + self.assertIsNotNone(self.search_disabled('DateTime'), + 'disable_classes user') + if __name__ == '__main__': TestUnitPHPApplication.main() -- cgit From aafa4bbaf92cca2968eac19d74c59f7c7711b9dd Mon Sep 17 00:00:00 2001 From: Andrey Zelenkov Date: Tue, 11 Dec 2018 18:30:58 +0300 Subject: Tests: more Node.js tests. --- test/node/double_end/app.js | 6 ++ test/node/get_header_names/app.js | 8 +++ test/node/has_header/app.js | 6 ++ test/node/header_name_case/app.js | 8 +++ test/node/header_name_valid/app.js | 7 +++ test/node/header_value_object/app.js | 6 ++ test/node/promise_end/app.js | 16 +++++ test/node/promise_handler/app.js | 18 ++++++ test/node/remove_header/app.js | 2 +- test/node/write_multiple/app.js | 8 +++ test/test_node_application.py | 117 ++++++++++++++++++++++++++++++++++- 11 files changed, 200 insertions(+), 2 deletions(-) create mode 100755 test/node/double_end/app.js create mode 100755 test/node/get_header_names/app.js create mode 100755 test/node/has_header/app.js create mode 100755 test/node/header_name_case/app.js create mode 100755 test/node/header_name_valid/app.js create mode 100755 test/node/header_value_object/app.js create mode 100755 test/node/promise_end/app.js create mode 100755 test/node/promise_handler/app.js create mode 100755 test/node/write_multiple/app.js diff --git a/test/node/double_end/app.js b/test/node/double_end/app.js new file mode 100755 index 00000000..d8280917 --- /dev/null +++ b/test/node/double_end/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.end(); + res.end(); +}).listen(7080); diff --git a/test/node/get_header_names/app.js b/test/node/get_header_names/app.js new file mode 100755 index 00000000..4cbccc16 --- /dev/null +++ b/test/node/get_header_names/app.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('DATE', ['date1', 'date2']); + res.setHeader('X-Header', 'blah'); + res.setHeader('X-Names', res.getHeaderNames()); + res.end(); +}).listen(7080); diff --git a/test/node/has_header/app.js b/test/node/has_header/app.js new file mode 100755 index 00000000..040f551e --- /dev/null +++ b/test/node/has_header/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Has-Header', res.hasHeader(req['headers']['X-Header']) + ''); + res.end(); +}).listen(7080); diff --git a/test/node/header_name_case/app.js b/test/node/header_name_case/app.js new file mode 100755 index 00000000..490bd4d5 --- /dev/null +++ b/test/node/header_name_case/app.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Header', '1'); + res.setHeader('X-header', '2'); + res.setHeader('X-HEADER', '3'); + res.end(); +}).listen(7080); diff --git a/test/node/header_name_valid/app.js b/test/node/header_name_valid/app.js new file mode 100755 index 00000000..425f026f --- /dev/null +++ b/test/node/header_name_valid/app.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {}); + res.setHeader('@$', 'test'); + res.end(); +}).listen(7080); diff --git a/test/node/header_value_object/app.js b/test/node/header_value_object/app.js new file mode 100755 index 00000000..ff4e2bb0 --- /dev/null +++ b/test/node/header_value_object/app.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.setHeader('X-Header', {}); + res.end(); +}).listen(7080); diff --git a/test/node/promise_end/app.js b/test/node/promise_end/app.js new file mode 100755 index 00000000..ed22464c --- /dev/null +++ b/test/node/promise_end/app.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +var fs = require('fs'); + +require('unit-http').createServer(function (req, res) { + res.write('blah'); + + Promise.resolve().then(() => { + res.end(); + }); + + req.on('data', (data) => { + fs.appendFile('callback', '', function() {}); + }); + +}).listen(7080); diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js new file mode 100755 index 00000000..54df09d1 --- /dev/null +++ b/test/node/promise_handler/app.js @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +var fs = require('fs'); + +require('unit-http').createServer(function (req, res) { + res.end(); + + if (req.headers['X-Write-Call']) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('blah'); + } + + Promise.resolve().then(() => { + req.on('data', (data) => { + fs.appendFile(data.toString(), '', function() {}); + }); + }); +}).listen(7080); diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js index 28fee16d..578b72a7 100755 --- a/test/node/remove_header/app.js +++ b/test/node/remove_header/app.js @@ -4,7 +4,7 @@ require('unit-http').createServer(function (req, res) { res.setHeader('X-Header', 'test'); res.setHeader('Was-Header', res.hasHeader('X-Header').toString()); - res.removeHeader('X-Header'); + res.removeHeader(req['headers']['X-Remove']); res.setHeader('Has-Header', res.hasHeader('X-Header').toString()); res.end(); diff --git a/test/node/write_multiple/app.js b/test/node/write_multiple/app.js new file mode 100755 index 00000000..3cbb3b86 --- /dev/null +++ b/test/node/write_multiple/app.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +require('unit-http').createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain', 'Content-Length': 14}); + res.write('write'); + res.write('write2'); + res.end('end'); +}).listen(7080); diff --git a/test/test_node_application.py b/test/test_node_application.py index b736058c..5dedb5a3 100644 --- a/test/test_node_application.py +++ b/test/test_node_application.py @@ -127,6 +127,12 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode): self.get() + def test_node_application_double_end(self): + self.load('double_end') + + self.assertEqual(self.get()['status'], 200, 'double end') + self.assertEqual(self.get()['status'], 200, 'double end 2') + def test_node_application_write_return(self): self.load('write_return') @@ -136,11 +142,22 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode): def test_node_application_remove_header(self): self.load('remove_header') - resp = self.get() + resp = self.get(headers={ + 'Host': 'localhost', + 'X-Remove': 'X-Header' + }) self.assertEqual(resp['headers']['Was-Header'], 'true', 'was header') self.assertEqual(resp['headers']['Has-Header'], 'false', 'has header') self.assertFalse('X-Header' in resp['headers'], 'remove header') + def test_node_application_remove_header_nonexisting(self): + self.load('remove_header') + + self.assertEqual(self.get(headers={ + 'Host': 'localhost', + 'X-Remove': 'blah' + })['headers']['Has-Header'], 'true', 'remove header nonexisting') + def test_node_application_update_header(self): self.load('update_header') @@ -165,5 +182,103 @@ class TestUnitNodeApplication(unit.TestUnitApplicationNode): self.assertEqual(self.get()['headers']['X-Type'], 'number', 'get header type') + @unittest.expectedFailure + def test_node_application_header_name_case(self): + self.load('header_name_case') + + headers = self.get()['headers'] + + self.assertEqual(headers['X-HEADER'], '3', 'header value') + self.assertNotIn('X-Header', headers, 'insensitive') + self.assertNotIn('X-header', headers, 'insensitive 2') + + def test_node_application_promise_handler(self): + self.load('promise_handler') + + self.assertEqual(self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html' + }, body='callback')['status'], 200, 'promise handler request') + self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'), + 'promise handler') + + @unittest.expectedFailure + def test_node_application_promise_handler_write_after_end(self): + self.skip_alerts.append(r'process \d+ exited on signal') + self.load('promise_handler') + + self.assertEqual(self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html', + 'X-Write-Call': '1' + }, body='callback')['status'], 200, + 'promise handler request write after end') + + def test_node_application_promise_end(self): + self.load('promise_end') + + self.assertEqual(self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html' + }, body='end')['status'], 200, 'promise end request') + self.assertTrue(self.waitforfiles(self.testdir + '/node/callback'), + 'promise end') + + def test_node_application_promise_multiple_calls(self): + self.load('promise_handler') + + self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html' + }, body='callback1') + + self.assertTrue(self.waitforfiles(self.testdir + '/node/callback1'), + 'promise first call') + + self.post(headers={ + 'Host': 'localhost', + 'Content-Type': 'text/html' + }, body='callback2') + + self.assertTrue(self.waitforfiles(self.testdir + '/node/callback2'), + 'promise second call') + + @unittest.expectedFailure + def test_node_application_header_name_valid(self): + self.load('header_name_valid') + + self.assertNotIn('status', self.get(), 'header name valid') + + @unittest.expectedFailure + def test_node_application_header_value_object(self): + self.load('header_value_object') + + self.assertIn('X-Header', self.get()['headers'], 'header value object') + + @unittest.expectedFailure + def test_node_application_get_header_names(self): + self.load('get_header_names') + + self.assertListEqual(self.get()['headers']['X-Names'], + ['date', 'x-header'], 'get header names') + + def test_node_application_has_header(self): + self.load('has_header') + + self.assertEqual(self.get(headers={ + 'Host': 'localhost', + 'X-Header': 'length' + })['headers']['X-Has-Header'], 'false', 'has header length') + + self.assertEqual(self.get(headers={ + 'Host': 'localhost', + 'X-Header': 'Date' + })['headers']['X-Has-Header'], 'false', 'has header date') + + def test_node_application_write_multiple(self): + self.load('write_multiple') + + self.assertEqual(self.get()['body'], 'writewrite2end', 'write multiple') + if __name__ == '__main__': TestUnitNodeApplication.main() -- cgit From f51f95fa54b51a6c70939eba54c9823f3b941b87 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 12 Dec 2018 19:54:52 +0300 Subject: Node.js: fixed global install in some cases. By default "npm install" switches to non-privileged user to run package scripts if it is invoked by root. As a result it may prevent node-gyp from writing to package directory and break installation of the module. To disable this switching the --unsafe-perm flag is added. --- auto/modules/nodejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto/modules/nodejs b/auto/modules/nodejs index 443ee9d5..96ef6b98 100644 --- a/auto/modules/nodejs +++ b/auto/modules/nodejs @@ -161,7 +161,7 @@ install: ${NXT_NODE}-$NXT_NODE_INSTALL ${NXT_NODE}-install: ${NXT_NODE_TARBALL} \ $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC ${NXT_NODE_EXPORTS} && \\ - ${NXT_NPM} install -g ${PWD}/${NXT_NODE_TARBALL} + ${NXT_NPM} install -g --unsafe-perm ${PWD}/${NXT_NODE_TARBALL} ${NXT_NODE}-uninstall: ${NXT_NPM} uninstall -g unit-http -- cgit From aeb026c8ab41b907f2e19a9f2fa978717d034830 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 12 Dec 2018 19:57:15 +0300 Subject: Node.js: removed unused dependency. --- src/nodejs/unit-http/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index 3a15d573..6a2cd27c 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -22,8 +22,5 @@ }, "author": "Alexander Borisov", "license": "Apache-2.0", - "gypfile": true, - "dependencies": { - "node-addon-api": "1.2.0" - } + "gypfile": true } -- cgit From de3c062c6e3e869d726b93a1ffe617059df7611a Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:55:54 +0300 Subject: Node.js: buffering HTTP headers before writing the body. --- src/nodejs/unit-http/http_server.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 57163c0b..47851c98 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -178,21 +178,16 @@ function writeHead(statusCode, reason, obj) { } } } - - unit_lib.unit_response_headers(this, statusCode, this.headers, this.headers_count, this.headers_len); - - this.headersSent = true; }; ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { var contentLength = 0; if (!this.headersSent) { - this.writeHead(this.statusCode); - } + unit_lib.unit_response_headers(this, this.statusCode, this.headers, + this.headers_count, this.headers_len); - if (this.finished) { - return this; + this.headersSent = true; } if (typeof chunk === 'function') { @@ -225,15 +220,23 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { }; ServerResponse.prototype.write = function write(chunk, encoding, callback) { + if (this.finished) { + throw new Error("Write after end"); + } + this._writeBody(chunk, encoding, callback); return true; }; ServerResponse.prototype.end = function end(chunk, encoding, callback) { - this._writeBody(chunk, encoding, callback); + if (!this.finished) { + this._writeBody(chunk, encoding, callback); - this.finished = true; + unit_lib.unit_response_end(this); + + this.finished = true; + } return this; }; -- cgit From 13c9ebccca9c7bee80f4b9c1da4c128435d9dac1 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:55:58 +0300 Subject: Node.js: changed the 'data' event calling sequence for the request. The problem is caused by Promises' inconsistency. The 'date' event could have been triggered before the user has started listening for it. To resolve the issue, we override the 'on' method of the request's emitter. --- src/nodejs/unit-http/http_server.js | 40 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 47851c98..9b7b8403 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -288,6 +288,28 @@ ServerRequest.prototype.resume = function resume() { return []; }; +/* + * The "on" method is overridden to defer reading data until user code is + * ready, that is (ev === "data"). This can occur after req.emit("end") is + * executed, since the user code can be scheduled asynchronously by Promises + * and so on. Passing the data is postponed by process.nextTick() until + * the "on" method caller completes. + */ +ServerRequest.prototype.on = function on(ev, fn) { + Server.prototype.on.call(this, ev, fn); + + if (ev === "data") { + process.nextTick(function () { + if (this.server.buffer.length !== 0) { + this.emit("data", this.server.buffer); + } + + }.bind(this)); + } +}; + +ServerRequest.prototype.addListener = ServerRequest.prototype.on; + function Server(requestListener) { EventEmitter.call(this); @@ -321,22 +343,20 @@ Server.prototype.listen = function () { }; Server.prototype.run_events = function (server, req, res) { + req.server = server; + res.server = server; + req.res = res; + res.req = req; + + server.buffer = server.unit._read(req.socket.req_pointer); + /* Important!!! setImmediate starts the next iteration in Node.js loop. */ setImmediate(function () { server.emit("request", req, res); - Promise.resolve().then(() => { - let buf = server.unit._read(req.socket.req_pointer); - - if (buf.length != 0) { - req.emit("data", buf); - } - - req.emit("end"); - }); - Promise.resolve().then(() => { req.emit("finish"); + req.emit("end"); if (res.finished) { unit_lib.unit_response_end(res); -- cgit From f47a5db506911f1cf117bdc15474d40508bb7601 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:01 +0300 Subject: Node.js: napi_call_function() replaced with napi_make_callback(). The sequence of napi_open_callback_scope(), napi_call_function(), and napi_close_callback_scope() functions calls executes the provided JS code and all functions enqueued by process.nextTick() and Promises during this execution. --- src/nodejs/unit-http/http_server.js | 17 +++------ src/nodejs/unit-http/unit.cpp | 73 +++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 9b7b8403..8a536cde 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -342,7 +342,7 @@ Server.prototype.listen = function () { this.unit.listen(); }; -Server.prototype.run_events = function (server, req, res) { +Server.prototype.emit_events = function (server, req, res) { req.server = server; res.server = server; req.res = res; @@ -350,18 +350,11 @@ Server.prototype.run_events = function (server, req, res) { server.buffer = server.unit._read(req.socket.req_pointer); - /* Important!!! setImmediate starts the next iteration in Node.js loop. */ - setImmediate(function () { - server.emit("request", req, res); + server.emit("request", req, res); - Promise.resolve().then(() => { - req.emit("finish"); - req.emit("end"); - - if (res.finished) { - unit_lib.unit_response_end(res); - } - }); + process.nextTick(() => { + req.emit("finish"); + req.emit("end"); }); }; diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index be64a59b..b233359c 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -276,12 +276,13 @@ Unit::_read(napi_env env, napi_callback_info info) void Unit::request_handler(nxt_unit_request_info_t *req) { - Unit *obj; - napi_value socket, request, response; - napi_value global, server_obj; - napi_value run_events, events_res; - napi_status status; - napi_value events_args[3]; + Unit *obj; + napi_value socket, request, response, global, server_obj; + napi_value emit_events, events_res, async_name, resource_object; + napi_status status; + napi_async_context async_context; + napi_callback_scope async_scope; + napi_value events_args[3]; obj = reinterpret_cast(req->unit->data); @@ -328,11 +329,11 @@ Unit::request_handler(nxt_unit_request_info_t *req) return; } - status = napi_get_named_property(obj->env_, server_obj, "run_events", - &run_events); + status = napi_get_named_property(obj->env_, server_obj, "emit_events", + &emit_events); if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to get" - " 'run_events' function"); + napi_throw_error(obj->env_, NULL, "Failed to get " + "'emit_events' function"); return; } @@ -340,15 +341,57 @@ Unit::request_handler(nxt_unit_request_info_t *req) events_args[1] = request; events_args[2] = response; - status = napi_call_function(obj->env_, server_obj, run_events, 3, - events_args, &events_res); + status = napi_create_string_utf8(obj->env_, "unit_request_handler", + sizeof("unit_request_handler") - 1, + &async_name); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to create utf-8 string"); + return; + } + + status = napi_async_init(obj->env_, NULL, async_name, &async_context); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to init async object"); + return; + } + + status = napi_create_object(obj->env_, &resource_object); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to create object for " + "callback scope"); + return; + } + + status = napi_open_callback_scope(obj->env_, resource_object, async_context, + &async_scope); if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to call" - " 'run_events' function"); + napi_throw_error(obj->env_, NULL, "Failed to open callback scope"); return; } - napi_close_handle_scope(obj->env_, scope); + status = napi_make_callback(obj->env_, async_context, server_obj, + emit_events, 3, events_args, &events_res); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to make callback"); + return; + } + + status = napi_close_callback_scope(obj->env_, async_scope); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to close callback scope"); + return; + } + + status = napi_async_destroy(obj->env_, async_context); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to destroy async object"); + return; + } + + status = napi_close_handle_scope(obj->env_, scope); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to close handle scope"); + } } -- cgit From dc16885b60f1d31d09114ea6140384531cad78e0 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:06 +0300 Subject: Node.js: changed the unit-http socket constructor. Third-party file descriptors are not supported. Socket "readable" and "writable" options are set true by default. --- src/nodejs/unit-http/socket.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/nodejs/unit-http/socket.js b/src/nodejs/unit-http/socket.js index aef065bf..6e836949 100755 --- a/src/nodejs/unit-http/socket.js +++ b/src/nodejs/unit-http/socket.js @@ -18,10 +18,16 @@ function Socket(options) { throw new TypeError('Options must be object'); } - this.readable = (typeof options.readable === 'boolean' ? options.readable - : false); - this.writable = (typeof options.writable === 'boolean' ? options.writable - : false); + if ("fd" in options) { + throw new TypeError('Working with file descriptors not supported'); + } + + /* + * For HTTP TCP socket 'readable' and 'writable' are always true. + * These options are required by Express and Koa frameworks. + */ + this.readable = true; + this.writable = true; } util.inherits(Socket, EventEmitter); @@ -43,7 +49,6 @@ Socket.prototype.connect = function connect(options, connectListener) { this.once('connect', connectListener); this.connecting = true; - this.writable = true; return this; }; -- cgit From 704fe556b423795d06f79cec3a3406ef6defb635 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:09 +0300 Subject: Node.js: style fixes. No functional changes. --- src/nodejs/unit-http/http_server.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 8a536cde..c93d2b10 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -105,21 +105,23 @@ ServerResponse.prototype.removeHeader = function removeHeader(name) { } let name_len = Buffer.byteLength(name + "", 'latin1'); + let value = this.headers[name]; - if (Array.isArray(this.headers[name])) { - this.headers_count -= this.headers[name].length; - this.headers_len -= this.headers[name].length * name_len; + delete this.headers[name]; + + if (Array.isArray(value)) { + this.headers_count -= value.length; + this.headers_len -= value.length * name_len; - this.headers[name].forEach(function(val) { + value.forEach(function(val) { this.headers_len -= Buffer.byteLength(val + "", 'latin1'); }); - } else { - this.headers_count--; - this.headers_len -= name_len + Buffer.byteLength(this.headers[name] + "", 'latin1'); + return; } - delete this.headers[name]; + this.headers_count--; + this.headers_len -= name_len + Buffer.byteLength(value + "", 'latin1'); }; ServerResponse.prototype.sendDate = function sendDate() { -- cgit From 607653c0f12a99981dbf3118b60b84ded4084d50 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:13 +0300 Subject: Node.js: calling write callback asynchronously. --- src/nodejs/unit-http/http_server.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index c93d2b10..28f2303f 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -217,7 +217,19 @@ ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { } if (typeof callback === 'function') { - callback(this); + /* + * The callback must be called only when response.write() caller + * completes. process.nextTick() postpones the callback execution. + * + * process.nextTick() is not technically part of the event loop. + * Instead, the nextTickQueue will be processed after the current + * operation completes, regardless of the current phase of + * the event loop. All callbacks passed to process.nextTick() + * will be resolved before the event loop continues. + */ + process.nextTick(function () { + callback(this); + }.bind(this)); } }; -- cgit From dcf51274ce0953e577fbfffd81afb592319a2267 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:30 +0300 Subject: Node.js: checking uniqueness of HTTP headers for different case. --- src/nodejs/unit-http/http_server.js | 72 +++++++++++++++++++++++++------------ src/nodejs/unit-http/unit.cpp | 14 ++++++-- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 28f2303f..216c4394 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -47,24 +47,23 @@ ServerResponse.prototype.writeContinue = function writeContinue(cb) { ServerResponse.prototype.writeProcessing = function writeProcessing(cb) { }; -ServerResponse.prototype.setHeader = function setHeader(key, value) { - if (typeof key !== 'string') { - throw new TypeError('Key argument must be a string'); +ServerResponse.prototype.setHeader = function setHeader(name, value) { + if (typeof name !== 'string') { + throw new TypeError('Name argument must be a string'); } - let header_key_len = Buffer.byteLength(key, 'latin1'); - let header_len = 0 - let header_count = 0; + let value_len = 0 + let count = 0; if (Array.isArray(value)) { - header_count = value.length; + count = value.length; value.forEach(function(val) { if (typeof val !== 'string' && typeof val !== 'number') { throw new TypeError('Array entries must be string or number'); } - header_len += Buffer.byteLength(val + "", 'latin1'); + value_len += Buffer.byteLength(val + "", 'latin1'); }); } else { @@ -72,19 +71,27 @@ ServerResponse.prototype.setHeader = function setHeader(key, value) { throw new TypeError('Value argument must be string, number, or array'); } - header_count = 1; - header_len = Buffer.byteLength(value + "", 'latin1'); + count = 1; + value_len = Buffer.byteLength(value + "", 'latin1'); } - this.removeHeader(key); + let lc_name = name.toLowerCase(); - this.headers[key] = value; - this.headers_len += header_len + (header_key_len * header_count); - this.headers_count += header_count; + if (lc_name in this.headers) { + this._removeHeader(lc_name); + } + + let name_len = Buffer.byteLength(name, 'latin1'); + + this.headers[lc_name] = [name, value]; + this.headers_len += value_len + (name_len * count); + this.headers_count += count; }; ServerResponse.prototype.getHeader = function getHeader(name) { - return this.headers[name]; + const entry = this.headers[name.toLowerCase()]; + + return entry && entry[1]; }; ServerResponse.prototype.getHeaderNames = function getHeaderNames() { @@ -92,22 +99,43 @@ ServerResponse.prototype.getHeaderNames = function getHeaderNames() { }; ServerResponse.prototype.getHeaders = function getHeaders() { - return this.headers; + const ret = Object.create(null); + + if (this.headers) { + const keys = Object.keys(this.headers); + + for (var i = 0; i < keys.length; i++) { + const key = keys[i]; + + ret[key] = this.headers[key][1]; + } + } + + return ret; }; ServerResponse.prototype.hasHeader = function hasHeader(name) { - return name in this.headers; + return name.toLowerCase() in this.headers; }; ServerResponse.prototype.removeHeader = function removeHeader(name) { - if (!(name in this.headers)) { - return; + if (typeof name !== 'string') { + throw new TypeError('Name argument must be a string'); } - let name_len = Buffer.byteLength(name + "", 'latin1'); - let value = this.headers[name]; + let lc_name = name.toLowerCase(); + + if (lc_name in this.headers) { + this._removeHeader(lc_name); + } +}; + +ServerResponse.prototype._removeHeader = function _removeHeader(lc_name) { + let entry = this.headers[lc_name]; + let name_len = Buffer.byteLength(entry[0] + "", 'latin1'); + let value = entry[1]; - delete this.headers[name]; + delete this.headers[lc_name]; if (Array.isArray(value)) { this.headers_count -= value.length; diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index b233359c..36bc98db 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -737,7 +737,7 @@ Unit::response_send_headers(napi_env env, napi_callback_info info) uint32_t keys_count, i, j; uint16_t hash; napi_value this_arg, headers, keys, name, value, array_val; - napi_value req_num; + napi_value req_num, array_entry; napi_status status; napi_valuetype val_type; nxt_unit_field_t *f; @@ -814,7 +814,17 @@ Unit::response_send_headers(napi_env env, napi_callback_info info) goto failed; } - status = napi_get_property(env, headers, name, &value); + status = napi_get_property(env, headers, name, &array_entry); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_element(env, array_entry, 0, &name); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_element(env, array_entry, 1, &value); if (status != napi_ok) { goto failed; } -- cgit From ab461437b5714d75e710febe3b44da49ebfe83fc Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:37 +0300 Subject: Node.js: checking for exception after running JS code from C++. --- src/nodejs/unit-http/unit.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 36bc98db..60b0412a 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -277,7 +277,7 @@ void Unit::request_handler(nxt_unit_request_info_t *req) { Unit *obj; - napi_value socket, request, response, global, server_obj; + napi_value socket, request, response, global, server_obj, except; napi_value emit_events, events_res, async_name, resource_object; napi_status status; napi_async_context async_context; @@ -372,8 +372,25 @@ Unit::request_handler(nxt_unit_request_info_t *req) status = napi_make_callback(obj->env_, async_context, server_obj, emit_events, 3, events_args, &events_res); if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to make callback"); - return; + if (status != napi_pending_exception) { + napi_throw_error(obj->env_, NULL, "Failed to make callback"); + return; + } + + status = napi_get_and_clear_last_exception(obj->env_, &except); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, + "Failed to get and clear last exception"); + return; + } + + /* Logging a description of the error and call stack. */ + status = napi_fatal_exception(obj->env_, except); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to call " + "napi_fatal_exception() function"); + return; + } } status = napi_close_callback_scope(obj->env_, async_scope); -- cgit From c55f329ecf8d32bad0d5bc2aae1da1522b3a92f8 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:54 +0300 Subject: libunit: added generation of version header file. --- auto/make | 14 ++++++++++++-- auto/modules/nodejs | 5 +++-- src/nodejs/unit-http/binding.gyp | 2 +- src/nxt_unit.h | 2 ++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/auto/make b/auto/make index 5bbca9e4..4f716b93 100644 --- a/auto/make +++ b/auto/make @@ -82,6 +82,14 @@ $NXT_BUILD_DIR/$NXT_LIB_STATIC: \$(NXT_LIB_OBJS) $NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_STATIC \\ \$(NXT_LIB_OBJS) +$NXT_BUILD_DIR/nxt_unit_version.h: src/nxt_main.h + $echo -n '#define NXT_UNIT_VERNUM ' > $NXT_BUILD_DIR/nxt_unit_version.h + grep 'define NXT_VERNUM' src/nxt_main.h \\ + | sed -e 's/[^0-9]//g' >> $NXT_BUILD_DIR/nxt_unit_version.h + +$NXT_BUILD_DIR/src/nxt_unit.o: $NXT_BUILD_DIR/nxt_unit_version.h +$NXT_BUILD_DIR/src/nxt_lib.o: $NXT_BUILD_DIR/nxt_unit_version.h + $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC: \$(NXT_LIB_UNIT_OBJS) $NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \\ \$(NXT_LIB_UNIT_OBJS) @@ -288,7 +296,8 @@ libunit-install: $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC install -d \$(DESTDIR)$NXT_INCDIR install -p -m u=rw,go=r src/nxt_unit.h src/nxt_unit_field.h \ src/nxt_unit_request.h src/nxt_unit_response.h src/nxt_unit_sptr.h \ - src/nxt_unit_typedefs.h \$(DESTDIR)$NXT_INCDIR/ + src/nxt_unit_typedefs.h $NXT_BUILD_DIR/nxt_unit_version.h \ + \$(DESTDIR)$NXT_INCDIR/ libunit-uninstall: rm -f \$(DESTDIR)$NXT_LIBDIR/$NXT_LIB_UNIT_STATIC @@ -298,7 +307,8 @@ libunit-uninstall: \$(DESTDIR)$NXT_INCDIR/nxt_unit_request.h \ \$(DESTDIR)$NXT_INCDIR/nxt_unit_response.h \ \$(DESTDIR)$NXT_INCDIR/nxt_unit_sptr.h \ - \$(DESTDIR)$NXT_INCDIR/nxt_unit_typedefs.h + \$(DESTDIR)$NXT_INCDIR/nxt_unit_typedefs.h \ + \$(DESTDIR)$NXT_INCDIR/nxt_unit_version.h @rmdir -p \$(DESTDIR)$NXT_INCDIR 2>/dev/null || true END diff --git a/auto/modules/nodejs b/auto/modules/nodejs index 96ef6b98..d140eff1 100644 --- a/auto/modules/nodejs +++ b/auto/modules/nodejs @@ -123,8 +123,9 @@ fi NXT_NODE_TMP=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http NXT_NODE_TARBALL=${NXT_BUILD_DIR}/${NXT_NODE}-unit-http.tar.gz -NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src && \ - export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a" +NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src \ + && export UNIT_BUILD_PATH=${PWD}/${NXT_BUILD_DIR} \ + && export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a" if [ -n "$NXT_NODE_LOCAL" ]; then NXT_NODE_INSTALL=local-install diff --git a/src/nodejs/unit-http/binding.gyp b/src/nodejs/unit-http/binding.gyp index 171c2eb7..ee09bfed 100644 --- a/src/nodejs/unit-http/binding.gyp +++ b/src/nodejs/unit-http/binding.gyp @@ -3,7 +3,7 @@ 'target_name': "unit-http", 'sources': ["unit.cpp", "addon.cpp"], 'include_dirs': [ - " #include +#include "nxt_unit_version.h" #include "nxt_unit_typedefs.h" + enum { NXT_UNIT_OK = 0, NXT_UNIT_ERROR = 1, -- cgit From d6f38a3268dd4df717bdb9054f3f9ee47fa1f429 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:56:57 +0300 Subject: Node.js: added check for libunit version at compile time. --- auto/modules/nodejs | 11 +++++++++-- pkg/npm/Makefile | 5 +++++ src/nodejs/unit-http/package.json | 9 +++++---- src/nodejs/unit-http/unit.h | 9 +++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/auto/modules/nodejs b/auto/modules/nodejs index d140eff1..e0208f5d 100644 --- a/auto/modules/nodejs +++ b/auto/modules/nodejs @@ -123,6 +123,7 @@ fi NXT_NODE_TMP=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http NXT_NODE_TARBALL=${NXT_BUILD_DIR}/${NXT_NODE}-unit-http.tar.gz +NXT_NODE_VERSION_FILE=${NXT_NODE_TMP}/version.h NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src \ && export UNIT_BUILD_PATH=${PWD}/${NXT_BUILD_DIR} \ && export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a" @@ -149,9 +150,15 @@ ${NXT_NODE}: ${NXT_NODE}-copy $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC ${NXT_NODE_EXPORTS} && \\ cd ${NXT_NODE_TMP} && ${NXT_NODE_GYP} configure build clean -${NXT_NODE}-copy: +${NXT_NODE}-copy: ${NXT_NODE_VERSION_FILE} mkdir -p ${NXT_BUILD_DIR}/src/ - cp -rp src/nodejs/ ${NXT_BUILD_DIR}/src/${NXT_NODE} + cp -rp src/nodejs/* ${NXT_BUILD_DIR}/src/${NXT_NODE} + +${NXT_NODE_VERSION_FILE}: src/nxt_main.h + mkdir -p ${NXT_NODE_TMP} + $echo -n '#define NXT_NODE_VERNUM ' > $NXT_NODE_VERSION_FILE + grep 'define NXT_VERNUM' src/nxt_main.h \\ + | sed -e 's/[^0-9]//g' >> $NXT_NODE_VERSION_FILE ${NXT_NODE_TARBALL}: ${NXT_NODE}-copy tar -zcvf ${NXT_NODE_TARBALL} -C ${NXT_NODE_TMP} . diff --git a/pkg/npm/Makefile b/pkg/npm/Makefile index 2696c226..dfa9ccdc 100644 --- a/pkg/npm/Makefile +++ b/pkg/npm/Makefile @@ -3,7 +3,11 @@ DEFAULT_VERSION := $(shell grep 'define NXT_VERSION' ../../src/nxt_main.h \ | sed -e 's/^.*"\(.*\)".*/\1/') +DEFAULT_VERNUM := $(shell grep 'define NXT_VERNUM' ../../src/nxt_main.h \ + | sed -e 's/[^0-9]//g') + VERSION ?= $(DEFAULT_VERSION) +VERNUM ?= $(DEFAULT_VERNUM) NPM ?= npm default: @@ -11,6 +15,7 @@ default: copy: cp -rp ../../src/nodejs/unit-http . + echo '#define NXT_NODE_VERNUM ${VERNUM}' > unit-http/version.h mv unit-http/binding_pub.gyp unit-http/binding.gyp sed -e 's/"version"\s*:.*/"version": "${VERSION}.0",/' \ unit-http/package.json > unit-http/package.json.tmp diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index 6a2cd27c..13c91018 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -4,14 +4,15 @@ "description": "HTTP module for NGINX Unit", "main": "http.js", "files": [ + "unit.h", + "version.h", "addon.cpp", - "binding.gyp", - "http_server.js", + "unit.cpp", "http.js", + "http_server.js", "package.json", "socket.js", - "unit.cpp", - "unit.h", + "binding.gyp", "README.md" ], "scripts": { diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h index 5f541cc4..8baeb967 100644 --- a/src/nodejs/unit-http/unit.h +++ b/src/nodejs/unit-http/unit.h @@ -6,18 +6,23 @@ #ifndef _NXT_NODEJS_UNIT_H_INCLUDED_ #define _NXT_NODEJS_UNIT_H_INCLUDED_ - #include - #ifdef __cplusplus extern "C" { #endif +#include "version.h" #include + +#if NXT_UNIT_VERNUM != NXT_NODE_VERNUM +#error "libunit version mismatch." +#endif + #include #include + #ifdef __cplusplus } /* extern "C" */ #endif -- cgit From d9dad07934c17319b6aecd5b4958cdef3a196ad0 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:57:04 +0300 Subject: Node.js: removed unused _implicitHeader() function. --- src/nodejs/unit-http/http_server.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 216c4394..061c39aa 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -166,11 +166,6 @@ ServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) { return this; }; -// for Express -ServerResponse.prototype._implicitHeader = function _implicitHeader() { - this.writeHead(this.statusCode); -}; - ServerResponse.prototype.writeHead = writeHead; ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead; -- cgit From 98e8f366dabbe1b8e923678d9edd17ee61b54e0f Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 19 Dec 2018 15:57:07 +0300 Subject: Node.js: removed value checking for headers. --- src/nodejs/unit-http/http_server.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 061c39aa..057a1f26 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -59,18 +59,10 @@ ServerResponse.prototype.setHeader = function setHeader(name, value) { count = value.length; value.forEach(function(val) { - if (typeof val !== 'string' && typeof val !== 'number') { - throw new TypeError('Array entries must be string or number'); - } - value_len += Buffer.byteLength(val + "", 'latin1'); }); } else { - if (typeof value !== 'string' && typeof value !== 'number') { - throw new TypeError('Value argument must be string, number, or array'); - } - count = 1; value_len = Buffer.byteLength(value + "", 'latin1'); } -- cgit From cebec46353b3a185f96520e012cbf52e40238cae Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 19 Dec 2018 20:06:53 +0300 Subject: Python: replaced PyErr_PrintEx(1) with PyErr_Print(). These function calls are equivalent. No functional changes. --- src/nxt_python_wsgi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 3a5f1913..739ee0b9 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -354,7 +354,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) if (nxt_slow_path(module == NULL)) { nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); - PyErr_PrintEx(1); + PyErr_Print(); return NXT_ERROR; } @@ -369,7 +369,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) if (nxt_slow_path(PyCallable_Check(obj) == 0)) { nxt_alert(task, "\"application\" in module \"%s\" " "is not a callable object", nxt_py_module); - PyErr_PrintEx(1); + PyErr_Print(); goto fail; } @@ -804,7 +804,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name, nxt_unit_req_error(ctx->req, "Python failed to create value string \"%.*s\"", (int) size, src); - PyErr_PrintEx(1); + PyErr_Print(); return NXT_UNIT_ERROR; } @@ -839,7 +839,7 @@ nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name, nxt_unit_req_error(ctx->req, "Python failed to create value string \"%.*s\"", (int) size, str); - PyErr_PrintEx(1); + PyErr_Print(); return NXT_UNIT_ERROR; } -- cgit From 9c03079e3a4a7aead2f761a3a425bee065cc89d1 Mon Sep 17 00:00:00 2001 From: Artem Konev Date: Thu, 20 Dec 2018 14:17:58 +0300 Subject: Python: fixed a typo in path error message. --- src/nxt_python_wsgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 739ee0b9..df1c74ad 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -284,7 +284,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) c->path.length); if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, "Python failed create string object \"%V\"", + nxt_alert(task, "Python failed to create string object \"%V\"", &c->path); goto fail; } -- cgit From 1ce7e860b2913bc5c89258dca6c8fa9cf86ade35 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 20 Dec 2018 15:47:10 +0300 Subject: Python: cleanup of nxt_python_init(). - Removed surplus NULL assignments; - Added missing nxt_slow_path(); - Style cleanup. --- src/nxt_python_wsgi.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index df1c74ad..b6566788 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -276,7 +276,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) Py_InitializeEx(0); - obj = NULL; module = NULL; if (c->path.length > 0) { @@ -303,11 +302,9 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) } Py_DECREF(obj); - obj = NULL; } obj = PyCFunction_New(nxt_py_start_resp_method, NULL); - if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to initialize the \"start_response\" function"); @@ -317,7 +314,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_py_start_resp_obj = obj; obj = PyCFunction_New(nxt_py_write_method, NULL); - if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to initialize the \"write\" function"); goto fail; @@ -326,20 +322,19 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_py_write_obj = obj; obj = nxt_python_create_environ(task); - - if (obj == NULL) { + if (nxt_slow_path(obj == NULL)) { goto fail; } nxt_py_environ_ptyp = obj; obj = Py_BuildValue("[s]", "unit"); - if (obj == NULL) { + if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to create the \"sys.argv\" list"); goto fail; } - if (PySys_SetObject((char *) "argv", obj) != 0) { + if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) { nxt_alert(task, "Python failed to set the \"sys.argv\" list"); goto fail; } @@ -351,7 +346,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_py_module[c->module.length] = '\0'; module = PyImport_ImportModule(nxt_py_module); - if (nxt_slow_path(module == NULL)) { nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); PyErr_Print(); @@ -359,7 +353,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) } obj = PyDict_GetItemString(PyModule_GetDict(module), "application"); - if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to get \"application\" " "from module \"%s\"", nxt_py_module); -- cgit From 27394118b32ab395be50de15ecf514d5529090c5 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 20 Dec 2018 15:47:10 +0300 Subject: Python: fixed error reporting on initialization of applications. PyErr_Print() writes traceback to "sys.stderr", which is a file object that can buffer the output. If the process exits immediately, the buffer can be destroyed before flushing to the log. As a result, the user doesn't see the traceback. Now Py_Finalize() is also called in case of any errors during initialization. It finalizes the interpreter and flushes all data. --- src/nxt_python_wsgi.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index b6566788..bd3a2cb2 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -339,7 +339,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) goto fail; } - Py_DECREF(obj); + Py_CLEAR(obj); nxt_py_module = nxt_alloca(c->module.length + 1); nxt_memcpy(nxt_py_module, c->module.start, c->module.length); @@ -349,7 +349,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) if (nxt_slow_path(module == NULL)) { nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); PyErr_Print(); - return NXT_ERROR; + goto fail; } obj = PyDict_GetItemString(PyModule_GetDict(module), "application"); @@ -367,9 +367,10 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) } Py_INCREF(obj); - Py_DECREF(module); + Py_CLEAR(module); nxt_py_application = obj; + obj = NULL; nxt_unit_default_init(task, &python_init); @@ -377,7 +378,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) unit_ctx = nxt_unit_init(&python_init); if (nxt_slow_path(unit_ctx == NULL)) { - return NXT_ERROR; + goto fail; } rc = nxt_unit_run(unit_ctx); @@ -395,9 +396,7 @@ fail: Py_XDECREF(obj); Py_XDECREF(module); - if (nxt_py_home != NULL) { - nxt_free(nxt_py_home); - } + nxt_python_atexit(); return NXT_ERROR; } @@ -529,10 +528,10 @@ fail: static void nxt_python_atexit(void) { - Py_DECREF(nxt_py_application); - Py_DECREF(nxt_py_start_resp_obj); - Py_DECREF(nxt_py_write_obj); - Py_DECREF(nxt_py_environ_ptyp); + Py_XDECREF(nxt_py_application); + Py_XDECREF(nxt_py_start_resp_obj); + Py_XDECREF(nxt_py_write_obj); + Py_XDECREF(nxt_py_environ_ptyp); Py_Finalize(); -- cgit From 61a87bc285e08f17dfc48c2bcbaf300565d62f77 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 20 Dec 2018 19:17:08 +0300 Subject: Fixed style in CHANGES. --- CHANGES | 4 ++-- docs/changes.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 69761f50..a2083592 100644 --- a/CHANGES +++ b/CHANGES @@ -17,7 +17,7 @@ Changes with Unit 1.6 15 Nov 2018 *) Bugfix: "freed pointer is out of pool" alerts might have appeared in log. - *) Bugfix: module discovery didn't work on 64-bit big-endian systems + *) Bugfix: module discovery did not work on 64-bit big-endian systems like IBM/S390x. @@ -41,7 +41,7 @@ Changes with Unit 1.5 25 Oct 2018 producing "last message send failed: Resource temporarily unavailable" alerts in log; the bug had appeared in 1.4. - *) Bugfix: Go applications didn't work when Unit was built with musl C + *) Bugfix: Go applications did not work when Unit was built with musl C library. diff --git a/docs/changes.xml b/docs/changes.xml index b3bc33ee..f6f29848 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -66,7 +66,7 @@ various compatibility issues with Node.js applications. -module discovery didn't work on 64-bit big-endian systems like IBM/S390x. +module discovery did not work on 64-bit big-endian systems like IBM/S390x. @@ -138,7 +138,7 @@ the bug had appeared in 1.4. -Go applications didn't work when Unit was built with musl C library. +Go applications did not work when Unit was built with musl C library. -- cgit From 45655f26527c43a4606b0c8b5fbba1c426e10273 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 20 Dec 2018 19:17:08 +0300 Subject: Added version 1.7 CHANGES. --- CHANGES | 35 +++++++++++++++++++ docs/changes.xml | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/CHANGES b/CHANGES index a2083592..674120ac 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,39 @@ +Changes with Unit 1.7 20 Dec 2018 + + *) Change: now rpath is set in Ruby module only if the library was not + found in default search paths; this allows to meet packaging + restrictions on some systems. + + *) Bugfix: "disable_functions" and "disable_classes" PHP options set via + Control API did not work. + + *) Bugfix: Promises on request data in Node.js were not triggered. + + *) Bugfix: various compatibility issues with Node.js applications. + + *) Bugfix: a segmentation fault occurred in Node.js module if + application tried to read request body after request.end() was + called. + + *) Bugfix: a segmentation fault occurred in Node.js module if + application attempted to send header twice. + + *) Bugfix: names of response header fields in Node.js module were + erroneously treated as case-sensitive. + + *) Bugfix: uncatched exceptions in Node.js were not logged. + + *) Bugfix: global install of Node.js module from sources was broken on + some systems; the bug had appeared in 1.6. + + *) Bugfix: traceback for exceptions during initialization of Python + applications might not be logged. + + *) Bugfix: PHP module build failed if PHP interpreter was built with + thread safety enabled. + + Changes with Unit 1.6 15 Nov 2018 *) Change: "make install" now installs Node.js module as well if it was diff --git a/docs/changes.xml b/docs/changes.xml index f6f29848..7443795b 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -5,6 +5,106 @@ + + + + +NGINX Unit updated to 1.7. + + + + + + + + + + +now rpath is set in Ruby module only if the library was not found in default +search paths; this allows to meet packaging restrictions on some systems. + + + + + +"disable_functions" and "disable_classes" PHP options set via Control API +did not work. + + + + + +Promises on request data in Node.js were not triggered. + + + + + +various compatibility issues with Node.js applications. + + + + + +a segmentation fault occurred in Node.js module if application tried to read +request body after request.end() was called. + + + + + +a segmentation fault occurred in Node.js module if application attempted to +send header twice. + + + + + +names of response header fields in Node.js module were erroneously treated as +case-sensitive. + + + + + +uncatched exceptions in Node.js were not logged. + + + + + +global install of Node.js module from sources was broken on some systems; +the bug had appeared in 1.6. + + + + + +traceback for exceptions during initialization of Python applications might not +be logged. + + + + + +PHP module build failed if PHP interpreter was built with thread safety +enabled. + + + + + + " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.go1.7-dev b/pkg/docker/Dockerfile.go1.7-dev index 2c6975e2..7c8b2af2 100644 --- a/pkg/docker/Dockerfile.go1.7-dev +++ b/pkg/docker/Dockerfile.go1.7-dev @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.go1.8-dev b/pkg/docker/Dockerfile.go1.8-dev index 1cd1acfc..aecdeb41 100644 --- a/pkg/docker/Dockerfile.go1.8-dev +++ b/pkg/docker/Dockerfile.go1.8-dev @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal index 0ffff5c9..0c42a942 100644 --- a/pkg/docker/Dockerfile.minimal +++ b/pkg/docker/Dockerfile.minimal @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.perl5.24 b/pkg/docker/Dockerfile.perl5.24 index 9cbbc645..a7c8c9dc 100644 --- a/pkg/docker/Dockerfile.perl5.24 +++ b/pkg/docker/Dockerfile.perl5.24 @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.php7.0 b/pkg/docker/Dockerfile.php7.0 index 70ea8bbc..48aa472a 100644 --- a/pkg/docker/Dockerfile.php7.0 +++ b/pkg/docker/Dockerfile.php7.0 @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7 index 4cb4d5f3..fdd0bc7a 100644 --- a/pkg/docker/Dockerfile.python2.7 +++ b/pkg/docker/Dockerfile.python2.7 @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.python3.5 b/pkg/docker/Dockerfile.python3.5 index 26c54174..da6f825a 100644 --- a/pkg/docker/Dockerfile.python3.5 +++ b/pkg/docker/Dockerfile.python3.5 @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ diff --git a/pkg/docker/Dockerfile.ruby2.3 b/pkg/docker/Dockerfile.ruby2.3 index b0d9de49..0672a8c1 100644 --- a/pkg/docker/Dockerfile.ruby2.3 +++ b/pkg/docker/Dockerfile.ruby2.3 @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL maintainer="NGINX Docker Maintainers " -ENV UNIT_VERSION 1.6-1~stretch +ENV UNIT_VERSION 1.7-1~stretch RUN set -x \ && apt-get update \ -- cgit From 4195a29fabfe65f5a28baf2405c2077e2ba3c09a Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 20 Dec 2018 19:23:59 +0300 Subject: Added tag 1.7 for changeset 784b45adb0fe --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 06061b5b..aaefc396 100644 --- a/.hgtags +++ b/.hgtags @@ -12,3 +12,4 @@ b3cf22b8a17e0e35ca80decb03ed2cceb662c3de 1.3 8f4524a9cf87fbddf626302da071f5055cf33f28 1.4 b3dee0cc5a4edd046345511769b5cfec49044f1c 1.5 d411e7fdee9e03036adb652f8d9f4c45a420bdd5 1.6 +784b45adb0fe8bdd707510f59ed18309087e5c21 1.7 -- cgit