Skip to content

feat: allow to set client.webSocketURL.protocol #3380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion client-src/utils/createSocketURL.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ function createSocketURL(parsedURL) {
hostname = self.location.hostname;
}

if (protocol === 'auto:') {
protocol = self.location.protocol;
}

// `hostname` can be empty when the script path is relative. In that case, specifying a protocol would result in an invalid URL.
// When https is used in the app, secure websockets are always necessary because the browser doesn't accept non-secure websockets.
// When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets.
if (hostname && isInAddrAny && self.location.protocol === 'https:') {
protocol = self.location.protocol;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@
"type": "object",
"additionalProperties": false,
"properties": {
"protocol": {
"anyOf": [
{
"enum": ["auto"]
},
{
"type": "string",
"minLength": 1
}
],
"description": "Tells clients connected to devServer to use the provided protocol."
},
"host": {
"type": "string",
"minLength": 1,
Expand Down
13 changes: 10 additions & 3 deletions lib/utils/DevServerPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ class DevServerPlugin {
apply(compiler) {
const { options } = this;

/** @type {"ws" | "wss"} */
const protocol = options.https ? 'wss' : 'ws';
/** @type {"ws:" | "wss:" | "http:" | "https:" | "auto:"} */
let protocol;

// We are proxying dev server and need to specify custom `host`
if (typeof options.client.webSocketURL.protocol !== 'undefined') {
protocol = options.client.webSocketURL.protocol;
} else {
protocol = options.https ? 'wss:' : 'ws:';
}

/** @type {string} */
let host;
Expand Down Expand Up @@ -111,7 +118,7 @@ class DevServerPlugin {

const webSocketURL = encodeURIComponent(
new URL(
`${protocol}://${ipaddr.IPv6.isIPv6(host) ? `[${host}]` : host}${
`${protocol}//${ipaddr.IPv6.isIPv6(host) ? `[${host}]` : host}${
port ? `:${port}` : ''
}${path || '/'}${
Object.keys(searchParams).length > 0
Expand Down
3 changes: 2 additions & 1 deletion lib/utils/normalizeOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ function normalizeOptions(compiler, options, logger) {
const parsedURL = new URL(options.client.webSocketURL);

options.client.webSocketURL = {
protocol: parsedURL.protocol,
host: parsedURL.hostname,
port: Number(parsedURL.port),
port: parsedURL.port.length > 0 ? Number(parsedURL.port) : '',
path: parsedURL.pathname,
};
} else if (typeof options.client.webSocketURL.port === 'string') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ exports[`options validate should throw an error on the "client" option with '{"w
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.client.webSocketURL should be one of these:
non-empty string | object { host?, port?, path? }
non-empty string | object { protocol?, host?, port?, path? }
-> When using dev server and you're proxying dev-server, the client script does not always know where to connect to.
Details:
* configuration.client.webSocketURL.port should be one of these:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ exports[`options validate should throw an error on the "client" option with '{"w
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.client.webSocketURL should be one of these:
non-empty string | object { host?, port?, path? }
non-empty string | object { protocol?, host?, port?, path? }
-> When using dev server and you're proxying dev-server, the client script does not always know where to connect to.
Details:
* configuration.client.webSocketURL.port should be one of these:
Expand Down
161 changes: 133 additions & 28 deletions test/e2e/web-socket-server-and-url.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,15 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client port and path', () => {
describe('should work with the "client.webSocketURL.protocol" option', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
path: '/foo/test/bar/',
port: port3,
protocol: 'ws:',
},
},
};
Expand All @@ -265,11 +264,11 @@ for (const webSocketServerType of webSocketServerTypes) {
afterAll(testServer.close);

describe('browser client', () => {
it('uses correct port and path', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /foo\/test\/bar/, (websocketUrl) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://localhost:${port3}/foo/test/bar`
`${websocketUrlProtocol}://localhost:${port2}/ws`
);

done();
Expand All @@ -281,15 +280,15 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client port', () => {
describe('should work with the "client.webSocketURL.protocol" option using "auto:" value', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
port: port3,
protocol: 'auto:',
},
},
};
Expand All @@ -300,11 +299,11 @@ for (const webSocketServerType of webSocketServerTypes) {
afterAll(testServer.close);

describe('browser client', () => {
it('uses correct port and path', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://localhost:${port3}/ws`
`${websocketUrlProtocol}://localhost:${port2}/ws`
);

done();
Expand All @@ -316,15 +315,15 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client port as string', () => {
describe('should work with the "client.webSocketURL.protocol" option using "http:" value and covert to "ws"', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
port: `${port3}`,
protocol: 'http:',
},
},
};
Expand All @@ -335,11 +334,11 @@ for (const webSocketServerType of webSocketServerTypes) {
afterAll(testServer.close);

describe('browser client', () => {
it('uses correct port and path', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://localhost:${port3}/ws`
`${websocketUrlProtocol}://localhost:${port2}/ws`
);

done();
Expand All @@ -351,16 +350,79 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client.webSocketURL.port and webSocketServer.options.port both as string', () => {
describe('should work with the "client.webSocketURL.host" option', () => {
beforeAll((done) => {
const options = {
webSocketServer: {
type: webSocketServerType,
options: {
host: '0.0.0.0',
port: `${port2}`,
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
host: 'myhost.test',
},
},
};
testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('browser client', () => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://myhost.test:${port2}/ws`
);

done();
});

page.goto(`http://localhost:${port2}/main`);
});
});
});
});

describe('should work with the "client.webSocketURL.port" option', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
port: port3,
},
},
};

testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('browser client', () => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://localhost:${port3}/ws`
);

done();
});

page.goto(`http://localhost:${port2}/main`);
});
});
});
});

