From a0c083af208cd9f676bb56762b4e27a3174a773d Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Wed, 12 May 2021 09:26:55 +0000 Subject: [PATCH] Node.js: a shim for overriding "http" and "websocket" modules. Also added stubs for Server.address() This was done to prevent crashes in some popular frameworks like express Supports both CommonJS and the new ES Modules system syntax e.g: app.js: const http = require('http') app.mjs: import http from "http" Usage on Node 14.16.x and higher: { "type": "external", "processes": {"spare": 0}, "working_directory": '/project', "executable": "/usr/bin/env", "arguments": [ "node", "--loader", "unit-http/require_shim.mjs" "--require", "unit-http/require_shim", "app.js" ] } Usage on Node 14.15.x and lower: { "type": "external", "processes": {"spare": 0}, "working_directory": '/project', "executable": "/usr/bin/env", "arguments": [ "node", "--require", "unit-http/require_shim", "app.js" ] } --- docs/changes.xml | 6 +++ src/nodejs/unit-http/http.js | 15 +++--- src/nodejs/unit-http/http_server.js | 24 ++++++--- src/nodejs/unit-http/require_shim.js | 27 ++++++++++ src/nodejs/unit-http/require_shim.mjs | 18 +++++++ test/node/404/app.js | 3 +- test/node/basic/app.js | 3 +- test/node/double_end/app.js | 3 +- test/node/get_header_names/app.js | 3 +- test/node/get_header_type/app.js | 3 +- test/node/get_variables/app.js | 3 +- test/node/has_header/app.js | 3 +- test/node/header_name_case/app.js | 3 +- test/node/header_name_valid/app.js | 3 +- test/node/header_value_object/app.js | 3 +- test/node/mirror/app.js | 3 +- test/node/post_variables/app.js | 3 +- test/node/promise_end/app.js | 3 +- test/node/promise_handler/app.js | 3 +- test/node/remove_header/app.js | 3 +- .../node/require_shim/es_modules_http/app.mjs | 6 +++ .../es_modules_http_indirect/app.js | 1 + .../es_modules_http_indirect/module.mjs | 6 +++ .../require_shim/es_modules_websocket/app.mjs | 30 +++++++++++ .../es_modules_websocket_indirect/app.js | 1 + .../es_modules_websocket_indirect/module.mjs | 30 +++++++++++ .../require_shim/transitive_dependency/app.js | 1 + .../transitive_dependency/transitive_http.js | 8 +++ test/node/require_shim/unit_http/app.js | 4 ++ test/node/set_header_array/app.js | 3 +- test/node/status_message/app.js | 3 +- test/node/update_header/app.js | 3 +- test/node/variables/app.js | 3 +- test/node/websockets/mirror/app.js | 7 +-- .../websockets/mirror_fragmentation/app.js | 7 +-- test/node/write_before_write_head/app.js | 3 +- test/node/write_buffer/app.js | 3 +- test/node/write_callback/app.js | 3 +- test/node/write_multiple/app.js | 3 +- test/node/write_return/app.js | 3 +- test/test_node_application.py | 19 +++++-- test/test_node_es_modules.py | 50 +++++++++++++++++++ test/unit/applications/lang/node.py | 21 ++++++-- test/unit/check/node.py | 13 ++++- 44 files changed, 288 insertions(+), 78 deletions(-) create mode 100644 src/nodejs/unit-http/require_shim.js create mode 100644 src/nodejs/unit-http/require_shim.mjs mode change 100755 => 100644 test/node/404/app.js mode change 100755 => 100644 test/node/basic/app.js mode change 100755 => 100644 test/node/double_end/app.js mode change 100755 => 100644 test/node/get_header_names/app.js mode change 100755 => 100644 test/node/get_header_type/app.js mode change 100755 => 100644 test/node/get_variables/app.js mode change 100755 => 100644 test/node/has_header/app.js mode change 100755 => 100644 test/node/header_name_case/app.js mode change 100755 => 100644 test/node/header_name_valid/app.js mode change 100755 => 100644 test/node/header_value_object/app.js mode change 100755 => 100644 test/node/mirror/app.js mode change 100755 => 100644 test/node/post_variables/app.js mode change 100755 => 100644 test/node/promise_end/app.js mode change 100755 => 100644 test/node/promise_handler/app.js mode change 100755 => 100644 test/node/remove_header/app.js create mode 100644 test/node/require_shim/es_modules_http/app.mjs create mode 100644 test/node/require_shim/es_modules_http_indirect/app.js create mode 100644 test/node/require_shim/es_modules_http_indirect/module.mjs create mode 100644 test/node/require_shim/es_modules_websocket/app.mjs create mode 100644 test/node/require_shim/es_modules_websocket_indirect/app.js create mode 100644 test/node/require_shim/es_modules_websocket_indirect/module.mjs create mode 100644 test/node/require_shim/transitive_dependency/app.js create mode 100644 test/node/require_shim/transitive_dependency/transitive_http.js create mode 100644 test/node/require_shim/unit_http/app.js mode change 100755 => 100644 test/node/set_header_array/app.js mode change 100755 => 100644 test/node/status_message/app.js mode change 100755 => 100644 test/node/update_header/app.js mode change 100755 => 100644 test/node/variables/app.js mode change 100755 => 100644 test/node/websockets/mirror/app.js mode change 100755 => 100644 test/node/websockets/mirror_fragmentation/app.js mode change 100755 => 100644 test/node/write_before_write_head/app.js mode change 100755 => 100644 test/node/write_buffer/app.js mode change 100755 => 100644 test/node/write_callback/app.js mode change 100755 => 100644 test/node/write_multiple/app.js mode change 100755 => 100644 test/node/write_return/app.js create mode 100644 test/test_node_es_modules.py diff --git a/docs/changes.xml b/docs/changes.xml index e2cb4f1d..c69523f9 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -31,6 +31,12 @@ NGINX Unit updated to 1.24.0. date="" time="" packager="Andrei Belov <defan@nginx.com>"> + + +a shim for automatic overriding "http" and "websocket" modules in Node.js. + + + ability to limit serving of static files by MIME types. diff --git a/src/nodejs/unit-http/http.js b/src/nodejs/unit-http/http.js index 3a25fa2f..d298a35f 100644 --- a/src/nodejs/unit-http/http.js +++ b/src/nodejs/unit-http/http.js @@ -5,19 +5,22 @@ 'use strict'; -const server = require('unit-http/http_server'); - -const { Server } = server; +const { + Server, + ServerRequest, + ServerResponse, +} = require('./http_server'); function createServer (requestHandler) { return new Server(requestHandler); } +const http = require("http") module.exports = { + ...http, Server, - STATUS_CODES: server.STATUS_CODES, createServer, - IncomingMessage: server.ServerRequest, - ServerResponse: server.ServerResponse + IncomingMessage: ServerRequest, + ServerResponse, }; diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index e59296ae..89964ec3 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -444,17 +444,30 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) { Server.prototype.listen = function (...args) { this.unit.listen(); - const cb = args.pop(); - - if (typeof cb === 'function') { - this.once('listening', cb); + if (typeof args[args.length - 1] === 'function') { + this.once('listening', args[args.length - 1]); } - this.emit('listening'); + /* + * Some express.js apps use the returned server object inside the listening + * callback, so we timeout the listening event to occur after this function + * returns. + */ + setImmediate(function() { + this.emit('listening') + }.bind(this)) return this; }; +Server.prototype.address = function () { + return { + family: "IPv4", + address: "127.0.0.1", + port: 80 + } +} + Server.prototype.emit_request = function (req, res) { if (req._websocket_handshake && this._upgradeListenerCount > 0) { this.emit('upgrade', req, req.socket); @@ -530,7 +543,6 @@ function connectionListener(socket) { } module.exports = { - STATUS_CODES: http.STATUS_CODES, Server, ServerResponse, ServerRequest, diff --git a/src/nodejs/unit-http/require_shim.js b/src/nodejs/unit-http/require_shim.js new file mode 100644 index 00000000..2b307629 --- /dev/null +++ b/src/nodejs/unit-http/require_shim.js @@ -0,0 +1,27 @@ +// can only be ran as part of a --require param on the node process +if (module.parent && module.parent.id === "internal/preload") { + const { Module } = require("module") + + if (!Module.prototype.require.__unit_shim) { + const http = require("./http") + const websocket = require("./websocket") + + const original = Module.prototype.require; + + Module.prototype.require = function (id) { + switch(id) { + case "http": + case "unit-http": + return http + + case "websocket": + case "unit-http/websocket": + return websocket + } + + return original.apply(this, arguments); + } + + Module.prototype.require.__unit_shim = true; + } +} diff --git a/src/nodejs/unit-http/require_shim.mjs b/src/nodejs/unit-http/require_shim.mjs new file mode 100644 index 00000000..067d63d4 --- /dev/null +++ b/src/nodejs/unit-http/require_shim.mjs @@ -0,0 +1,18 @@ +// must be ran as part of a --loader or --experimental-loader param +export async function resolve(specifier, context, defaultResolver) { + switch (specifier) { + case "websocket": + return { + url: new URL("./websocket.js", import.meta.url).href, + format: "cjs" + } + + case "http": + return { + url: new URL("./http.js", import.meta.url).href, + format: "cjs" + } + } + + return defaultResolver(specifier, context, defaultResolver) +} diff --git a/test/node/404/app.js b/test/node/404/app.js old mode 100755 new mode 100644 index 587c432d..ba15c104 --- a/test/node/404/app.js +++ b/test/node/404/app.js @@ -1,7 +1,6 @@ -#!/usr/bin/env node var fs = require('fs'); -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(404, {}).end(fs.readFileSync('404.html')); }).listen(7080); diff --git a/test/node/basic/app.js b/test/node/basic/app.js old mode 100755 new mode 100644 index 7820c474..9092022c --- a/test/node/basic/app.js +++ b/test/node/basic/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); }).listen(7080); diff --git a/test/node/double_end/app.js b/test/node/double_end/app.js old mode 100755 new mode 100644 index 63912097..653e33b1 --- a/test/node/double_end/app.js +++ b/test/node/double_end/app.js @@ -1,5 +1,4 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.end().end(); }).listen(7080); diff --git a/test/node/get_header_names/app.js b/test/node/get_header_names/app.js old mode 100755 new mode 100644 index 4cbccc16..a938b762 --- a/test/node/get_header_names/app.js +++ b/test/node/get_header_names/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('DATE', ['date1', 'date2']); res.setHeader('X-Header', 'blah'); res.setHeader('X-Names', res.getHeaderNames()); diff --git a/test/node/get_header_type/app.js b/test/node/get_header_type/app.js old mode 100755 new mode 100644 index b606f142..6e45b71f --- a/test/node/get_header_type/app.js +++ b/test/node/get_header_type/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('X-Number', 100); res.setHeader('X-Type', typeof(res.getHeader('X-Number'))); res.end(); diff --git a/test/node/get_variables/app.js b/test/node/get_variables/app.js old mode 100755 new mode 100644 index 5c1faf41..cded43d2 --- a/test/node/get_variables/app.js +++ b/test/node/get_variables/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('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); diff --git a/test/node/has_header/app.js b/test/node/has_header/app.js old mode 100755 new mode 100644 index eff7f4ff..04b13916 --- a/test/node/has_header/app.js +++ b/test/node/has_header/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('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 old mode 100755 new mode 100644 index 490bd4d5..af157547 --- a/test/node/header_name_case/app.js +++ b/test/node/header_name_case/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('X-Header', '1'); res.setHeader('X-header', '2'); res.setHeader('X-HEADER', '3'); diff --git a/test/node/header_name_valid/app.js b/test/node/header_name_valid/app.js old mode 100755 new mode 100644 index 425f026f..c0c36098 --- a/test/node/header_name_valid/app.js +++ b/test/node/header_name_valid/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {}); res.setHeader('@$', 'test'); res.end(); diff --git a/test/node/header_value_object/app.js b/test/node/header_value_object/app.js old mode 100755 new mode 100644 index ff4e2bb0..bacdc7d5 --- a/test/node/header_value_object/app.js +++ b/test/node/header_value_object/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('X-Header', {}); res.end(); }).listen(7080); diff --git a/test/node/mirror/app.js b/test/node/mirror/app.js old mode 100755 new mode 100644 index 1488917e..bdefe1cd --- a/test/node/mirror/app.js +++ b/test/node/mirror/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { let body = ''; req.on('data', chunk => { body += chunk.toString(); diff --git a/test/node/post_variables/app.js b/test/node/post_variables/app.js old mode 100755 new mode 100644 index 928a38cf..12b867cb --- a/test/node/post_variables/app.js +++ b/test/node/post_variables/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { let body = ''; req.on('data', chunk => { body += chunk.toString(); diff --git a/test/node/promise_end/app.js b/test/node/promise_end/app.js old mode 100755 new mode 100644 index ed22464c..373c3bc6 --- a/test/node/promise_end/app.js +++ b/test/node/promise_end/app.js @@ -1,8 +1,7 @@ -#!/usr/bin/env node var fs = require('fs'); -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.write('blah'); Promise.resolve().then(() => { diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js old mode 100755 new mode 100644 index 51c3666b..32d7d7b9 --- a/test/node/promise_handler/app.js +++ b/test/node/promise_handler/app.js @@ -1,8 +1,7 @@ -#!/usr/bin/env node var fs = require('fs'); -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.end(); if (req.headers['x-write-call']) { diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js old mode 100755 new mode 100644 index cd7b80c3..2a591235 --- a/test/node/remove_header/app.js +++ b/test/node/remove_header/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('X-Header', 'test'); res.setHeader('Was-Header', res.hasHeader('X-Header').toString()); diff --git a/test/node/require_shim/es_modules_http/app.mjs b/test/node/require_shim/es_modules_http/app.mjs new file mode 100644 index 00000000..c7bcfe49 --- /dev/null +++ b/test/node/require_shim/es_modules_http/app.mjs @@ -0,0 +1,6 @@ +import http from "http" + +http.createServer(function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) + .end('Hello World\n'); +}).listen(7080); diff --git a/test/node/require_shim/es_modules_http_indirect/app.js b/test/node/require_shim/es_modules_http_indirect/app.js new file mode 100644 index 00000000..535befba --- /dev/null +++ b/test/node/require_shim/es_modules_http_indirect/app.js @@ -0,0 +1 @@ +import("./module.mjs") diff --git a/test/node/require_shim/es_modules_http_indirect/module.mjs b/test/node/require_shim/es_modules_http_indirect/module.mjs new file mode 100644 index 00000000..c7bcfe49 --- /dev/null +++ b/test/node/require_shim/es_modules_http_indirect/module.mjs @@ -0,0 +1,6 @@ +import http from "http" + +http.createServer(function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) + .end('Hello World\n'); +}).listen(7080); diff --git a/test/node/require_shim/es_modules_websocket/app.mjs b/test/node/require_shim/es_modules_websocket/app.mjs new file mode 100644 index 00000000..a71ffa9d --- /dev/null +++ b/test/node/require_shim/es_modules_websocket/app.mjs @@ -0,0 +1,30 @@ +import http from "http" +import websocket from "websocket" + +let server = http.createServer(function() {}); +let webSocketServer = websocket.server; + +server.listen(7080, function() {}); + +var wsServer = new webSocketServer({ + maxReceivedMessageSize: 0x1000000000, + maxReceivedFrameSize: 0x1000000000, + fragmentOutgoingMessages: false, + fragmentationThreshold: 0x1000000000, + httpServer: server, +}); + +wsServer.on('request', function(request) { + var connection = request.accept(null); + + connection.on('message', function(message) { + if (message.type === 'utf8') { + connection.send(message.utf8Data); + } else if (message.type === 'binary') { + connection.send(message.binaryData); + } + + }); + + connection.on('close', function(r) {}); +}); diff --git a/test/node/require_shim/es_modules_websocket_indirect/app.js b/test/node/require_shim/es_modules_websocket_indirect/app.js new file mode 100644 index 00000000..535befba --- /dev/null +++ b/test/node/require_shim/es_modules_websocket_indirect/app.js @@ -0,0 +1 @@ +import("./module.mjs") diff --git a/test/node/require_shim/es_modules_websocket_indirect/module.mjs b/test/node/require_shim/es_modules_websocket_indirect/module.mjs new file mode 100644 index 00000000..a71ffa9d --- /dev/null +++ b/test/node/require_shim/es_modules_websocket_indirect/module.mjs @@ -0,0 +1,30 @@ +import http from "http" +import websocket from "websocket" + +let server = http.createServer(function() {}); +let webSocketServer = websocket.server; + +server.listen(7080, function() {}); + +var wsServer = new webSocketServer({ + maxReceivedMessageSize: 0x1000000000, + maxReceivedFrameSize: 0x1000000000, + fragmentOutgoingMessages: false, + fragmentationThreshold: 0x1000000000, + httpServer: server, +}); + +wsServer.on('request', function(request) { + var connection = request.accept(null); + + connection.on('message', function(message) { + if (message.type === 'utf8') { + connection.send(message.utf8Data); + } else if (message.type === 'binary') { + connection.send(message.binaryData); + } + + }); + + connection.on('close', function(r) {}); +}); diff --git a/test/node/require_shim/transitive_dependency/app.js b/test/node/require_shim/transitive_dependency/app.js new file mode 100644 index 00000000..aaca5216 --- /dev/null +++ b/test/node/require_shim/transitive_dependency/app.js @@ -0,0 +1 @@ +require("./transitive_http") diff --git a/test/node/require_shim/transitive_dependency/transitive_http.js b/test/node/require_shim/transitive_dependency/transitive_http.js new file mode 100644 index 00000000..f1eb98e5 --- /dev/null +++ b/test/node/require_shim/transitive_dependency/transitive_http.js @@ -0,0 +1,8 @@ +const http = require("http"); + +http.createServer(function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) + .end('Hello World\n'); +}).listen(7080); + +module.exports = http; diff --git a/test/node/require_shim/unit_http/app.js b/test/node/require_shim/unit_http/app.js new file mode 100644 index 00000000..9172e44f --- /dev/null +++ b/test/node/require_shim/unit_http/app.js @@ -0,0 +1,4 @@ +require("unit-http").createServer(function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) + .end('Hello World\n'); +}).listen(7080); diff --git a/test/node/set_header_array/app.js b/test/node/set_header_array/app.js old mode 100755 new mode 100644 index faac45c7..965330e2 --- a/test/node/set_header_array/app.js +++ b/test/node/set_header_array/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('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 old mode 100755 new mode 100644 index e8a798dd..ba51d35b --- a/test/node/status_message/app.js +++ b/test/node/status_message/app.js @@ -1,5 +1,4 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, 'blah', {'Content-Type': 'text/plain'}).end(); }).listen(7080); diff --git a/test/node/update_header/app.js b/test/node/update_header/app.js old mode 100755 new mode 100644 index 0c5cd237..905ac294 --- a/test/node/update_header/app.js +++ b/test/node/update_header/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.setHeader('X-Header', 'test'); res.setHeader('X-Header', 'new'); res.end(); diff --git a/test/node/variables/app.js b/test/node/variables/app.js old mode 100755 new mode 100644 index d8cdc20c..a569dddd --- a/test/node/variables/app.js +++ b/test/node/variables/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { let body = ''; req.on('data', chunk => { body += chunk.toString(); diff --git a/test/node/websockets/mirror/app.js b/test/node/websockets/mirror/app.js old mode 100755 new mode 100644 index 23746465..0443adb2 --- a/test/node/websockets/mirror/app.js +++ b/test/node/websockets/mirror/app.js @@ -1,9 +1,6 @@ -#!/usr/bin/env node -server = require('unit-http').createServer(function() {}); -webSocketServer = require('unit-http/websocket').server; -//server = require('http').createServer(function() {}); -//webSocketServer = require('websocket').server; +server = require('http').createServer(function() {}); +webSocketServer = require('websocket').server; server.listen(7080, function() {}); diff --git a/test/node/websockets/mirror_fragmentation/app.js b/test/node/websockets/mirror_fragmentation/app.js old mode 100755 new mode 100644 index 7024252a..ea580ac2 --- a/test/node/websockets/mirror_fragmentation/app.js +++ b/test/node/websockets/mirror_fragmentation/app.js @@ -1,9 +1,6 @@ -#!/usr/bin/env node -server = require('unit-http').createServer(function() {}); -webSocketServer = require('unit-http/websocket').server; -//server = require('http').createServer(function() {}); -//webSocketServer = require('websocket').server; +server = require('http').createServer(function() {}); +webSocketServer = require('websocket').server; server.listen(7080, function() {}); diff --git a/test/node/write_before_write_head/app.js b/test/node/write_before_write_head/app.js old mode 100755 new mode 100644 index 724b0efb..2293111a --- a/test/node/write_before_write_head/app.js +++ b/test/node/write_before_write_head/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.write('blah'); res.writeHead(200, {'Content-Type': 'text/plain'}).end(); }).listen(7080); diff --git a/test/node/write_buffer/app.js b/test/node/write_buffer/app.js old mode 100755 new mode 100644 index a7623523..506e8613 --- a/test/node/write_buffer/app.js +++ b/test/node/write_buffer/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}) .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 old mode 100755 new mode 100644 index 3a9e51e8..71eb4116 --- a/test/node/write_callback/app.js +++ b/test/node/write_callback/app.js @@ -1,8 +1,7 @@ -#!/usr/bin/env node var fs = require('fs'); -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); var a = 'world'; res.write('hello', 'utf8', function() { diff --git a/test/node/write_multiple/app.js b/test/node/write_multiple/app.js old mode 100755 new mode 100644 index 3cbb3b86..e9c51ae0 --- a/test/node/write_multiple/app.js +++ b/test/node/write_multiple/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain', 'Content-Length': 14}); res.write('write'); res.write('write2'); diff --git a/test/node/write_return/app.js b/test/node/write_return/app.js old mode 100755 new mode 100644 index 82dfbc6e..345b6c4b --- a/test/node/write_return/app.js +++ b/test/node/write_return/app.js @@ -1,6 +1,5 @@ -#!/usr/bin/env node -require('unit-http').createServer(function (req, res) { +require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}) .end(res.write('body').toString()); }).listen(7080); diff --git a/test/test_node_application.py b/test/test_node_application.py index 52ad72ee..59601ff0 100644 --- a/test/test_node_application.py +++ b/test/test_node_application.py @@ -9,13 +9,26 @@ from unit.utils import waitforfiles class TestNodeApplication(TestApplicationNode): prerequisites = {'modules': {'node': 'all'}} - def test_node_application_basic(self): - self.load('basic') - + def assert_basic_application(self): resp = self.get() assert resp['headers']['Content-Type'] == 'text/plain', 'basic header' assert resp['body'] == 'Hello World\n', 'basic body' + def test_node_application_basic(self): + self.load('basic') + + self.assert_basic_application() + + def test_node_application_require_shim_unit_http(self): + self.load('require_shim/unit_http') + + self.assert_basic_application() + + def test_node_application_require_shim_transitive_dependency(self): + self.load('require_shim/transitive_dependency') + + self.assert_basic_application() + def test_node_application_seq(self): self.load('basic') diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py new file mode 100644 index 00000000..ce27e474 --- /dev/null +++ b/test/test_node_es_modules.py @@ -0,0 +1,50 @@ +import pytest + +from distutils.version import LooseVersion +from unit.applications.lang.node import TestApplicationNode +from unit.applications.websockets import TestApplicationWebsocket + + +class TestNodeESModules(TestApplicationNode): + prerequisites = { + 'modules': { + 'node': lambda v: LooseVersion(v) >= LooseVersion("14.16.0") + } + } + + es_modules = True + ws = TestApplicationWebsocket() + + def assert_basic_application(self): + resp = self.get() + assert resp['headers']['Content-Type'] == 'text/plain', 'basic header' + assert resp['body'] == 'Hello World\n', 'basic body' + + def test_node_es_modules_require_shim_http(self): + self.load('require_shim/es_modules_http', name="app.mjs") + + self.assert_basic_application() + + def test_node_es_modules_require_shim_http_indirect(self): + self.load('require_shim/es_modules_http_indirect', name="app.js") + + self.assert_basic_application() + + def test_node_es_modules_require_shim_websockets(self): + self.load('require_shim/es_modules_websocket', name="app.mjs") + + message = 'blah' + + _, sock, _ = self.ws.upgrade() + + self.ws.frame_write(sock, self.ws.OP_TEXT, message) + frame = self.ws.frame_read(sock) + + assert message == frame['data'].decode('utf-8'), 'mirror' + + self.ws.frame_write(sock, self.ws.OP_TEXT, message) + frame = self.ws.frame_read(sock) + + assert message == frame['data'].decode('utf-8'), 'mirror 2' + + sock.close() diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py index cc6d06ef..3254f3d4 100644 --- a/test/unit/applications/lang/node.py +++ b/test/unit/applications/lang/node.py @@ -7,15 +7,16 @@ from unit.utils import public_dir class TestApplicationNode(TestApplicationProto): + application_type = "node" + es_modules = False + def prepare_env(self, script): # copy application - shutil.copytree( option.test_dir + '/node/' + script, option.temp_dir + '/node' ) # copy modules - shutil.copytree( option.current_dir + '/node/node_modules', option.temp_dir + '/node/node_modules', @@ -26,6 +27,19 @@ class TestApplicationNode(TestApplicationProto): def load(self, script, name='app.js', **kwargs): self.prepare_env(script) + if self.es_modules: + arguments = [ + "node", + "--loader", + "unit-http/require_shim.mjs", + "--require", + "unit-http/require_shim", + name, + ] + + else: + arguments = ["node", "--require", "unit-http/require_shim", name] + self._load_conf( { "listeners": { @@ -36,7 +50,8 @@ class TestApplicationNode(TestApplicationProto): "type": "external", "processes": {"spare": 0}, "working_directory": option.temp_dir + '/node', - "executable": name, + "executable": '/usr/bin/env', + "arguments": arguments, } }, }, diff --git a/test/unit/check/node.py b/test/unit/check/node.py index 236ba7b5..e053a749 100644 --- a/test/unit/check/node.py +++ b/test/unit/check/node.py @@ -1,6 +1,15 @@ import os +import subprocess def check_node(current_dir): - if os.path.exists(current_dir + '/node/node_modules'): - return True + if not os.path.exists(current_dir + '/node/node_modules'): + return None + + try: + v_bytes = subprocess.check_output(['/usr/bin/env', 'node', '-v']) + + return [str(v_bytes, 'utf-8').lstrip('v').rstrip()] + + except subprocess.CalledProcessError: + return None