Skip to content

fix(pubsub): correct CloudEvents content type mapping - #1329

Open
pbandj082 wants to merge 2 commits into
cloudevents:mainfrom
pbandj082:feature/refactor-google-pubsub-content-type
Open

pbandj082 wants to merge 2 commits into
cloudevents:mainfrom
pbandj082:feature/refactor-google-pubsub-content-type

Conversation

@pbandj082

@pbandj082 pbandj082 commented Sep 20, 2026 •

Copy link
Copy Markdown

Binary Pub/Sub messages currently publish the event data content type only as Content-Type, so consumers cannot filter on ce-datacontenttype. Publish ce-datacontenttype while retaining Content-Type for existing consumers. Both attributes are updated and deleted together, including when the destination message already contains either attribute.

Encoding detection uses lowercase content-type, falling back to legacy Content-Type only when the lowercase attribute is absent. A known CloudEvents format selects Structured mode even when ce-specversion is also present. Structured publishing sets lowercase content-type to the event format's media type; the JSON body's datacontenttype describes the event data. These rules follow the Google Cloud Pub/Sub binding draft, with the uppercase fallback retained for compatibility.

Binary reads accept content-type or ce-datacontenttype. When both are present, their values must match exactly; conflicting values return an error before event attributes or data are copied. Legacy Content-Type supplies the data content type only when neither attribute is present. Binary publishing writes ce-datacontenttype and legacy Content-Type, without adding lowercase content-type.

Compatibility notes:

  • Existing Binary messages using only legacy Content-Type for their data content type, and legacy Structured messages without an outer ce-specversion, remain supported.
  • Lowercase content-type determines the encoding whenever present, even if empty or unrecognized. For example, a message with Content-Type: application/cloudevents+json, content-type: application/json, and no Pub/Sub ce-specversion previously decoded as Structured when its body was a valid CloudEvent. It now has EncodingUnknown, and ToEvent returns unknown Message encoding, even if the JSON body contains specversion.
  • With content-type: application/cloudevents+json, a supported ce-specversion, and no legacy Content-Type, a message previously decoded as Binary. It now decodes as Structured, using the CloudEvent in the body; a body that is not a valid structured CloudEvent can therefore fail conversion.
  • Binary messages with different content-type and ce-datacontenttype values are now rejected. Equality is exact, including empty strings. When either canonical attribute is present, conflicting legacy Content-Type is ignored. The decoded data content type can therefore change; conflicting prefixed and legacy values previously depended on map iteration order.
  • Binary writing clears a pre-existing lowercase content-type on the destination, preventing a stale Structured media type when reusing a message. It publishes the data content type through ce-datacontenttype and legacy Content-Type.

A CloudEvents format in the selected content-type attribute is interpreted as Structured. This change does not add Binary round-trip support for nested CloudEvents carrying a legacy CloudEvents content type.

Regression tests cover CloudEvents 0.3 and 1.0, individual and combined content-type attributes, conflicts, prepopulated destination attributes, updates and deletions, encoding changes, Structured metadata duplication, and a missing specversion in the JSON body. The Pub/Sub test server verifies both outgoing Binary attributes.

Closes #812. Related to #1119.

Validation:

  • cd protocol/pubsub/v2 && GOTOOLCHAIN=go1.26.0 go test -race -timeout 30s -count=1 ./...
  • cd protocol/pubsub/v2 && GOTOOLCHAIN=go1.26.0 go vet ./...
  • GOTOOLCHAIN=go1.26.0 ./hack/unit-test.sh (all 28 modules passed in a clean temporary copy on macOS containing the final working-tree changes)
  • Compared original and updated decoding for the compatibility cases above.
  • gofmt and git diff --check

@pbandj082
pbandj082 requested a review from a team as a code owner September 20, 2026 14:42
@pbandj082
pbandj082 marked this pull request as draft September 20, 2026 15:06
@embano1
embano1 requested a lite review from Copilot September 20, 2026 15:30
@pbandj082

Copy link
Copy Markdown
Author

I’ve moved this PR to draft while I revise the change to preserve the existing Content-Type attribute for backward compatibility. I’m also checking binary/structured mode detection against the Google Pub/Sub binding and the related behavior in #1119.

Please hold off on further review for now. I’ll update the patch and mark the PR ready once the revisions and tests are complete. Thanks for your patience.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The writer has unresolved conflicting-attribute and nil-deletion handling issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Updates Pub/Sub binary CloudEvents encoding to write data content type as ce-datacontenttype while retaining legacy decoding.

Changes:

  • Updates outgoing attribute mapping.
  • Adds encoding, round-trip, and compatibility tests.
  • Updates publish-request assertions.
File Summary Findings
protocol/​pubsub/​v2/​write_pubsub_message.go Changes outgoing attribute mapping. Critical (1 vote): Remove conflicting legacy Content-Type when setting the new attribute. Moderate (1 vote): Return immediately when deleting a nil-valued attribute.
protocol/​pubsub/​v2/​write_pubsub_message_test.go Tests binary encoding and round trips. —
protocol/​pubsub/​v2/​protocol_test.go Expects the new publish attribute. —
protocol/​pubsub/​v2/​message_test.go Tests decoding both attribute names. —

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread protocol/pubsub/v2/write_pubsub_message.go
@pbandj082
pbandj082 force-pushed the feature/refactor-google-pubsub-content-type branch from 7c53e92 to 762008a Compare September 20, 2026 18:22
@pbandj082 pbandj082 changed the title fix(pubsub): send datacontenttype as ce-datacontenttype fix(pubsub): correct CloudEvents content type mapping Sep 20, 2026
@pbandj082
pbandj082 marked this pull request as ready for review September 20, 2026 18:27
@pbandj082

