From 33e8a04394c5229fb52ace2f1e4e9ee9247ec43b Mon Sep 17 00:00:00 2001 From: Joel Thoms Date: Wed, 15 Mar 2023 11:42:51 -0700 Subject: [PATCH 1/4] fix: undici connect timeout (#302) --- index.js | 3 ++ lib/errors.js | 1 + test/disable-request-logging.test.js | 2 +- test/undici-connect-timeout.test.js | 58 ++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 test/undici-connect-timeout.test.js diff --git a/index.js b/index.js index 5ae70ff0..a677dbb9 100644 --- a/index.js +++ b/index.js @@ -17,6 +17,7 @@ const { ServiceUnavailableError, GatewayTimeoutError, ConnectionResetError, + ConnectTimeoutError, UndiciSocketError, InternalServerError } = require('./lib/errors') @@ -157,6 +158,8 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) { onError(this, { error: new ConnectionResetError() }) } else if (err.code === 'UND_ERR_SOCKET') { onError(this, { error: new UndiciSocketError() }) + } else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') { + onError(this, { error: new ConnectTimeoutError() }) } else { onError(this, { error: new InternalServerError(err.message) }) } diff --git a/lib/errors.js b/lib/errors.js index 0efc1234..87919b2e 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -9,5 +9,6 @@ module.exports.Http2SessionTimeoutError = createError('FST_REPLY_FROM_HTTP2_SESS module.exports.ServiceUnavailableError = createError('FST_REPLY_FROM_SERVICE_UNAVAILABLE', 'Service Unavailable', 503) module.exports.GatewayTimeoutError = createError('FST_REPLY_FROM_GATEWAY_TIMEOUT', 'Gateway Timeout', 504) module.exports.ConnectionResetError = createError('ECONNRESET', 'Connection Reset', 500) +module.exports.ConnectTimeoutError = createError('UND_ERR_CONNECT_TIMEOUT', 'Connect Timeout Error', 500) module.exports.UndiciSocketError = createError('UND_ERR_SOCKET', 'Undici Socket Error', 500) module.exports.InternalServerError = createError('FST_REPLY_FROM_INTERNAL_SERVER_ERROR', '%s', 500) diff --git a/test/disable-request-logging.test.js b/test/disable-request-logging.test.js index f08f658a..77e1c25b 100644 --- a/test/disable-request-logging.test.js +++ b/test/disable-request-logging.test.js @@ -24,7 +24,7 @@ t.test('use a custom instance of \'undici\'', async t => { await new Promise((resolve, reject) => target.listen({ port: 0 }, err => err ? reject(err) : resolve())) t.test('disableRequestLogging is set to true', t => { - t.plan(10) + t.plan(9) const logStream = split(JSON.parse) const instance = Fastify({ logger: { diff --git a/test/undici-connect-timeout.test.js b/test/undici-connect-timeout.test.js new file mode 100644 index 00000000..1d516638 --- /dev/null +++ b/test/undici-connect-timeout.test.js @@ -0,0 +1,58 @@ +'use strict' + +const t = require('tap') +const http = require('http') +const net = require('net') +const Fastify = require('fastify') +const From = require('..') +const got = require('got') + +t.autoend(false) + +// never connect +net.connect = function (options) { + return new net.Socket(options) +} + +const target = http.createServer((req, res) => { + t.fail('target never called') +}) + +async function main () { + t.plan(2) + await target.listen({ port: 0 }) + + const instance = Fastify() + t.teardown(instance.close.bind(instance)) + t.teardown(target.close.bind(target)) + + instance.register(From, { + base: `http://localhost:${target.address().port}`, + undici: { + connectTimeout: 50 + } + }) + + instance.get('/', (request, reply) => { + reply.from() + }) + + await instance.listen({ port: 0 }) + + try { + await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 }) + } catch (err) { + t.equal(err.response.statusCode, 500) + t.same(JSON.parse(err.response.body), { + statusCode: 500, + code: 'UND_ERR_CONNECT_TIMEOUT', + error: 'Internal Server Error', + message: 'Connect Timeout Error' + }) + return + } + + t.fail() +} + +main() From 8f5caa9e9d4e27f0c66f56fcaca30bbb6c0b8032 Mon Sep 17 00:00:00 2001 From: Uzlopak Date: Thu, 16 Mar 2023 12:24:03 +0100 Subject: [PATCH 2/4] add t.plan --- test/disable-request-logging.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/disable-request-logging.test.js b/test/disable-request-logging.test.js index 77e1c25b..e68164f2 100644 --- a/test/disable-request-logging.test.js +++ b/test/disable-request-logging.test.js @@ -19,6 +19,7 @@ const target = http.createServer((req, res) => { }) t.test('use a custom instance of \'undici\'', async t => { + t.plan(3) t.teardown(target.close.bind(target)) await new Promise((resolve, reject) => target.listen({ port: 0 }, err => err ? reject(err) : resolve())) From 12d4200aad30f890d82d160be18561ec7dac882c Mon Sep 17 00:00:00 2001 From: Uzlopak Date: Thu, 16 Mar 2023 12:31:45 +0100 Subject: [PATCH 3/4] remove t.end --- test/disable-request-logging.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/disable-request-logging.test.js b/test/disable-request-logging.test.js index e68164f2..4f1c8dc9 100644 --- a/test/disable-request-logging.test.js +++ b/test/disable-request-logging.test.js @@ -64,7 +64,6 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') - t.end() }) }) }) @@ -109,7 +108,6 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') - t.end() }) }) }) @@ -153,7 +151,6 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') - t.end() }) }) }) From 44ec77461b73f04591d3ddbb1e459339a7e8d0a2 Mon Sep 17 00:00:00 2001 From: Uzlopak Date: Thu, 16 Mar 2023 12:36:01 +0100 Subject: [PATCH 4/4] remove t.plans --- test/disable-request-logging.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/disable-request-logging.test.js b/test/disable-request-logging.test.js index 4f1c8dc9..f650ca68 100644 --- a/test/disable-request-logging.test.js +++ b/test/disable-request-logging.test.js @@ -25,7 +25,6 @@ t.test('use a custom instance of \'undici\'', async t => { await new Promise((resolve, reject) => target.listen({ port: 0 }, err => err ? reject(err) : resolve())) t.test('disableRequestLogging is set to true', t => { - t.plan(9) const logStream = split(JSON.parse) const instance = Fastify({ logger: { @@ -64,12 +63,12 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') + t.end() }) }) }) t.test('disableRequestLogging is set to false', t => { - t.plan(8) const logStream = split(JSON.parse) const instance = Fastify({ logger: { @@ -108,12 +107,12 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') + t.end() }) }) }) t.test('disableRequestLogging is not defined', t => { - t.plan(8) const logStream = split(JSON.parse) const instance = Fastify({ logger: { @@ -151,6 +150,7 @@ t.test('use a custom instance of \'undici\'', async t => { t.equal(res.headers['x-my-header'], 'hello!') t.equal(res.statusCode, 205) t.equal(data.toString(), 'hello world') + t.end() }) }) })