Repository navigation
Conversation
|
@parker89 Thank you for bringing the problem to our attention and proposing a fix. This particular piece of functionality was introduced in order to tighten conformance to the HTTP spec but it is of little practical use, actually. I now regret having introduced it in the first place. I would like to approach this problem from two angles: (1) see if this feature could be made context dependent (applicable to BASIC and DIGEST auth only) (2) optimization of the path extraction code as you suggested. I. however, would really like to avoid having to have another path normalization routine just for this specific case. I would like to see if there another way of solving the problem. Please bear with me. |
|
Thanks for picking this up so quickly, closing in favor of #915. Two follow-ups, if useful:
|
RequestSupport#extractPathPrefixruns on every request throughProtocolExec,AsyncProtocolExecandRequestAuthCache, whether or not authentication is configured. It builds aURIBuilderfrom the whole request target, so every query parameter gets percent-decoded only to be thrown away byclearParameters(). Then the path is parsed byjava.net.URI, decoded segment by segment and re-encoded, even when there is nothing to normalize.In our production profiles this method showed up in almost every service using the classic client, at a median of about 0.6% CPU and 1.2% of allocation per service and up to about 6.6% CPU / 11.6% allocation for services that send long query strings.
This change:
?or#, so the query and fragment are never parsed/, unreserved characters only, no empty or dot segments), which is the common caseURIBuildernormalization on the path alone, falling back to the full request target if the path by itself isn't a valid URI reference (e.g.//?x)The result is the same as before for every request target that is a valid URI, including
%2F, non-normalized percent encoding, dot segments, empty segments and;parameters. The existing assertions are unchanged. I added a randomized test that compares the new code against the previous implementation over a mix of those cases, plus explicit cases for queries with encoded characters, fragments, trailing slashes and encoded slashes.One intentional difference: when the path is valid but the query or fragment has characters
java.net.URIrejects (e.g./a/b?q=x y), the old code returned the whole request target, query included, as the prefix. Now it returns/a/.JMH, JDK 21, average time and
gc.alloc.rate.norm:%2Fin the path (still usesURIBuilder)Happy to share the benchmark if useful, I didn't want to add a JMH module just for this.
The commit also applies cleanly to 5.6.x and passes there against httpcore 5.4.4, so it would be great to get it backported for the next 5.6.x release.