Uri cannot preserve percent-encoded path segments, breaking AWS SigV4 in aws-spi-pekko-http
- Dominant language
- Scala
- Stars
- 196
- Forks
- 55
- Avg merge
- 4d 52m
- Merged PRs (30d)
- 74
Description
### 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.
```scala
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:
1. A raw `Uri.Path` representation (e.g. a segment/path variant flagged as already-encoded) that `renderPath` emits verbatim.
2. 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.
Contributor guide
Research direction
Start with UriParser and Uri.scala, especially renderPath and encode, to trace where percent-encoded path segments are decoded or re-encoded. Then inspect the HttpRequest renderer and aws-spi-pekko-http's use of SdkHttpRequest. Done means the SDK's encodedPath can reach the wire byte-identically, including encoded pchar characters and literal percent signs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, scala
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100