https://stackblitz.com/edit/node-m3qwkx?file=index.js
We're migrating from v11 to v12 and it seems that the behavior is now slightly different. It's either a bug, or a change in behavior we can't find a reasonable explanation for in the documentation.
For what it's worth, the final request URL is well-formed with the prefix being included.
Here's the code & output copied from Stackblitz, for completeness
// run `node index.js` in the terminal
import got from 'got';
console.log(`Hello Node.js v${process.versions.node}!`);
const client = got.extend(
{
prefixUrl: 'http://localhost/test/',
},
{
retry: {
limit: 1,
},
},
{
hooks: {
init: [
(plain, options) => {
// We don't use `init` hooks, it was added here so we can log the values for visibility
console.log('init, plain prefix URL: ', plain.prefixUrl);
console.log('init, options prefix URL: ', options.prefixUrl);
},
],
beforeRequest: [
(options) => {
console.log('beforeRequest, prefix URL: ', options.prefixUrl);
},
],
beforeRetry: [
async (error) => {
console.log('beforeRetry, prefix URL: ', error.options.prefixUrl);
// error.options.prefixUrl = "http://localhost/other";
// error.options.url = ... replace the old prefix with the new prefix
// retry the request with the new URL
},
],
},
}
);
const result = await client.get('endpoint');
Notice how prefixUrl
is blank in the error as well.
❯ node index.js
Hello Node.js v16.14.2!
init, plain prefix URL: undefined
init, options prefix URL: http://localhost/test/
init, plain prefix URL: undefined
init, options prefix URL: http://localhost/test/
beforeRequest, prefix URL:
beforeRetry, prefix URL:
beforeRequest, prefix URL:
Error: connect ECONNREFUSED 127.0.0.1:80
at async ESMLoader.import (https://node-m3qwkx.w.staticblitz.com/blitz.4f16907aeb1fd170bcc93609cea1d8dc6ad28342.js:6:1209278)
at async i.loadESM (https://node-m3qwkx.w.staticblitz.com/blitz.4f16907aeb1fd170bcc93609cea1d8dc6ad28342.js:6:246622)
at async handleMainPromise (https://node-m3qwkx.w.staticblitz.com/blitz.4f16907aeb1fd170bcc93609cea1d8dc6ad28342.js:6:989287) {
input: undefined,
code: 'ECONNREFUSED',
timings: {
start: 1661932864878,
socket: 1661932864879,
lookup: undefined,
connect: undefined,
secureConnect: undefined,
upload: undefined,
response: undefined,
end: undefined,
error: 1661932864879,
abort: undefined,
phases: {
wait: 1,
dns: undefined,
tcp: undefined,
tls: undefined,
request: undefined,
firstByte: undefined,
download: undefined,
total: 1
}
},
name: 'RequestError',
options: {
request: undefined,
agent: { http: undefined, https: undefined, http2: undefined },
h2session: undefined,
decompress: true,
timeout: {
connect: undefined,
lookup: undefined,
read: undefined,
request: undefined,
response: undefined,
secureConnect: undefined,
send: undefined,
socket: undefined
},
prefixUrl: '',
body: undefined,
form: undefined,
json: undefined,
cookieJar: undefined,
ignoreInvalidCookies: false,
searchParams: undefined,
dnsLookup: undefined,
dnsCache: undefined,
context: {},
hooks: {
init: [ [Function (anonymous)] ],
beforeRequest: [ [Function (anonymous)] ],
beforeError: [],
beforeRedirect: [],
beforeRetry: [ [AsyncFunction (anonymous)] ],
afterResponse: []
},
followRedirect: true,
maxRedirects: 10,
cache: undefined,
throwHttpErrors: true,
username: '',
password: '',
http2: false,
allowGetBody: false,
headers: {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'accept-encoding': 'gzip, deflate, br'
},
methodRewriting: false,
dnsLookupIpVersion: undefined,
parseJson: [Function: parse],
stringifyJson: [Function: stringify],
retry: {
limit: 1,
methods: [ 'GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE' ],
statusCodes: [
408, 413, 429, 500,
502, 503, 504, 521,
522, 524
],
errorCodes: [
'ETIMEDOUT',
'ECONNRESET',
'EADDRINUSE',
'ECONNREFUSED',
'EPIPE',
'ENOTFOUND',
'ENETUNREACH',
'EAI_AGAIN'
],
maxRetryAfter: undefined,
calculateDelay: [Function: calculateDelay],
backoffLimit: Infinity,
noise: 100
},
localAddress: undefined,
method: 'GET',
createConnection: undefined,
cacheOptions: {
shared: undefined,
cacheHeuristic: undefined,
immutableMinTimeToLive: undefined,
ignoreCargoCult: undefined
},
https: {
alpnProtocols: undefined,
rejectUnauthorized: undefined,
checkServerIdentity: undefined,
certificateAuthority: undefined,
key: undefined,
certificate: undefined,
passphrase: undefined,
pfx: undefined,
ciphers: undefined,
honorCipherOrder: undefined,
minVersion: undefined,
maxVersion: undefined,
signatureAlgorithms: undefined,
tlsSessionLifetime: undefined,
dhparam: undefined,
ecdhCurve: undefined,
certificateRevocationLists: undefined
},
encoding: undefined,
resolveBodyOnly: false,
isStream: false,
responseType: 'text',
url: URL {
href: 'http://localhost/test/endpoint',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/test/endpoint',
search: '',
searchParams: URLSearchParams {},
hash: ''
},
pagination: {
transform: [Function: transform],
paginate: [Function: paginate],
filter: [Function: filter],
shouldContinue: [Function: shouldContinue],
countLimit: Infinity,
backoff: 0,
requestLimit: 10000,
stackAllItems: false
},
setHost: true,
maxHeaderSize: undefined,
signal: undefined,
enableUnixSockets: true
}
}
The prefix URL is retained in options and passed to the retry hook(s)
We're capturing a 'prefix' in a closure - from a function that creates the hooks - instead of using the one that's defined in options
. It seems that replacing error.options.url
in beforeRetry
handler still works the same as it used to, i.e. it retries the request with the new URL.
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too