diff --git a/package.json b/package.json index b041f07c..6ed45fcc 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "math-helpers": "~0.1.0", "nodemon": "^3.1.3", "prettier": "2.2.1", - "tap": "^21.1.3", "ws": "^8.17.0" }, "scripts": { @@ -39,7 +38,7 @@ "pre-commit": "npm run autofix && npm test", "lint": "eslint .", "autofix": "eslint . --fix", - "test-spec": "tap --allow-incomplete-coverage test/*spec.js", + "test-spec": "node --test test/*spec.js", "test-perf": "node test/performance.js" }, "license": "AGPL-3.0", diff --git a/test/charset_spec.js b/test/charset_spec.js index d7c17a35..41cdfab6 100644 --- a/test/charset_spec.js +++ b/test/charset_spec.js @@ -1,101 +1,76 @@ "use strict"; -var test = require("tap").test; -var fs = require("fs"); -var crypto = require("crypto"); -var http = require("http"); -var concat = require("concat-stream"); -var getServers = require("./test_utils.js").getServers; +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const crypto = require("node:crypto"); +const { test } = require("node:test"); +const { getServersAsync, closeServers, readUrl } = require("./test_utils.js"); const Unblocker = require("../lib/unblocker.js"); // source is http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml-windows-1250.xhtml which is linked to from http://validator.w3.org/dev/tests/#encoding -var sourceContent = fs.readFileSync( +const sourceContent = fs.readFileSync( __dirname + "/source/xhtml-windows-1250.xhtml" ); -var expected = fs.readFileSync( +const expected = fs.readFileSync( __dirname + "/expected/xhtml-windows-1250-converted-to-utf-8.xhtml" ); // first validate that the IDE or whatever didn't change the file encoding -var SOURCE_HASH = "11f694099b205b26a19648ab22602b39c6deb125"; -var EXPECTED_HASH = "4a04a0aa660da6f0eec9534c0e25212a7045ea7c"; -test("source and expected xhtml-windows-1250.xhtml files should not have changed", function (t) { - t.equal( +const SOURCE_HASH = "11f694099b205b26a19648ab22602b39c6deb125"; +const EXPECTED_HASH = "4a04a0aa660da6f0eec9534c0e25212a7045ea7c"; + +test("source and expected xhtml-windows-1250.xhtml files should not have changed", () => { + assert.strictEqual( crypto.createHash("sha1").update(sourceContent).digest("hex"), SOURCE_HASH ); - t.equal( + assert.strictEqual( crypto.createHash("sha1").update(expected).digest("hex"), EXPECTED_HASH ); - t.end(); }); -test("should properly decode and update non-native charsets when charset is in header", function (t) { - t.plan(1); - getServers( - { - unblocker: new Unblocker({ clientScripts: false }), - sourceContent, - charset: "windows-1250", - }, - function (err, servers) { - http - .get(servers.proxiedUrl, function (res) { - res.pipe( - concat(function (actual) { - servers.kill(); - t.same(actual, expected); - }) - ); - }) - .on("error", function (e) { - t.bailout(e); - }); - } - ); +test("should properly decode and update non-native charsets when charset is in header", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + charset: "windows-1250", + }); + + try { + const actual = await readUrl(servers.proxiedUrl); + assert.deepStrictEqual(actual, expected); + } finally { + await closeServers(servers); + } }); -test("should properly decode and update charsets when charset is in body", function (t) { - t.plan(1); - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - http - .get(servers.proxiedUrl, function (res) { - res.pipe( - concat(function (actual) { - servers.kill(); - t.same(actual, expected); - }) - ); - }) - .on("error", function (e) { - t.bailout(e); - }); - } - ); +test("should properly decode and update charsets when charset is in body", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + const actual = await readUrl(servers.proxiedUrl); + assert.deepStrictEqual(actual, expected); + } finally { + await closeServers(servers); + } }); -test("should still work when charset can be determined", function (t) { - t.plan(1); - var sourceContent = "

test

", - expected = "

test

"; - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - http - .get(servers.proxiedUrl, function (res) { - res.pipe( - concat(function (actual) { - servers.kill(); - t.same(actual.toString(), expected); - }) - ); - }) - .on("error", function (e) { - t.bailout(e); - }); - } - ); +test("should still work when charset can be determined", async () => { + const sourceContent = "

test

"; + const expectedValue = "

test

"; + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + const actual = await readUrl(servers.proxiedUrl); + assert.strictEqual(actual.toString(), expectedValue); + } finally { + await closeServers(servers); + } }); diff --git a/test/content-types_spec.js b/test/content-types_spec.js index f0f7b516..7174dc8e 100644 --- a/test/content-types_spec.js +++ b/test/content-types_spec.js @@ -1,18 +1,18 @@ "use strict"; -var test = require("tap").test; -var contentTypes = require("../lib/content-types.js"); +const assert = require("node:assert/strict"); +const contentTypes = require("../lib/content-types.js"); +const { test } = require("node:test"); -test("should handle content types with a charset", function (t) { - var config = { +test("should handle content types with a charset", () => { + const config = { processContentTypes: ["text/html"], }; - var data = { + const data = { headers: { "content-type": "text/html; charset=utf-8", }, }; data.contentType = contentTypes.getType(data); - t.ok(contentTypes.shouldProcess(config, data)); - t.end(); + assert.ok(contentTypes.shouldProcess(config, data)); }); diff --git a/test/cookies_spec.js b/test/cookies_spec.js index 0875973f..be8f3bb2 100644 --- a/test/cookies_spec.js +++ b/test/cookies_spec.js @@ -1,82 +1,81 @@ "use strict"; -var test = require("tap").test, - utils = require("./test_utils.js"), - getData = utils.getData, - cookies = require("../lib/cookies.js"), - PassThrough = require("stream").PassThrough, - concat = require("concat-stream"); +const assert = require("node:assert/strict"); +const { PassThrough } = require("node:stream"); +const { test } = require("node:test"); +const { getData, pipeToString } = require("./test_utils.js"); +const cookies = require("../lib/cookies.js"); -test("should copy cookies and redirect in response to a __proxy_cookies_to query param", function (t) { - t.plan(2); - var instance = cookies({ +test("should copy cookies and redirect in response to a __proxy_cookies_to query param", async () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: [], }); - var data = getData(); + const data = getData(); data.url += "?__proxy_cookies_to=https%3A%2F%2Fexample.com%2F"; data.headers.cookie = "one=1; two=2; three=3"; - data.clientResponse = { - redirectTo: function (path, headers) { - var expectedPath = "https://example.com/"; - var expectedHeaders = { - "set-cookie": [ - "one=1; Path=/proxy/https://example.com/", - "two=2; Path=/proxy/https://example.com/", - "three=3; Path=/proxy/https://example.com/", - ], - }; - t.equal(path, expectedPath); - t.same(headers, expectedHeaders); - t.end(); - }, - }; - instance.handleRequest(data); + + await new Promise((resolve) => { + data.clientResponse = { + redirectTo(path, headers) { + const expectedPath = "https://example.com/"; + const expectedHeaders = { + "set-cookie": [ + "one=1; Path=/proxy/https://example.com/", + "two=2; Path=/proxy/https://example.com/", + "three=3; Path=/proxy/https://example.com/", + ], + }; + assert.strictEqual(path, expectedPath); + assert.deepStrictEqual(headers, expectedHeaders); + resolve(); + }, + }; + + instance.handleRequest(data); + }); }); -test("should rewrite set-cookie paths", function (t) { - var instance = cookies({ +test("should rewrite set-cookie paths", () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: [], }); - var data = getData(); + const data = getData(); data.headers["set-cookie"] = ["one=1", "two=2; path=/", "three=3; path=/foo"]; instance.handleResponse(data); - var expected = [ + const expected = [ "one=1; Path=/proxy/http://example.com/", "two=2; Path=/proxy/http://example.com/", "three=3; Path=/proxy/http://example.com/foo", ]; - var actual = data.headers["set-cookie"]; - t.same(actual, expected); - t.end(); + const actual = data.headers["set-cookie"]; + assert.deepStrictEqual(actual, expected); }); -test("should rewrite the cookie that is percent-encoded correctly", function (t) { - var instance = cookies({ +test("should rewrite the cookie that is percent-encoded correctly", () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: [], }); - var data = getData(); + const data = getData(); data.headers["set-cookie"] = [ "asdf=asdf%3Basdf%3Dtrue%3Basdf%3Dasdf%3Basdf%3Dtrue%40asdf", ]; instance.handleResponse(data); - var expected = [ + const expected = [ "asdf=asdf%3Basdf%3Dtrue%3Basdf%3Dasdf%3Basdf%3Dtrue%40asdf; Path=/proxy/http://example.com/", ]; - var actual = data.headers["set-cookie"]; - t.same(actual, expected); - t.end(); + const actual = data.headers["set-cookie"]; + assert.deepStrictEqual(actual, expected); }); -test("should copy any missing cookies to a 3xx redirect", function (t) { - t.plan(1); - var instance = cookies({ +test("should copy any missing cookies to a 3xx redirect", () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: ["text/html"], }); - var data = getData(); + const data = getData(); data.clientRequest = { headers: { cookie: "one=oldvalue; two=2", @@ -87,33 +86,31 @@ test("should copy any missing cookies to a 3xx redirect", function (t) { }; data.redirectUrl = "https://example.com/"; // this is normally set by the redirects middleware before it changes the location header instance.handleResponse(data); - var expected = { + const expected = { "set-cookie": [ "one=1; Path=/proxy/https://example.com/; HttpOnly", "two=2; Path=/proxy/https://example.com/", ], }; - t.same(data.headers, expected); + assert.deepStrictEqual(data.headers, expected); }); -test("should rewrite urls that change subdomain or protocol (but not domain)", function (t) { - t.plan(2); - var instance = cookies({ +test("should rewrite urls that change subdomain or protocol (but not domain)", async () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: ["text/html"], }); - var data = getData(); - var sourceStream = new PassThrough({ - encoding: "utf8", - }); + const data = getData(); + const sourceStream = new PassThrough({ encoding: "utf8" }); data.stream = sourceStream; instance.handleResponse(data); - t.not( + assert.notStrictEqual( data.stream, sourceStream, "cookies.handleResponse should create a new stream to process content" ); - var source = [ + + const source = [ 'no change', 'new proto', 'new subdomain', @@ -124,7 +121,7 @@ test("should rewrite urls that change subdomain or protocol (but not domain)", f 'new proto', ].join("\n"); - var expected = [ + const expected = [ 'no change', 'new proto', 'new subdomain', @@ -135,29 +132,21 @@ test("should rewrite urls that change subdomain or protocol (but not domain)", f 'new proto', ].join("\n"); - data.stream.setEncoding("utf8"); - data.stream.pipe( - concat(function (actual) { - t.equal(actual, expected); - t.end(); - }) - ); - sourceStream.end(source); + const actual = await pipeToString(data.stream); + assert.strictEqual(actual, expected); }); -test("should work with SameSite attributes", function (t) { - var instance = cookies({ +test("should work with SameSite attributes", () => { + const instance = cookies({ prefix: "/proxy/", processContentTypes: [], }); - var data = getData(); + const data = getData(); data.headers["set-cookie"] = [ "1P_JAR=2019-12-19-00; expires=Sat, 18-Jan-2020 00:42:02 GMT; path=/; domain=.google.com; SameSite=none", ]; instance.handleResponse(data); - var actual = data.headers["set-cookie"][0]; - console.log(actual); - t.ok(actual.toLowerCase().indexOf("samesite=none") > -1); - t.end(); + const actual = data.headers["set-cookie"][0]; + assert.ok(actual.toLowerCase().includes("samesite=none")); }); diff --git a/test/decompress_spec.js b/test/decompress_spec.js index 14712a79..bb287a42 100644 --- a/test/decompress_spec.js +++ b/test/decompress_spec.js @@ -1,15 +1,16 @@ "use strict"; -var PassThrough = require("stream").PassThrough; -var zlib = require("zlib"); -var test = require("tap").test; -var concat = require("concat-stream"); -var decompress = require("../lib/decompress.js"); -var defaultConfig = require("../lib/unblocker.js").defaultConfig; - -test("should decompress data compressed with gzip", function (t) { - var source = zlib.createGzip(); - var data = { +const assert = require("node:assert/strict"); +const { PassThrough } = require("node:stream"); +const zlib = require("node:zlib"); +const { test } = require("node:test"); +const { streamToString } = require("./test_utils.js"); +const decompress = require("../lib/decompress.js"); +const defaultConfig = require("../lib/unblocker.js").defaultConfig; + +test("should decompress data compressed with gzip", async () => { + const source = zlib.createGzip(); + const data = { remoteResponse: { statusCode: 200, }, @@ -19,32 +20,29 @@ test("should decompress data compressed with gzip", function (t) { contentType: "text/html", stream: source, }; - var content = "this is some content to compress and decompress"; - var expected = content; + const content = "this is some content to compress and decompress"; + const expected = content; decompress(defaultConfig).handleResponse(data); - t.not(source, data.stream, "it should create a new stream for decompression"); - - t.notOk( - data.headers["content-encoding"], - "it should remove the encoding header when decompressing" + assert.notStrictEqual( + source, + data.stream, + "it should create a new stream for decompression" ); - - data.stream.pipe( - concat(function (data) { - var actual = data.toString(); - t.same(actual, expected); - t.end(); - }) + assert.ok( + !data.headers["content-encoding"], + "it should remove the encoding header when decompressing" ); source.end(content); + const actual = await streamToString(data.stream); + assert.strictEqual(actual, expected); }); -test("should decompress data compressed with deflate", function (t) { - var source = zlib.createDeflate(); - var data = { +test("should decompress data compressed with deflate", async () => { + const source = zlib.createDeflate(); + const data = { remoteResponse: { statusCode: 200, }, @@ -54,32 +52,29 @@ test("should decompress data compressed with deflate", function (t) { contentType: "text/html", stream: source, }; - var content = "this is some content to compress and decompress"; - var expected = content; + const content = "this is some content to compress and decompress"; + const expected = content; decompress(defaultConfig).handleResponse(data); - t.not(source, data.stream, "it should create a new stream for decompression"); - - t.notOk( - data.headers["content-encoding"], - "it should remove the encoding header when decompressing" + assert.notStrictEqual( + source, + data.stream, + "it should create a new stream for decompression" ); - - data.stream.pipe( - concat(function (data) { - var actual = data.toString(); - t.same(actual, expected); - t.end(); - }) + assert.ok( + !data.headers["content-encoding"], + "it should remove the encoding header when decompressing" ); source.end(content); + const actual = await streamToString(data.stream); + assert.strictEqual(actual, expected); }); -test("should skip requests with no content (#105)", function (t) { - var source = new PassThrough(); - var data = { +test("should skip requests with no content (#105)", () => { + const source = new PassThrough(); + const data = { remoteResponse: { statusCode: 304, }, @@ -92,23 +87,21 @@ test("should skip requests with no content (#105)", function (t) { decompress(defaultConfig).handleResponse(data); - t.equal( + assert.strictEqual( data.headers["content-encoding"], "gzip", "it should keep the encoding header when skipping" ); - - t.equal( + assert.strictEqual( source, data.stream, "it should not change the stream when it can tell there's no content" ); - t.end(); }); -test("should skip requests with no content, even if it can't tell ahead of time", function (t) { - var source = new PassThrough(); - var data = { +test("should skip requests with no content, even if it can't tell ahead of time", async () => { + const source = new PassThrough(); + const data = { remoteResponse: { statusCode: 200, }, @@ -121,18 +114,19 @@ test("should skip requests with no content, even if it can't tell ahead of time" decompress(defaultConfig).handleResponse(data); - t.not(source, data.stream, "it should create a new stream for decompression"); - - data.stream.on("end", function () { - t.end(); - }); + assert.notStrictEqual( + source, + data.stream, + "it should create a new stream for decompression" + ); data.stream.resume(); // put the stream into flowing mode so that 'end' fires source.end(); + await new Promise((resolve) => data.stream.on("end", resolve)); }); -test("should request only gzip if the client supports multiple encodings (#151)", function (t) { - var data = { +test("should request only gzip if the client supports multiple encodings (#151)", () => { + const data = { headers: { "accept-encoding": "deflate, gzip", }, @@ -140,16 +134,15 @@ test("should request only gzip if the client supports multiple encodings (#151)" decompress(defaultConfig).handleRequest(data); - t.equal( + assert.strictEqual( data.headers["accept-encoding"], "gzip", "it should change the header to gzip only" ); - t.end(); }); -test("should remove the accept-encoding header if the client does not support gzip", function (t) { - var data = { +test("should remove the accept-encoding header if the client does not support gzip", () => { + const data = { headers: { "accept-encoding": "deflate", }, @@ -157,9 +150,8 @@ test("should remove the accept-encoding header if the client does not support gz decompress(defaultConfig).handleRequest(data); - t.notOk( - data.headers["accept-encoding"], + assert.ok( + !data.headers["accept-encoding"], "it should remove unsupported encodings" ); - t.end(); }); diff --git a/test/get-real-url-spec.js b/test/get-real-url-spec.js index a5027990..229015b3 100644 --- a/test/get-real-url-spec.js +++ b/test/get-real-url-spec.js @@ -1,50 +1,55 @@ "use strict"; -var it = require("tap").test, - getRealUrl = require("../lib/get-real-url.js"); +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const getRealUrl = require("../lib/get-real-url.js"); -var config = { +const config = { prefix: "/proxy/", }; -var instance = getRealUrl(config); +const instance = getRealUrl(config); -it("should extract the url", function (t) { - t.equal(instance("/proxy/http://example.com/"), "http://example.com/"); - t.end(); +test("should extract the url", () => { + assert.strictEqual( + instance("/proxy/http://example.com/"), + "http://example.com/" + ); }); -it("should extract incpmplete urls", function (t) { - t.equal(instance("/proxy/example.com/"), "example.com/"); - t.end(); +test("should extract incpmplete urls", () => { + assert.strictEqual(instance("/proxy/example.com/"), "example.com/"); }); -it("should keep querystring data", function (t) { - t.equal( +test("should keep querystring data", () => { + assert.strictEqual( instance("/proxy/http://example.com/?foo=bar"), "http://example.com/?foo=bar" ); - t.end(); }); -it("should should fix merged slashes (http:/ instead of http://", function (t) { - t.equal(instance("/proxy/http:/example.com/"), "http://example.com/"); - t.equal(instance("/proxy/https:/example.com/"), "https://example.com/"); - t.end(); +test("should should fix merged slashes (http:/ instead of http://", () => { + assert.strictEqual( + instance("/proxy/http:/example.com/"), + "http://example.com/" + ); + assert.strictEqual( + instance("/proxy/https:/example.com/"), + "https://example.com/" + ); }); -it("should fix double-prefixed urls)", function (t) { - t.equal( +test("should fix double-prefixed urls)", () => { + assert.strictEqual( instance("/proxy/http://proxy/http://example.com/"), "http://example.com/" ); - t.equal( + assert.strictEqual( instance("/proxy/http:/proxy/http://example.com/"), "http://example.com/" ); - t.equal( + assert.strictEqual( instance("/proxy/https://proxy/https://example.com/"), "https://example.com/" ); - t.end(); }); diff --git a/test/metarobots_spec.js b/test/metarobots_spec.js index 5b19e7f2..0b101258 100644 --- a/test/metarobots_spec.js +++ b/test/metarobots_spec.js @@ -1,65 +1,51 @@ "use strict"; -var test = require("tap").test, - concat = require("concat-stream"), - utils = require("./test_utils.js"), - getData = utils.getData, - defaultConfig = require("../lib/unblocker").defaultConfig; - -var metaRobots = require("../lib/meta-robots.js"); - -var head = "test"; -var body = "

