public AmqpCloudEventMessage(CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter) { ApplicationProperties = new ApplicationProperties(); MapHeaders(cloudEvent); if (contentMode == ContentMode.Structured) { BodySection = new Data { Binary = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType) }; Properties = new Properties { ContentType = contentType.MediaType }; ApplicationProperties = new ApplicationProperties(); MapHeaders(cloudEvent); return; } else { BodySection = SerializeData(cloudEvent.Data); Properties = new Properties { ContentType = cloudEvent.DataContentType }; } }
/// <summary> /// Constructor /// </summary> /// <param name="cloudEvent">CloudEvent</param> /// <param name="contentMode">Content mode. Structured or binary.</param> /// <param name="formatter">Event formatter</param> public CloudEventHttpContent(CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter) { byte[] content; ContentType contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); // This is optional in the specification, but can be useful. MapHeaders(cloudEvent, includeDataContentType: true); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(cloudEvent.DataContentType); MapHeaders(cloudEvent, includeDataContentType: false); break; default: throw new ArgumentException($"Unsupported content mode: {contentMode}"); } inner = new InnerByteArrayContent(content); if (contentType is object) { Headers.ContentType = contentType.ToMediaTypeHeaderValue(); } else if (content.Length != 0) { throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent)); } }
internal CloudEvent ConvertToCloudEvent(Request request, CloudEventFormatter formatter) { MaybeReshapeData(request); var context = request.Context; var resource = context.Resource; // Default the service name if necessary resource.Service = ValidateNotNullOrEmpty(resource.Service ?? _defaultService, "service"); ValidateNotNullOrEmpty(resource.Name, "resource name"); var evt = new CloudEvent { Type = _cloudEventType, Id = context.Id, Time = context.Timestamp, DataContentType = JsonContentType }; PopulateAttributes(request, evt); var bytes = JsonSerializer.SerializeToUtf8Bytes(request.Data); formatter.DecodeBinaryModeEventData(bytes, evt); return(evt); }
internal CloudEvent ConvertToCloudEvent(Request request, CloudEventFormatter formatter) { MaybeReshapeData(request); var context = request.Context; var resource = context.Resource; var service = ValidateNotNullOrEmpty(resource.Service ?? _defaultService, "service"); var resourceName = ValidateNotNullOrEmpty(resource.Name, "resource name"); var(source, subject) = ConvertSourceAndSubject(service, resourceName, request.Data); var evt = new CloudEvent { Type = _cloudEventType, Source = source, Id = context.Id, Time = context.Timestamp, DataContentType = JsonContentType, Subject = subject }; var bytes = JsonSerializer.SerializeToUtf8Bytes(request.Data); formatter.DecodeBinaryModeEventData(bytes, evt); return(evt); }
/// <summary> /// Converts this Kafka message into a CloudEvent object. /// </summary> /// <param name="message">The Kafka message to convert. Must not be null.</param> /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param> /// <returns>A reference to a validated CloudEvent instance.</returns> public static CloudEvent ToCloudEvent(this Message <string, byte[]> message, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute> extensionAttributes) { Validation.CheckNotNull(message, nameof(message)); Validation.CheckNotNull(formatter, nameof(formatter)); if (!IsCloudEvent(message)) { throw new InvalidOperationException(); } var contentType = ExtractContentType(message); CloudEvent cloudEvent; // Structured mode if (MimeUtilities.IsCloudEventsContentType(contentType)) { cloudEvent = formatter.DecodeStructuredModeMessage(message.Value, new ContentType(contentType), extensionAttributes); } else { // Binary mode if (!(GetHeaderValue(message, SpecVersionKafkaHeader) is byte[] versionIdBytes)) { throw new ArgumentException("Request is not a CloudEvent"); } string versionId = Encoding.UTF8.GetString(versionIdBytes); CloudEventsSpecVersion version = CloudEventsSpecVersion.FromVersionId(versionId) ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(message)); cloudEvent = new CloudEvent(version, extensionAttributes) { DataContentType = contentType }; foreach (var header in message.Headers.Where(h => h.Key.StartsWith(KafkaHeaderPrefix))) { var attributeName = header.Key.Substring(KafkaHeaderPrefix.Length).ToLowerInvariant(); if (attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } // TODO: Is this feasible? var headerValue = header.GetValueBytes(); if (headerValue is null) { continue; } string attributeValue = Encoding.UTF8.GetString(headerValue); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } formatter.DecodeBinaryModeEventData(message.Value, cloudEvent); } InitPartitioningKey(message, cloudEvent); return(Validation.CheckCloudEventArgument(cloudEvent, nameof(message))); }
/// <summary> /// Converts this HTTP request message into a CloudEvent batch. /// </summary> /// <param name="httpRequestMessage">The HTTP request message to convert. Must not be null.</param> /// <param name="formatter">The event formatter to use to parse the CloudEvents. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvents. May be null.</param> /// <returns>The decoded batch of CloudEvents.</returns> public static Task <IReadOnlyList <CloudEvent> > ToCloudEventBatchAsync( this HttpRequestMessage httpRequestMessage, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes) { Validation.CheckNotNull(httpRequestMessage, nameof(httpRequestMessage)); return(ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage))); }
/// <summary> /// Converts this HTTP response message into a CloudEvent object /// </summary> /// <param name="httpResponseMessage">The HTTP response message to convert. Must not be null.</param> /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param> /// <returns>A reference to a validated CloudEvent instance.</returns> public static Task <CloudEvent> ToCloudEventAsync( this HttpResponseMessage httpResponseMessage, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes) { Validation.CheckNotNull(httpResponseMessage, nameof(httpResponseMessage)); return(ToCloudEventInternalAsync(httpResponseMessage.Headers, httpResponseMessage.Content, formatter, extensionAttributes, nameof(httpResponseMessage))); }
public static CloudEvent ToCloudEvent(this Message <string, byte[]> message, CloudEventFormatter eventFormatter, IEnumerable <CloudEventAttribute> extensionAttributes) { if (!IsCloudEvent(message)) { throw new InvalidOperationException(); } var contentType = ExtractContentType(message); CloudEvent cloudEvent; // Structured mode if (contentType?.StartsWith(CloudEvent.MediaType, StringComparison.InvariantCultureIgnoreCase) == true) { cloudEvent = eventFormatter.DecodeStructuredModeMessage(message.Value, new ContentType(contentType), extensionAttributes); } else { // Binary mode if (!(GetHeaderValue(message, KafkaCloudEventMessage.SpecVersionKafkaHeader) is byte[] versionIdBytes)) { throw new ArgumentException("Request is not a CloudEvent"); } string versionId = Encoding.UTF8.GetString(versionIdBytes); CloudEventsSpecVersion version = CloudEventsSpecVersion.FromVersionId(versionId); if (version is null) { throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId}'"); } cloudEvent = new CloudEvent(version, extensionAttributes) { Data = message.Value, DataContentType = contentType }; foreach (var header in message.Headers.Where(h => h.Key.StartsWith(KafkaCloudEventMessage.KafkaHeaderPrefix))) { var attributeName = header.Key.Substring(KafkaCloudEventMessage.KafkaHeaderPrefix.Length).ToLowerInvariant(); if (attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } // TODO: Is this feasible? var headerValue = header.GetValueBytes(); if (headerValue is null) { continue; } string attributeValue = Encoding.UTF8.GetString(headerValue); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } } InitPartitioningKey(message, cloudEvent); return(cloudEvent); }
/// <summary> /// Converts this MQTT message into a CloudEvent object. /// </summary> /// <param name="message">The MQTT message to convert. Must not be null.</param> /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param> /// <returns>A reference to a validated CloudEvent instance.</returns> public static CloudEvent ToCloudEvent(this MqttApplicationMessage message, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes) { Validation.CheckNotNull(formatter, nameof(formatter)); Validation.CheckNotNull(message, nameof(message)); // TODO: Determine if there's a sensible content type we should apply. return(formatter.DecodeStructuredModeMessage(message.Payload, contentType: null, extensionAttributes)); }
/// <summary> /// Constructs a new instance that uses the given formatter for deserialization. /// </summary> /// <param name="formatter"></param> public CloudEventJsonInputFormatter(CloudEventFormatter formatter) { _formatter = Validation.CheckNotNull(formatter, nameof(formatter)); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json")); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/cloudevents+json")); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); }
public KafkaMessagePublisher( IOptions <KafkaPublisherOptions <TOutput> > options, CloudEventFormatter cloudEventFormatter, IOptions <PublisherOptions> publisherOptions) { _cloudEventFormatter = cloudEventFormatter; _options = options.Value ?? throw new ArgumentNullException(nameof(options)); _publisherOptions = publisherOptions.Value ?? throw new ArgumentNullException(nameof(publisherOptions)); _producer = new ProducerBuilder <string?, byte[]>(_options).Build(); }
public NATSMessagePublisher( IOptions <NATSBaseOptions> options, INATSClientFactory natsClientFactory, CloudEventFormatter cloudEventFormatter, IOptions <PublisherOptions> publisherOptions) { _options = options.Value; _client = natsClientFactory.From(_options); _cloudEventFormatter = cloudEventFormatter; _publisherOptions = publisherOptions.Value; }
public RabbitMQMessagePublisher( IRabbitMQConnectionFactory <TOutput> connectionFactory, IOptions <RabbitMQPublisherOptions <TOutput> > config, IOptions <PublisherOptions> publisherOptions, CloudEventFormatter cloudEventFormatter ) { ConnectionFactory = connectionFactory; _options = config.Value; _publisherOptions = publisherOptions.Value; _cloudEventFormatter = cloudEventFormatter; }
/// <summary> /// Copies a <see cref="CloudEvent"/> batch into the specified <see cref="HttpWebRequest"/>. /// </summary> /// <param name="cloudEvents">CloudEvent batch to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The request to populate. Must not be null.</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpWebRequestAsync(this IReadOnlyList <CloudEvent> cloudEvents, HttpWebRequest destination, CloudEventFormatter formatter) { Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); byte[] content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType); destination.ContentType = contentType.ToString(); await destination.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false); }
/// <summary> /// Copies a <see cref="CloudEvent"/> into an <see cref="HttpResponse" />. /// </summary> /// <param name="cloudEvent">The CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpResponseAsync(this CloudEvent cloudEvent, HttpResponse destination, ContentMode contentMode, CloudEventFormatter formatter) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); ReadOnlyMemory <byte> content; ContentType? contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(formatter.GetOrInferDataContentType(cloudEvent)); break; default: throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); } if (contentType is object) { destination.ContentType = contentType.ToString(); } else if (content.Length != 0) { throw new ArgumentException("The 'datacontenttype' attribute value must be specified", nameof(cloudEvent)); } // Map headers in either mode. // Including the headers in structured mode is optional in the spec (as they're already within the body) but // can be useful. destination.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; // The content type is already handled based on the content mode. if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); destination.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } destination.ContentLength = content.Length; await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); }
/// <summary> /// Copies a <see cref="CloudEvent"/> batch into the specified <see cref="HttpWebRequest"/>. /// </summary> /// <param name="cloudEvents">CloudEvent batch to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The request to populate. Must not be null.</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpWebRequestAsync(this IReadOnlyList <CloudEvent> cloudEvents, HttpWebRequest destination, CloudEventFormatter formatter) { Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); ReadOnlyMemory <byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType); destination.ContentType = contentType.ToString(); await BinaryDataUtilities.CopyToStreamAsync(content, destination.GetRequestStream()).ConfigureAwait(false); }
// TODO: HttpWebResponse as well? /// <summary> /// Copies a <see cref="CloudEvent"/> into the specified <see cref="HttpWebRequest"/>. /// </summary> /// <param name="cloudEvent">CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The request to populate. Must not be null.</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpWebRequestAsync(this CloudEvent cloudEvent, HttpWebRequest destination, ContentMode contentMode, CloudEventFormatter formatter) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); byte[] content; // The content type to include in the ContentType header - may be the data content type, or the formatter's content type. ContentType contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(cloudEvent.DataContentType); break; default: throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); } if (contentType is object) { destination.ContentType = contentType.ToString(); } else if (content.Length != 0) { throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent)); } // Map headers in either mode. // Including the headers in structured mode is optional in the spec (as they're already within the body) but // can be useful. destination.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); destination.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } await destination.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false); }
/// <summary> /// Converts this HTTP request into a CloudEvent object. /// </summary> /// <param name="httpRequest">The HTTP request to decode. Must not be null.</param> /// <param name="formatter">The event formatter to use to process the request body. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param> /// <returns>The decoded CloudEvent.</returns> /// <exception cref="ArgumentException">The request does not contain a CloudEvent.</exception> public static async Task <CloudEvent> ToCloudEventAsync( this HttpRequest httpRequest, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes) { Validation.CheckNotNull(httpRequest, nameof(httpRequest)); Validation.CheckNotNull(formatter, nameof(formatter)); if (HasCloudEventsContentType(httpRequest)) { var contentType = MimeUtilities.CreateContentTypeOrNull(httpRequest.ContentType); return(await formatter.DecodeStructuredModeMessageAsync(httpRequest.Body, contentType, extensionAttributes).ConfigureAwait(false)); } else { var headers = httpRequest.Headers; headers.TryGetValue(HttpUtilities.SpecVersionHttpHeader, out var versionId); if (versionId.Count == 0) { throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpRequest)); } var version = CloudEventsSpecVersion.FromVersionId(versionId.FirstOrDefault()) ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(httpRequest)); if (version is null) { throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId.First()}'"); } var cloudEvent = new CloudEvent(version, extensionAttributes); foreach (var header in headers) { string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First()); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } cloudEvent.DataContentType = httpRequest.ContentType; if (httpRequest.Body is Stream body) { ReadOnlyMemory <byte> data = await BinaryDataUtilities.ToReadOnlyMemoryAsync(body).ConfigureAwait(false); formatter.DecodeBinaryModeEventData(data, cloudEvent); } return(Validation.CheckCloudEventArgument(cloudEvent, nameof(httpRequest))); } }
/// <summary> /// Constructs a new instance based on the given CloudEvent Function. /// </summary> /// <param name="function">The CloudEvent Function to invoke.</param> /// <param name="formatter">The CloudEvent formatter to use to deserialize the CloudEvent. This is expected to be a single-type formatter, /// targeting <typeparamref name="TData"/>.</param> /// <param name="logger">The logger to use to report errors.</param> public CloudEventAdapter(ICloudEventFunction <TData> function, CloudEventFormatter formatter, ILogger <CloudEventAdapter <TData> > logger) { _function = Preconditions.CheckNotNull(function, nameof(function)); if (formatter is null) { throw new ArgumentNullException(nameof(formatter), "No CloudEventFormatter configured for the CloudEvent function. " + "The Functions Framework hosting will provide an event formatter if the target " + "data type is decorated with CloudEventFormatterAttribute; otherwise, the formatter " + "should be configured via dependency injection."); } _formatter = Preconditions.CheckNotNull(formatter, nameof(formatter)); _logger = Preconditions.CheckNotNull(logger, nameof(logger)); }
private void MapHeaders(CloudEvent cloudEvent, CloudEventFormatter formatter) { foreach (var pair in cloudEvent.GetPopulatedAttributes()) { var attribute = pair.Key; if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute || attribute.Name == Partitioning.PartitionKeyAttribute.Name) { continue; } var value = attribute.Format(pair.Value); Headers.Add(KafkaHeaderPrefix + attribute.Name, Encoding.UTF8.GetBytes(value)); } }
private static CloudEvent StructuredToCloudEvent( Message message, string contentType, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes ) { using (var stream = new MemoryStream(message.Body)) { var cloudEvent = formatter.DecodeStructuredModeMessage(stream, new ContentType(contentType), extensionAttributes); cloudEvent.Id = message.MessageId; return(cloudEvent); } }
private async static Task <CloudEvent> ToCloudEventAsyncImpl(HttpListenerRequest httpListenerRequest, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes, bool async) { Validation.CheckNotNull(httpListenerRequest, nameof(httpListenerRequest)); Validation.CheckNotNull(formatter, nameof(formatter)); var stream = httpListenerRequest.InputStream; if (HasCloudEventsContentType(httpListenerRequest)) { var contentType = MimeUtilities.CreateContentTypeOrNull(httpListenerRequest.ContentType); return(async ? await formatter.DecodeStructuredModeMessageAsync(stream, contentType, extensionAttributes).ConfigureAwait(false) : formatter.DecodeStructuredModeMessage(stream, contentType, extensionAttributes)); } else { string versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader]; if (versionId is null) { throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpListenerRequest)); } var version = CloudEventsSpecVersion.FromVersionId(versionId) ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(httpListenerRequest)); var cloudEvent = new CloudEvent(version, extensionAttributes); var headers = httpListenerRequest.Headers; foreach (var key in headers.AllKeys) { string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } // The data content type should not have been set via a "ce-" header; instead, // it's in the regular content type. cloudEvent.DataContentType = httpListenerRequest.ContentType; ReadOnlyMemory <byte> data = async ? await BinaryDataUtilities.ToReadOnlyMemoryAsync(stream).ConfigureAwait(false) : BinaryDataUtilities.ToReadOnlyMemory(stream); formatter.DecodeBinaryModeEventData(data, cloudEvent); return(Validation.CheckCloudEventArgument(cloudEvent, nameof(httpListenerRequest))); } }
public KafkaMessageConsumer(ILogger <KafkaMessageConsumer <TData> > logger, IOptions <KafkaConsumerOptions <TData> > config, IMetricsFactory <KafkaMessageConsumer <TData> >?metricsFactory, IApplicationNameService applicationNameService, CloudEventFormatter cloudEventFormatter) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _applicationNameService = applicationNameService ?? throw new ArgumentNullException(nameof(config)); _cloudEventFormatter = cloudEventFormatter; _options = config.Value ?? throw new ArgumentNullException(nameof(config)); _consumerLagSummary = metricsFactory?.CreateSummary("consumer_lag_distribution", "Contains a summary of current consumer lag of each partition", new[] { "topic", "partition" }); _consumerLagGauge = metricsFactory?.CreateGauge("consumer_lag", "Contains current number consumer lag of each partition", false, "topic", "partition"); }
private static async Task <CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute> extensionAttributes) { if (HasCloudEventsContentType(content)) { // FIXME: Handle no formatter being specified. var stream = await content.ReadAsStreamAsync().ConfigureAwait(false); return(await formatter.DecodeStructuredModeMessageAsync(stream, content.Headers.ContentType.ToContentType(), extensionAttributes).ConfigureAwait(false)); } else { string versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader) ? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First() : null; if (versionId is null) { throw new ArgumentException("Request is not a CloudEvent"); } var version = CloudEventsSpecVersion.FromVersionId(versionId); if (version is null) { throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId}'"); } var cloudEvent = new CloudEvent(version, extensionAttributes); foreach (var header in headers) { string attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First()); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } if (content is object) { // TODO: Should this just be the media type? We probably need to take a full audit of this... cloudEvent.DataContentType = content.Headers?.ContentType?.ToString(); var data = await content.ReadAsByteArrayAsync().ConfigureAwait(false); formatter.DecodeBinaryModeEventData(data, cloudEvent); } return(cloudEvent); } }
/// <summary> /// Copies a <see cref="CloudEvent"/> batch into an <see cref="HttpResponse" />. /// </summary> /// <param name="cloudEvents">The CloudEvent batch to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpResponseAsync(this IReadOnlyList <CloudEvent> cloudEvents, HttpResponse destination, CloudEventFormatter formatter) { Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); // TODO: Validate that all events in the batch have the same version? // See https://github.com/cloudevents/spec/issues/807 ReadOnlyMemory <byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType); destination.ContentType = contentType.ToString(); destination.ContentLength = content.Length; await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false); }
// TODO: HttpWebResponse as well? // TODO: Change to a CopyTo extension method on CloudEvent? /// <summary> /// Copies a CloudEvent into the specified HttpWebRequest instance. /// </summary> /// <param name="httpWebRequest">The request to populate.</param> /// <param name="cloudEvent">CloudEvent to copy</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">Formatter</param> /// <returns>Task</returns> public static async Task CopyFromAsync(this HttpWebRequest httpWebRequest, CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter) { if (contentMode == ContentMode.Structured) { var buffer = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType); httpWebRequest.ContentType = contentType.ToString(); await httpWebRequest.GetRequestStream().WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); return; } httpWebRequest.ContentType = cloudEvent.DataContentType?.ToString() ?? "application/json"; MapAttributesToWebRequest(cloudEvent, httpWebRequest); byte[] content = formatter.EncodeBinaryModeEventData(cloudEvent); await httpWebRequest.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false); }
/// <summary> /// Converts this <see cref="Message"/> into a CloudEvent object. /// </summary> /// <param name="message">The message to convert. Must not be null.</param> /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param> /// <returns>A validated CloudEvent.</returns> public static CloudEvent ToCloudEvent( this Message message, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes ) { Validation.CheckNotNull(message, nameof(message)); Validation.CheckNotNull(formatter, nameof(formatter)); if (HasCloudEventsContentType(message, out var contentType)) { return(StructuredToCloudEvent(message, contentType, formatter, extensionAttributes)); } else { return(BinaryToCloudEvent(message, formatter, extensionAttributes)); } }
// TODO: Check the pattern here. I suspect that cloudEvent.CopyToAsync(response) would be more natural. /// <summary> /// Copies the CloudEvent into this HttpListenerResponse instance /// </summary> /// <param name="httpListenerResponse">this</param> /// <param name="cloudEvent">CloudEvent to copy</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">Formatter</param> /// <returns>Task</returns> public static Task CopyFromAsync(this HttpListenerResponse httpListenerResponse, CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter) { if (contentMode == ContentMode.Structured) { var buffer = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType); httpListenerResponse.ContentType = contentType.ToString(); MapAttributesToListenerResponse(cloudEvent, httpListenerResponse); return(httpListenerResponse.OutputStream.WriteAsync(buffer, 0, buffer.Length)); } // TODO: Check the defaulting to JSON here... httpListenerResponse.ContentType = cloudEvent.DataContentType?.ToString() ?? "application/json"; MapAttributesToListenerResponse(cloudEvent, httpListenerResponse); byte[] content = formatter.EncodeBinaryModeEventData(cloudEvent); return(httpListenerResponse.OutputStream.WriteAsync(content, 0, content.Length)); }
private static async Task <CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes, string paramName) { Validation.CheckNotNull(formatter, nameof(formatter)); if (HasCloudEventsContentType(content)) { var stream = await content.ReadAsStreamAsync().ConfigureAwait(false); return(await formatter.DecodeStructuredModeMessageAsync(stream, MimeUtilities.ToContentType(content.Headers.ContentType), extensionAttributes).ConfigureAwait(false)); } else { string?versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader) ? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First() : null; if (versionId is null) { throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName)); } var version = CloudEventsSpecVersion.FromVersionId(versionId) ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName); var cloudEvent = new CloudEvent(version, extensionAttributes); foreach (var header in headers) { string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First()); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } if (content is object) { // TODO: Should this just be the media type? We probably need to take a full audit of this... cloudEvent.DataContentType = content.Headers?.ContentType?.ToString(); var data = await content.ReadAsByteArrayAsync().ConfigureAwait(false); formatter.DecodeBinaryModeEventData(data, cloudEvent); } return(Validation.CheckCloudEventArgument(cloudEvent, paramName)); } }
/// <summary> /// Converts this HTTP request into a batch of CloudEvents. /// </summary> /// <param name="httpRequest">The HTTP request to decode. Must not be null.</param> /// <param name="formatter">The event formatter to use to process the request body. Must not be null.</param> /// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param> /// <returns>The decoded CloudEvent.</returns> /// <exception cref="ArgumentException">The request does not contain a CloudEvent.</exception> public static async Task <IReadOnlyList <CloudEvent> > ToCloudEventBatchAsync( this HttpRequest httpRequest, CloudEventFormatter formatter, IEnumerable <CloudEventAttribute> extensionAttributes) { Validation.CheckNotNull(httpRequest, nameof(httpRequest)); Validation.CheckNotNull(formatter, nameof(formatter)); if (HasCloudEventsBatchContentType(httpRequest)) { var contentType = MimeUtilities.CreateContentTypeOrNull(httpRequest.ContentType); return(await formatter.DecodeBatchModeMessageAsync(httpRequest.Body, contentType, extensionAttributes).ConfigureAwait(false)); } else { throw new ArgumentException("HTTP message does not represent a CloudEvents batch.", nameof(httpRequest)); } }