diff --git a/package-lock.json b/package-lock.json index e36d39ef2..b13759ff4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3262,8 +3262,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.18", - "license": "MIT", + "version": "18.19.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.44.tgz", + "integrity": "sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==", "dependencies": { "undici-types": "~5.26.4" } diff --git a/package.json b/package.json index 53dd042c6..dd244c390 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "url": "https://github.com/tediousjs/tedious.git" }, "engines": { - "node": ">=18" + "node": ">=18.17" }, "publishConfig": { "tag": "next", diff --git a/src/connector.ts b/src/connector.ts index 53d167f5f..199085629 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -2,14 +2,11 @@ import net from 'net'; import dns, { type LookupAddress } from 'dns'; import url from 'node:url'; -import AbortError from './errors/abort-error'; type LookupFunction = (hostname: string, options: dns.LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: dns.LookupAddress[]) => void) => void; export async function connectInParallel(options: { host: string, port: number, localAddress?: string | undefined }, lookup: LookupFunction, signal: AbortSignal) { - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); const addresses = await lookupAllAddresses(options.host, lookup, signal); @@ -61,7 +58,7 @@ export async function connectInParallel(options: { host: string, port: number, l socket.destroy(); } - reject(new AbortError()); + reject(signal.reason); }; for (let i = 0, len = addresses.length; i < len; i++) { @@ -80,9 +77,7 @@ export async function connectInParallel(options: { host: string, port: number, l } export async function connectInSequence(options: { host: string, port: number, localAddress?: string | undefined }, lookup: LookupFunction, signal: AbortSignal) { - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); const errors: any[] = []; const addresses = await lookupAllAddresses(options.host, lookup, signal); @@ -102,7 +97,7 @@ export async function connectInSequence(options: { host: string, port: number, l socket.destroy(); - reject(new AbortError()); + reject(signal.reason); }; const onError = (err: Error) => { @@ -131,9 +126,8 @@ export async function connectInSequence(options: { host: string, port: number, l socket.on('connect', onConnect); }); } catch (err) { - if (err instanceof Error && err.name === 'AbortError') { - throw err; - } + // If the signal was aborted, re-throw the error. + signal.throwIfAborted(); errors.push(err); @@ -148,9 +142,7 @@ export async function connectInSequence(options: { host: string, port: number, l * Look up all addresses for the given hostname. */ export async function lookupAllAddresses(host: string, lookup: LookupFunction, signal: AbortSignal): Promise { - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); if (net.isIPv6(host)) { return [{ address: host, family: 6 }]; @@ -159,7 +151,7 @@ export async function lookupAllAddresses(host: string, lookup: LookupFunction, s } else { return await new Promise((resolve, reject) => { const onAbort = () => { - reject(new AbortError()); + reject(signal.reason); }; signal.addEventListener('abort', onAbort); diff --git a/src/errors/abort-error.ts b/src/errors/abort-error.ts deleted file mode 100644 index 9fad3821f..000000000 --- a/src/errors/abort-error.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default class AbortError extends Error { - declare code: string; - - constructor() { - super('The operation was aborted'); - - this.code = 'ABORT_ERR'; - this.name = 'AbortError'; - } -} diff --git a/src/errors/timeout-error.ts b/src/errors/timeout-error.ts deleted file mode 100644 index 58c34288f..000000000 --- a/src/errors/timeout-error.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default class TimeoutError extends Error { - declare code: string; - - constructor() { - super('The operation was aborted due to timeout'); - - this.code = 'TIMEOUT_ERR'; - this.name = 'TimeoutError'; - } -} diff --git a/src/instance-lookup.ts b/src/instance-lookup.ts index eeca6f926..4d67fea2c 100644 --- a/src/instance-lookup.ts +++ b/src/instance-lookup.ts @@ -1,8 +1,6 @@ import dns from 'dns'; -import AbortError from './errors/abort-error'; import { sendMessage } from './sender'; -import { withTimeout } from './utils/with-timeout'; const SQL_SERVER_BROWSER_PORT = 1434; const TIMEOUT = 2 * 1000; @@ -46,21 +44,20 @@ export async function instanceLookup(options: { server: string, instanceName: st const signal = options.signal; - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); let response; + const request = Buffer.from([0x02]); + for (let i = 0; i <= retries; i++) { + const timeoutSignal = AbortSignal.timeout(timeout); + try { - response = await withTimeout(timeout, async (signal) => { - const request = Buffer.from([0x02]); - return await sendMessage(options.server, port, lookup, signal, request); - }, signal); + response = await sendMessage(options.server, port, lookup, AbortSignal.any([ signal, timeoutSignal ]), request); } catch (err) { // If the current attempt timed out, continue with the next - if (!signal.aborted && err instanceof Error && err.name === 'TimeoutError') { + if (timeoutSignal.aborted) { continue; } diff --git a/src/sender.ts b/src/sender.ts index 7a28e3cfd..cdd3a3b64 100644 --- a/src/sender.ts +++ b/src/sender.ts @@ -3,14 +3,10 @@ import dns from 'dns'; import net from 'net'; import url from 'node:url'; -import AbortError from './errors/abort-error'; - type LookupFunction = (hostname: string, options: dns.LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: dns.LookupAddress[]) => void) => void; export async function sendInParallel(addresses: dns.LookupAddress[], port: number, request: Buffer, signal: AbortSignal) { - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); return await new Promise((resolve, reject) => { const sockets: dgram.Socket[] = []; @@ -38,7 +34,7 @@ export async function sendInParallel(addresses: dns.LookupAddress[], port: numbe const onAbort = () => { clearSockets(); - reject(new AbortError()); + reject(signal.reason); }; const clearSockets = () => { @@ -64,9 +60,7 @@ export async function sendInParallel(addresses: dns.LookupAddress[], port: numbe } export async function sendMessage(host: string, port: number, lookup: LookupFunction, signal: AbortSignal, request: Buffer) { - if (signal.aborted) { - throw new AbortError(); - } + signal.throwIfAborted(); let addresses: dns.LookupAddress[]; @@ -77,7 +71,7 @@ export async function sendMessage(host: string, port: number, lookup: LookupFunc } else { addresses = await new Promise((resolve, reject) => { const onAbort = () => { - reject(new AbortError()); + reject(signal.reason); }; const domainInASCII = url.domainToASCII(host); diff --git a/src/utils/with-timeout.ts b/src/utils/with-timeout.ts deleted file mode 100644 index 5cc4714a5..000000000 --- a/src/utils/with-timeout.ts +++ /dev/null @@ -1,28 +0,0 @@ -import TimeoutError from '../errors/timeout-error'; - -/** - * Run the function `func` with an `AbortSignal` that will automatically abort after the time specified - * by `timeout` or when the given `signal` is aborted. - * - * On timeout, the `timeoutSignal` will be aborted and a `TimeoutError` will be thrown. - */ -export async function withTimeout(timeout: number, func: (timeoutSignal: AbortSignal) => Promise, signal?: AbortSignal): Promise { - const timeoutController = new AbortController(); - const abortCurrentAttempt = () => { timeoutController.abort(); }; - - const timer = setTimeout(abortCurrentAttempt, timeout); - signal?.addEventListener('abort', abortCurrentAttempt, { once: true }); - - try { - return await func(timeoutController.signal); - } catch (err) { - if (err instanceof Error && err.name === 'AbortError' && !(signal && signal.aborted)) { - throw new TimeoutError(); - } - - throw err; - } finally { - signal?.removeEventListener('abort', abortCurrentAttempt); - clearTimeout(timer); - } -}