Skip to content

Client option to percent-encode additional characters (e.g. .) in path parameters #120

Description

@tanmaykm

Problem

Some HTTP servers cannot route a path parameter that contains a literal . unless the client sends it percent-encoded as %2E. The common case is Rails: its default dynamic-segment pattern is [^/.?]+, so a . ends the segment and the remainder is parsed as a format suffix. A route like

resources :customers, param: :external_id

matches GET /customers/acme.example.com-42 as external_id = "acme" with format example.com-42 and returns 404, while GET /customers/acme%2Eexample%2Ecom-42 matches correctly, because Rails routes on the raw path before decoding. Identifiers built from hostnames, emails or dotted tenant names hit this routinely, and the API owner is often not in a position to change the server's routing.

Current behaviour in 1.x

There is no supported way to send %2E for a path parameter:

  • _escapeuri follows RFC 3986 and leaves the unreserved set A-Z a-z 0-9 - _ . ~ untouched, so . always goes on the wire as ..
  • Pre-encoding the value ("acme%2Eexample%2Ecom-42") is double-encoded to %252E.
  • allowReserved: true on the parameter would pass a pre-encoded %2E through, but it is the wrong tool twice over: a 3.1 document that declares it on a path parameter fails document validation at load time (3.1 scopes the field to in: query), and it also stops escaping every reserved character, which is far broader than wanted.
  • 0.2 exposed pre_request_hook, which is how this was typically handled there. 1.x deliberately has no request hook.

Minimal example

openapi: 3.1.0
info: { title: Example, version: 1.0.0 }
paths:
  /customers/{external_id}:
    get:
      operationId: getCustomer
      parameters:
        - name: external_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { description: ok }
client = Example.Client("https://api.example.com")
Example.getcustomer("acme.example.com-42"; client)
# sends GET /customers/acme.example.com-42  -> 404 on a Rails server
# needed: GET /customers/acme%2Eexample%2Ecom-42

Proposal

A client-level option naming extra characters to percent-encode in path parameters, applied after the standard escaping:

client = Example.Client("https://api.example.com"; escape_path_chars = ".")
Example.getcustomer("acme.example.com-42"; client)
# sends GET /customers/acme%2Eexample%2Ecom-42
  • Default is empty, so existing behaviour is unchanged.
  • Applies to every path parameter of every operation, so callers cannot forget it for one route.
  • Percent-encoding an unreserved character is permitted by RFC 3986 §2.3; the encoded and unencoded forms are equivalent identifiers, so a server that decodes before routing is unaffected.
  • Declarative and narrow: no hook, no spec edit, works with 3.1 documents, and orthogonal to allowReserved.

Implementation looks small: _path_scalar (and the array/object variants that call it) would run the listed characters through %XX after _escape, reading the set from the Client.

Alternatives considered

  • allowReserved on the path parameter. Rejected by the 3.1 loader, and semantically the opposite of what is needed (less escaping, not more).
  • Caller pre-encodes the value. Double-encoded today; making the escaper pass through existing %XX triplets would silently change the meaning of legitimate values that contain a literal %.
  • A general pre-request hook. Much larger surface than the problem, and reintroduces the 0.2 pattern that 1.x removed on purpose.

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions