summaryrefslogtreecommitdiff
path: root/lib/querystring.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/querystring.js')
-rw-r--r--lib/querystring.js246
1 files changed, 134 insertions, 112 deletions
diff --git a/lib/querystring.js b/lib/querystring.js
index 9b4ca27640..32ba307120 100644
--- a/lib/querystring.js
+++ b/lib/querystring.js
@@ -250,6 +250,25 @@ function stringify(obj, sep, eq, options) {
return '';
}
+const isHexTable = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ...
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // ... 256
+];
+
function charCodes(str) {
if (str.length === 0) return [];
if (str.length === 1) return [str.charCodeAt(0)];
@@ -292,7 +311,6 @@ function parse(qs, sep, eq, options) {
const customDecode = (decode !== qsUnescape);
const keys = [];
- var posIdx = 0;
var lastPos = 0;
var sepIdx = 0;
var eqIdx = 0;
@@ -300,6 +318,7 @@ function parse(qs, sep, eq, options) {
var value = '';
var keyEncoded = customDecode;
var valEncoded = customDecode;
+ const plusChar = (customDecode ? '%20' : ' ');
var encodeCheck = 0;
for (var i = 0; i < qs.length; ++i) {
const code = qs.charCodeAt(i);
@@ -310,142 +329,145 @@ function parse(qs, sep, eq, options) {
// Key/value pair separator match!
const end = i - sepIdx + 1;
if (eqIdx < eqLen) {
- // If we didn't find the key/value separator, treat the substring as
- // part of the key instead of the value
- if (lastPos < end)
+ // We didn't find the (entire) key/value separator
+ if (lastPos < end) {
+ // Treat the substring as part of the key instead of the value
key += qs.slice(lastPos, end);
- } else if (lastPos < end)
- value += qs.slice(lastPos, end);
- if (keyEncoded)
- key = decodeStr(key, decode);
- if (valEncoded)
- value = decodeStr(value, decode);
-
- if (key || value || lastPos - posIdx > sepLen || i === 0) {
- // Use a key array lookup instead of using hasOwnProperty(), which is
- // slower
- if (keys.indexOf(key) === -1) {
- obj[key] = value;
- keys[keys.length] = key;
+ if (keyEncoded)
+ key = decodeStr(key, decode);
} else {
- const curValue = obj[key] || '';
- // A simple Array-specific property check is enough here to
- // distinguish from a string value and is faster and still safe
- // since we are generating all of the values being assigned.
- if (curValue.pop)
- curValue[curValue.length] = value;
- else if (curValue)
- obj[key] = [curValue, value];
+ // We saw an empty substring between separators
+ if (--pairs === 0)
+ return obj;
+ lastPos = i + 1;
+ sepIdx = eqIdx = 0;
+ continue;
+ }
+ } else {
+ if (lastPos < end) {
+ value += qs.slice(lastPos, end);
+ if (valEncoded)
+ value = decodeStr(value, decode);
}
- } else if (i === 1) {
- // A pair with repeated sep could be added into obj in the first loop
- // and it should be deleted
- delete obj[key];
+ if (keyEncoded)
+ key = decodeStr(key, decode);
+ }
+
+ // Use a key array lookup instead of using hasOwnProperty(), which is
+ // slower
+ if (keys.indexOf(key) === -1) {
+ obj[key] = value;
+ keys[keys.length] = key;
+ } else {
+ const curValue = obj[key];
+ // A simple Array-specific property check is enough here to
+ // distinguish from a string value and is faster and still safe
+ // since we are generating all of the values being assigned.
+ if (curValue.pop)
+ curValue[curValue.length] = value;
+ else
+ obj[key] = [curValue, value];
}
if (--pairs === 0)
- break;
+ return obj;
keyEncoded = valEncoded = customDecode;
- encodeCheck = 0;
key = value = '';
- posIdx = lastPos;
+ encodeCheck = 0;
lastPos = i + 1;
sepIdx = eqIdx = 0;
}
- continue;
} else {
sepIdx = 0;
- if (!valEncoded) {
- // 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 &&
- ((code >= 48/*0*/ && code <= 57/*9*/) ||
- (code >= 65/*A*/ && code <= 70/*F*/) ||
- (code >= 97/*a*/ && code <= 102/*f*/))) {
- if (++encodeCheck === 3)
- valEncoded = true;
+ // Try matching key/value separator (e.g. '=') if we haven't already
+ if (eqIdx < eqLen) {
+ if (code === eqCodes[eqIdx]) {
+ if (++eqIdx === eqLen) {
+ // Key/value separator match!
+ const end = i - eqIdx + 1;
+ if (lastPos < end)
+ key += qs.slice(lastPos, end);
+ encodeCheck = 0;
+ lastPos = i + 1;
+ }
+ continue;
} else {
- encodeCheck = 0;
+ eqIdx = 0;
+ if (!keyEncoded) {
+ // Try to match an (valid) encoded byte once to minimize unnecessary
+ // calls to string decoding functions
+ if (code === 37/*%*/) {
+ encodeCheck = 1;
+ continue;
+ } else if (encodeCheck > 0) {
+ // eslint-disable-next-line no-extra-boolean-cast
+ if (!!isHexTable[code]) {
+ if (++encodeCheck === 3)
+ keyEncoded = true;
+ continue;
+ } else {
+ encodeCheck = 0;
+ }
+ }
+ }
}
- }
- }
-
- // Try matching key/value separator (e.g. '=') if we haven't already
- if (eqIdx < eqLen) {
- if (code === eqCodes[eqIdx]) {
- if (++eqIdx === eqLen) {
- // Key/value separator match!
- const end = i - eqIdx + 1;
- if (lastPos < end)
- key += qs.slice(lastPos, end);
- encodeCheck = 0;
+ if (code === 43/*+*/) {
+ if (lastPos < i)
+ key += qs.slice(lastPos, i);
+ key += plusChar;
lastPos = i + 1;
+ continue;
}
- continue;
- } else {
- eqIdx = 0;
- if (!keyEncoded) {
- // 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 &&
- ((code >= 48/*0*/ && code <= 57/*9*/) ||
- (code >= 65/*A*/ && code <= 70/*F*/) ||
- (code >= 97/*a*/ && code <= 102/*f*/))) {
+ }
+ if (code === 43/*+*/) {
+ if (lastPos < i)
+ value += qs.slice(lastPos, i);
+ value += plusChar;
+ lastPos = i + 1;
+ } else if (!valEncoded) {
+ // 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)
- keyEncoded = true;
+ valEncoded = true;
} else {
encodeCheck = 0;
}
}
}
}
-
- if (code === 43/*+*/) {
- if (eqIdx < eqLen) {
- if (lastPos < i)
- key += qs.slice(lastPos, i);
- key += '%20';
- keyEncoded = true;
- } else {
- if (lastPos < i)
- value += qs.slice(lastPos, i);
- value += '%20';
- valEncoded = true;
- }
- lastPos = i + 1;
- }
}
- // Check if we have leftover key or value data
- if (pairs !== 0 && (lastPos < qs.length || eqIdx > 0)) {
- if (lastPos < qs.length) {
- if (eqIdx < eqLen)
- key += qs.slice(lastPos);
- else if (sepIdx < sepLen)
- value += qs.slice(lastPos);
- }
- if (keyEncoded)
- key = decodeStr(key, decode);
- if (valEncoded)
- value = decodeStr(value, decode);
- // Use a key array lookup instead of using hasOwnProperty(), which is
- // slower
- if (keys.indexOf(key) === -1) {
- obj[key] = value;
- keys[keys.length] = key;
- } else {
- const curValue = obj[key];
- // A simple Array-specific property check is enough here to
- // distinguish from a string value and is faster and still safe since
- // we are generating all of the values being assigned.
- if (curValue.pop)
- curValue[curValue.length] = value;
- else
- obj[key] = [curValue, value];
- }
+ // Deal with any leftover key or value data
+ if (lastPos < qs.length) {
+ if (eqIdx < eqLen)
+ key += qs.slice(lastPos);
+ else if (sepIdx < sepLen)
+ value += qs.slice(lastPos);
+ } else if (eqIdx === 0) {
+ // We ended on an empty substring
+ return obj;
+ }
+ if (keyEncoded)
+ key = decodeStr(key, decode);
+ if (valEncoded)
+ value = decodeStr(value, decode);
+ // Use a key array lookup instead of using hasOwnProperty(), which is slower
+ if (keys.indexOf(key) === -1) {
+ obj[key] = value;
+ keys[keys.length] = key;
+ } else {
+ const curValue = obj[key];
+ // A simple Array-specific property check is enough here to
+ // distinguish from a string value and is faster and still safe since
+ // we are generating all of the values being assigned.
+ if (curValue.pop)
+ curValue[curValue.length] = value;
+ else
+ obj[key] = [curValue, value];
}
return obj;