Skip to content

Should Header::Generic.trailer? permit unknown fields as trailers? #127

Open

Description

@samuel-williams-shopify

Context

Protocol::HTTP::Headers#add(key, value, trailer: true) decides whether a field may be a trailer by consulting POLICY[key].trailer?. This is the right place for that decision: it is a property of the field, independent of whether the Headers instance is currently sending trailers (@tail). protocol-http1 and async-http apply it to every received trailer field.

The policies that currently permit trailers are Date, Digest, ETag, ServerTiming — and Generic, the fallback for every header name without a specific policy. So any unknown field (x-api-key, x-forwarded-host, x-request-id, …) is accepted as a trailer, and because a field permitted as a trailer is treated as an ordinary field once received, it is indistinguishable to the application from a header that arrived in the head.

RFC 9110 §6.5.1: a recipient MUST NOT merge a trailer field into the header section unless the field's definition explicitly permits it; otherwise it MAY discard the field, store it separately, or forward it separately.

Question

Should Generic.trailer? return false?

  • For: unknown fields have, by definition, no definition that "explicitly permits" trailer use, so accepting them is the non-conforming default. Custom authentication/routing headers are exactly the fields an application might check before reading the body and expect not to change afterwards.
  • Against: it is a behaviour change for anyone sending or receiving custom trailers (e.g. gRPC-style grpc-status/grpc-message are trailers by design and have no policy here — they would need explicit entries). It also raises the question of what a recipient should do on rejection: add currently raises InvalidTrailerError, which protocol-http1 surfaces as a failed message; the RFC's "MAY discard" suggests a softer option for the receive path.

Possible shapes

  1. Flip Generic.trailer? to false and add explicit trailer-permitting policies for well-known trailer fields (e.g. grpc-status, grpc-message).
  2. Keep Generic permissive but let consumers opt into strictness via a custom policy hash (already possible: Headers.new(fields, policy)), and document the default.
  3. Separate "reject the message" from "discard the field" on receive, so a stricter default does not turn an unexpected custom trailer into a 400.

Related: socketry/protocol-http1#64 and socketry/async-http#259 were closed because marking received trailers with trailer! conflated this policy question with the sender-side @tail marker.

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