Skip to content

Latest commit

 

History

History
214 lines (173 loc) · 8.21 KB

bindings.md

File metadata and controls

214 lines (173 loc) · 8.21 KB

Implementing a protocol binding

From the specification:

A protocol binding describes how events are sent and received over a given protocol.

In SDK terms, this usually means methods converting between the SDK CloudEvent type and protocol-specific request/response types.

This document describes suggested conventions for implementing a new protocol binding. These are only conventions rather than requirements, but following them will promote consistency, leading to a more predictable user experience.

In this document, it is assumed that the binding has the concepts of "structured" or "binary" content modes. Readers should make appropriate choices where this is not the case.

It is encouraged to provide conversions in both directions where this makes sense, but that won't be the case in all situations.

Conversions to CloudEvent

Provide extension methods on the protocol-specific message types, with the following pattern, demonstrated with an imaginary ProtocolMessage type (such as HttpRequestMessage or HttpResponseMessage):

public static bool IsCloudEvent(this ProtocolMessage message);

public static CloudEvent ToCloudEvent(
    this ProtocolMessage message,
    CloudEventFormatter formatter,
    params CloudEventAtribute[] extensionAttributes);

public static CloudEvent ToCloudEvent(
    this ProtocolMessage message,
    CloudEventFormatter formatter,
    IEnumerable<CloudEventAtribute> extensionAttributes);

Where message content can only be read asynchronously, async methods (with a type Task<CloudEvent> or ValueTask<CloudEvent> and an Async method name suffix) should be provided instead.

The reason for providing two overloads is for caller convenience to cover three scenarios:

  • No extension attribute (call uses the params overload)
  • A single extension attribute, or multiple known individual ones (call uses the params overload)
  • An immutable collection of extension attributes, either provided by the SDK (e.g. Sampling.AllAttriutes) or constructed once by the caller.

IsCloudEvent

The IsCloudEvent method should provide a reasonable indication of whether or not the message contains a CloudEvent, but should not perform full decoding and validation. Typically it checks metadata for one of:

  • A content type beginning with "application/cloudevents" (for structured content mode events) but not beginning with "application/cloudevents-batch" (even if the binding does not currently support batch events)
  • The presence of a CloudEvents specification version header (for binary mode events)

ToCloudEvent methods

Typically multiple overloads of ToCloudEvent will resolve to a single internal implementation. The implementation should use the following pseudo-code as structure:

  • Validate that message and formatter are non-null. (The extensionAttributes parameter may be null, which is equivalent to an empty collection.)
  • Determine the content mode of the message
  • For a structured content mode message, delegate to the CloudEventFormatter specified in the formatter parameter, using its DecodeStructuredModeMessage method. The formatter should take care of validating the resulting event.
  • For binary content mode message:
    • Determine the specification version based on metadata. (The transport may specify a default specification, but typically this is required metadata which serves to check that the message is intended to represent a CloudEvent.) Use CloudEventsSpecVersion.FromVersionId and check for a null return value.
    • Construct a CloudEvent instance with the given specification version and extension attributes.
    • Look for all CloudEvent attributes within metadata, and populate the attributes within the CloudEvent instance.
    • If the message contains content, call the formatter.DecodeBinaryModeEventData method to populate the CloudEvent.Data property appropriately.
    • Return the result of Validation.CheckCloudEventArgument which will validate the event, and either return the original reference if the event is valid, or throw an appropriate ArgumentException otherwise.

Conversions from CloudEvent to protocol message types

There are two patterns here, depending on whether it's appropriate for the conversion to construct a new instance or whether it should populate an existing instance:

public static ProtocolMessage ToProtocolMessage(
    this CloudEvent cloudEvent,
    ContentMode contentMode,
    CloudEventFormatter formatter);

public static void CopyToProtocolMessage(
    this CloudEvent cloudEvent,
    ProtocolMessage destination,
    ContentMode contentMode,
    CloudEventFormatter formatter);

Here the ProtocolMessage part of the message name varies by target type. Where the type involved is sufficiently clear, that can be used directly. If the type involved only has a generic name such as Message or Request, it is useful to qualify it with the protocol name. Examples:

// HttpContent is already unambiguous.
public static HttpContent ToHttpContent(
    this CloudEvent cloudEvent,
    ContentMode contentMode,
    CloudEventFormatter formatter);

// Message is ambiguous, so clarify it in the method name.
public static Message ToAmqpMessage(
    this CloudEvent cloudEvent,
    ContentMode contentMode,
    CloudEventFormatter formatter);

Again, where asynchrony is required, make the methods async with appropriate changes to the return type and method name.

Even if only a single content mode is currently supported, the inclusion of the contentMode parameter provides consistency and a seamless path for change later. If the protocol inherently means that contentMode is meaningless (so can never be supported), it can be omitted.

Any additional parameters required for the conversion should be added after the formatter parameter.

The conversion should follow the following steps of pseudo-code:

  • Parameter validation (which may be completed in any order):
    • cloudEvent and formatter should be non-null (CheckCloudEventArgument will validate this for the cloudEvent parameter)
    • In a CopyTo... method, destination should be non-null
    • The contentMode should be a known, supported value
    • Call Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)) for validation of the original CloudEvent.
  • For structured mode encoding:
    • Call formatter.EncodeStructuredModeMessage to encode the CloudEvent and indicate the resulting content type for the protocol message.
  • For binary mode encoding:
    • Call formatter.EncodeBinaryModeEventData to encode the data within the CloudEvent
    • Call formatter.GetOrInferDataContentType to obtain the appropriate content type, which may be inferred from the data if the CloudEvent itself does not specify the data content type.
    • Populate metadata in the message from the attributes in the CloudEvent and the content type.
  • For To... methods, return the resulting protocol message. This must not be null. (CopyTo... messages do not return anything.)

Note that depending on the protocol binding, when encoding a CloudEvent in binary mode, it may still be worth populating metadata in the message from the CloudEvent attributes.

Batch conversions

Protocol bindings that support batch conversions should introduce equivalent batch methods:

public static bool IsCloudEventBatch(this ProtocolMessage message);

public static IReadOnlyList<CloudEvent> ToCloudEventBatch(
    this ProtocolMessage message,
    CloudEventFormatter formatter,
    params CloudEventAtribute[] extensionAttributes);

public static IReadOnlyList<CloudEvent> ToCloudEventBatch(
    this ProtocolMessage message,
    CloudEventFormatter formatter,
    IEnumerable<CloudEventAtribute> extensionAttributes);

public static ProtocolMessage ToProtocolMessage(
    this IReadOnlyList<CloudEvent> cloudEvents,
    CloudEventFormatter formatter);

public static void CopyToProtocolMessage(
    this IReadOnlyList<CloudEvent> cloudEvents,
    ProtocolMessage destination,
    CloudEventFormatter formatter);

Note that no ContentMode is specified when converting to protocol messages, as there is no ambiguity.