Problem
Uri cannot round-trip a percent-encoded path. Parsing decodes %XX triplets in path segments (UriParser), and UriRendering.renderPath re-encodes segments with the pchar-base keep-set ("pchar without percent"). Two consequences:
- A percent-encoded pchar character can never be rendered back in encoded form:
%2B becomes +, %3D becomes =, etc. (sub-delims, : and @ are kept raw by the renderer).
- A literal
% in a Path segment is always re-encoded to %25, so constructing Uri.Path from the still-encoded string does not work either.
Uri("https://b.s3.amazonaws.com/a%2Bb%20c%3Dx/d?x=%2B7&y=a+b").toString
// https://b.s3.amazonaws.com/a+b%20c=x/d?x=%2B7&y=a+b
The query is preserved verbatim via rawQueryString; the path is not. There is no raw-path escape hatch in the Uri model or the client request renderer, so no downstream construction can produce a byte-identical request target.
Why it matters
pekko-connectors aws-spi-pekko-http implements the AWS SDK async HTTP client SPI with Http().singleRequest. The SDK signs the request (SigV4) with the URI-encoded path it built, e.g. /a%2Bb for S3 object key a+b; the transport must send that path byte-identically. After the Uri round trip the wire path is /a+b, and S3 — which verifies the signature against the path as received, without normalization — rejects the request with SignatureDoesNotMatch. Any S3 object key containing sub-delims characters (+ = ! ( ) , ; ' & $ @ : *) is affected. The same applies to any proxy/pass-through use case that must not alter the request target.
Proposal
Add a way to send a request whose path renders exactly as provided. Options, roughly in order of preference:
- A raw
Uri.Path representation (e.g. a segment/path variant flagged as already-encoded) that renderPath emits verbatim.
- An
HttpRequest attribute carrying a pre-rendered request target that the client renderer honors (analogous in spirit to the server-side raw-request-uri-header).
With either, aws-spi-pekko-http can pass SdkHttpRequest.encodedPath() through untouched.
References
UriParser percent-decodes matched strings when a % was seen.
Uri.scala renderPath/encode: keep-set is pchar-base (excludes %), so encoded pchars are irrecoverable and literal % double-encodes.
Problem
Uricannot round-trip a percent-encoded path. Parsing decodes%XXtriplets in path segments (UriParser), andUriRendering.renderPathre-encodes segments with thepchar-basekeep-set ("pchar without percent"). Two consequences:%2Bbecomes+,%3Dbecomes=, etc. (sub-delims,:and@are kept raw by the renderer).%in aPathsegment is always re-encoded to%25, so constructingUri.Pathfrom the still-encoded string does not work either.The query is preserved verbatim via
rawQueryString; the path is not. There is no raw-path escape hatch in theUrimodel or the client request renderer, so no downstream construction can produce a byte-identical request target.Why it matters
pekko-connectorsaws-spi-pekko-httpimplements the AWS SDK async HTTP client SPI withHttp().singleRequest. The SDK signs the request (SigV4) with the URI-encoded path it built, e.g./a%2Bbfor S3 object keya+b; the transport must send that path byte-identically. After theUriround trip the wire path is/a+b, and S3 — which verifies the signature against the path as received, without normalization — rejects the request withSignatureDoesNotMatch. Any S3 object key containing sub-delims characters (+ = ! ( ) , ; ' & $ @ : *) is affected. The same applies to any proxy/pass-through use case that must not alter the request target.Proposal
Add a way to send a request whose path renders exactly as provided. Options, roughly in order of preference:
Uri.Pathrepresentation (e.g. a segment/path variant flagged as already-encoded) thatrenderPathemits verbatim.HttpRequestattribute carrying a pre-rendered request target that the client renderer honors (analogous in spirit to the server-sideraw-request-uri-header).With either,
aws-spi-pekko-httpcan passSdkHttpRequest.encodedPath()through untouched.References
UriParserpercent-decodes matched strings when a%was seen.Uri.scalarenderPath/encode: keep-set ispchar-base(excludes%), so encoded pchars are irrecoverable and literal%double-encodes.