diff --git a/test/js/node/test/parallel/test-http-full-response.js b/test/js/node/test/parallel/test-http-full-response.js new file mode 100644 index 00000000000000..d08e091ebd9808 --- /dev/null +++ b/test/js/node/test/parallel/test-http-full-response.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +// This test requires the program 'ab' +const http = require('http'); +const exec = require('child_process').exec; + +const bodyLength = 12345; + +const body = 'c'.repeat(bodyLength); + +const server = http.createServer(function(req, res) { + res.writeHead(200, { + 'Content-Length': bodyLength, + 'Content-Type': 'text/plain' + }); + res.end(body); +}); + +function runAb(opts, callback) { + const command = `ab ${opts} http://127.0.0.1:${server.address().port}/`; + exec(command, function(err, stdout, stderr) { + if (err) { + if (/ab|apr/i.test(stderr)) { + common.printSkipMessage(`problem spawning \`ab\`.\n${stderr}`); + process.reallyExit(0); + } + throw err; + } + + let m = /Document Length:\s*(\d+) bytes/i.exec(stdout); + const documentLength = parseInt(m[1]); + + m = /Complete requests:\s*(\d+)/i.exec(stdout); + const completeRequests = parseInt(m[1]); + + m = /HTML transferred:\s*(\d+) bytes/i.exec(stdout); + const htmlTransferred = parseInt(m[1]); + + assert.strictEqual(bodyLength, documentLength); + assert.strictEqual(completeRequests * documentLength, htmlTransferred); + + if (callback) callback(); + }); +} + +server.listen(0, common.mustCall(function() { + runAb('-c 1 -n 10', common.mustCall(function() { + console.log('-c 1 -n 10 okay'); + + runAb('-c 1 -n 100', common.mustCall(function() { + console.log('-c 1 -n 100 okay'); + + runAb('-c 1 -n 1000', common.mustCall(function() { + console.log('-c 1 -n 1000 okay'); + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-no-content-length.js b/test/js/node/test/parallel/test-http-no-content-length.js new file mode 100644 index 00000000000000..a3a51c015ec86f --- /dev/null +++ b/test/js/node/test/parallel/test-http-no-content-length.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const http = require('http'); + +const server = net.createServer(function(socket) { + // Neither Content-Length nor Connection + socket.end('HTTP/1.1 200 ok\r\n\r\nHello'); +}).listen(0, common.mustCall(function() { + http.get({ port: this.address().port }, common.mustCall(function(res) { + let body = ''; + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + body += chunk; + }); + res.on('end', common.mustCall(function() { + assert.strictEqual(body, 'Hello'); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-server-connections-checking-leak.js b/test/js/node/test/parallel/test-http-server-connections-checking-leak.js new file mode 100644 index 00000000000000..282c9a569fba7d --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-connections-checking-leak.js @@ -0,0 +1,24 @@ +'use strict'; + +// Flags: --expose-gc + +// Check that creating a server without listening does not leak resources. + +require('../common'); +const { onGC } = require('../common/gc'); +const Countdown = require('../common/countdown'); + +const http = require('http'); +const max = 100; + +// Note that Countdown internally calls common.mustCall, that's why it's not done here. +const countdown = new Countdown(max, () => {}); + +for (let i = 0; i < max; i++) { + const server = http.createServer((req, res) => {}); + onGC(server, { ongc: countdown.dec.bind(countdown) }); +} + +setImmediate(() => { + global.gc(); +}); diff --git a/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js new file mode 100644 index 00000000000000..bcbb1434cbec91 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This tests that the http2 server sends data early when it accumulates +// enough from ongoing requests to avoid DoS as mitigation for +// CVE-2019-9517 and CVE-2019-9511. +// Added by https://github.com/nodejs/node/commit/8a4a193 +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const content = fixtures.readSync('person-large.jpg'); + +const server = http2.createServer({ + maxSessionMemory: 1000 +}); +let streamCount = 0; +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'image/jpeg', + ':status': 200 + }); + stream.end(content); + console.log('server sends content', ++streamCount); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}/`); + + let endCount = 0; + let finished = 0; + for (let i = 0; i < 100; i++) { + const req = client.request({ ':path': '/' }).end(); + const chunks = []; + req.on('data', (chunk) => { + chunks.push(chunk); + }); + req.on('end', common.mustCall(() => { + console.log('client receives content', ++endCount); + assert.deepStrictEqual(Buffer.concat(chunks), content); + + if (++finished === 100) { + client.close(); + server.close(); + } + })); + req.on('error', (e) => { + console.log('client error', e); + }); + } +})); diff --git a/test/js/node/test/parallel/test-http2-server-close-callback.js b/test/js/node/test/parallel/test-http2-server-close-callback.js new file mode 100644 index 00000000000000..e4cd24ce209782 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-close-callback.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const Countdown = require('../common/countdown'); +const http2 = require('http2'); + +const server = http2.createServer(); + +let session; + +const countdown = new Countdown(2, () => { + server.close(common.mustSucceed()); + session.close(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.on('connect', common.mustCall(() => countdown.dec())); +})); + +server.on('session', common.mustCall((s) => { + session = s; + countdown.dec(); +})); diff --git a/test/js/node/test/parallel/test-https-server-connections-checking-leak.js b/test/js/node/test/parallel/test-https-server-connections-checking-leak.js new file mode 100644 index 00000000000000..e920c8e403705f --- /dev/null +++ b/test/js/node/test/parallel/test-https-server-connections-checking-leak.js @@ -0,0 +1,29 @@ +'use strict'; + +// Flags: --expose-gc + +// Check that creating a server without listening does not leak resources. + +const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} + +const { onGC } = require('../common/gc'); +const Countdown = require('../common/countdown'); + +const https = require('https'); +const max = 100; + +// Note that Countdown internally calls common.mustCall, that's why it's not done here. +const countdown = new Countdown(max, () => {}); + +for (let i = 0; i < max; i++) { + const server = https.createServer((req, res) => {}); + onGC(server, { ongc: countdown.dec.bind(countdown) }); +} + +setImmediate(() => { + global.gc(); +});