Skip to content

Public key-addressed web seam: fetchWebKey(key, request) #162

Description

@gmoon

Proposal

Promote the internal key-addressed path to the public web API:

// Caller names the S3 key; the Request contributes method (GET/HEAD) and
// conditional/Range headers. The request URL plays no role in key selection.
fetchWebKey(key: string, request: Request): Promise<Response>

fetchWeb then becomes a thin adapter over it:

fetchWeb(request) {
  return this.fetchWebKey(keyFromUrl(request), request);
}

No new behavior — this publishes a seam that already exists internally (fetchKey is private) and sharpens the v4 layering: URL→key mapping is adapter business; the core primitive is "stream this key with these request semantics."

Motivation

Today the public web API derives the key exclusively from the request URL path. Any consumer whose URL→key mapping is not identity has to fabricate a synthetic Request to smuggle the key through the URL:

// e.g. an app mounting a bucket under /files/*
const key = url.pathname.replace(/^\/files\//, '');  // derive key
url.pathname = `/${key}`;                             // re-embed in a URL
return proxy.fetchWeb(new Request(url, c.req.raw));    // s3proxy re-parses key from URL

The key round-trips key → URL-encode → Request → URL-parse → key. Three costs, ascending:

  1. Waste — a synthetic URL + Request per request, and a duplicated parse.
  2. Split ownership — "how a path becomes a key" lives in two places that must agree.
  3. Encoding hazard (the real one) — the key is smuggled through URL encoding/decoding. For keys containing %, +, spaces, or the specialCharacters!... fixture already in the test bucket, the consumer's encoder and s3proxy's decoder must agree exactly, or the wrong object is served / a valid key 404s. Identity mapping never hits this; every rewriting consumer does.

Consumer classes that need non-identity mapping:

  • Sub-path mounts — serving a bucket under /files/*, /assets/* inside an existing app
  • Multi-tenant prefix injection — request identity resolves to a server-side key prefix (tenants/<org>/…) the client never sees; a security property, since the client shouldn't dictate the raw key
  • Key rewriting — default documents, versioned keys, vanity paths

Design notes

  • Alternative shape: an options override on the existing method — fetchWeb(request, { key }). Smaller surface; the separate method is arguably more honest (URL ignored vs. URL authoritative), maintainer's call.
  • Contract to document: key is the raw S3 key (not URL-encoded); method must be GET/HEAD; Range/If-* headers flow through unchanged.
  • Conformance case: fetchWebKey(weirdKey, req) must behave identically to a correctly-encoded fetchWeb for the special-characters fixture.

Estimated scope: ~10 lines plus tests. The cost is permanent public API surface on a deliberately minimal package — hence framing as promoting the existing internal seam, not adding a feature.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions