Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent query parameter handling/encoding between server and client #1779

Open
zlondrej opened this issue Sep 2, 2024 · 0 comments
Open

Comments

@zlondrej
Copy link

zlondrej commented Sep 2, 2024

The query parameter URL-encoding has undocumented behavior. I was trying to write some HasServer and HasClient instances working with Query and oh boy, what it took me a while to get out of that rabbit hole.

servant-server is based on wai which uses parseQuery (https://github.com/yesodweb/wai/blob/master/warp/Network/Wai/Handler/Warp/Request.hs#L108). This will always properly decode both query key and value.

However the servant-client introduced a risky approach to client parameter encoding when #1432 was merged (and doesn't even seems necessary). Now only query keys are implicitly encoded. QueryParam' and QueryParams seem to be aware of this (after some bugfixes), and explicitly encode the value using encodeQueryParam (QueryParam' and QueryParams instances).

However since then, few more query handling types were introduced and they seem to be unaware of the fact. Namely QueryString (the main issue here is undocumented behavior - both server and client-vise, developers won't know what to expect while using it) and DeepQuery (no URL-encoding is performed here either).

There have been few issues/PRs related to URL-encoding reported in the past:

Now there's a proposed solution based on fizruk/http-api-data#120 (toEncodedQueryParam) that is attempting somewhat fixing the symptoms:

But the source of the issue is still there. There's nothing saying whether the requestQueryString :: Query in the client's `Request' should or should not be URL-encoded (be it documentation or the types).


Proposal

I think the surefire solution would be to switch to PartialEscapeQuery for servant-client, which forces selection of URL-encoding on the type level and encoding it using renderQueryPartialEscape.

Such change will introduce an API incompatibility with older versions, but that's actually for the best as it will force everyone to review their implementations. Also the migration should be quite easy, wrapping any existing values into list of QE's or QN's (EscapeItem's constructors) and replacing Nothing with empty list. Introducting some smart constructors can make it even easier.

And for servant-server documenting that QueryString returns URL-decoded keys and values would also be a good idea. Not sure whether anything more can be done. Wrapping value in something like newtype UrlDecodedQueryValue = QD ByteString (names are just example, not a suggestion) to give the info on the type level seems to be excessive. Maybe a documented type alias like type DecodedQuery = Query would not hurt.

Any reservations/feedback before I start producing a PR?

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

No branches or pull requests

1 participant