Skip to content

Send a CloudEvent over HTTP

http.Header is also map[string][]string, so nothing about the codec changes:

headers, body, err := cloudevents.Marshal(e)
if err != nil {
    return err
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, target, bytes.NewReader(body))
if err != nil {
    return err
}

req.Header = http.Header(headers)

The case-folding wrinkle

http.Header canonicalises keys when you use its Set and Add methods: ce-id becomes Ce-Id, content-type becomes Content-Type. Assigning the map directly, as above, skips that — Go's HTTP server and client both handle lower-case keys correctly on the wire.

Either way round, Unmarshal lower-cases every key it reads, so it accepts both spellings.

Header.Get cannot see Marshal's output

Get canonicalises the key it is given, so req.Header.Get("Ce-Id") returns "" against the map Marshal produced. Assign the map as above and read it back with Unmarshal, or iterate it — do not reach for Get in a test or a middleware.

func handler(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "unreadable body", http.StatusBadRequest)

        return
    }

    e, err := cloudevents.Unmarshal(r.Header, body)
    if err != nil {
        http.Error(w, "not a CloudEvent this service accepts", http.StatusBadRequest)

        return
    }

    handle(e)
}

The fixture at testdata/sdk-binary-1.0.txt was produced by the CloudEvents SDK's own HTTP binding and carries Ce-Id rather than ce-id. It is checked in precisely so that the folding cannot be removed without a test noticing.

What if two spellings disagree

Unmarshal returns ErrAmbiguousHeader. It does not pick one. An event whose ce-id and CE-ID differ means two different things depending on which transport carried it, and choosing either would make that difference invisible.

Only headers the codec owns are considered, so an ordinary proxy's repeated X-Forwarded-For, Via or Accept is ignored rather than treated as an ambiguous event.