Skip to content

Uri cannot preserve percent-encoded path segments, breaking AWS SigV4 in aws-spi-pekko-http #1273

Description

@pjfanning

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:

  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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions