axios
Version:
Promise based HTTP client for the browser and node.js
89 lines (77 loc) • 3.15 kB
JavaScript
;
import AxiosError, { REDACTED } from './AxiosError.js';
import isAbsoluteURL from '../helpers/isAbsoluteURL.js';
import combineURLs from '../helpers/combineURLs.js';
const malformedHttpProtocol = /^https?:(?!\/\/)/i;
const httpProtocolControlCharacters = /[\t\n\r]/g;
function stripLeadingC0ControlOrSpace(url) {
let i = 0;
while (i < url.length && url.charCodeAt(i) <= 0x20) {
i++;
}
return url.slice(i);
}
function normalizeURLForProtocolCheck(url) {
return stripLeadingC0ControlOrSpace(url).replace(httpProtocolControlCharacters, '');
}
// Redact the parts of a URL that can carry secrets before it is embedded in an
// error message. AxiosError.toJSON() serializes `message` verbatim and errors
// are commonly logged, while the opt-in `config.redact` model only cleans
// config keys — it cannot reach the message. Redact only the genuinely
// sensitive substrings — userinfo (credentials), query parameter values and
// fragment contents — with the same REDACTED marker the config redaction uses,
// while keeping the scheme, host, path and parameter names so the offending
// request stays accurately identifiable.
function redactFragment(fragment) {
if (!fragment) {
return fragment;
}
return fragment.replace(/(^|&)([^=&]*=)?[^&]+/g, (match, separator, parameterName = '') => {
return `${separator}${parameterName}${REDACTED}`;
});
}
function redactSensitiveURLParts(url) {
const redactedURL = url.replace(/^(https?:\/{0,2})[^/?#]*@/i, `$1${REDACTED}@`);
const fragmentIndex = redactedURL.indexOf('#');
const urlWithoutFragment =
fragmentIndex === -1 ? redactedURL : redactedURL.slice(0, fragmentIndex);
const redactedURLWithoutFragment = urlWithoutFragment.replace(
/([?&][^=&#]*=)[^&#]*/g,
`$1${REDACTED}`
);
if (fragmentIndex === -1) {
return redactedURLWithoutFragment;
}
return `${redactedURLWithoutFragment}#${redactFragment(redactedURL.slice(fragmentIndex + 1))}`;
}
function assertValidHttpProtocolURL(url, config) {
if (typeof url === 'string') {
const normalizedURL = normalizeURLForProtocolCheck(url);
if (malformedHttpProtocol.test(normalizedURL)) {
throw new AxiosError(
`Invalid URL ${JSON.stringify(redactSensitiveURLParts(normalizedURL))}: missing "//" after protocol`,
AxiosError.ERR_INVALID_URL,
config
);
}
}
}
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
*
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls, config) {
assertValidHttpProtocolURL(requestedURL, config);
let isRelativeUrl = !isAbsoluteURL(requestedURL);
if (baseURL && (isRelativeUrl || allowAbsoluteUrls === false)) {
assertValidHttpProtocolURL(baseURL, config);
return combineURLs(baseURL, requestedURL);
}
return requestedURL;
}