Skip to content

Commit 6699595

Browse files
committed
feat: allow username and password in clientURL
1 parent 809631c commit 6699595

11 files changed

+239
-5
lines changed

lib/options.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@
153153
"description": "Tells clients connected to devServer to use the provided path to connect.",
154154
"type": "string"
155155
},
156+
"username": {
157+
"description": "Tells clients connected to devServer to use the provided username to connect.",
158+
"type": "string"
159+
},
160+
"password": {
161+
"description": "Tells clients connected to devServer to use the provided password to connect.",
162+
"type": "string"
163+
},
156164
"port": {
157165
"description": "Tells clients connected to devServer to use the provided port.",
158166
"anyOf": [

lib/utils/DevServerPlugin.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class DevServerPlugin {
9393
if (typeof options.client.webSocketURL.path !== 'undefined') {
9494
path = options.client.webSocketURL.path;
9595
}
96+
9697
// Web socket server works on custom `path`
9798
else if (
9899
typeof options.webSocketServer.options.prefix !== 'undefined' ||
@@ -103,6 +104,22 @@ class DevServerPlugin {
103104
options.webSocketServer.options.path;
104105
}
105106

107+
/** @type {string} */
108+
let username = '';
109+
110+
// We are proxying dev server and need to specify custom `path`
111+
if (typeof options.client.webSocketURL.username !== 'undefined') {
112+
username = options.client.webSocketURL.username;
113+
}
114+
115+
/** @type {string} */
116+
let password = '';
117+
118+
// We are proxying dev server and need to specify custom `path`
119+
if (typeof options.client.webSocketURL.password !== 'undefined') {
120+
password = options.client.webSocketURL.password;
121+
}
122+
106123
/** @type {Record<string, any>} */
107124
const searchParams = {};
108125

@@ -112,7 +129,9 @@ class DevServerPlugin {
112129

113130
return encodeURIComponent(
114131
new URL(
115-
`${protocol}//${ipaddr.IPv6.isIPv6(host) ? `[${host}]` : host}${
132+
`${protocol}//${
133+
username && password ? `${username}:${password}@` : ''
134+
}${ipaddr.IPv6.isIPv6(host) ? `[${host}]` : host}${
116135
port ? `:${port}` : ''
117136
}${path || '/'}${
118137
Object.keys(searchParams).length > 0

lib/utils/normalizeOptions.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ function normalizeOptions(compiler, options, logger) {
124124
host: parsedURL.hostname,
125125
port: parsedURL.port.length > 0 ? Number(parsedURL.port) : '',
126126
path: parsedURL.pathname,
127+
username: parsedURL.username,
128+
password: parsedURL.password,
127129
};
128130
} else if (typeof options.client.webSocketURL.port === 'string') {
129131
options.client.webSocketURL.port = Number(options.client.webSocketURL.port);

test/__snapshots__/validate-options.test.js.snap.webpack4

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ exports[`options validate should throw an error on the "client" option with '{"w
162162
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
163163
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
164164
- options.client.webSocketURL should be one of these:
165-
non-empty string | object { host?, path?, port?, protocol? }
165+
non-empty string | object { host?, path?, username?, password?, port?, protocol? }
166166
-> Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).
167167
Details:
168168
* options.client.webSocketURL.port should be one of these:
@@ -173,6 +173,18 @@ exports[`options validate should throw an error on the "client" option with '{"w
173173
* options.client.webSocketURL.port should be a non-empty string."
174174
`;
175175

176+
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"username":123,"password":976}}' value 1`] = `
177+
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
178+
- options.client.webSocketURL should be one of these:
179+
non-empty string | object { host?, path?, username?, password?, port?, protocol? }
180+
-> Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).
181+
Details:
182+
* options.client.webSocketURL.username should be a string.
183+
-> Tells clients connected to devServer to use the provided username to connect.
184+
* options.client.webSocketURL.password should be a string.
185+
-> Tells clients connected to devServer to use the provided password to connect."
186+
`;
187+
176188
exports[`options validate should throw an error on the "client" option with 'whoops!' value 1`] = `
177189
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
178190
- options.client should be an object:

test/__snapshots__/validate-options.test.js.snap.webpack5

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ exports[`options validate should throw an error on the "client" option with '{"w
162162
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
163163
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
164164
- options.client.webSocketURL should be one of these:
165-
non-empty string | object { host?, path?, port?, protocol? }
165+
non-empty string | object { host?, path?, username?, password?, port?, protocol? }
166166
-> Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).
167167
Details:
168168
* options.client.webSocketURL.port should be one of these:
@@ -173,6 +173,18 @@ exports[`options validate should throw an error on the "client" option with '{"w
173173
* options.client.webSocketURL.port should be a non-empty string."
174174
`;
175175

176+
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"username":123,"password":976}}' value 1`] = `
177+
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
178+
- options.client.webSocketURL should be one of these:
179+
non-empty string | object { host?, path?, username?, password?, port?, protocol? }
180+
-> Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).
181+
Details:
182+
* options.client.webSocketURL.username should be a string.
183+
-> Tells clients connected to devServer to use the provided username to connect.
184+
* options.client.webSocketURL.password should be a string.
185+
-> Tells clients connected to devServer to use the provided password to connect."
186+
`;
187+
176188
exports[`options validate should throw an error on the "client" option with 'whoops!' value 1`] = `
177189
"ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
178190
- options.client should be an object:

test/e2e/web-socket-server-url.test.js

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const { createProxyMiddleware } = require('http-proxy-middleware');
77
const Server = require('../../lib/Server');
88
const config = require('../fixtures/client-config/webpack.config');
99
const runBrowser = require('../helpers/run-browser');
10-
const [port1, port2, port3, port4] = require('../ports-map').ClientOptions;
10+
const [port1, port2, port3, port4, port5] =
11+
require('../ports-map').ClientOptions;
1112

1213
const webSocketServers = ['ws', 'sockjs'];
1314

@@ -1187,6 +1188,91 @@ describe('web socket server URL', () => {
11871188
});
11881189
});
11891190

1191+
it(`should work with the "client.webSocketURL.username" and "client.webSocketURL.password" option ("${webSocketServer}")`, async () => {
1192+
const compiler = webpack(config);
1193+
const devServerOptions = {
1194+
client: {
1195+
webSocketURL: {
1196+
username: 'zenitsu',
1197+
password: 'chuntaro',
1198+
},
1199+
},
1200+
webSocketServer,
1201+
port: port5,
1202+
host: '0.0.0.0',
1203+
allowedHosts: 'all',
1204+
};
1205+
const server = new Server(devServerOptions, compiler);
1206+
1207+
await new Promise((resolve, reject) => {
1208+
server.listen(devServerOptions.port, devServerOptions.host, (error) => {
1209+
if (error) {
1210+
reject(error);
1211+
1212+
return;
1213+
}
1214+
1215+
resolve();
1216+
});
1217+
});
1218+
1219+
const { page, browser } = await runBrowser();
1220+
1221+
const pageErrors = [];
1222+
const consoleMessages = [];
1223+
1224+
page
1225+
.on('console', (message) => {
1226+
consoleMessages.push(message);
1227+
})
1228+
.on('pageerror', (error) => {
1229+
pageErrors.push(error);
1230+
});
1231+
1232+
const webSocketRequests = [];
1233+
1234+
if (webSocketServer === 'ws') {
1235+
const client = page._client;
1236+
1237+
client.on('Network.webSocketCreated', (test) => {
1238+
webSocketRequests.push(test);
1239+
});
1240+
} else {
1241+
page.on('request', (request) => {
1242+
if (/\/ws\//.test(request.url())) {
1243+
webSocketRequests.push({ url: request.url() });
1244+
}
1245+
});
1246+
}
1247+
1248+
await page.goto(`http://127.0.0.1:${port5}/main`, {
1249+
waitUntil: 'networkidle0',
1250+
});
1251+
1252+
const webSocketRequest = webSocketRequests[0];
1253+
1254+
expect(webSocketRequest.url).toContain(
1255+
`${websocketURLProtocol}://127.0.0.1:${port5}/ws`
1256+
);
1257+
expect(consoleMessages.map((message) => message.text())).toMatchSnapshot(
1258+
'console messages'
1259+
);
1260+
expect(pageErrors).toMatchSnapshot('page errors');
1261+
1262+
await browser.close();
1263+
await new Promise((resolve, reject) => {
1264+
server.close((error) => {
1265+
if (error) {
1266+
reject(error);
1267+
1268+
return;
1269+
}
1270+
1271+
resolve();
1272+
});
1273+
});
1274+
});
1275+
11901276
it(`should work with the "client.webSocketURL.path" option and custom web socket server "path" ("${webSocketServer}")`, async () => {
11911277
const compiler = webpack(config);
11921278
const devServerOptions = {

test/ports-map.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const portsList = {
88
SockJSServer: 1,
99
'hot-and-live-reload': 1,
1010
Client: 1,
11-
ClientOptions: 4,
11+
ClientOptions: 5,
1212
logging: 1,
1313
MultiCompiler: 1,
1414
UniversalCompiler: 1,

test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,44 @@ Object {
14181418
}
14191419
`;
14201420

1421+
exports[`normalizeOptions username and password should set correct options 1`] = `
1422+
Object {
1423+
"allowedHosts": "auto",
1424+
"client": Object {
1425+
"hotEntry": true,
1426+
"overlay": true,
1427+
"webSocketURL": Object {
1428+
"password": "chuntaro",
1429+
"username": "zenitsu",
1430+
},
1431+
},
1432+
"compress": true,
1433+
"devMiddleware": Object {},
1434+
"hot": true,
1435+
"liveReload": true,
1436+
"setupExitSignals": true,
1437+
"static": Array [
1438+
Object {
1439+
"directory": "CWD",
1440+
"publicPath": Array [
1441+
"/",
1442+
],
1443+
"serveIndex": Object {
1444+
"icons": true,
1445+
},
1446+
"staticOptions": Object {},
1447+
"watch": Object {},
1448+
},
1449+
],
1450+
"webSocketServer": Object {
1451+
"options": Object {
1452+
"path": "/ws",
1453+
},
1454+
"type": "ws",
1455+
},
1456+
}
1457+
`;
1458+
14211459
exports[`normalizeOptions webSocketServer custom server class should set correct options 1`] = `
14221460
Object {
14231461
"allowedHosts": "auto",

test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack5

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,44 @@ Object {
14181418
}
14191419
`;
14201420

1421+
exports[`normalizeOptions username and password should set correct options 1`] = `
1422+
Object {
1423+
"allowedHosts": "auto",
1424+
"client": Object {
1425+
"hotEntry": true,
1426+
"overlay": true,
1427+
"webSocketURL": Object {
1428+
"password": "chuntaro",
1429+
"username": "zenitsu",
1430+
},
1431+
},
1432+
"compress": true,
1433+
"devMiddleware": Object {},
1434+
"hot": true,
1435+
"liveReload": true,
1436+
"setupExitSignals": true,
1437+
"static": Array [
1438+
Object {
1439+
"directory": "CWD",
1440+
"publicPath": Array [
1441+
"/",
1442+
],
1443+
"serveIndex": Object {
1444+
"icons": true,
1445+
},
1446+
"staticOptions": Object {},
1447+
"watch": Object {},
1448+
},
1449+
],
1450+
"webSocketServer": Object {
1451+
"options": Object {
1452+
"path": "/ws",
1453+
},
1454+
"type": "ws",
1455+
},
1456+
}
1457+
`;
1458+
14211459
exports[`normalizeOptions webSocketServer custom server class should set correct options 1`] = `
14221460
Object {
14231461
"allowedHosts": "auto",

test/server/utils/normalizeOptions.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ describe('normalizeOptions', () => {
152152
},
153153
optionsResults: null,
154154
},
155+
{
156+
title: 'username and password',
157+
multiCompiler: false,
158+
options: {
159+
client: {
160+
webSocketURL: {
161+
username: 'zenitsu',
162+
password: 'chuntaro',
163+
},
164+
},
165+
},
166+
optionsResults: null,
167+
},
155168
{
156169
title: 'client path without leading/ending slashes',
157170
multiCompiler: false,

test/validate-options.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ const tests = {
9898
{
9999
webSocketURL: { host: 'localhost', port: 8080, path: '/my-path/' },
100100
},
101+
{
102+
webSocketURL: { username: 'zoro', password: 'roronoa' },
103+
},
101104
],
102105
failure: [
103106
'whoops!',
@@ -155,6 +158,9 @@ const tests = {
155158
{
156159
webSocketURL: { port: '' },
157160
},
161+
{
162+
webSocketURL: { username: 123, password: 976 },
163+
},
158164
],
159165
},
160166
compress: {

0 commit comments

Comments
 (0)