A delivery can carry Content-Type twice when the inbound event also supplied one #246

Closed
opened 2026-08-20 10:16:01 +02:00 by clawbot · 2 comments
Collaborator

Found during the rework of #242 and deliberately not fixed there — it predates that PR and is outside the scope of #233 and #243. Not milestoned for 1.0; filed so it is on the record rather than only in a PR comment.

applyRequestHeaders sets Content-Type from event.ContentType, then Adds the inbound Content-Type that the forwarded event header map also carries. The outbound request can therefore go out with two Content-Type values.

Definition of done:

  • An outbound delivery carries exactly one Content-Type when the inbound event supplied one and event.ContentType is also set.
  • A test asserts the outbound request has exactly one value for the header, covering both the agreeing and disagreeing cases.
  • Which value wins is stated explicitly in the fix rather than left to map iteration order.
Found during the rework of https://git.eeqj.de/sneak/webhooker/pulls/242 and deliberately not fixed there — it predates that PR and is outside the scope of https://git.eeqj.de/sneak/webhooker/issues/233 and https://git.eeqj.de/sneak/webhooker/issues/243. Not milestoned for 1.0; filed so it is on the record rather than only in a PR comment. `applyRequestHeaders` sets `Content-Type` from `event.ContentType`, then `Add`s the inbound `Content-Type` that the forwarded event header map also carries. The outbound request can therefore go out with two `Content-Type` values. Definition of done: - An outbound delivery carries exactly one `Content-Type` when the inbound event supplied one and `event.ContentType` is also set. - A test asserts the outbound request has exactly one value for the header, covering both the agreeing and disagreeing cases. - Which value wins is stated explicitly in the fix rather than left to map iteration order.
clawbot added this to the 1.0.0 milestone 2026-09-21 09:20:33 +02:00
Author
Collaborator

Plan. The code the issue names is still on next (3cdab97). applyRequestHeaders (internal/delivery/target_http.go) calls Set on Content-Type from event.ContentType. forwardEventHeaders then calls Add on every forwardable inbound header, and the inbound Content-Type is one of them.

  • Precedence, stated in a comment at applyRequestHeaders:

    1. A Content-Type the operator configured on the target wins.
    2. Otherwise the event's ContentType, which describes the stored body being sent.
    3. Otherwise nothing.

    An inbound Content-Type is never forwarded as a second value. The receiver already sets event.ContentType from the inbound header (internal/handlers/webhook.go), so nothing is lost.

  • Fix: the plainest is to make forwardEventHeaders skip Content-Type and let applyRequestHeaders set it. Keep the redirect handling as it is: the existing delete(originScoped, "Content-Type") exists so a 307/308 does not strip the body's type. Check that it still holds.

  • Tests: the outbound request has exactly one Content-Type value, in each of these cases:

    • the inbound value and event.ContentType agree;
    • they disagree (the event's wins);
    • the event has none (no Content-Type is sent, even if the stored inbound headers carry one);
    • the target configures its own (that one wins).

    Each case fails when the fix is removed.

Model: opus-5-5

Plan. The code the issue names is still on `next` (`3cdab97`). `applyRequestHeaders` (`internal/delivery/target_http.go`) calls `Set` on `Content-Type` from `event.ContentType`. `forwardEventHeaders` then calls `Add` on every forwardable inbound header, and the inbound `Content-Type` is one of them. - **Precedence, stated in a comment at `applyRequestHeaders`:** 1. A `Content-Type` the operator configured on the target wins. 2. Otherwise the event's `ContentType`, which describes the stored body being sent. 3. Otherwise nothing. An inbound `Content-Type` is never forwarded as a second value. The receiver already sets `event.ContentType` from the inbound header (`internal/handlers/webhook.go`), so nothing is lost. - **Fix:** the plainest is to make `forwardEventHeaders` skip `Content-Type` and let `applyRequestHeaders` set it. Keep the redirect handling as it is: the existing `delete(originScoped, "Content-Type")` exists so a 307/308 does not strip the body's type. Check that it still holds. - **Tests:** the outbound request has exactly one `Content-Type` value, in each of these cases: - the inbound value and `event.ContentType` agree; - they disagree (the event's wins); - the event has none (no `Content-Type` is sent, even if the stored inbound headers carry one); - the target configures its own (that one wins). Each case fails when the fix is removed. Model: opus-5-5
clawbot self-assigned this 2026-09-29 04:31:40 +02:00
Author
Collaborator

Built in #331: the inbound Content-Type is no longer copied from the stored headers, and applyRequestHeaders states which value wins (a Content-Type configured on the target, then the event's ContentType, then none). A test covers the four cases in the plan.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/webhooker/pulls/331: the inbound `Content-Type` is no longer copied from the stored headers, and `applyRequestHeaders` states which value wins (a `Content-Type` configured on the target, then the event's `ContentType`, then none). A test covers the four cases in the plan. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#246