From 23e2d46426c6dbc6dd1411183ea332386bfed511 Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 2 Feb 2023 17:23:58 +0800 Subject: [PATCH 1/5] fix: undici headers --- lib/request.js | 4 ++-- lib/utils.js | 26 +++++++++++++++++++++ test/undici-chaining.js | 51 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 test/undici-chaining.js diff --git a/lib/request.js b/lib/request.js index 1a1f6cd1..105ea3dc 100644 --- a/lib/request.js +++ b/lib/request.js @@ -5,7 +5,7 @@ const querystring = require('querystring') const eos = require('end-of-stream') const pump = require('pump') const undici = require('undici') -const { stripHttp1ConnectionHeaders } = require('./utils') +const { patchUndiciHeaders, stripHttp1ConnectionHeaders } = require('./utils') const http2 = require('http2') const { @@ -158,7 +158,7 @@ function buildRequest (opts) { // using delete, otherwise it will render as an empty string delete res.headers['transfer-encoding'] - done(null, { statusCode: res.statusCode, headers: res.headers, stream: res.body }) + done(null, { statusCode: res.statusCode, headers: patchUndiciHeaders(res.headers), stream: res.body }) }) } diff --git a/lib/utils.js b/lib/utils.js index e98a6b08..052a5399 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,6 +12,31 @@ function filterPseudoHeaders (headers) { return dest } +// http required the header to be encoded with latin1 +// undici will convert the latin1 buffer back to utf8 +// https://github.com/nodejs/undici/blob/2b260c997ad4efe4ed2064b264b4b546a59e7a67/lib/core/util.js#L216-L229 +// after chaining, the header will converted in wrong byte +// Buffer.from('', 'latin1').toString('utf8') applied +// +// in order to presist the encoding, always encode it +// back to latin1 +function patchUndiciHeaders (headers) { + const headersKeys = Object.keys(headers) + const dist = {} + + let header + let i + + for (i = 0; i < headersKeys.length; i++) { + header = headersKeys[i] + if (header.charCodeAt(0) !== 58) { // fast path for indexOf(':') === 0 + dist[header] = Buffer.from(headers[header]).toString('latin1') + } + } + + return dist +} + function copyHeaders (headers, reply) { const headersKeys = Object.keys(headers) @@ -74,6 +99,7 @@ function buildURL (source, reqBase) { } module.exports = { + patchUndiciHeaders, copyHeaders, stripHttp1ConnectionHeaders, filterPseudoHeaders, diff --git a/test/undici-chaining.js b/test/undici-chaining.js new file mode 100644 index 00000000..fed60186 --- /dev/null +++ b/test/undici-chaining.js @@ -0,0 +1,51 @@ +'use strict' + +const t = require('tap') +const Fastify = require('fastify') +const get = require('simple-get').concat +const From = require('..') + +const header = 'attachment; filename="år.pdf"' + +t.plan(6) + +const instance = Fastify() +t.teardown(instance.close.bind(instance)) +instance.listen({ port: 0 }) + +const proxy1 = Fastify() +t.teardown(proxy1.close.bind(proxy1)) +const proxy2 = Fastify() +t.teardown(proxy2.close.bind(proxy2)) + +instance.get('/', (request, reply) => { + reply.header('content-disposition', header).send('OK') +}) + +proxy1.register(From) +proxy1.get('/', (request, reply) => { + return reply.from(`http://localhost:${instance.server.address().port}`) +}) + +proxy2.register(From) +proxy2.get('/', (request, reply) => { + return reply.from(`http://localhost:${proxy1.server.address().port}`) +}) + +instance.listen({ port: 0 }, err => { + t.error(err) + + proxy1.listen({ port: 0 }, err => { + t.error(err) + + proxy2.listen({ port: 0 }, err => { + t.error(err) + + get(`http://localhost:${proxy2.server.address().port}`, (err, res, data) => { + t.error(err) + t.equal(res.statusCode, 200) + t.equal(data.toString(), 'OK') + }) + }) + }) +}) From 5baeca7b43a9f8066238010a1f2aa0fb70d64884 Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 2 Feb 2023 17:34:16 +0800 Subject: [PATCH 2/5] test: fix multiple listen --- test/undici-chaining.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/undici-chaining.js b/test/undici-chaining.js index fed60186..8272ae87 100644 --- a/test/undici-chaining.js +++ b/test/undici-chaining.js @@ -11,8 +11,6 @@ t.plan(6) const instance = Fastify() t.teardown(instance.close.bind(instance)) -instance.listen({ port: 0 }) - const proxy1 = Fastify() t.teardown(proxy1.close.bind(proxy1)) const proxy2 = Fastify() From 4b1a1d95e28705049ed4147f7acf19a135bf20f5 Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 2 Feb 2023 17:43:32 +0800 Subject: [PATCH 3/5] test: provide undici option --- test/undici-chaining.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/undici-chaining.js b/test/undici-chaining.js index 8272ae87..d5847e5b 100644 --- a/test/undici-chaining.js +++ b/test/undici-chaining.js @@ -3,6 +3,7 @@ const t = require('tap') const Fastify = require('fastify') const get = require('simple-get').concat +const { getUndiciOptions } = require('../lib/request') const From = require('..') const header = 'attachment; filename="år.pdf"' @@ -20,12 +21,16 @@ instance.get('/', (request, reply) => { reply.header('content-disposition', header).send('OK') }) -proxy1.register(From) +proxy1.register(From, { + undici: buildUndiciOptions() +}) proxy1.get('/', (request, reply) => { return reply.from(`http://localhost:${instance.server.address().port}`) }) -proxy2.register(From) +proxy2.register(From, { + undici: buildUndiciOptions() +}) proxy2.get('/', (request, reply) => { return reply.from(`http://localhost:${proxy1.server.address().port}`) }) @@ -47,3 +52,12 @@ instance.listen({ port: 0 }, err => { }) }) }) + +function buildUndiciOptions () { + return getUndiciOptions({ + connections: 42, + pipelining: 24, + keepAliveTimeout: 4242, + strictContentLength: false + }) +} From 8eef3366ec2cd67fb8632c6a2da594d35f2ecd60 Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 2 Feb 2023 17:48:57 +0800 Subject: [PATCH 4/5] test: use keepAliveMaxTimeout 10 --- test/undici-chaining.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/test/undici-chaining.js b/test/undici-chaining.js index d5847e5b..faf89235 100644 --- a/test/undici-chaining.js +++ b/test/undici-chaining.js @@ -3,7 +3,6 @@ const t = require('tap') const Fastify = require('fastify') const get = require('simple-get').concat -const { getUndiciOptions } = require('../lib/request') const From = require('..') const header = 'attachment; filename="år.pdf"' @@ -22,14 +21,18 @@ instance.get('/', (request, reply) => { }) proxy1.register(From, { - undici: buildUndiciOptions() + undici: { + keepAliveMaxTimeout: 10 + } }) proxy1.get('/', (request, reply) => { return reply.from(`http://localhost:${instance.server.address().port}`) }) proxy2.register(From, { - undici: buildUndiciOptions() + undici: { + keepAliveMaxTimeout: 10 + } }) proxy2.get('/', (request, reply) => { return reply.from(`http://localhost:${proxy1.server.address().port}`) @@ -52,12 +55,3 @@ instance.listen({ port: 0 }, err => { }) }) }) - -function buildUndiciOptions () { - return getUndiciOptions({ - connections: 42, - pipelining: 24, - keepAliveTimeout: 4242, - strictContentLength: false - }) -} From bfcd8d3be8e82e4e80bbd1e39342a00db2527a73 Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 2 Feb 2023 18:18:48 +0800 Subject: [PATCH 5/5] chore: update comment Co-authored-by: Simen Bekkhus --- lib/utils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 052a5399..d1f857e4 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,13 +12,13 @@ function filterPseudoHeaders (headers) { return dest } -// http required the header to be encoded with latin1 -// undici will convert the latin1 buffer back to utf8 +// http requires the header to be encoded with latin1 +// undici will convert the latin1 buffer to utf8 // https://github.com/nodejs/undici/blob/2b260c997ad4efe4ed2064b264b4b546a59e7a67/lib/core/util.js#L216-L229 -// after chaining, the header will converted in wrong byte +// after chaining, the header will be serialised using wrong encoding // Buffer.from('', 'latin1').toString('utf8') applied // -// in order to presist the encoding, always encode it +// in order to persist the encoding, always encode it // back to latin1 function patchUndiciHeaders (headers) { const headersKeys = Object.keys(headers)