diff options
author | Timothy Gu <timothygu99@gmail.com> | 2017-03-14 21:01:04 -0700 |
---|---|---|
committer | Timothy Gu <timothygu99@gmail.com> | 2017-03-21 17:27:11 -0700 |
commit | c515a985ea77495986391dada7baaffcc144b197 (patch) | |
tree | d0b5b6cf574b634dc45f0fcefef1cbaf0a08d829 /lib/internal/url.js | |
parent | c98a8022b77234f6257359ecc361fcb9248b2d42 (diff) | |
download | node-new-c515a985ea77495986391dada7baaffcc144b197.tar.gz |
url: spec-compliant URLSearchParams parser
The entire `URLSearchParams` class is now fully spec-compliant.
PR-URL: https://github.com/nodejs/node/pull/11858
Fixes: https://github.com/nodejs/node/issues/10821
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
Diffstat (limited to 'lib/internal/url.js')
-rw-r--r-- | lib/internal/url.js | 115 |
1 files changed, 101 insertions, 14 deletions
diff --git a/lib/internal/url.js b/lib/internal/url.js index 91b0640b50..040771c228 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1,7 +1,11 @@ 'use strict'; const util = require('util'); -const { hexTable, StorageObject } = require('internal/querystring'); +const { + hexTable, + isHexTable, + StorageObject +} = require('internal/querystring'); const binding = process.binding('url'); const context = Symbol('context'); const cannotBeBase = Symbol('cannot-be-base'); @@ -585,23 +589,106 @@ function initSearchParams(url, init) { url[searchParams] = []; return; } - url[searchParams] = getParamsFromObject(querystring.parse(init)); + url[searchParams] = parseParams(init); } -function getParamsFromObject(obj) { - const keys = Object.keys(obj); - const values = []; - for (var i = 0; i < keys.length; i++) { - const name = keys[i]; - const value = obj[name]; - if (Array.isArray(value)) { - for (const item of value) - values.push(name, item); - } else { - values.push(name, value); +// application/x-www-form-urlencoded parser +// Ref: https://url.spec.whatwg.org/#concept-urlencoded-parser +function parseParams(qs) { + const out = []; + var pairStart = 0; + var lastPos = 0; + var seenSep = false; + var buf = ''; + var encoded = false; + var encodeCheck = 0; + var i; + for (i = 0; i < qs.length; ++i) { + const code = qs.charCodeAt(i); + + // Try matching key/value pair separator + if (code === 38/*&*/) { + if (pairStart === i) { + // We saw an empty substring between pair separators + lastPos = pairStart = i + 1; + continue; + } + + if (lastPos < i) + buf += qs.slice(lastPos, i); + if (encoded) + buf = querystring.unescape(buf); + out.push(buf); + + // If `buf` is the key, add an empty value. + if (!seenSep) + out.push(''); + + seenSep = false; + buf = ''; + encoded = false; + encodeCheck = 0; + lastPos = pairStart = i + 1; + continue; + } + + // Try matching key/value separator (e.g. '=') if we haven't already + if (!seenSep && code === 61/*=*/) { + // Key/value separator match! + if (lastPos < i) + buf += qs.slice(lastPos, i); + if (encoded) + buf = querystring.unescape(buf); + out.push(buf); + + seenSep = true; + buf = ''; + encoded = false; + encodeCheck = 0; + lastPos = i + 1; + continue; + } + + // Handle + and percent decoding. + if (code === 43/*+*/) { + if (lastPos < i) + buf += qs.slice(lastPos, i); + buf += ' '; + lastPos = i + 1; + } else if (!encoded) { + // Try to match an (valid) encoded byte (once) to minimize unnecessary + // calls to string decoding functions + if (code === 37/*%*/) { + encodeCheck = 1; + } else if (encodeCheck > 0) { + // eslint-disable-next-line no-extra-boolean-cast + if (!!isHexTable[code]) { + if (++encodeCheck === 3) + encoded = true; + } else { + encodeCheck = 0; + } + } } } - return values; + + // Deal with any leftover key or value data + + // There is a trailing &. No more processing is needed. + if (pairStart === i) + return out; + + if (lastPos < i) + buf += qs.slice(lastPos, i); + if (encoded) + buf = querystring.unescape(buf); + out.push(buf); + + // If `buf` is the key, add an empty value. + if (!seenSep) + out.push(''); + + return out; } // Adapted from querystring's implementation. |