Skip to content

fix: always encode = in query key and values#164

Closed
undermoonn wants to merge 1 commit intounjs:mainfrom
undermoonn:fix/encoding
Closed

fix: always encode = in query key and values#164
undermoonn wants to merge 1 commit intounjs:mainfrom
undermoonn:fix/encoding

Conversation

@undermoonn
Copy link

@undermoonn undermoonn commented Aug 16, 2023

🔗 Linked issue

resolves #162

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

URL ponyfill output is different from encoding query value = with new URL

  • new URL
const url = new URL('https://unjs.io/')
url.searchParams.set('foo', 'a=1&b=1')

console.log(url.toString()) // output -> `https://unjs.io/?foo=a%3D1%26b%3D1`
  • withQuery
import { withQuery } from 'ufo'

const url = withQuery('https://unjs.io/', { foo: 'a=1&b=1' })

console.log(url) // output -> `https://unjs.io/?foo=a=1%26b=1`

https://url.spec.whatwg.org/#concept-urlencoded-serializer

Let name be the result of running percent-encode after encoding with encoding, tuple’s name, the application/x-www-form-urlencoded percent-encode set, and true.

Let value be the result of running percent-encode after encoding with encoding, tuple’s value, the application/x-www-form-urlencoded percent-encode set, and true.

From the URL serializing document, encodeQueryValue should be same as encodeQueryKey

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@pi0 pi0 changed the title fix: URL ponyfill of query value encoding fix: always encode = in query key and values Aug 24, 2023
*/
export function encodeQueryKey(text: string | number): string {
return encodeQueryValue(text).replace(EQUAL_RE, "%3D");
return encodeQueryValue(text);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @posva was there any specific reason = wasn't being encoded before? (This was taken from vue-router utils i guess)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember exactly but I think that it's because it wasn't necessary to encode the = within a value since it already appeared after a key and the parser won't be expecting one until it gets into the state of reading a key. In other words, ?q=value=with=&b=other is parsable as the = only has a meaning when parsing a key

Copy link
Member

@pi0 pi0 Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it is parsable and valid without encoding.. However URL sppec encoded like this. I think it would be nice if we could preserve a consistent behavior between ufo, vue-router (and URL) spec if you are okay I can make a PR to vue-router as well to keep them in sync and avoid mistmatching behavior between different places (as we use ufo in nuxt)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will give it a look in 2-3 weeks. I did a lot of testing in different browsers when writing that code and I imagine that if I added 2 functions instead of one, there must be a reason 😆

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the discrepancy:

  • When directly entering some characters in a URL, the browser encodes some of them. The = within a query value, isn't encoded e.g. reads as = in location.href.
  • URLSearchParams() encodes =

Depending on the browsers, they display the value on the URL as encoded or not, therefore I tried, as much as possible, to display the original characters across browsers when they didn't need encoding. Also, Vue Router supports some browsers that do not have full URLSearchParams() support.

BTW I don't think the URL spec specifies that = must be encoded in query values.

When I did the research on what was supported and what the spec said, it turned out encodeURIComponent() was doing too much in some scenarios and that's why it's not used in vue router. Unless there is a bug, changing this behavior in vue router would be tiny breaking change, so it's better to keep it the way it is, being more readable across browsers.

Cheers

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for detailed feedback. Is there a list of officially supported browsers by vue-router btw? And do you think it would be an issue if ufo (in a major version) choose to always encode things like = with URLSearchParams compatibility? (basically: does vue-router guarantees to decode encoded values like = in query even if it doesn't encodes it?)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a list, it's the same as Vue 3 😅

It's better to try by yourself, there might be problems with redirects and reloads. However, if I were you, since there is currently no problem with the encoding, I wouldn't change it in ufo either. The opened issue is fundamentally wrong, specifically, this statement is not true:

When we encode a query not a url. we should use encodeURIComponent not encodeURI.

The spec specifies encode sets, not functions to use. Since the issue lacks a real-world scenario with a problem, I would personally wait to see if this fixes anything, if not, it's better to keep the current behavior for the reasons I mentioned in my previous comment

Copy link
Member

@pi0 pi0 Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is, for the next major version of UFO I am thinking of relying on native URL and URLSearchParams for encoding and parsing since it has good coverage in platforms (I guess at least all Vue 3 supported targets -- have to double check) and as a result, we might end up with compatibility issues if didn't anticipate edge cases.

Thanks for feedbacks anyway, if we want to do this, i will certainly reach you out for more through tests across ecosystem packages.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I wanted to rely on URL and URLSearchParams but there were small bugs in some old versions of browsers that forced me to not use them yet. I do hope to use them in the future major version of Vue Router though!

@codecov
Copy link

codecov bot commented Aug 24, 2023

Codecov Report

Merging #164 (9442bab) into main (94347b0) will increase coverage by 0.00%.
Report is 2 commits behind head on main.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##             main     #164   +/-   ##
=======================================
  Coverage   94.91%   94.92%           
=======================================
  Files           7        7           
  Lines         846      847    +1     
  Branches      177      177           
=======================================
+ Hits          803      804    +1     
  Misses         43       43           
Files Changed Coverage Δ
src/encoding.ts 100.00% <100.00%> (ø)

@pi0 pi0 mentioned this pull request Aug 24, 2023
8 tasks
@pi0
Copy link
Member

pi0 commented Feb 5, 2024

Following up discussion, it seems we are not going to change behavior at least until next major version that likely will use URL and this behavior change by default.

If you spot any bugs with this current behavior we can certainly reconsider. and thanks for PR @undermoonn anyway ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wrong = encoding in query values

3 participants