diff options
author | Minteck <contact@minteck.org> | 2022-04-09 16:39:03 +0200 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-04-09 16:40:02 +0200 |
commit | 0f8967b9113d698cdeb2d05ca85d2d9a80461c24 (patch) | |
tree | 00664ddd9c55a2c33631fd1bd33e556cea9c67e5 /node_modules/got/source/normalize-arguments.js | |
parent | dac03ac82bc0f8044a4b339c27b5390e4dcecf2f (diff) | |
download | voicer-trunk.tar.gz voicer-trunk.tar.bz2 voicer-trunk.zip |
Diffstat (limited to 'node_modules/got/source/normalize-arguments.js')
-rw-r--r-- | node_modules/got/source/normalize-arguments.js | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/node_modules/got/source/normalize-arguments.js b/node_modules/got/source/normalize-arguments.js new file mode 100644 index 0000000..665cbce --- /dev/null +++ b/node_modules/got/source/normalize-arguments.js @@ -0,0 +1,265 @@ +'use strict'; +const {URL, URLSearchParams} = require('url'); // TODO: Use the `URL` global when targeting Node.js 10 +const urlLib = require('url'); +const is = require('@sindresorhus/is'); +const urlParseLax = require('url-parse-lax'); +const lowercaseKeys = require('lowercase-keys'); +const urlToOptions = require('./utils/url-to-options'); +const isFormData = require('./utils/is-form-data'); +const merge = require('./merge'); +const knownHookEvents = require('./known-hook-events'); + +const retryAfterStatusCodes = new Set([413, 429, 503]); + +// `preNormalize` handles static options (e.g. headers). +// For example, when you create a custom instance and make a request +// with no static changes, they won't be normalized again. +// +// `normalize` operates on dynamic options - they cannot be saved. +// For example, `body` is everytime different per request. +// When it's done normalizing the new options, it performs merge() +// on the prenormalized options and the normalized ones. + +const preNormalize = (options, defaults) => { + if (is.nullOrUndefined(options.headers)) { + options.headers = {}; + } else { + options.headers = lowercaseKeys(options.headers); + } + + if (options.baseUrl && !options.baseUrl.toString().endsWith('/')) { + options.baseUrl += '/'; + } + + if (options.stream) { + options.json = false; + } + + if (is.nullOrUndefined(options.hooks)) { + options.hooks = {}; + } else if (!is.object(options.hooks)) { + throw new TypeError(`Parameter \`hooks\` must be an object, not ${is(options.hooks)}`); + } + + for (const event of knownHookEvents) { + if (is.nullOrUndefined(options.hooks[event])) { + if (defaults) { + options.hooks[event] = [...defaults.hooks[event]]; + } else { + options.hooks[event] = []; + } + } + } + + if (is.number(options.timeout)) { + options.gotTimeout = {request: options.timeout}; + } else if (is.object(options.timeout)) { + options.gotTimeout = options.timeout; + } + + delete options.timeout; + + const {retry} = options; + options.retry = { + retries: 0, + methods: [], + statusCodes: [], + errorCodes: [] + }; + + if (is.nonEmptyObject(defaults) && retry !== false) { + options.retry = {...defaults.retry}; + } + + if (retry !== false) { + if (is.number(retry)) { + options.retry.retries = retry; + } else { + options.retry = {...options.retry, ...retry}; + } + } + + if (options.gotTimeout) { + options.retry.maxRetryAfter = Math.min(...[options.gotTimeout.request, options.gotTimeout.connection].filter(n => !is.nullOrUndefined(n))); + } + + if (is.array(options.retry.methods)) { + options.retry.methods = new Set(options.retry.methods.map(method => method.toUpperCase())); + } + + if (is.array(options.retry.statusCodes)) { + options.retry.statusCodes = new Set(options.retry.statusCodes); + } + + if (is.array(options.retry.errorCodes)) { + options.retry.errorCodes = new Set(options.retry.errorCodes); + } + + return options; +}; + +const normalize = (url, options, defaults) => { + if (is.plainObject(url)) { + options = {...url, ...options}; + url = options.url || {}; + delete options.url; + } + + if (defaults) { + options = merge({}, defaults.options, options ? preNormalize(options, defaults.options) : {}); + } else { + options = merge({}, preNormalize(options)); + } + + if (!is.string(url) && !is.object(url)) { + throw new TypeError(`Parameter \`url\` must be a string or object, not ${is(url)}`); + } + + if (is.string(url)) { + if (options.baseUrl) { + if (url.toString().startsWith('/')) { + url = url.toString().slice(1); + } + + url = urlToOptions(new URL(url, options.baseUrl)); + } else { + url = url.replace(/^unix:/, 'http://$&'); + url = urlParseLax(url); + } + } else if (is(url) === 'URL') { + url = urlToOptions(url); + } + + // Override both null/undefined with default protocol + options = merge({path: ''}, url, {protocol: url.protocol || 'https:'}, options); + + for (const hook of options.hooks.init) { + const called = hook(options); + + if (is.promise(called)) { + throw new TypeError('The `init` hook must be a synchronous function'); + } + } + + const {baseUrl} = options; + Object.defineProperty(options, 'baseUrl', { + set: () => { + throw new Error('Failed to set baseUrl. Options are normalized already.'); + }, + get: () => baseUrl + }); + + const {query} = options; + if (is.nonEmptyString(query) || is.nonEmptyObject(query) || query instanceof URLSearchParams) { + if (!is.string(query)) { + options.query = (new URLSearchParams(query)).toString(); + } + + options.path = `${options.path.split('?')[0]}?${options.query}`; + delete options.query; + } + + if (options.hostname === 'unix') { + const matches = /(.+?):(.+)/.exec(options.path); + + if (matches) { + const [, socketPath, path] = matches; + options = { + ...options, + socketPath, + path, + host: null + }; + } + } + + const {headers} = options; + for (const [key, value] of Object.entries(headers)) { + if (is.nullOrUndefined(value)) { + delete headers[key]; + } + } + + if (options.json && is.undefined(headers.accept)) { + headers.accept = 'application/json'; + } + + if (options.decompress && is.undefined(headers['accept-encoding'])) { + headers['accept-encoding'] = 'gzip, deflate'; + } + + const {body} = options; + if (is.nullOrUndefined(body)) { + options.method = options.method ? options.method.toUpperCase() : 'GET'; + } else { + const isObject = is.object(body) && !is.buffer(body) && !is.nodeStream(body); + if (!is.nodeStream(body) && !is.string(body) && !is.buffer(body) && !(options.form || options.json)) { + throw new TypeError('The `body` option must be a stream.Readable, string or Buffer'); + } + + if (options.json && !(isObject || is.array(body))) { + throw new TypeError('The `body` option must be an Object or Array when the `json` option is used'); + } + + if (options.form && !isObject) { + throw new TypeError('The `body` option must be an Object when the `form` option is used'); + } + + if (isFormData(body)) { + // Special case for https://github.com/form-data/form-data + headers['content-type'] = headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`; + } else if (options.form) { + headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded'; + options.body = (new URLSearchParams(body)).toString(); + } else if (options.json) { + headers['content-type'] = headers['content-type'] || 'application/json'; + options.body = JSON.stringify(body); + } + + options.method = options.method ? options.method.toUpperCase() : 'POST'; + } + + if (!is.function(options.retry.retries)) { + const {retries} = options.retry; + + options.retry.retries = (iteration, error) => { + if (iteration > retries) { + return 0; + } + + if ((!error || !options.retry.errorCodes.has(error.code)) && (!options.retry.methods.has(error.method) || !options.retry.statusCodes.has(error.statusCode))) { + return 0; + } + + if (Reflect.has(error, 'headers') && Reflect.has(error.headers, 'retry-after') && retryAfterStatusCodes.has(error.statusCode)) { + let after = Number(error.headers['retry-after']); + if (is.nan(after)) { + after = Date.parse(error.headers['retry-after']) - Date.now(); + } else { + after *= 1000; + } + + if (after > options.retry.maxRetryAfter) { + return 0; + } + + return after; + } + + if (error.statusCode === 413) { + return 0; + } + + const noise = Math.random() * 100; + return ((2 ** (iteration - 1)) * 1000) + noise; + }; + } + + return options; +}; + +const reNormalize = options => normalize(urlLib.format(options), options); + +module.exports = normalize; +module.exports.preNormalize = preNormalize; +module.exports.reNormalize = reNormalize; |