From 6f9cb982a147c0d0e3165b20b33f299692ac63b1 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 21 Feb 2023 12:09:03 +0100 Subject: [PATCH] http: unify header treatment PR-URL: https://github.com/nodejs/node/pull/46528 Fixes: https://github.com/nodejs/node/issues/46395 Reviewed-By: Paolo Insogna Reviewed-By: Matteo Collina Reviewed-By: Robert Nagy --- lib/_http_outgoing.js | 13 +++++ .../test-http-server-non-utf8-header.js | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 test/parallel/test-http-server-non-utf8-header.js diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index e362ea62472e45..cc13e2bc3982b8 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -100,6 +100,10 @@ function isCookieField(s) { return s.length === 6 && StringPrototypeToLowerCase(s) === 'cookie'; } +function isContentDispositionField(s) { + return s.length === 19 && StringPrototypeToLowerCase(s) === 'content-disposition'; +} + function OutgoingMessage() { Stream.call(this); @@ -569,6 +573,15 @@ function _storeHeader(firstLine, headers) { function processHeader(self, state, key, value, validate) { if (validate) validateHeaderName(key); + + // If key is content-disposition and there is content-length + // encode the value in latin1 + // https://www.rfc-editor.org/rfc/rfc6266#section-4.3 + // Refs: https://github.com/nodejs/node/pull/46528 + if (isContentDispositionField(key) && self._contentLength) { + value = Buffer.from(value, 'latin1'); + } + if (ArrayIsArray(value)) { if ( (value.length < 2 || !isCookieField(key)) && diff --git a/test/parallel/test-http-server-non-utf8-header.js b/test/parallel/test-http-server-non-utf8-header.js new file mode 100644 index 00000000000000..331965ae38d0f8 --- /dev/null +++ b/test/parallel/test-http-server-non-utf8-header.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const nonUtf8Header = 'bår'; +const nonUtf8ToLatin1 = Buffer.from(nonUtf8Header).toString('latin1'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'Content-Length', '5', + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +}