diff options
Diffstat (limited to 'lib/querystring.js')
-rw-r--r-- | lib/querystring.js | 246 |
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; |