From 6f587379156f9a8bb8ea8a7b9e400e9043a1e885 Mon Sep 17 00:00:00 2001 From: Angus Russell Date: Wed, 13 Oct 2021 16:51:02 +1000 Subject: [PATCH] Add a configurable timeout to the requests (#37) --- package.json | 3 ++- src/index.test.ts | 26 ++++++++++++++++++++++++++ src/index.ts | 20 ++++++++++++-------- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 51eea55..a7efaeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tall", - "version": "4.0.1", + "version": "4.1.0", "description": "Promise-based, No-dependency URL unshortner (expander) module for Node.js", "type": "module", "main": "./lib/cjs/index.js", @@ -23,6 +23,7 @@ "build": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && echo '{\"type\":\"commonjs\"}' > lib/cjs/package.json", "build:test": "node test/commonjs.cjs && node test/esm.js", "prepublish": "npm run build", + "prepare": "npm run build", "test:lint": "eslint .", "test:unit": "jest --coverage --verbose", "test": "npm run test:lint && npm run test:unit" diff --git a/src/index.test.ts b/src/index.test.ts index 2be447e..7acad54 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -43,6 +43,32 @@ test('it should fail if a URL without protocol is given', async () => { await expect(() => tall('example.com')).rejects.toMatchObject({ message: 'Invalid URL: example.com' }) }) +test('it should fail if the request times out', async () => { + nock('http://example.com') + .get('/a-link') + .times(1) + .delay(1000) + .reply(200, 'OK') + + await expect(() => tall('http://example.com/a-link', { timeout: 1 })).rejects.toMatchObject({ message: 'socket hang up' }) +}) + +test('it should not fail if the request is within the timeout', async () => { + nock('http://example.com') + .get('/a-link') + .times(1) + .delay(1) + .reply(301, 'Moved', { location: 'http://dest.pizza/a-link' }) + nock('http://dest.pizza') + .get('/a-link') + .times(1) + .reply(200, 'OK') + + const url = await tall('http://example.com/a-link', { timeout: 1000 }) + + expect(url).toBe('http://dest.pizza/a-link') +}) + test('it should return the original url if no redirect', async () => { nock('https://example.com') .get('/test') diff --git a/src/index.ts b/src/index.ts index 076185b..e209eef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,18 +9,20 @@ export interface TallHTTPHeaders { } export interface TallOptions { - method?: TallAvailableHTTPMethod - maxRedirects?: number - headers?: TallHTTPHeaders + method: TallAvailableHTTPMethod + maxRedirects: number + headers: TallHTTPHeaders + timeout: number } const defaultOptions: TallOptions = { method: 'GET', maxRedirects: 3, - headers: {} + headers: {}, + timeout: 120000 } -export const tall = (url: string, options?: TallOptions): Promise => { +export const tall = (url: string, options?: Partial): Promise => { const opt = Object.assign({}, defaultOptions, options) return new Promise((resolve, reject) => { try { @@ -35,7 +37,7 @@ export const tall = (url: string, options?: TallOptions): Promise => { const method = opt.method const request = protocol === 'https:' ? httpsReq : httpReq const headers = opt.headers - return request(url, { method, headers }, response => { + const req = request(url, { method, headers }, response => { if (response.headers.location && opt.maxRedirects) { opt.maxRedirects-- return resolve( @@ -48,8 +50,10 @@ export const tall = (url: string, options?: TallOptions): Promise => { resolve(url) }) - .on('error', reject) - .end() + req.on('error', reject) + req.setTimeout(opt.timeout, () => req.destroy()) + req.end() + return req } catch (err) { return reject(err) }