summaryrefslogtreecommitdiff
path: root/lib/internal/url.js
diff options
context:
space:
mode:
authorTimothy Gu <timothygu99@gmail.com>2017-03-14 21:01:04 -0700
committerTimothy Gu <timothygu99@gmail.com>2017-03-21 17:27:11 -0700
commitc515a985ea77495986391dada7baaffcc144b197 (patch)
treed0b5b6cf574b634dc45f0fcefef1cbaf0a08d829 /lib/internal/url.js
parentc98a8022b77234f6257359ecc361fcb9248b2d42 (diff)
downloadnode-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.js115
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.