diff --git a/client-src/default/index.js b/client-src/default/index.js index cfee77ba62..6340a9d1f6 100644 --- a/client-src/default/index.js +++ b/client-src/default/index.js @@ -181,6 +181,7 @@ const onSocketMsg = { let hostname = urlParts.hostname; let protocol = urlParts.protocol; +let port = urlParts.port; // check ipv4 and ipv6 `all hostname` if (hostname === '0.0.0.0' || hostname === '::') { @@ -190,6 +191,7 @@ if (hostname === '0.0.0.0' || hostname === '::') { // eslint-disable-next-line no-bitwise if (self.location.hostname && !!~self.location.protocol.indexOf('http')) { hostname = self.location.hostname; + port = self.location.port; } } @@ -208,7 +210,7 @@ const socketUrl = url.format({ protocol, auth: urlParts.auth, hostname, - port: urlParts.port, + port, // If sockPath is provided it'll be passed in via the __resourceQuery as a // query param so it has to be parsed out of the querystring in order for the // client to open the socket to the correct location. diff --git a/package-lock.json b/package-lock.json index 15ac1c895f..048d8fb584 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1069,6 +1069,15 @@ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", "dev": true }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "6.9.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", @@ -3363,6 +3372,21 @@ "is-symbol": "^1.0.2" } }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "es6-templates": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", @@ -3988,6 +4012,29 @@ } } }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -4039,6 +4086,15 @@ "bser": "^2.0.0" } }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -4337,8 +4393,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -4356,13 +4411,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4375,18 +4428,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -4489,8 +4539,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -4500,7 +4549,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4513,20 +4561,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4543,7 +4588,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4616,8 +4660,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -4627,7 +4670,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4703,8 +4745,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -4734,7 +4775,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4752,7 +4792,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4791,13 +4830,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -5539,6 +5576,33 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "husky": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", @@ -7547,7 +7611,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -9229,6 +9293,12 @@ "sha.js": "^2.4.8" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -9488,6 +9558,12 @@ "ipaddr.js": "1.8.0" } }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -9550,6 +9626,30 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "puppeteer": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.12.2.tgz", + "integrity": "sha512-xWSyCeD6EazGlfnQweMpM+Hs6X6PhUYhNTHKFj/axNZDq4OmrVERf70isBf7HsnFgB3zOC1+23/8+wCAZYg+Pg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "extract-zip": "^1.6.6", + "https-proxy-agent": "^2.2.1", + "mime": "^2.0.3", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^2.6.1", + "ws": "^6.1.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -12024,6 +12124,15 @@ "camelcase": "^4.1.0" } }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + }, "yup": { "version": "0.26.10", "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", diff --git a/package.json b/package.json index e903fe821d..3eabe24887 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "marked": "^0.6.1", "nyc": "^13.3.0", "prettier": "^1.16.3", + "puppeteer": "^1.12.2", "rimraf": "^2.6.2", "standard-version": "^5.0.0", "style-loader": "^0.23.1", diff --git a/test/Client.test.js b/test/Client.test.js new file mode 100644 index 0000000000..641d059876 --- /dev/null +++ b/test/Client.test.js @@ -0,0 +1,75 @@ +'use strict'; + +const express = require('express'); +const httpProxy = require('http-proxy-middleware'); +const request = require('supertest'); +const addEntries = require('../lib/utils/addEntries'); +const helper = require('./helper'); +const config = require('./fixtures/client-config/webpack.config'); +const runBrowser = require('./helpers/run-browser'); + +function startProxy(port) { + const proxy = express(); + proxy.use( + '/', + httpProxy({ + target: 'http://localhost:9001', + ws: true, + changeOrigin: true, + }) + ); + return proxy.listen(port); +} + +describe('Client code', () => { + beforeAll((done) => { + const options = { + compress: true, + port: 9001, + host: '0.0.0.0', + disableHostCheck: true, + hot: true, + watchOptions: { + poll: true, + }, + }; + addEntries(config, options); + helper.start(config, options, done); + }); + + afterAll(helper.close); + + describe('behind a proxy', () => { + let proxy; + + jest.setTimeout(30000); + + beforeAll(() => { + proxy = startProxy(9000); + }); + + afterAll(() => { + proxy.close(); + }); + + it('responds with a 200', (done) => { + const req = request('http://localhost:9000'); + req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n', done); + }); + + it('requests websocket through the proxy with proper port number', (done) => { + runBrowser().then(({ page, browser }) => { + page + .waitForRequest((requestObj) => requestObj.url().match(/sockjs-node/)) + .then((requestObj) => { + expect(requestObj.url()).toMatch( + /^http:\/\/localhost:9000\/sockjs-node/ + ); + browser.close(); + done(); + }); + page.goto('http://localhost:9000/main'); + }); + }); + }); +}); diff --git a/test/fixtures/client-config/foo.js b/test/fixtures/client-config/foo.js new file mode 100644 index 0000000000..eab26534f3 --- /dev/null +++ b/test/fixtures/client-config/foo.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('Hey.'); diff --git a/test/fixtures/client-config/index.html b/test/fixtures/client-config/index.html new file mode 100644 index 0000000000..fdfcaae91f --- /dev/null +++ b/test/fixtures/client-config/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/client-config/webpack.config.js b/test/fixtures/client-config/webpack.config.js new file mode 100644 index 0000000000..832793338a --- /dev/null +++ b/test/fixtures/client-config/webpack.config.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + mode: 'development', + context: __dirname, + entry: './foo.js', + output: { + path: '/', + }, +}; diff --git a/test/helper.js b/test/helper.js index 3b10256f3b..4feb6b4e88 100644 --- a/test/helper.js +++ b/test/helper.js @@ -14,7 +14,9 @@ module.exports = { const compiler = webpack(config); server = new Server(compiler, options); - server.listen(8080, 'localhost', (err) => { + const port = options.port || 8080; + const host = options.host || 'localhost'; + server.listen(port, host, (err) => { if (err) return done(err); done(); }); diff --git a/test/helpers/run-browser.js b/test/helpers/run-browser.js new file mode 100644 index 0000000000..53993d88d1 --- /dev/null +++ b/test/helpers/run-browser.js @@ -0,0 +1,37 @@ +'use strict'; + +const puppeteer = require('puppeteer'); + +function runBrowser(config) { + const options = { + viewport: { + width: 500, + height: 500, + }, + userAgent: '', + ...config, + }; + + return new Promise((resolve, reject) => { + let page; + let browser; + + puppeteer + .launch({ + headless: true, + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }) + .then((launchedBrowser) => { + browser = launchedBrowser; + return browser.newPage(); + }) + .then((newPage) => { + page = newPage; + page.emulate(options); + resolve({ page, browser }); + }) + .catch(reject); + }); +} + +module.exports = runBrowser;