diff --git a/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs b/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs
index c391d38..3b6991a 100644
--- a/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs
+++ b/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs
@@ -212,6 +212,77 @@ namespace CloudNative.CloudEvents.Http
}
}
+ ///
+ /// Converts this HTTP request message into a CloudEvent batch.
+ ///
+ /// The HTTP request to convert. Must not be null.
+ /// The event formatter to use to parse the CloudEvents. Must not be null.
+ /// The extension attributes to use when parsing the CloudEvents. May be null.
+ /// The decoded batch of CloudEvents.
+ public static Task> ToCloudEventBatchAsync(
+ this HttpListenerRequest httpListenerRequest,
+ CloudEventFormatter formatter,
+ params CloudEventAttribute[] extensionAttributes) =>
+ ToCloudEventBatchAsync(httpListenerRequest, formatter, (IEnumerable)extensionAttributes);
+
+ ///
+ /// Converts this HTTP request message into a CloudEvent batch.
+ ///
+ /// The HTTP request to convert. Must not be null.
+ /// The event formatter to use to parse the CloudEvent. Must not be null.
+ /// The extension attributes to use when parsing the CloudEvent. May be null.
+ /// The decoded batch of CloudEvents.
+ public static async Task> ToCloudEventBatchAsync(
+ this HttpListenerRequest httpListenerRequest,
+ CloudEventFormatter formatter,
+ IEnumerable extensionAttributes) =>
+ await ToCloudEventBatchInternalAsync(httpListenerRequest, formatter, extensionAttributes, async: true).ConfigureAwait(false);
+
+ ///
+ /// Converts this HTTP request message into a CloudEvent batch.
+ ///
+ /// The HTTP request to convert. Must not be null.
+ /// The event formatter to use to parse the CloudEvents. Must not be null.
+ /// The extension attributes to use when parsing the CloudEvents. May be null.
+ /// The decoded batch of CloudEvents.
+ public static IReadOnlyList ToCloudEventBatch(
+ this HttpListenerRequest httpListenerRequest,
+ CloudEventFormatter formatter,
+ params CloudEventAttribute[] extensionAttributes) =>
+ ToCloudEventBatch(httpListenerRequest, formatter, (IEnumerable) extensionAttributes);
+
+ ///
+ /// Converts this HTTP request message into a CloudEvent batch.
+ ///
+ /// The HTTP request to convert. Must not be null.
+ /// The event formatter to use to parse the CloudEvents. Must not be null.
+ /// The extension attributes to use when parsing the CloudEvents. May be null.
+ /// The decoded batch of CloudEvents.
+ public static IReadOnlyList ToCloudEventBatch(
+ this HttpListenerRequest httpListenerRequest,
+ CloudEventFormatter formatter,
+ IEnumerable extensionAttributes) =>
+ ToCloudEventBatchInternalAsync(httpListenerRequest, formatter, extensionAttributes, async: false).GetAwaiter().GetResult();
+
+ private async static Task> ToCloudEventBatchInternalAsync(HttpListenerRequest httpListenerRequest,
+ CloudEventFormatter formatter, IEnumerable extensionAttributes, bool async)
+ {
+ Validation.CheckNotNull(httpListenerRequest, nameof(httpListenerRequest));
+ Validation.CheckNotNull(formatter, nameof(formatter));
+
+ if (HasCloudEventsBatchContentType(httpListenerRequest))
+ {
+ var contentType = MimeUtilities.CreateContentTypeOrNull(httpListenerRequest.ContentType);
+ return async
+ ? await formatter.DecodeBatchModeMessageAsync(httpListenerRequest.InputStream, contentType, extensionAttributes).ConfigureAwait(false)
+ : formatter.DecodeBatchModeMessage(httpListenerRequest.InputStream, contentType, extensionAttributes);
+ }
+ else
+ {
+ throw new ArgumentException("HTTP message does not represent a CloudEvents batch.", nameof(httpListenerRequest));
+ }
+ }
+
private static bool HasCloudEventsContentType(HttpListenerRequest request) =>
MimeUtilities.IsCloudEventsContentType(request.ContentType);
diff --git a/test/CloudNative.CloudEvents.UnitTests/Http/HttpClientExtensionsTest.cs b/test/CloudNative.CloudEvents.UnitTests/Http/HttpClientExtensionsTest.cs
index a6a327e..026d2a5 100644
--- a/test/CloudNative.CloudEvents.UnitTests/Http/HttpClientExtensionsTest.cs
+++ b/test/CloudNative.CloudEvents.UnitTests/Http/HttpClientExtensionsTest.cs
@@ -438,7 +438,7 @@ namespace CloudNative.CloudEvents.Http.UnitTests
}
}
- private static HttpRequestMessage CreateRequestMessage(ReadOnlyMemory content, ContentType contentType) =>
+ internal static HttpRequestMessage CreateRequestMessage(ReadOnlyMemory content, ContentType contentType) =>
new HttpRequestMessage
{
Content = new ByteArrayContent(content.ToArray())
@@ -447,7 +447,7 @@ namespace CloudNative.CloudEvents.Http.UnitTests
}
};
- private static HttpResponseMessage CreateResponseMessage(ReadOnlyMemory content, ContentType contentType) =>
+ internal static HttpResponseMessage CreateResponseMessage(ReadOnlyMemory content, ContentType contentType) =>
new HttpResponseMessage
{
Content = new ByteArrayContent(content.ToArray())
diff --git a/test/CloudNative.CloudEvents.UnitTests/Http/HttpListenerExtensionsTest.cs b/test/CloudNative.CloudEvents.UnitTests/Http/HttpListenerExtensionsTest.cs
index 856a14b..8c9d29b 100644
--- a/test/CloudNative.CloudEvents.UnitTests/Http/HttpListenerExtensionsTest.cs
+++ b/test/CloudNative.CloudEvents.UnitTests/Http/HttpListenerExtensionsTest.cs
@@ -141,6 +141,48 @@ namespace CloudNative.CloudEvents.Http.UnitTests
Assert.Equal(originalCloudEvent.Data, parsedCloudEvent.Data);
}
+ [Fact]
+ public async Task ToCloudEventBatch_Valid()
+ {
+ var batch = CreateSampleBatch();
+
+ var formatter = new JsonEventFormatter();
+ var contentBytes = formatter.EncodeBatchModeMessage(batch, out var contentType);
+
+ AssertBatchesEqual(batch, await GetBatchAsync(context => context.Request.ToCloudEventBatchAsync(formatter, EmptyExtensionArray)));
+ AssertBatchesEqual(batch, await GetBatchAsync(context => context.Request.ToCloudEventBatchAsync(formatter, EmptyExtensionSequence)));
+ AssertBatchesEqual(batch, await GetBatchAsync(context => Task.FromResult(context.Request.ToCloudEventBatch(formatter, EmptyExtensionArray))));
+ AssertBatchesEqual(batch, await GetBatchAsync(context => Task.FromResult(context.Request.ToCloudEventBatch(formatter, EmptyExtensionSequence))));
+
+ Task> GetBatchAsync(Func>> handler)
+ {
+ var request = HttpClientExtensionsTest.CreateRequestMessage(contentBytes, contentType);
+ request.RequestUri = new Uri(ListenerAddress);
+ return SendRequestAsync(request, handler);
+ }
+ }
+
+ [Fact]
+ public async Task ToCloudEventBatchAsync_Invalid()
+ {
+ // Most likely accident: calling ToCloudEventBatchAsync with a single event in structured mode.
+ var cloudEvent = new CloudEvent().PopulateRequiredAttributes();
+ var formatter = new JsonEventFormatter();
+ var contentBytes = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType);
+
+ await ExpectFailure(context => context.Request.ToCloudEventBatchAsync(formatter, EmptyExtensionArray));
+ await ExpectFailure(context => context.Request.ToCloudEventBatchAsync(formatter, EmptyExtensionSequence));
+ await ExpectFailure(context => Task.FromResult(context.Request.ToCloudEventBatch(formatter, EmptyExtensionArray)));
+ await ExpectFailure(context => Task.FromResult(context.Request.ToCloudEventBatch(formatter, EmptyExtensionSequence)));
+
+ async Task ExpectFailure(Func>> handler)
+ {
+ var request = HttpClientExtensionsTest.CreateRequestMessage(contentBytes, contentType);
+ request.RequestUri = new Uri(ListenerAddress);
+ await SendRequestAsync(request, context => Assert.ThrowsAsync(() => handler(context)));
+ }
+ }
+
[Fact]
public async Task CopyToHttpListenerResponseAsync_BinaryMode()
{