From e8055725bd13bdb7e58b5f7fb36412fb5c908f86 Mon Sep 17 00:00:00 2001 From: Rene Turcios <95189389+CloudCorpRecords@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:48:00 -0700 Subject: [PATCH] fix(response): preserve user-supplied content-type parameters on send res.contentType = 'text/plain; version=0.0.4' was stripped to 'text/plain' because send() split the type on ';' for formatter lookup and then used the stripped value for the outgoing Content-Type header. Keep the parameters aside for lookup, then restore them on the header. res.charSet() behavior is unchanged. Fixes restify/node-restify#1589. Signed-off-by: Rene Turcios <95189389+CloudCorpRecords@users.noreply.github.com> --- lib/response.js | 16 ++++++++++++- test/response.test.js | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index 7119bb15..452fdda3 100644 --- a/lib/response.js +++ b/lib/response.js @@ -458,7 +458,16 @@ function patch(Response) { ); } - type = type.split(';')[0]; + // Keep any user-supplied content-type parameters (e.g. + // "; version=0.0.4") so they survive on the outgoing header below. + // Only the base type is used for formatter lookup. + var contentTypeParams = ''; + var paramIndex = type.indexOf(';'); + + if (paramIndex !== -1) { + contentTypeParams = type.substring(paramIndex); + type = type.substring(0, paramIndex); + } if (!self.formatters[type] && type.indexOf('/') === -1) { type = mime.getType(type); @@ -496,6 +505,11 @@ function patch(Response) { if (self._charSet) { type = type + '; charset=' + self._charSet; + } else if (contentTypeParams) { + // Restore the parameters the user originally provided, e.g. + // res.contentType = 'text/plain; version=0.0.4' keeps the + // version parameter on the outgoing Content-Type header. + type = type + contentTypeParams; } // Update Content-Type header to the one originally set or to the diff --git a/test/response.test.js b/test/response.test.js index 8db5874b..fdb4de35 100644 --- a/test/response.test.js +++ b/test/response.test.js @@ -863,3 +863,55 @@ test(module, 'GH-1791: should send NaN as null with application/json', function( t.end(); }); }); + +test( + module, + 'GH-1589: should preserve content-type parameters set by the user', + function(t) { + SERVER.get('/content-type-params', function(req, res, next) { + res.contentType = 'text/plain; version=0.0.4; charset=utf-8'; + res.send('hello'); + return next(); + }); + + STRING_CLIENT.get('/content-type-params', function( + err, + req, + res, + data + ) { + t.ifError(err); + t.equal(data, 'hello'); + t.equal( + res.headers['content-type'], + 'text/plain; version=0.0.4; charset=utf-8' + ); + t.end(); + }); + } +); + +test( + module, + 'GH-1589: res.charSet still appends charset without duplicating params', + function(t) { + SERVER.get('/content-type-charset', function(req, res, next) { + res.contentType = 'text/plain'; + res.charSet('utf-8'); + res.send('hello'); + return next(); + }); + + STRING_CLIENT.get('/content-type-charset', function( + err, + req, + res, + data + ) { + t.ifError(err); + t.equal(data, 'hello'); + t.equal(res.headers['content-type'], 'text/plain; charset=utf-8'); + t.end(); + }); + } +);