Skip to content

execute-api: both proxy event payloads omit published members, and PayloadFormatVersion is ignored #1215

Description

@scttfrdmn

What

Both proxy event payloads omit most of their published members, the v1 event carries a member AWS does
not publish, and the PayloadFormatVersion a consumer configures is stored and never read.

1. PayloadFormatVersion is stored and ignored

apigatewayv2_types.go:66-87 decodes and persists PayloadFormatVersion on the integration. Nothing
reads it. The format is chosen instead by isV2 (apigateway_plugin.go:1605-1609), derived from
which API object the request arrived through. So an HTTP API integration explicitly configured with
PayloadFormatVersion: "1.0" — a supported, documented combination, and the one a consumer uses when
migrating a REST API handler to an HTTP API — receives a 2.0 event. The handler reads
event["httpMethod"], finds nothing, and fails on a configuration AWS supports.

2. The v1 event carries an unpublished version

:1778 emits "version": "1.0". The 1.0 payload format publishes no version member — it is a
2.0-only member. A handler that branches on "version" in event to tell the formats apart, which is
the idiomatic way to write a dual-format handler, takes the 2.0 branch for a 1.0 event.

3. The v1 event omits the multi-value members and most of requestContext

:1772-1806 emits 5 of the 15 published requestContext members, and omits
multiValueHeaders and multiValueQueryStringParameters entirely. Those two are how the 1.0 format
represents a repeated header or query parameter — ?tag=a&tag=b, or two Set-Cookies — so a handler
reading them reads nil and a repeated parameter is silently reduced to one value. Of the
requestContext members, the absent ones include identity (with sourceIp, userAgent),
authorizer, requestId, requestTime, protocol and resourcePath — identity.sourceIp being the
one most handlers actually use, for logging or rate limiting.

stageVariables is emitted as {} where AWS's own sample shows null.

4. The v2 event omits four published members

:1810-1842 omits cookies, queryStringParameters, pathParameters and stageVariables. The 2.0
format publishes all four. cookies is the notable one: in 2.0 it is the only place cookies
appear — they are not in headers — so a handler reading a session cookie from a 2.0 event gets
nothing at all.

Why this matters

A proxy integration's event payload is the interface between API Gateway and a consumer's handler,
and it is the one thing a handler's code reads directly on every request. Every other API Gateway
divergence affects whether a request arrives; these affect whether the handler can do its job once it
does. A handler that reads event["requestContext"]["identity"]["sourceIp"] — logging, rate limiting,
geo rules, audit — panics or reads nil against substrate and works against AWS.

The PayloadFormatVersion case is the sharpest because the consumer configured the thing that is
ignored. They wrote PayloadFormatVersion: "1.0" in their template, substrate stored it, and then sent
the other format. That is worse than an unimplemented member: it is a member whose configuration is
accepted and contradicted, so the consumer's reasonable conclusion is that their handler is broken.

The unpublished version member is the subtlest and the most likely to cost someone an afternoon,
because it makes the standard format-detection idiom give the wrong answer — and it gives it silently,
on the format where the mistake is hardest to see.

Acceptance criteria

  • PayloadFormatVersion selects the event format. A 1.0 integration on an HTTP API receives a
    1.0 event; the default when unset is 2.0 for an HTTP API, as AWS documents.
  • The v1 event does not carry version.
  • The v1 event carries multiValueHeaders and multiValueQueryStringParameters, so a repeated
    header or query parameter survives.
  • The v1 requestContext carries its published members, identity.sourceIp and requestId
    included.
  • stageVariables matches the published sample — null when there are none, in v1.
  • The v2 event carries cookies, queryStringParameters, pathParameters and stageVariables.
  • A test asserts each event payload as raw JSON against the published sample, per
    iam_shape_members_test.go:115. Substrate's own tests build the event and read it back through
    its own structs, which is why ten missing members survived.
  • A test sends ?tag=a&tag=b and two cookies through both formats and asserts both values survive
    in the format-appropriate member.
  • A test configures PayloadFormatVersion: "1.0" on an HTTP API and asserts a 1.0 event. It must
    fail against today's tree.
  • docs/services.md's execute-api section carries both event shapes, stating which members
    substrate emits.

Provenance

Both payload formats' complete member lists, the fifteen requestContext members, the samples showing
stageVariables: null in 1.0, and the statement that version is a 2.0 member are from
api-gateway-simple-proxy-for-lambda-input.html (1.0) and
http-api-develop-integrations-lambda.html (2.0). That cookies is the only cookie carrier in 2.0,
and that PayloadFormatVersion defaults to 2.0 and accepts 1.0, are from the same 2.0 page.

In-tree: emulator/apigatewayv2_types.go:66-87 (stored and unread);
emulator/apigateway_plugin.go:1605-1609 (isV2), :1778 (the unpublished version), :1772-1806
(the v1 event), :1810-1842 (the v2 event). Line citations are from the tree at the commit this issue
was filed against.

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

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions