-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
createSocketURL.js
96 lines (79 loc) · 3.23 KB
/
createSocketURL.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
'use strict';
const url = require('url');
// We handle legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers
// Please look at https://nodejs.org/api/url.html#url_url_strings_and_url_objects
function createSocketURL(parsedURL) {
let { auth, hostname, protocol, port } = parsedURL;
const getURLSearchParam = (name) => {
if (parsedURL.searchParams) {
return parsedURL.searchParams.get(name);
}
return parsedURL.query && parsedURL.query[name];
};
// Node.js module parses it as `::`
// `new URL(urlString, [baseURLstring])` parses it as '[::]'
const isInAddrAny =
hostname === '0.0.0.0' || hostname === '::' || hostname === '[::]';
// check ipv4 and ipv6 `all hostname`
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
if (
isInAddrAny &&
self.location.hostname &&
self.location.protocol.indexOf('http') === 0
) {
hostname = self.location.hostname;
}
// `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.
if (hostname && isInAddrAny && self.location.protocol === 'https:') {
protocol = self.location.protocol;
}
const socketURLProtocol = protocol.replace(
/^(?:http|.+-extension|file)/i,
'ws'
);
// `new URL(urlString, [baseURLstring])` doesn't have `auth` property
// Parse authentication credentials in case we need them
if (parsedURL.username) {
auth = parsedURL.username;
// Since HTTP basic authentication does not allow empty username,
// we only include password if the username is not empty.
if (parsedURL.password) {
// Result: <username>:<password>
auth = auth.concat(':', parsedURL.password);
}
}
const socketURLAuth = auth;
// In case the host is a raw IPv6 address, it can be enclosed in
// the brackets as the brackets are needed in the final URL string.
// Need to remove those as url.format blindly adds its own set of brackets
// if the host string contains colons. That would lead to non-working
// double brackets (e.g. [[::]]) host
//
// 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
const socketURLHostname = (
getURLSearchParam('host') ||
hostname ||
'localhost'
).replace(/^\[(.*)\]$/, '$1');
if (!port || port === '0') {
port = self.location.port;
}
const socketURLPort = getURLSearchParam('port') || port;
// If path 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.
const socketURLPathname = getURLSearchParam('path') || '/ws';
return url.format({
protocol: socketURLProtocol,
auth: socketURLAuth,
hostname: socketURLHostname,
port: socketURLPort,
pathname: socketURLPathname,
slashes: true,
});
}
module.exports = createSocketURL;