Copy link
Copy Markdown
Author

The revisions are complete, and this PR is ready for review again. The patch retains legacy Content-Type alongside ce-datacontenttype, fixes content-mode detection, and adds regression tests. Compatibility changes are documented in the PR description.

The local unit-test script passed across all 28 modules. GitHub Actions currently reports action_required; could a maintainer approve the workflow runs?

Thanks for your patience.

@embano1

embano1 commented Sep 21, 2026

Copy link
Copy Markdown
Member

@duglin would like your 👀 on this one. Ideally, we'd normalize all to lower when comparing values to simplify the logic, but that in itself is a breaking change.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Structured-mode reuse must clear stale ce-datacontenttype and legacy Content-Type attributes.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

if b.Attributes == nil {
b.Attributes = make(map[string]string)
}
b.Attributes[contentType] = f.MediaType()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 702a45b4.

This follow-up addresses a pre-existing issue with reusing a destination Pub/Sub message: attributes or data omitted by the next event could remain from the previous event.

This goes beyond #812's content-type mapping fix, but I'm including it as a separate commit in this PR because it was identified while addressing the review feedback on stale content-type attributes.

The change clears ce-*, content-type, and legacy Content-Type attributes while preserving custom Pub/Sub attributes. It also clears previous data when starting binary output. Regression tests cover both binary and structured output, for CloudEvents 0.3 and 1.0.

@duglin

duglin commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor
image

I believe this was purposely done to avoid duplicating information already present in the message and because we wanted CE to use existing technology whenever possible, not reinvent things - which in this case means: use the well-established HTTP headers if they exist.

@pbandj082

Copy link
Copy Markdown
Author

Thanks for the feedback, @duglin and @embano1.

@duglin, I understand the HTTP binding reference as background on the design principle of reusing a protocol’s existing content-type metadata and avoiding duplicate information.

This PR changes Pub/Sub message.attributes. The Google Pub/Sub binding draft defines the ce- attribute mapping and explicitly uses ce-datacontenttype in its binary-mode examples. Accordingly, this PR follows that mapping while retaining the SDK’s existing Content-Type attribute for backward compatibility.

@embano1, I understand the normalization suggestion to mean normalizing attribute names to lowercase when comparing them within the Pub/Sub binding. Given the compatibility implications you mentioned, I’ve understood this as a possible future improvement rather than a required change in this PR.

@duglin

duglin commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

@pbandj082 it sounds to me like you're saying Google's pub/sub is wrong and violates the spec, right? I'd prefer if we started with getting them to fix their bug - after all, it's just in a sample (at least per that page you referenced).

@pbandj082

Copy link
Copy Markdown
Author

Thanks, @duglin. I may not have explained my interpretation clearly. I wasn't intending to say that Google's Pub/Sub violates the CloudEvents spec. I was reading that document as a Pub/Sub-specific binding draft.

My understanding was that the prohibition in HTTP binding §3.1.1 applies to HTTP headers, while the CloudEvents core specification leaves the content-type mapping rules to the respective protocol bindings.

I was also relying on the draft's text beyond the examples: §3.1.3 requires all CloudEvents attributes to be mapped individually to Pub/Sub attributes, and §3.1.3.1 defines the ce- prefix. §3.1.1 additionally maps the optional binary-mode content-type attribute to datacontenttype when present.

That is the interpretation behind this PR. I recognize that the draft itself may need clarification if this was not the intended Pub/Sub mapping.

Publish ce-datacontenttype alongside legacy Content-Type and keep both
attributes synchronized when setting or deleting the data content type.

Use lowercase content-type to detect and publish structured events, with
an uppercase fallback for legacy input. Prefer a known structured format
over duplicated ce-specversion metadata. Reject conflicting binary
content-type and ce-datacontenttype values before copying the event.

Document compatibility changes and cover attribute combinations,
prepopulated destinations, encoding changes, and CloudEvents 0.3 and 1.0.

Fixes cloudevents#812.
Related to cloudevents#1119.

Signed-off-by: Masahiro Yanagita <pbandj082@gmail.com>
Clear ce-* attributes and both content-type keys before writing the next
binary or structured event, while preserving custom Pub/Sub attributes.
Reset binary data so an event without data cannot retain the previous body.

Address a pre-existing destination-reuse issue identified while reviewing
the content-type mapping fix. Add regression tests for changed or omitted
data content types and minimal events in both output encodings and
CloudEvents versions 0.3 and 1.0.

Signed-off-by: Masahiro Yanagita <pbandj082@gmail.com>
@duglin
duglin force-pushed the feature/refactor-google-pubsub-content-type branch from 702a45b to c9412dc Compare October 7, 2026 12:53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataContentType in the pubsub protocol should be sent as "ce-datacontenttype"

4 participants