asdf

"; - -test("should add a meta tag to the head", function (t) { - var expected = - 'test\n'; - var stream = metaRobots().createStream(); +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const { getData, streamToString } = require("./test_utils.js"); +const defaultConfig = require("../lib/unblocker").defaultConfig; +const metaRobots = require("../lib/meta-robots.js"); + +const head = "test"; +const body = "

asdf

"; + +test("should add a meta tag to the head", async () => { + const expected = `test +`; + const stream = metaRobots().createStream(); stream.setEncoding("utf8"); - stream.pipe( - concat(function (actual) { - t.equal(actual, expected); - t.end(); - }) - ); stream.end(head); + const actual = await streamToString(stream); + assert.strictEqual(actual, expected); }); -test("should do nothing to the body", function (t) { - var expected = body; - var stream = metaRobots().createStream(); +test("should do nothing to the body", async () => { + const expected = body; + const stream = metaRobots().createStream(); stream.setEncoding("utf8"); - stream.pipe( - concat(function (actual) { - t.equal(actual, expected); - t.end(); - }) - ); stream.end(body); + const actual = await streamToString(stream); + assert.strictEqual(actual, expected); }); -test("should not modify javascript", function (t) { - var config = Object.assign({}, defaultConfig); - var instance = metaRobots(config); - var data = getData(); +test("should not modify javascript", async () => { + const config = Object.assign({}, defaultConfig); + const instance = metaRobots(config); + const data = getData(); data.contentType = "text/javascript"; - var streamStart = data.stream; + const streamStart = data.stream; streamStart.setEncoding("utf8"); instance(data); // this will replace data.stream when modifying the contents - var streamEnd = data.stream; + const streamEnd = data.stream; // commented out so that we can test the results rather than the implimentation details //t.equal(streamStart, streamEnd); - var js = `document.write('${head}')`; - var expected = js; + const js = `document.write('${head}')`; + const expected = js; streamEnd.setEncoding("utf8"); - streamEnd.pipe( - concat(function (actual) { - t.equal(actual, expected); - t.end(); - }) - ); streamStart.end(js); + const actual = await streamToString(streamEnd); + assert.strictEqual(actual, expected); }); diff --git a/test/performance.js b/test/performance.js index 199de2b4..628011f8 100644 --- a/test/performance.js +++ b/test/performance.js @@ -6,7 +6,7 @@ const concat = require("concat-stream"); const hyperquest = require("hyperquest"); const math = require("math-helpers")(); const async = require("async"); -const { getServers } = require("./test_utils.js"); +const { getServersAsync, closeServers } = require("./test_utils.js"); const html_path = path.join(__dirname, "source/index.html"); const js_path = path.join( @@ -22,14 +22,8 @@ function remoteApp(req, res) { } } -// fire up the server and actually run the tests -getServers({ remoteApp }, function (err, servers) { - // set up the cleanup work first - //process.on('SIGINT', servers.kill); - //process.on('SIGTERM', servers.kill); - if (err) { - throw err; - } +async function main() { + const servers = await getServersAsync({ remoteApp }); const iterations_html = 1000; const concurrency_html = 30; @@ -39,87 +33,96 @@ getServers({ remoteApp }, function (err, servers) { var baseline, proxy; - new async.series( - [ - function (next) { - runTest( - "Baseline HTML", - servers.remoteUrl, - iterations_html, - concurrency_html, - function (baseFailures, baseSuccesses, time) { - baseline = getStats( + try { + await new Promise((resolve, reject) => { + async.series( + [ + function (next) { + runTest( + "Baseline HTML", + servers.remoteUrl, iterations_html, - baseFailures, - baseSuccesses, - time + concurrency_html, + function (baseFailures, baseSuccesses, time) { + baseline = getStats( + iterations_html, + baseFailures, + baseSuccesses, + time + ); + printStats(baseline); + next(); + } ); - printStats(baseline); - next(); - } - ); - }, - function (next) { - runTest( - "Proxy HTML", - servers.proxiedUrl, - iterations_html, - concurrency_html, - function (proxyFailures, proxySuccesses, time) { - proxy = getStats( + }, + function (next) { + runTest( + "Proxy HTML", + servers.proxiedUrl, iterations_html, - proxyFailures, - proxySuccesses, - time + concurrency_html, + function (proxyFailures, proxySuccesses, time) { + proxy = getStats( + iterations_html, + proxyFailures, + proxySuccesses, + time + ); + printStats(proxy, baseline); + next(); + } ); - printStats(proxy, baseline); - next(); - } - ); - }, - function (next) { - runTest( - "Baseline JS", - servers.remoteUrl + "js", - iterations_js, - concurrency_js, - function (baseFailures, baseSuccesses, time) { - baseline = getStats( + }, + function (next) { + runTest( + "Baseline JS", + servers.remoteUrl + "js", iterations_js, - baseFailures, - baseSuccesses, - time + concurrency_js, + function (baseFailures, baseSuccesses, time) { + baseline = getStats( + iterations_js, + baseFailures, + baseSuccesses, + time + ); + printStats(baseline); + next(); + } ); - printStats(baseline); - next(); - } - ); - }, - function (next) { - runTest( - "Proxy JS", - servers.proxiedUrl + "js", - iterations_js, - concurrency_js, - function (proxyFailures, proxySuccesses, time) { - proxy = getStats( + }, + function (next) { + runTest( + "Proxy JS", + servers.proxiedUrl + "js", iterations_js, - proxyFailures, - proxySuccesses, - time + concurrency_js, + function (proxyFailures, proxySuccesses, time) { + proxy = getStats( + iterations_js, + proxyFailures, + proxySuccesses, + time + ); + printStats(proxy, baseline); + next(); + } ); - printStats(proxy, baseline); - next(); - } - ); - }, - ], - function (err) { - console.log(err || ""); - servers.kill(); - } - ); -}); + }, + ], + function (err) { + console.log(err || ""); + if (err) reject(err); + else resolve(); + } + ); + }); + } finally { + await closeServers(servers); + } +} + +main(); function runTest(name, url, iterations, concurrency, cb) { console.log("\n\n=========\n" + name + "\n========="); diff --git a/test/redirect_spec.js b/test/redirect_spec.js index f934715f..c9d04435 100644 --- a/test/redirect_spec.js +++ b/test/redirect_spec.js @@ -1,58 +1,56 @@ "use strict"; -var redirect = require("../lib/redirects.js"); -var test = require("tap").test; +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const redirect = require("../lib/redirects.js"); -test("should correctly redirect with http://", function (t) { - var expected = "http://foobar.com/proxy/http://example.com/not-a-test/"; - var data = { +test("should correctly redirect with http://", () => { + const expected = "http://foobar.com/proxy/http://example.com/not-a-test/"; + const data = { url: "http://example.com/test/", headers: { location: "http://example.com/not-a-test/", }, clientRequest: { - thisSite: function () { + thisSite() { return "http://foobar.com/proxy/"; }, }, }; redirect()(data); - t.equal(data.headers.location, expected); - t.end(); + assert.strictEqual(data.headers.location, expected); }); -test("should correctly redirect with //", function (t) { - var expected = "http://foobar.com/proxy/http://example.com/not-a-test/"; - var data = { +test("should correctly redirect with //", () => { + const expected = "http://foobar.com/proxy/http://example.com/not-a-test/"; + const data = { url: "http://example.com/test/", headers: { location: "//example.com/not-a-test/", }, clientRequest: { - thisSite: function () { + thisSite() { return "http://foobar.com/proxy/"; }, }, }; redirect()(data); - t.equal(data.headers.location, expected); - t.end(); + assert.strictEqual(data.headers.location, expected); }); -test("should correctly redirect with // and https", function (t) { - var expected = "http://foobar.com/proxy/https://example.com/not-a-test/"; - var data = { +test("should correctly redirect with // and https", () => { + const expected = "http://foobar.com/proxy/https://example.com/not-a-test/"; + const data = { url: "https://example.com/test/", headers: { location: "//example.com/not-a-test/", }, clientRequest: { - thisSite: function () { + thisSite() { return "http://foobar.com/proxy/"; }, }, }; redirect()(data); - t.equal(data.headers.location, expected); - t.end(); + assert.strictEqual(data.headers.location, expected); }); diff --git a/test/referer_spec.js b/test/referer_spec.js index 73ca6da5..07baf724 100644 --- a/test/referer_spec.js +++ b/test/referer_spec.js @@ -1,19 +1,17 @@ "use strict"; -var referer = require("../lib/referer.js"); -var test = require("tap").test; +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const referer = require("../lib/referer.js"); -test("should correctly rewrite referers", function (t) { - var expected = "http://foobar.com/proxy/a"; - var data = { +test("should correctly rewrite referers", () => { + const expected = "http://foobar.com/proxy/a"; + const data = { url: "http://foobar.com/b", headers: { referer: "http://localhost:8080/proxy/" + expected, }, }; - referer({ - prefix: "/proxy/", - })(data); - t.equal(data.headers.referer, expected); - t.end(); + referer({ prefix: "/proxy/" })(data); + assert.strictEqual(data.headers.referer, expected); }); diff --git a/test/short_response_spec.js b/test/short_response_spec.js index 2495030c..84428d15 100644 --- a/test/short_response_spec.js +++ b/test/short_response_spec.js @@ -1,34 +1,26 @@ "use strict"; -var fs = require("fs"), - concat = require("concat-stream"), - test = require("tap").test, - hyperquest = require("hyperquest"), - getServers = require("./test_utils.js").getServers; +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const { test } = require("node:test"); +const { + getServersAsync, + closeServers, + requestAndConcat, +} = require("./test_utils.js"); -var source = fs.readFileSync(__dirname + "/source/short.html"); -var expected = fs.readFileSync(__dirname + "/expected/short.html"); +const source = fs.readFileSync(__dirname + "/source/short.html"); +const expected = fs.readFileSync(__dirname + "/expected/short.html"); -test("url_rewriting should support short html documents", function (t) { - getServers(source, function (err, servers) { - function cleanup() { - servers.kill(function () { - t.end(); - }); - } - hyperquest(servers.proxiedUrl) - .pipe( - concat(function (data) { - t.equal( - data.toString(), - expected.toString().replace(//g, servers.remotePort) - ); - cleanup(); - }) - ) - .on("error", function (err) { - console.error("error retrieving data from proxy", err); - cleanup(); - }); - }); +test("url_rewriting should support short html documents", async () => { + const servers = await getServersAsync(source); + try { + const data = await requestAndConcat(servers.proxiedUrl); + assert.strictEqual( + data, + expected.toString().replace(//g, servers.remotePort) + ); + } finally { + await closeServers(servers); + } }); diff --git a/test/test_utils.js b/test/test_utils.js index daee3f1c..d254bb9b 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -4,6 +4,7 @@ const http = require("http"); const async = require("async"); const { PassThrough } = require("stream"); const Unblocker = require("../lib/unblocker.js"); +const concat = require("concat-stream"); function getUnblocker(options) { if (options.unblocker) { @@ -44,67 +45,66 @@ function getProxyApp(unblocker) { * - options is an object with one or more of {sourceContent,charset,remoteApp,proxyApp}, * or * - sourceContent can be a buffer or string that is automatically served by the default remoteApp - * @param next */ -exports.getServers = function (options, next) { - if (typeof options == "string" || options instanceof Buffer) { - options = { - sourceContent: options, - }; - } - - const remoteApp = - options.remoteApp || - function sendContent(req, res) { - res.writeHead(200, { - "content-type": - "text/html" + (options.charset ? "; charset=" + options.charset : ""), - }); - res.end(options.sourceContent); - }; +exports.getServersAsync = function (options) { + return new Promise((resolve, reject) => { + if (typeof options == "string" || options instanceof Buffer) { + options = { + sourceContent: options, + }; + } - const unblocker = getUnblocker(options); + const remoteApp = + options.remoteApp || + function sendContent(req, res) { + res.writeHead(200, { + "content-type": + "text/html" + + (options.charset ? "; charset=" + options.charset : ""), + }); + res.end(options.sourceContent); + }; - const proxyApp = options.proxyApp || getProxyApp(unblocker); + const unblocker = getUnblocker(options); + const proxyApp = options.proxyApp || getProxyApp(unblocker); - const proxyServer = http.createServer(proxyApp); - const remoteServer = http.createServer(remoteApp); + const proxyServer = http.createServer(proxyApp); + const remoteServer = http.createServer(remoteApp); - proxyServer.setTimeout(5000); - remoteServer.setTimeout(5000); + proxyServer.setTimeout(5000); + remoteServer.setTimeout(5000); - proxyServer.on("upgrade", unblocker.onUpgrade); + proxyServer.on("upgrade", unblocker.onUpgrade); - async.parallel( - [ - proxyServer.listen.bind(proxyServer), - remoteServer.listen.bind(remoteServer), - ], - function (err) { - if (err) { - return next(err); + async.parallel( + [ + proxyServer.listen.bind(proxyServer), + remoteServer.listen.bind(remoteServer), + ], + function (err) { + if (err) return reject(err); + const ret = { + proxyServer, + proxyPort: proxyServer.address().port, + remoteServer, + remotePort: remoteServer.address().port, + kill: function (next) { + async.parallel( + [ + remoteServer.close.bind(remoteServer), + proxyServer.close.bind(proxyServer), + ], + next + ); + }, + }; + ret.homeUrl = "http://localhost:" + ret.proxyPort + "/"; + ret.remoteUrl = "http://localhost:" + ret.remotePort + "/"; + ret.proxiedUrl = ret.homeUrl + "proxy/" + ret.remoteUrl; + resolve(ret); } - const ret = { - proxyServer: proxyServer, - proxyPort: proxyServer.address().port, - remoteServer: remoteServer, - remotePort: remoteServer.address().port, - kill: function (next) { - async.parallel( - [ - remoteServer.close.bind(remoteServer), - proxyServer.close.bind(proxyServer), - ], - next - ); - }, - }; - ret.homeUrl = "http://localhost:" + ret.proxyPort + "/"; - ret.remoteUrl = "http://localhost:" + ret.remotePort + "/"; - ret.proxiedUrl = ret.homeUrl + "proxy/" + ret.remoteUrl; - next(null, ret); - } - ); + ); + }); }; exports.getData = function () { @@ -121,3 +121,49 @@ exports.getData = function () { }, }; }; + +exports.streamToString = function (stream) { + return new Promise((resolve, reject) => { + if (typeof stream.setEncoding === "function") { + stream.setEncoding("utf8"); + } + stream.pipe(concat(resolve)).on("error", reject); + }); +}; + +// alias for older tests that used pipeToString +exports.pipeToString = exports.streamToString; + +exports.closeServers = function (servers) { + return new Promise((resolve, reject) => { + servers.kill(function (err) { + if (err) return reject(err); + resolve(); + }); + }); +}; + +exports.requestAndConcat = function (url) { + return new Promise((resolve, reject) => { + http + .get(url, (res) => { + res.pipe( + concat(function (data) { + resolve(data.toString()); + }) + ); + }) + .on("error", reject); + }); +}; + +// Returns raw Buffer (not string) for binary/charset-sensitive comparisons +exports.readUrl = function (url) { + return new Promise((resolve, reject) => { + http + .get(url, (res) => { + res.pipe(concat(resolve)).on("error", reject); + }) + .on("error", reject); + }); +}; diff --git a/test/unblocker-client-spec.js b/test/unblocker-client-spec.js index 77037c3c..0efce024 100644 --- a/test/unblocker-client-spec.js +++ b/test/unblocker-client-spec.js @@ -1,5 +1,7 @@ "use strict"; -const { test } = require("tap"); + +const assert = require("node:assert/strict"); +const { test } = require("node:test"); const prefix = "/proxy/"; const proxy = "http://localhost"; const target = "http://example.com/page.html?query#hash"; @@ -57,12 +59,11 @@ const testCases = [ ]; testCases.forEach((tc) => { - test(JSON.stringify(tc), (t) => { - // todo: replace || with ?? + test(JSON.stringify(tc), () => { + // TODO: replace || with ?? const actual = fixUrl(tc.url, tc.config || config, tc.location || location); - t.equal(actual, tc.expected); - t.end(); + assert.strictEqual(actual, tc.expected); }); }); -// todo: something about cookies and subdomains +// TODO: something about cookies and subdomains diff --git a/test/unblocker_spec.js b/test/unblocker_spec.js index 10a04dd4..acfd033f 100644 --- a/test/unblocker_spec.js +++ b/test/unblocker_spec.js @@ -1,83 +1,59 @@ "use strict"; -var fs = require("fs"), - concat = require("concat-stream"), - test = require("tap").test, - hyperquest = require("hyperquest"), - getServers = require("./test_utils.js").getServers; +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const fs = require("fs"); +const hyperquest = require("hyperquest"); +const { + getServersAsync, + closeServers, + requestAndConcat, +} = require("./test_utils.js"); const express = require("express"); const Unblocker = require("../lib/unblocker.js"); -var sourceContent = fs.readFileSync(__dirname + "/source/index.html"); -var expected = fs.readFileSync(__dirname + "/expected/index.html"); - -test("url_rewriting should support support all kinds of links", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } - hyperquest(servers.proxiedUrl) - .pipe( - concat(function (data) { - t.equal( - data.toString(), - expected.toString().replace(//g, servers.remotePort) - ); - cleanup(); - }) - ) - .on("error", function (err) { - console.error("error retrieving data from proxy", err); - cleanup(); - }); - } - ); +const sourceContent = fs.readFileSync(__dirname + "/source/index.html"); +const expected = fs.readFileSync(__dirname + "/expected/index.html"); + +test("url_rewriting should support support all kinds of links", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + const actual = await requestAndConcat(servers.proxiedUrl); + assert.strictEqual( + actual, + expected.toString().replace(//g, servers.remotePort) + ); + } finally { + await closeServers(servers); + } }); -test("should return control to parent when route doesn't match and no referer is sent", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } - hyperquest(servers.homeUrl) - .pipe( - concat(function (data) { - t.equal( - data.toString(), - "this is the home page", - servers.remotePort - ); - cleanup(); - }) - ) - .on("error", function (err) { - console.error("error retrieving robots.txt from proxy", err); - cleanup(); - }); - } - ); +test("should return control to parent when route doesn't match and no referer is sent", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + const actual = await requestAndConcat(servers.homeUrl); + assert.strictEqual(actual, "this is the home page"); + } finally { + await closeServers(servers); + } }); -test("should redirect root-relative urls when the correct target can be determined from the referer header", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } +test("should redirect root-relative urls when the correct target can be determined from the referer header", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + await new Promise((resolve, reject) => { hyperquest( servers.homeUrl + "bar?query_param=new", { @@ -86,30 +62,32 @@ test("should redirect root-relative urls when the correct target can be determin }, }, function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 307, "http status code"); - t.equal( + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 307, "http status code"); + assert.strictEqual( res.headers.location, servers.proxiedUrl + "bar?query_param=new", "redirect location" ); - cleanup(); + resolve(); } - ); - } - ); + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); -test("should redirect root-relative urls when the correct target can be determined from the referer header including for urls that the site is already serving content on", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } +test("should redirect root-relative urls when the correct target can be determined from the referer header including for urls that the site is already serving content on", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + await new Promise((resolve, reject) => { hyperquest( servers.homeUrl, { @@ -118,126 +96,145 @@ test("should redirect root-relative urls when the correct target can be determin }, }, function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 307, "http status code"); - t.equal( + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 307, "http status code"); + assert.strictEqual( res.headers.location, servers.proxiedUrl, "redirect location" ); - cleanup(); + resolve(); } - ); - } - ); + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); -test("should NOT redirect http urls that have had the slashes merged (http:/ instead of http:// (#130)", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } +test("should NOT redirect http urls that have had the slashes merged (http:/ instead of http:// (#130)", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + await new Promise((resolve, reject) => { hyperquest( servers.proxiedUrl.replace("/proxy/http://", "/proxy/http:/"), function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 200, "http status code"); - t.notOk(res.headers.location, "no location header"); - cleanup(); + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 200, "http status code"); + assert.strictEqual( + res.headers.location, + undefined, + "no location header" + ); + resolve(); } - ); - } - ); + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); -test("should redirect http urls that have had the have two occurrences of /prefix/http://", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } +test("should redirect http urls that have had the have two occurrences of /prefix/http://", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + await new Promise((resolve, reject) => { hyperquest( servers.proxiedUrl.replace( "/proxy/http://", "/proxy/http://proxy/http://" ), function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 307, "http status code"); - t.equal( + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 307, "http status code"); + assert.strictEqual( res.headers.location, servers.proxiedUrl, "redirect location" ); - cleanup(); + resolve(); } - ); - } - ); + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); -test("should redirect http urls that end in a TLD without a /", function (t) { - getServers( - { unblocker: new Unblocker({ clientScripts: false }), sourceContent }, - function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } +test("should redirect http urls that end in a TLD without a /", async () => { + const servers = await getServersAsync({ + unblocker: new Unblocker({ clientScripts: false }), + sourceContent, + }); + + try { + await new Promise((resolve, reject) => { hyperquest( - // strip the trailing / servers.proxiedUrl.substr(0, servers.proxiedUrl.length - 1), function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 307, "http status code"); - t.equal( + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 307, "http status code"); + assert.strictEqual( res.headers.location, servers.proxiedUrl, // correct URL with the trailing / "redirect location" ); - cleanup(); + resolve(); } - ); - } - ); + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); -test("should redirect http urls that end in a TLD without a / when req.protocol is set", function (t) { +test("should redirect http urls that end in a TLD without a / when req.protocol is set", async () => { // express sets req.protocol const app = express(); const unblocker = new Unblocker({}); app.use(unblocker); - getServers({ app, unblocker, sourceContent }, function (err, servers) { - t.error(err); - function cleanup() { - servers.kill(function () { - t.end(); - }); - } - hyperquest( - // strip the trailing / - servers.proxiedUrl.substr(0, servers.proxiedUrl.length - 1), - function (err, res) { - t.notOk(err); - t.equal(res.statusCode, 307, "http status code"); - t.equal( - res.headers.location, - servers.proxiedUrl, // correct URL with the trailing / - "redirect location" - ); - cleanup(); - } - ); + const servers = await getServersAsync({ + app, + unblocker, + sourceContent, }); + + try { + await new Promise((resolve, reject) => { + hyperquest( + // strip the trailing / + servers.proxiedUrl.substr(0, servers.proxiedUrl.length - 1), + function (err, res) { + if (err) { + return reject(err); + } + assert.strictEqual(res.statusCode, 307, "http status code"); + assert.strictEqual( + res.headers.location, + servers.proxiedUrl, // correct URL with the trailing / + "redirect location" + ); + resolve(); + } + ).on("error", reject); + }); + } finally { + await closeServers(servers); + } }); diff --git a/test/urlprefixer_spec.js b/test/urlprefixer_spec.js index 80c0c9d5..c41405d0 100644 --- a/test/urlprefixer_spec.js +++ b/test/urlprefixer_spec.js @@ -1,15 +1,16 @@ "use strict"; -var URL = require("url"), - test = require("tap").test, - _ = require("lodash"), - concat = require("concat-stream"); +const assert = require("node:assert/strict"); +const { test } = require("node:test"); +const URL = require("url"); +const _ = require("lodash"); +const concat = require("concat-stream"); -var urlPrefix = require("../lib/url-prefixer.js")({ +const urlPrefix = require("../lib/url-prefixer.js")({ prefix: "/proxy/", }); -var testLines = { +const testLines = { // source => expected result // xmlns items first two should NOT get rewritten @@ -174,59 +175,56 @@ var testLines = { '