[Seaside] Handling of $+ in URLs
Sven Van Caekenberghe
sven at stfx.eu
Sat Feb 15 22:21:42 UTC 2014
Johan Brichau reported an issue a couple of days ago concerning the handling of $+ in ZnUrl (Pharo 3's URL class) and in Seaside's WAUrl. #bleedingEdge of Zinc HTTP Components fixes the issue, as far as I can see. I want to explain the problem and the solution.
Before october 24 of last year, ZnUrl used a 'better safe than sorry' safe set when doing percent encoding of unsafe characters. However, the URL spec defines different allowed characters per URL part. This behaviour was then added to Zinc-Resource-Meta-Core, ZnUrl's package.
Soon after that a discussion with Jan van de Sandt let to a first small change: since ZnUrl interprets the query part of a URL as key-value pairs, it is necessary to treat $= and $& as unsafe, even though they are not according to the URL spec (which doesn't concern itself with how the query part is interpreted).
All that time, $+ kept on being interpreted as a space, independent of the safe set. As Johan reported, this conflicted with $+ being a safe character. Which eventually let to the functional problem of not being able to enter a + in an input field, in Seaside.
Why only in Seaside ? Because ZnZincServerAdaptor>>#requestUrlFor: was implemented by printing the interpreted incoming ZnUrl and parsing it again. There, the escaping of $+ disappeared and it became an unintended space.
This situation is now fixed by
Changes to ZnPercentEncoder:
- adding an #decodePlusAsSpace boolean option
Changes to ZnResourceMetaUtils:
- #decodePercent: no longer decodes plus as space
- #decodePercentForQuery: does plus as space decoding
- #queryKeyValueSafeSet no longer includes $+
- #parseQueryFrom: not uses #decodePercentForQuery:
Added ZnDefaultServerDelegate>>#formTest1: to test simple form submit encoding handling
Modify ZnZincServerAdaptor>>#requestUrlFor: to build a WAUrl explicitely from the interpreted parts of the incoming ZnUrl instead of going via printing and parsing
Adding new unit tests
I think WAUrl should best be changed as well, but that is not my call.
In code, this summarises the implemented behaviour:
"While percent decoding, a + is translated as a space only in the context of
application/x-www-form-urlencoded get/post requests:
ZnUrl interprets its query part as key value pairs where this translation is applicable,
even though strictly speaking + (and =, &) are plain unreserved characters in the query"
"$+ is not special in the path part of the URL and it remains itself"
assert: 'http://localhost/foo+bar' asZnUrl firstPathSegment
assert: 'http://localhost/foo+bar' asZnUrl printString
"$+ gets decoded to space in the interpreted query part of the URL,
and becomes an encoded space if needed"
assert: ('http://localhost/test?q=foo+bar' asZnUrl queryAt: #q)
equals: 'foo bar'.
assert: 'http://localhost/test?q=foo+bar' asZnUrl printString
"to pass $+ as $+ in a query, it has to be encoded"
assert: 'http://localhost/test?q=foo%2Bbar' asZnUrl printString
I hope this is a good and correct solution. In any case, it fixes the functional problem that $+ disappeared in WAUrlEncodingFunctionalTest - which I took over in ZnDefaultServerDelegate>>#formTest1:
Thanks Johan for the whole discussion !
More information about the seaside