]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(query): encode space as +
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 13 Nov 2020 10:40:33 +0000 (11:40 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 13 Nov 2020 10:40:33 +0000 (11:40 +0100)
Close #561

__tests__/encoding.spec.ts
__tests__/parseQuery.spec.ts
src/encoding.ts
src/query.ts

index 8fd7ab992d2d0865b8d8db553d551f1f94d0e540..d299ac3a81a0abf3c0d945d8d059b5c56463c715 100644 (file)
@@ -58,9 +58,9 @@ describe('Encoding', () => {
   })
 
   describe('query params', () => {
-    const safePerSpec = "!$'*+,:;@[]_|?/{}^()`"
-    const toEncodeForKey = ' "<>#&='
-    const toEncodeForValue = ' "<>#&'
+    const safePerSpec = "!$'*,:;@[]_|?/{}^()`"
+    const toEncodeForKey = '"<>#&='
+    const toEncodeForValue = '"<>#&'
     const encodedToEncodeForKey = toEncodeForKey
       .split('')
       .map(c => {
@@ -100,6 +100,16 @@ describe('Encoding', () => {
       expect(encodeQueryKey(toEncodeForKey)).toBe(encodedToEncodeForKey)
       expect(encodeQueryValue(toEncodeForValue)).toBe(encodedToEncodeForValue)
     })
+
+    it('encodes space as +', () => {
+      expect(encodeQueryKey(' ')).toBe('+')
+      expect(encodeQueryValue(' ')).toBe('+')
+    })
+
+    it('encodes +', () => {
+      expect(encodeQueryKey('+')).toBe('%2B')
+      expect(encodeQueryValue('+')).toBe('%2B')
+    })
   })
 
   describe('hash', () => {
index 11542cb9919b1f09af1dbf7680fd55e2ceec9b75..b16c86245e3309843669915f19a28dd6aea649ce 100644 (file)
@@ -65,6 +65,18 @@ describe('parseQuery', () => {
     })
   })
 
+  it('decodes the + as space', () => {
+    expect(parseQuery('a+b=c+d')).toEqual({
+      'a b': 'c d',
+    })
+  })
+
+  it('decodes the encoded + as +', () => {
+    expect(parseQuery('a%2Bb=c%2Bd')).toEqual({
+      'a+b': 'c+d',
+    })
+  })
+
   // this is for browsers like IE that allow invalid characters
   it('keep invalid values as is', () => {
     expect(parseQuery('e=%&e=%25')).toEqual({
index 8d1a66028a0870b39918c1c35d2a9e0636143d4d..47964c47178426894bf6539197f84180f59bb2ff 100644 (file)
@@ -23,6 +23,7 @@ const AMPERSAND_RE = /&/g // %26
 const SLASH_RE = /\//g // %2F
 const EQUAL_RE = /=/g // %3D
 const IM_RE = /\?/g // %3F
+const PLUS_RE = /\+/g // %2B
 /**
  * NOTE: It's not clear to me if we should encode the + symbol in queries, it
  * seems to be less flexible than not doing so and I can't find out the legacy
@@ -37,7 +38,6 @@ const IM_RE = /\?/g // %3F
  * - https://url.spec.whatwg.org/#urlencoded-parsing
  * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
  */
-// const PLUS_RE = /\+/g // %3F
 
 const ENC_BRACKET_OPEN_RE = /%5B/g // [
 const ENC_BRACKET_CLOSE_RE = /%5D/g // ]
@@ -46,6 +46,7 @@ const ENC_BACKTICK_RE = /%60/g // `
 const ENC_CURLY_OPEN_RE = /%7B/g // {
 const ENC_PIPE_RE = /%7C/g // |
 const ENC_CURLY_CLOSE_RE = /%7D/g // }
+const ENC_SPACE_RE = /%20/g // }
 
 /**
  * Encode characters that need to be encoded on the path, search and hash
@@ -83,13 +84,18 @@ export function encodeHash(text: string): string {
  * @returns encoded string
  */
 export function encodeQueryValue(text: string | number): string {
-  return commonEncode(text)
-    .replace(HASH_RE, '%23')
-    .replace(AMPERSAND_RE, '%26')
-    .replace(ENC_BACKTICK_RE, '`')
-    .replace(ENC_CURLY_OPEN_RE, '{')
-    .replace(ENC_CURLY_CLOSE_RE, '}')
-    .replace(ENC_CARET_RE, '^')
+  return (
+    commonEncode(text)
+      // Encode the space as +, encode the + to differentiate it from the space
+      .replace(PLUS_RE, '%2B')
+      .replace(ENC_SPACE_RE, '+')
+      .replace(HASH_RE, '%23')
+      .replace(AMPERSAND_RE, '%26')
+      .replace(ENC_BACKTICK_RE, '`')
+      .replace(ENC_CURLY_OPEN_RE, '{')
+      .replace(ENC_CURLY_CLOSE_RE, '}')
+      .replace(ENC_CARET_RE, '^')
+  )
 }
 
 /**
index b8a1c02d7019626dac8d0c209e23ac03bf9acf2b..e7903c4bf2728b8012b104552b861042a4d471b4 100644 (file)
@@ -50,7 +50,9 @@ export function parseQuery(search: string): LocationQuery {
   const hasLeadingIM = search[0] === '?'
   const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&')
   for (let i = 0; i < searchParams.length; ++i) {
-    const searchParam = searchParams[i]
+    // pre decode the + into space
+    // FIXME: can't import PLUS_RE because it becomes a different regex ???
+    const searchParam = searchParams[i].replace(/\+/g, ' ')
     // allow the = character
     let eqPos = searchParam.indexOf('=')
     let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos))