From 5aaff532a6b820bb9ab6a8cd0f77131b47e2adb8 Mon Sep 17 00:00:00 2001 From: Dmitriy Mozgovoy Date: Sun, 22 Oct 2023 17:12:33 +0300 Subject: [PATCH] fix(dns): fixed lookup function decorator to work properly in node v20; (#6011) --- index.d.cts | 15 ++++++++--- index.d.ts | 15 ++++++++--- lib/adapters/http.js | 31 +++++++++++++++------- test/module/typings/esm/index.ts | 6 ++--- test/unit/adapters/http.js | 44 +++++++++++++++++++++++++++++--- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/index.d.cts b/index.d.cts index 6f07d1aec0..77214a9a0b 100644 --- a/index.d.cts +++ b/index.d.cts @@ -361,6 +361,15 @@ declare namespace axios { type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName; + type AddressFamily = 4 | 6 | undefined; + + interface LookupAddressEntry { + address: string; + family?: AddressFamily; + } + + type LookupAddress = string | LookupAddressEntry; + interface AxiosRequestConfig { url?: string; method?: Method | string; @@ -402,9 +411,9 @@ declare namespace axios { FormData?: new (...args: any[]) => object; }; formSerializer?: FormSerializerOptions; - family?: 4 | 6 | undefined; - lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: string, family: number) => void) => void) | - ((hostname: string, options: object) => Promise<[address: string, family: number] | string>); + family?: AddressFamily; + lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) | + ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>); } // Alias diff --git a/index.d.ts b/index.d.ts index 79cdd800c9..387861327e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -302,6 +302,15 @@ type AxiosAdapterName = 'xhr' | 'http' | string; type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName; +export type AddressFamily = 4 | 6 | undefined; + +export interface LookupAddressEntry { + address: string; + family?: AddressFamily; +} + +export type LookupAddress = string | LookupAddressEntry; + export interface AxiosRequestConfig { url?: string; method?: Method | string; @@ -343,9 +352,9 @@ export interface AxiosRequestConfig { FormData?: new (...args: any[]) => object; }; formSerializer?: FormSerializerOptions; - family?: 4 | 6 | undefined; - lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: string, family: number) => void) => void) | - ((hostname: string, options: object) => Promise<[address: string, family: number] | string>); + family?: AddressFamily; + lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) | + ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>); } // Alias diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 201252ff85..3f289c1061 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -144,6 +144,18 @@ const wrapAsync = (asyncExecutor) => { }) }; +const resolveFamily = ({address, family}) => { + if (!utils.isString(address)) { + throw TypeError('address must be a string'); + } + return ({ + address, + family: family || (address.indexOf('.') < 0 ? 6 : 4) + }); +} + +const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family}); + /*eslint consistent-return:0*/ export default isHttpAdapterSupported && function httpAdapter(config) { return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) { @@ -154,15 +166,16 @@ export default isHttpAdapterSupported && function httpAdapter(config) { let rejected = false; let req; - if (lookup && utils.isAsyncFn(lookup)) { - lookup = callbackify(lookup, (entry) => { - if(utils.isString(entry)) { - entry = [entry, entry.indexOf('.') < 0 ? 6 : 4] - } else if (!utils.isArray(entry)) { - throw new TypeError('lookup async function must return an array [ip: string, family: number]]') - } - return entry; - }) + if (lookup) { + const _lookup = callbackify(lookup, (value) => utils.isArray(value) ? value : [value]); + // hotfix to support opt.all option which is required for node 20.x + lookup = (hostname, opt, cb) => { + _lookup(hostname, opt, (err, arg0, arg1) => { + const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)]; + + opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family); + }); + } } // temporary internal emitter until the AxiosRequest class will be implemented diff --git a/test/module/typings/esm/index.ts b/test/module/typings/esm/index.ts index b7c4fbc5b7..811e150d2a 100644 --- a/test/module/typings/esm/index.ts +++ b/test/module/typings/esm/index.ts @@ -20,7 +20,7 @@ import axios, { all, isCancel, isAxiosError, - spread + spread, AddressFamily } from 'axios'; const config: AxiosRequestConfig = { @@ -574,7 +574,7 @@ axios.get('/user', { { // getAdapter - + getAdapter(axios.create().defaults.adapter); getAdapter(undefined); getAdapter([]); @@ -658,7 +658,7 @@ for (const [header, value] of headers) { // lookup axios.get('/user', { - lookup: (hostname: string, opt: object, cb: (err: Error | null, address: string, family: number) => void) => { + lookup: (hostname: string, opt: object, cb: (err: Error | null, address: string, family: AddressFamily) => void) => { cb(null, '127.0.0.1', 4); } }); diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index 6e07c7739b..51fb9d71b3 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -2122,7 +2122,7 @@ describe('supports http with nodejs', function () { }); describe('DNS', function() { - it('should support custom DNS lookup function', async function () { + it('should support a custom DNS lookup function', async function () { server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; @@ -2141,7 +2141,26 @@ describe('supports http with nodejs', function () { assert.strictEqual(data, payload); }); - it('should support custom DNS lookup function (async)', async function () { + it('should support a custom DNS lookup function with address entry passing', async function () { + server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); + + const payload = 'test'; + + let isCalled = false; + + const {data} = await axios.post(`http://fake-name.axios:4444`, payload,{ + lookup: (hostname, opt, cb) => { + isCalled = true; + cb(null, {address: '127.0.0.1', family: 4}); + } + }); + + assert.ok(isCalled); + + assert.strictEqual(data, payload); + }); + + it('should support a custom DNS lookup function (async)', async function () { server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; @@ -2160,7 +2179,26 @@ describe('supports http with nodejs', function () { assert.strictEqual(data, payload); }); - it('should support custom DNS lookup function that returns only IP address (async)', async function () { + it('should support a custom DNS lookup function with address entry (async)', async function () { + server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); + + const payload = 'test'; + + let isCalled = false; + + const {data} = await axios.post(`http://fake-name.axios:4444`, payload,{ + lookup: async (hostname, opt) => { + isCalled = true; + return {address: '127.0.0.1', family: 4}; + } + }); + + assert.ok(isCalled); + + assert.strictEqual(data, payload); + }); + + it('should support a custom DNS lookup function that returns only IP address (async)', async function () { server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test';