Innovenergy_trunk/frontend/node_modules/testcafe-hammerhead/lib/utils/url.js

397 lines
17 KiB
JavaScript
Raw Normal View History

"use strict";
// -------------------------------------------------------------
// WARNING: this file is used by both the client and the server.
// Do not use any browser or node-specific API!
// -------------------------------------------------------------
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processMetaRefreshContent = exports.updateScriptImportUrls = exports.prepareUrl = exports.omitDefaultPort = exports.ensureOriginTrailingSlash = exports.isValidUrl = exports.isRelativeUrl = exports.isSpecialPage = exports.ensureTrailingSlash = exports.processSpecialChars = exports.correctMultipleSlashes = exports.handleUrlsSet = exports.formatUrl = exports.resolveUrlAsDest = exports.isSupportedProtocol = exports.parseUrl = exports.getPathname = exports.parseProxyUrl = exports.getDomain = exports.getProxyUrl = exports.getURLString = exports.sameOriginCheck = exports.isSubDomain = exports.restoreShortOrigin = exports.getResourceTypeString = exports.parseResourceType = exports.Credentials = exports.HTTPS_DEFAULT_PORT = exports.HTTP_DEFAULT_PORT = exports.SPECIAL_PAGES = exports.SPECIAL_ERROR_PAGE = exports.SPECIAL_BLANK_PAGE = exports.TRAILING_SLASH_RE = exports.REQUEST_DESCRIPTOR_SESSION_INFO_VALUES_SEPARATOR = exports.REQUEST_DESCRIPTOR_VALUES_SEPARATOR = exports.HASH_RE = exports.SUPPORTED_PROTOCOL_RE = void 0;
const string_trim_1 = __importDefault(require("./string-trim"));
const URL_RE = /^\s*([\w-]+?:)?(?:\/\/(?:([^/]+)@)?(([^/%?;#: ]*)(?::(\d+))?))?(.*?)\s*$/;
const PROTOCOL_RE = /^([\w-]+?:)(\/\/|[^\\/]|$)/;
const QUERY_AND_HASH_RE = /(\?.+|#[^#]*)$/;
const PATH_AFTER_HOST_RE = /^\/([^/]+?)\/([\S\s]+)$/;
const HTTP_RE = /^https?:/;
const FILE_RE = /^file:/i;
const SHORT_ORIGIN_RE = /^http(s)?:\/\//;
const IS_SECURE_ORIGIN_RE = /^s\*/;
const META_REFRESH_RE = /^(.+?[;,]\s*(?:url\s*=\s*)?(['"])?)(.+?)?(\2)?$/i;
exports.SUPPORTED_PROTOCOL_RE = /^(?:https?|file):/i;
exports.HASH_RE = /^#/;
exports.REQUEST_DESCRIPTOR_VALUES_SEPARATOR = '!';
exports.REQUEST_DESCRIPTOR_SESSION_INFO_VALUES_SEPARATOR = '*';
exports.TRAILING_SLASH_RE = /\/$/;
exports.SPECIAL_BLANK_PAGE = 'about:blank';
exports.SPECIAL_ERROR_PAGE = 'about:error';
exports.SPECIAL_PAGES = [exports.SPECIAL_BLANK_PAGE, exports.SPECIAL_ERROR_PAGE];
exports.HTTP_DEFAULT_PORT = '80';
exports.HTTPS_DEFAULT_PORT = '443';
var Credentials;
(function (Credentials) {
Credentials[Credentials["include"] = 0] = "include";
Credentials[Credentials["sameOrigin"] = 1] = "sameOrigin";
Credentials[Credentials["omit"] = 2] = "omit";
Credentials[Credentials["unknown"] = 3] = "unknown";
})(Credentials = exports.Credentials || (exports.Credentials = {})); // eslint-disable-line no-shadow
const SPECIAL_PAGE_DEST_RESOURCE_INFO = {
protocol: 'about:',
host: '',
hostname: '',
port: '',
partAfterHost: '',
};
const RESOURCE_TYPES = [
{ name: 'isIframe', flag: 'i' },
{ name: 'isForm', flag: 'f' },
{ name: 'isScript', flag: 's' },
{ name: 'isEventSource', flag: 'e' },
{ name: 'isHtmlImport', flag: 'h' },
{ name: 'isWebSocket', flag: 'w' },
{ name: 'isServiceWorker', flag: 'c' },
{ name: 'isAjax', flag: 'a' },
{ name: 'isObject', flag: 'o' },
];
function parseResourceType(resourceType) {
const parsedResourceType = {};
if (!resourceType)
return parsedResourceType;
for (const { name, flag } of RESOURCE_TYPES) {
if (resourceType.indexOf(flag) > -1)
parsedResourceType[name] = true;
}
return parsedResourceType;
}
exports.parseResourceType = parseResourceType;
function getResourceTypeString(parsedResourceType) {
if (!parsedResourceType)
return null;
let resourceType = '';
for (const { name, flag } of RESOURCE_TYPES) {
if (parsedResourceType[name])
resourceType += flag;
}
return resourceType || null;
}
exports.getResourceTypeString = getResourceTypeString;
function makeShortOrigin(origin) {
return origin === 'null' ? '' : origin.replace(SHORT_ORIGIN_RE, (_, secure) => secure ? 's*' : '');
}
function restoreShortOrigin(origin) {
if (!origin)
return 'null';
return IS_SECURE_ORIGIN_RE.test(origin) ? origin.replace(IS_SECURE_ORIGIN_RE, 'https://') : 'http://' + origin;
}
exports.restoreShortOrigin = restoreShortOrigin;
function isSubDomain(domain, subDomain) {
domain = domain.replace(/^www./i, '');
subDomain = subDomain.replace(/^www./i, '');
if (domain === subDomain)
return true;
const index = subDomain.lastIndexOf(domain);
return subDomain[index - 1] === '.' && subDomain.length === index + domain.length;
}
exports.isSubDomain = isSubDomain;
function sameOriginCheck(location, checkedUrl) {
if (!checkedUrl)
return true;
const parsedCheckedUrl = parseUrl(checkedUrl);
const isRelative = !parsedCheckedUrl.host;
if (isRelative)
return true;
const parsedLocation = parseUrl(location);
const parsedProxyLocation = parseProxyUrl(location);
if (parsedCheckedUrl.host === parsedLocation.host && parsedCheckedUrl.protocol === parsedLocation.protocol)
return true;
const parsedDestUrl = parsedProxyLocation ? parsedProxyLocation.destResourceInfo : parsedLocation;
if (!parsedDestUrl)
return false;
const isSameProtocol = !parsedCheckedUrl.protocol || parsedCheckedUrl.protocol === parsedDestUrl.protocol;
const portsEq = !parsedDestUrl.port && !parsedCheckedUrl.port ||
parsedDestUrl.port && parsedDestUrl.port.toString() === parsedCheckedUrl.port;
return isSameProtocol && !!portsEq && parsedDestUrl.hostname === parsedCheckedUrl.hostname;
}
exports.sameOriginCheck = sameOriginCheck;
// NOTE: Convert the destination protocol and hostname to the lower case. (GH-1)
function convertHostToLowerCase(url) {
const parsedUrl = parseUrl(url);
parsedUrl.protocol = parsedUrl.protocol && parsedUrl.protocol.toLowerCase();
parsedUrl.host = parsedUrl.host && parsedUrl.host.toLowerCase();
return formatUrl(parsedUrl);
}
function getURLString(url) {
// TODO: fix it
// eslint-disable-next-line no-undef
if (url === null && /iPad|iPhone/i.test(window.navigator.userAgent))
return '';
return String(url).replace(/[\n\t]/g, '');
}
exports.getURLString = getURLString;
function getProxyUrl(url, opts) {
const sessionInfo = [opts.sessionId];
if (opts.windowId)
sessionInfo.push(opts.windowId);
const params = [sessionInfo.join(exports.REQUEST_DESCRIPTOR_SESSION_INFO_VALUES_SEPARATOR)];
if (opts.resourceType)
params.push(opts.resourceType);
if (opts.charset)
params.push(opts.charset.toLowerCase());
if (typeof opts.credentials === 'number')
params.push(opts.credentials.toString());
if (opts.reqOrigin)
params.push(encodeURIComponent(makeShortOrigin(opts.reqOrigin)));
const descriptor = params.join(exports.REQUEST_DESCRIPTOR_VALUES_SEPARATOR);
const proxyProtocol = opts.proxyProtocol || 'http:';
return `${proxyProtocol}//${opts.proxyHostname}:${opts.proxyPort}/${descriptor}/${convertHostToLowerCase(url)}`;
}
exports.getProxyUrl = getProxyUrl;
function getDomain(parsed) {
if (parsed.protocol === 'file:')
return 'null';
return formatUrl({
protocol: parsed.protocol,
host: parsed.host,
hostname: parsed.hostname,
port: String(parsed.port || ''),
});
}
exports.getDomain = getDomain;
function parseRequestDescriptor(desc) {
const [sessionInfo, resourceType, ...resourceData] = desc.split(exports.REQUEST_DESCRIPTOR_VALUES_SEPARATOR);
if (!sessionInfo)
return null;
const [sessionId, windowId] = sessionInfo.split(exports.REQUEST_DESCRIPTOR_SESSION_INFO_VALUES_SEPARATOR);
const parsedDesc = { sessionId, resourceType: resourceType || null };
if (windowId)
parsedDesc.windowId = windowId;
if (resourceType && resourceData.length) {
const parsedResourceType = parseResourceType(resourceType);
if (parsedResourceType.isScript || parsedResourceType.isServiceWorker)
parsedDesc.charset = resourceData[0];
else if (parsedResourceType.isWebSocket)
parsedDesc.reqOrigin = decodeURIComponent(restoreShortOrigin(resourceData[0]));
else if (parsedResourceType.isIframe && resourceData[0])
parsedDesc.reqOrigin = decodeURIComponent(restoreShortOrigin(resourceData[0]));
else if (parsedResourceType.isAjax) {
parsedDesc.credentials = parseInt(resourceData[0], 10);
if (resourceData.length === 2)
parsedDesc.reqOrigin = decodeURIComponent(restoreShortOrigin(resourceData[1]));
}
}
return parsedDesc;
}
function parseProxyUrl(proxyUrl) {
// TODO: Remove it.
const parsedUrl = parseUrl(proxyUrl);
if (!parsedUrl.partAfterHost)
return null;
const match = parsedUrl.partAfterHost.match(PATH_AFTER_HOST_RE);
if (!match)
return null;
const parsedDesc = parseRequestDescriptor(match[1]);
// NOTE: We should have, at least, the job uid and the owner token.
if (!parsedDesc)
return null;
let destUrl = match[2];
// Browser can redirect to a special page with hash (GH-1671)
const destUrlWithoutHash = destUrl.replace(/#[\S\s]*$/, '');
if (!isSpecialPage(destUrlWithoutHash) && !exports.SUPPORTED_PROTOCOL_RE.test(destUrl))
return null;
let destResourceInfo;
if (isSpecialPage(destUrlWithoutHash))
destResourceInfo = SPECIAL_PAGE_DEST_RESOURCE_INFO;
else {
destUrl = omitDefaultPort(destUrl);
destResourceInfo = parseUrl(destUrl);
}
return {
destUrl,
destResourceInfo,
partAfterHost: parsedUrl.partAfterHost,
proxy: {
hostname: parsedUrl.hostname || '',
port: parsedUrl.port || '',
},
sessionId: parsedDesc.sessionId,
resourceType: parsedDesc.resourceType,
charset: parsedDesc.charset,
reqOrigin: parsedDesc.reqOrigin,
windowId: parsedDesc.windowId,
credentials: parsedDesc.credentials,
};
}
exports.parseProxyUrl = parseProxyUrl;
function getPathname(path) {
return path.replace(QUERY_AND_HASH_RE, '');
}
exports.getPathname = getPathname;
function parseUrl(url) {
url = processSpecialChars(url);
if (!url)
return {};
const urlMatch = url.match(URL_RE);
return urlMatch ? {
protocol: urlMatch[1],
auth: urlMatch[2],
host: urlMatch[3],
hostname: urlMatch[4],
port: urlMatch[5],
partAfterHost: urlMatch[6],
} : {};
}
exports.parseUrl = parseUrl;
function isSupportedProtocol(url) {
url = (0, string_trim_1.default)(url || '');
const isHash = exports.HASH_RE.test(url);
if (isHash)
return false;
const protocol = url.match(PROTOCOL_RE);
if (!protocol)
return true;
return exports.SUPPORTED_PROTOCOL_RE.test(protocol[0]);
}
exports.isSupportedProtocol = isSupportedProtocol;
function resolveUrlAsDest(url, getProxyUrlMeth, isUrlsSet = false) {
if (isUrlsSet)
return handleUrlsSet(resolveUrlAsDest, url, getProxyUrlMeth);
getProxyUrlMeth = getProxyUrlMeth || getProxyUrl;
if (isSupportedProtocol(url)) {
const proxyUrl = getProxyUrlMeth(url);
const parsedProxyUrl = parseProxyUrl(proxyUrl);
return parsedProxyUrl ? formatUrl(parsedProxyUrl.destResourceInfo) : url;
}
return url;
}
exports.resolveUrlAsDest = resolveUrlAsDest;
function formatUrl(parsedUrl) {
// NOTE: the URL is relative.
if (parsedUrl.protocol !== 'file:' && parsedUrl.protocol !== 'about:' &&
!parsedUrl.host && (!parsedUrl.hostname || !parsedUrl.port))
return parsedUrl.partAfterHost || '';
let url = parsedUrl.protocol || '';
if (parsedUrl.protocol !== 'about:')
url += '//';
if (parsedUrl.auth)
url += parsedUrl.auth + '@';
if (parsedUrl.host)
url += parsedUrl.host;
else if (parsedUrl.hostname) {
url += parsedUrl.hostname;
if (parsedUrl.port)
url += ':' + parsedUrl.port;
}
if (parsedUrl.partAfterHost)
url += parsedUrl.partAfterHost;
return url;
}
exports.formatUrl = formatUrl;
function handleUrlsSet(handler, url, ...args) {
const resourceUrls = url.split(',');
const replacedUrls = [];
for (const fullUrlStr of resourceUrls) {
const [urlStr, postUrlStr] = fullUrlStr.replace(/ +/g, ' ').trim().split(' ');
if (urlStr) {
const replacedUrl = handler(urlStr, ...args);
replacedUrls.push(replacedUrl + (postUrlStr ? ` ${postUrlStr}` : ''));
}
}
return replacedUrls.join(',');
}
exports.handleUrlsSet = handleUrlsSet;
function correctMultipleSlashes(url, pageProtocol = '') {
// NOTE: Remove unnecessary slashes from the beginning of the url and after scheme.
// For example:
// "//////example.com" -> "//example.com" (scheme-less HTTP(S) URL)
// "////home/testcafe/documents" -> "///home/testcafe/documents" (scheme-less unix file URL)
// "http:///example.com" -> "http://example.com"
//
// And add missing slashes after the file scheme.
// "file://C:/document.txt" -> "file:///C:/document.txt"
if (url.match(FILE_RE) || pageProtocol.match(FILE_RE)) {
return url
.replace(/^(file:)?\/+(\/\/\/.*$)/i, '$1$2')
.replace(/^(file:)?\/*([A-Za-z]):/i, '$1///$2:');
}
return url.replace(/^(https?:)?\/+(\/\/.*$)/i, '$1$2');
}
exports.correctMultipleSlashes = correctMultipleSlashes;
function processSpecialChars(url) {
return correctMultipleSlashes(getURLString(url));
}
exports.processSpecialChars = processSpecialChars;
function ensureTrailingSlash(srcUrl, processedUrl) {
if (!isValidUrl(processedUrl))
return processedUrl;
const srcUrlEndsWithTrailingSlash = exports.TRAILING_SLASH_RE.test(srcUrl);
const processedUrlEndsWithTrailingSlash = exports.TRAILING_SLASH_RE.test(processedUrl);
if (srcUrlEndsWithTrailingSlash && !processedUrlEndsWithTrailingSlash)
processedUrl += '/';
else if (srcUrl && !srcUrlEndsWithTrailingSlash && processedUrlEndsWithTrailingSlash)
processedUrl = processedUrl.replace(exports.TRAILING_SLASH_RE, '');
return processedUrl;
}
exports.ensureTrailingSlash = ensureTrailingSlash;
function isSpecialPage(url) {
return exports.SPECIAL_PAGES.indexOf(url) !== -1;
}
exports.isSpecialPage = isSpecialPage;
function isRelativeUrl(url) {
const parsedUrl = parseUrl(url);
return parsedUrl.protocol !== 'file:' && !parsedUrl.host;
}
exports.isRelativeUrl = isRelativeUrl;
function isValidPort(port) {
const parsedPort = parseInt(port, 10);
return parsedPort > 0 && parsedPort <= 65535;
}
function isValidUrl(url) {
const parsedUrl = parseUrl(url);
return parsedUrl.protocol === 'file:' || parsedUrl.protocol === 'about:' ||
!!parsedUrl.hostname && (!parsedUrl.port || isValidPort(parsedUrl.port));
}
exports.isValidUrl = isValidUrl;
function ensureOriginTrailingSlash(url) {
// NOTE: If you request an url containing only port, host and protocol
// then browser adds the trailing slash itself.
const parsedUrl = parseUrl(url);
if (!parsedUrl.partAfterHost && parsedUrl.protocol && HTTP_RE.test(parsedUrl.protocol))
return url + '/';
return url;
}
exports.ensureOriginTrailingSlash = ensureOriginTrailingSlash;
function omitDefaultPort(url) {
// NOTE: If you request an url containing default port
// then browser remove this one itself.
const parsedUrl = parseUrl(url);
const hasDefaultPort = parsedUrl.protocol === 'https:' && parsedUrl.port === exports.HTTPS_DEFAULT_PORT ||
parsedUrl.protocol === 'http:' && parsedUrl.port === exports.HTTP_DEFAULT_PORT;
if (hasDefaultPort) {
parsedUrl.host = parsedUrl.hostname;
parsedUrl.port = '';
return formatUrl(parsedUrl);
}
return url;
}
exports.omitDefaultPort = omitDefaultPort;
function prepareUrl(url) {
url = omitDefaultPort(url);
url = ensureOriginTrailingSlash(url);
return url;
}
exports.prepareUrl = prepareUrl;
function updateScriptImportUrls(cachedScript, serverInfo, sessionId, windowId) {
const regExp = new RegExp('(' + serverInfo.protocol + '//' + serverInfo.hostname + ':(?:' + serverInfo.port + '|' +
serverInfo.crossDomainPort + ')/)[^/' + exports.REQUEST_DESCRIPTOR_VALUES_SEPARATOR + ']+', 'g');
const pattern = '$1' + sessionId + (windowId ? exports.REQUEST_DESCRIPTOR_SESSION_INFO_VALUES_SEPARATOR + windowId : '');
return cachedScript.replace(regExp, pattern);
}
exports.updateScriptImportUrls = updateScriptImportUrls;
function processMetaRefreshContent(content, urlReplacer) {
const match = content.match(META_REFRESH_RE);
if (!match || !match[3])
return content;
return match[1] + urlReplacer(match[3]) + (match[4] || '');
}
exports.processMetaRefreshContent = processMetaRefreshContent;