From 4e7a5a38bb0332662250cee1b05fa97fc70a62d1 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 12 Jan 2021 19:57:15 -0800 Subject: [PATCH] [Fix] `stringify`: do not encode parens for RFC1738 Fixes #390 --- lib/stringify.js | 10 +++++++--- lib/utils.js | 5 ++++- test/stringify.js | 7 ++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/stringify.js b/lib/stringify.js index da5380a8..f46bb0e1 100644 --- a/lib/stringify.js +++ b/lib/stringify.js @@ -65,6 +65,7 @@ var stringify = function stringify( sort, allowDots, serializeDate, + format, formatter, encodeValuesOnly, charset @@ -85,7 +86,7 @@ var stringify = function stringify( if (obj === null) { if (strictNullHandling) { - return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix; + return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix; } obj = ''; @@ -93,8 +94,8 @@ var stringify = function stringify( if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) { if (encoder) { - var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key'); - return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))]; + var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format); + return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))]; } return [formatter(prefix) + '=' + formatter(String(obj))]; } @@ -139,6 +140,7 @@ var stringify = function stringify( sort, allowDots, serializeDate, + format, formatter, encodeValuesOnly, charset @@ -186,6 +188,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) { encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, filter: filter, + format: format, formatter: formatter, serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, @@ -251,6 +254,7 @@ module.exports = function (object, opts) { options.sort, options.allowDots, options.serializeDate, + options.format, options.formatter, options.encodeValuesOnly, options.charset diff --git a/lib/utils.js b/lib/utils.js index 98ab1937..4ad6ea27 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,7 @@ 'use strict'; +var formats = require('./formats'); + var has = Object.prototype.hasOwnProperty; var isArray = Array.isArray; @@ -120,7 +122,7 @@ var decode = function (str, decoder, charset) { } }; -var encode = function encode(str, defaultEncoder, charset) { +var encode = function encode(str, defaultEncoder, charset, kind, format) { // This code was originally written by Brian White (mscdex) for the io.js core querystring library. // It has been adapted here for stricter adherence to RFC 3986 if (str.length === 0) { @@ -152,6 +154,7 @@ var encode = function encode(str, defaultEncoder, charset) { || (c >= 0x30 && c <= 0x39) // 0-9 || (c >= 0x41 && c <= 0x5A) // a-z || (c >= 0x61 && c <= 0x7A) // A-Z + || (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( ) ) { out += string.charAt(i); continue; diff --git a/test/stringify.js b/test/stringify.js index cbd90b2c..f4a1daa7 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -631,10 +631,13 @@ test('stringify()', function (t) { st.end(); }); - t.test('RFC 1738 spaces serialization', function (st) { + t.test('RFC 1738 serialization', function (st) { st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c'); st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d'); st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b'); + + st.equal(qs.stringify({ 'foo(ref)': 'bar' }, { format: qs.formats.RFC1738 }), 'foo(ref)=bar'); + st.end(); }); @@ -642,12 +645,14 @@ test('stringify()', function (t) { st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c'); st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d'); st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b'); + st.end(); }); t.test('Backward compatibility to RFC 3986', function (st) { st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c'); st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b'); + st.end(); });