-
-
Notifications
You must be signed in to change notification settings - Fork 153
URL handling for escaped characters inconsistent between routes and requests #414
Description
Dream seems to handle URL encoding inconsistently between route registration and request handling, which caused me a bunch of confusing and debugging, and I wonder if it could be made more consistent, or am I just using things wrong?
Current Behaviour
When registering routes with Dream, the route patterns must contain unencoded (raw UTF-8) characters:
Dream.get "/photos/södermalm_pride/" handler (* This works *)
Dream.get "/photos/s%C3%B6dermalm_pride/" handler (* This does NOT match *)However, when Dream logs incoming requests or when inspecting Dream.target, the paths are shown in their escaped form:
29.11.25 09:23:47.678 dream.logger INFO REQ 1 GET /photos/s%C3%B6dermalm_pride/ 127.0.0.1:52990 fd 8 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Safari/605.1.15
And the request here was from a link that I generated with the escaped for, so it came from:
<a href="/photos/s%C3%B6dermalm_pride/">
<img loading="lazy" src="/photos/s%C3%B6dermalm_pride/thumbnail.jpg" srcset="/photos/s%C3%B6dermalm_pride/thumbnail@2x.jpg 2x,
/photos/s%C3%B6dermalm_pride/thumbnail.jpg 1x" title="Södermalm pride" width="271" height="350" alt="Ett foto av en pride flagga över en väg på Södermalm på en solig dag.">
</a>It looks to me that Dream is unencoding the URL, despite the fact I'm being consistent in both the links my code generates being escaped and that's what I register for the route.
Expected Behaviour
I'd expect that I'd use the escaped form generally everywhere, given that this is what URLs require as I understand it.
I store URLs using Uri.t and render them with Uri.to_string, but now when I add routes I have to wrap them with Uri.pct_decode before I pass them to Dream routes.
Steps to repro
let () =
Dream.run
@@ Dream.logger
@@ Dream.router [
Dream.get "/photos/södermalm_pride/" (fun _ ->
Dream.html "Unencoded route works!");
Dream.get "/photos/s%C3%B6dermalm_pride/" (fun _ ->
Dream.html "Encoded route works!");
Dream.get "/**" (fun request ->
Dream.log "Path received: %s" (Dream.target request);
Dream.empty `Not_Found);
]When requesting /photos/södermalm_pride/:
- The first route (unencoded) matches (no matter which order I put the escaped and unescaped routes)
- Logs show:
INFO REQ 1 GET /photos/s%C3%B6dermalm_pride/ 127.0.0.1:53018