diff --git a/client-src/default/index.js b/client-src/default/index.js index a1d5443f22..0ea9f592a6 100644 --- a/client-src/default/index.js +++ b/client-src/default/index.js @@ -220,21 +220,35 @@ if ( ) { protocol = self.location.protocol; } + +// default values of the sock url if they are not provided +let sockHost = hostname; +let sockPath = '/sockjs-node'; +let sockPort = urlParts.port; +if ( + urlParts.path !== null && + // eslint-disable-next-line no-undefined + urlParts.path !== undefined && + urlParts.path !== '/' +) { + const parsedQuery = querystring.parse(urlParts.path); + // all of these sock url params are optionally passed in through + // __resourceQuery, so we need to fall back to the default if + // they are not provided + sockHost = parsedQuery.sockHost || sockHost; + sockPath = parsedQuery.sockPath || sockPath; + sockPort = parsedQuery.sockPort || sockPort; +} + const socketUrl = url.format({ protocol, auth: urlParts.auth, - hostname, - port: - urlParts.path == null || urlParts.path === '/' - ? urlParts.port - : querystring.parse(urlParts.path).sockPort || urlParts.port, + hostname: sockHost, + port: sockPort, // 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. - pathname: - urlParts.path == null || urlParts.path === '/' - ? '/sockjs-node' - : querystring.parse(urlParts.path).sockPath || urlParts.path, + pathname: sockPath, }); socket(socketUrl, onSocketMsg); diff --git a/lib/options.json b/lib/options.json index 9608b391d5..2f5b32caa5 100644 --- a/lib/options.json +++ b/lib/options.json @@ -288,6 +288,9 @@ "setup": { "instanceof": "Function" }, + "sockHost": { + "type": "string" + }, "sockPath": { "type": "string" }, @@ -395,8 +398,9 @@ "serveIndex": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverserveindex)", "serverSideRender": "should be {Boolean} (https://github.com/webpack/webpack-dev-middleware#serversiderender)", "setup": "should be {Function} (https://webpack.js.org/configuration/dev-server/#devserversetup)", + "sockHost": "should be {String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockhost)", "sockPath": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserversockpath)", - "sockPort": "should be {Number|String|Null}", + "sockPort": "should be {Number|String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockport)", "socket": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserversocket)", "staticOptions": "should be {Object} (https://webpack.js.org/configuration/dev-server/#devserverstaticoptions)", "stats": "should be {Object|Boolean} (https://webpack.js.org/configuration/dev-server/#devserverstats-)", diff --git a/lib/utils/addEntries.js b/lib/utils/addEntries.js index 9754fbeb00..aa9f621fe4 100644 --- a/lib/utils/addEntries.js +++ b/lib/utils/addEntries.js @@ -16,11 +16,12 @@ function addEntries(config, options, server) { }; const domain = createDomain(options, app); + const sockHost = options.sockHost ? `&sockHost=${options.sockHost}` : ''; const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : ''; const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : ''; const clientEntry = `${require.resolve( '../../client/' - )}?${domain}${sockPath}${sockPort}`; + )}?${domain}${sockHost}${sockPath}${sockPort}`; let hotEntry; if (options.hotOnly) { diff --git a/lib/utils/createConfig.js b/lib/utils/createConfig.js index 38d0d1a335..510b3f7307 100644 --- a/lib/utils/createConfig.js +++ b/lib/utils/createConfig.js @@ -30,6 +30,10 @@ function createConfig(config, argv, { port }) { options.socket = argv.socket; } + if (argv.sockHost) { + options.sockHost = argv.sockHost; + } + if (argv.sockPath) { options.sockPath = argv.sockPath; } diff --git a/test/Client.test.js b/test/Client.test.js index 2fa749856f..84292e11da 100644 --- a/test/Client.test.js +++ b/test/Client.test.js @@ -156,3 +156,76 @@ describe('Client complex inline script path with sockPort', () => { }); }); }); + +// previously, using sockPort without sockPath had the ability +// to alter the sockPath (based on a bug in client-src/index.js) +// so we need to make sure sockPath is not altered in this case +describe('Client complex inline script path with sockPort, no sockPath', () => { + beforeAll((done) => { + const options = { + port: 9000, + host: '0.0.0.0', + inline: true, + watchOptions: { + poll: true, + }, + sockPort: 8080, + }; + helper.startAwaitingCompilation(config, options, done); + }); + + afterAll(helper.close); + + describe('browser client', () => { + jest.setTimeout(30000); + + it('uses the correct sockPort and sockPath', (done) => { + runBrowser().then(({ page, browser }) => { + page + .waitForRequest((requestObj) => requestObj.url().match(/sockjs-node/)) + .then((requestObj) => { + expect(requestObj.url()).toMatch( + /^http:\/\/localhost:8080\/sockjs-node/ + ); + browser.close().then(done); + }); + page.goto('http://localhost:9000/main'); + }); + }); + }); +}); + +describe('Client complex inline script path with sockHost', () => { + beforeAll((done) => { + const options = { + port: 9000, + host: '0.0.0.0', + inline: true, + watchOptions: { + poll: true, + }, + sockHost: 'myhost.test', + }; + helper.startAwaitingCompilation(config, options, done); + }); + + afterAll(helper.close); + + describe('browser client', () => { + jest.setTimeout(30000); + + it('uses the correct sockHost', (done) => { + runBrowser().then(({ page, browser }) => { + page + .waitForRequest((requestObj) => requestObj.url().match(/sockjs-node/)) + .then((requestObj) => { + expect(requestObj.url()).toMatch( + /^http:\/\/myhost\.test:9000\/sockjs-node/ + ); + browser.close().then(done); + }); + page.goto('http://localhost:9000/main'); + }); + }); + }); +}); diff --git a/test/options.test.js b/test/options.test.js index 5bf520df6a..e659ffcddd 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -337,6 +337,10 @@ describe('options', () => { success: [''], failure: [false], }, + sockHost: { + success: [''], + failure: [false], + }, sockPath: { success: [''], failure: [false],