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(); + })); + }); + })); +}