137 lines
4.3 KiB
JavaScript
137 lines
4.3 KiB
JavaScript
|
/**
|
||
|
* @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function format(objURL) {
|
||
|
var protocol = objURL.protocol || "";
|
||
|
|
||
|
if (protocol && protocol.substr(-1) !== ":") {
|
||
|
protocol += ":";
|
||
|
}
|
||
|
|
||
|
var auth = objURL.auth || "";
|
||
|
|
||
|
if (auth) {
|
||
|
auth = encodeURIComponent(auth);
|
||
|
auth = auth.replace(/%3A/i, ":");
|
||
|
auth += "@";
|
||
|
}
|
||
|
|
||
|
var host = "";
|
||
|
|
||
|
if (objURL.hostname) {
|
||
|
host = auth + (objURL.hostname.indexOf(":") === -1 ? objURL.hostname : "[".concat(objURL.hostname, "]"));
|
||
|
|
||
|
if (objURL.port) {
|
||
|
host += ":".concat(objURL.port);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var pathname = objURL.pathname || "";
|
||
|
|
||
|
if (objURL.slashes) {
|
||
|
host = "//".concat(host || "");
|
||
|
|
||
|
if (pathname && pathname.charAt(0) !== "/") {
|
||
|
pathname = "/".concat(pathname);
|
||
|
}
|
||
|
} else if (!host) {
|
||
|
host = "";
|
||
|
}
|
||
|
|
||
|
var search = objURL.search || "";
|
||
|
|
||
|
if (search && search.charAt(0) !== "?") {
|
||
|
search = "?".concat(search);
|
||
|
}
|
||
|
|
||
|
var hash = objURL.hash || "";
|
||
|
|
||
|
if (hash && hash.charAt(0) !== "#") {
|
||
|
hash = "#".concat(hash);
|
||
|
}
|
||
|
|
||
|
pathname = pathname.replace(/[?#]/g,
|
||
|
/**
|
||
|
* @param {string} match
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function (match) {
|
||
|
return encodeURIComponent(match);
|
||
|
});
|
||
|
search = search.replace("#", "%23");
|
||
|
return "".concat(protocol).concat(host).concat(pathname).concat(search).concat(hash);
|
||
|
}
|
||
|
/**
|
||
|
* @param {URL & { fromCurrentScript?: boolean }} parsedURL
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
|
||
|
|
||
|
function createSocketURL(parsedURL) {
|
||
|
var hostname = parsedURL.hostname; // Node.js module parses it as `::`
|
||
|
// `new URL(urlString, [baseURLString])` parses it as '[::]'
|
||
|
|
||
|
var isInAddrAny = hostname === "0.0.0.0" || hostname === "::" || 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;
|
||
|
}
|
||
|
|
||
|
var socketURLProtocol = parsedURL.protocol || self.location.protocol; // When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets.
|
||
|
|
||
|
if (socketURLProtocol === "auto:" || hostname && isInAddrAny && self.location.protocol === "https:") {
|
||
|
socketURLProtocol = self.location.protocol;
|
||
|
}
|
||
|
|
||
|
socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, "ws");
|
||
|
var socketURLAuth = ""; // `new URL(urlString, [baseURLstring])` doesn't have `auth` property
|
||
|
// Parse authentication credentials in case we need them
|
||
|
|
||
|
if (parsedURL.username) {
|
||
|
socketURLAuth = 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>
|
||
|
socketURLAuth = socketURLAuth.concat(":", parsedURL.password);
|
||
|
}
|
||
|
} // 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 web socket url params are optionally passed in through resourceQuery,
|
||
|
// so we need to fall back to the default if they are not provided
|
||
|
|
||
|
|
||
|
var socketURLHostname = (hostname || self.location.hostname || "localhost").replace(/^\[(.*)\]$/, "$1");
|
||
|
var socketURLPort = parsedURL.port;
|
||
|
|
||
|
if (!socketURLPort || socketURLPort === "0") {
|
||
|
socketURLPort = self.location.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.
|
||
|
|
||
|
|
||
|
var socketURLPathname = "/ws";
|
||
|
|
||
|
if (parsedURL.pathname && !parsedURL.fromCurrentScript) {
|
||
|
socketURLPathname = parsedURL.pathname;
|
||
|
}
|
||
|
|
||
|
return format({
|
||
|
protocol: socketURLProtocol,
|
||
|
auth: socketURLAuth,
|
||
|
hostname: socketURLHostname,
|
||
|
port: socketURLPort,
|
||
|
pathname: socketURLPathname,
|
||
|
slashes: true
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export default createSocketURL;
|