describe('should work with the "client.webSocketURL.port" option as "string"', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
Expand All @@ -376,7 +438,7 @@ for (const webSocketServerType of webSocketServerTypes) {
afterAll(testServer.close);

describe('browser client', () => {
it('uses correct port and path', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
Expand All @@ -392,29 +454,72 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client host', () => {
describe('should work with "client.webSocketURL.port" and "client.webSocketURL.path" options', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
host: 'myhost.test',
path: '/foo/test/bar/',
port: port3,
},
},
};

testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('browser client', () => {
it('uses correct host', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /foo\/test\/bar/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://localhost:${port3}/foo/test/bar`
);

done();
});

page.goto(`http://localhost:${port2}/main`);
});
});
});
});

describe('should work with "client.webSocketURL.port" and "webSocketServer.options.port" options as string', () => {
beforeAll((done) => {
const options = {
webSocketServer: {
type: webSocketServerType,
options: {
host: '0.0.0.0',
port: `${port2}`,
},
},
port: port2,
host: '0.0.0.0',
client: {
webSocketURL: {
port: `${port3}`,
},
},
};

testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('browser client', () => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /ws/, (websocketUrl) => {
expect(websocketUrl).toContain(
`${websocketUrlProtocol}://myhost.test:${port2}/ws`
`${websocketUrlProtocol}://localhost:${port3}/ws`
);

done();
Expand All @@ -426,7 +531,7 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with custom client host, port, and path', () => {
describe('should work with "client.webSocketURL.host", "webSocketServer.options.port" and "webSocketServer.options.path" options', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
Expand Down Expand Up @@ -463,7 +568,7 @@ for (const webSocketServerType of webSocketServerTypes) {
});
});

describe('should work with the "client.webSocketURL" option and custom client path', () => {
describe('should work with the "client.webSocketURL" option as "string"', () => {
beforeAll((done) => {
const options = {
webSocketServer: webSocketServerType,
Expand All @@ -480,7 +585,7 @@ for (const webSocketServerType of webSocketServerTypes) {
afterAll(testServer.close);

describe('browser client', () => {
it('uses the correct webSocketURL hostname and path', (done) => {
it('should work', (done) => {
runBrowser().then(({ page, browser }) => {
waitForTest(browser, page, /foo\/test\/bar/, (websocketUrl) => {
expect(websocketUrl).toContain(
Expand Down