Skip to content

Commit 39b691c

Browse files
committed
fix: Use content headers when parsing HTTP requests/responses
Fixes #221 Signed-off-by: Jon Skeet <[email protected]>
1 parent 3ce4aa0 commit 39b691c

File tree

2 files changed

+49
-8
lines changed

2 files changed

+49
-8
lines changed

src/CloudNative.CloudEvents/Http/HttpClientExtensions.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System;
77
using System.Collections.Generic;
88
using System.Linq;
9-
using System.Net;
109
using System.Net.Http;
1110
using System.Net.Http.Headers;
1211
using System.Net.Mime;
@@ -36,7 +35,7 @@ public static bool IsCloudEvent(this HttpRequestMessage httpRequestMessage)
3635
{
3736
Validation.CheckNotNull(httpRequestMessage, nameof(httpRequestMessage));
3837
return HasCloudEventsContentType(httpRequestMessage.Content) ||
39-
httpRequestMessage.Headers.Contains(HttpUtilities.SpecVersionHttpHeader);
38+
(MaybeGetVersionId(httpRequestMessage.Headers) ?? MaybeGetVersionId(httpRequestMessage.Content?.Headers)) is not null;
4039
}
4140

4241
/// <summary>
@@ -48,7 +47,7 @@ public static bool IsCloudEvent(this HttpResponseMessage httpResponseMessage)
4847
{
4948
Validation.CheckNotNull(httpResponseMessage, nameof(httpResponseMessage));
5049
return HasCloudEventsContentType(httpResponseMessage.Content) ||
51-
httpResponseMessage.Headers.Contains(HttpUtilities.SpecVersionHttpHeader);
50+
(MaybeGetVersionId(httpResponseMessage.Headers) ?? MaybeGetVersionId(httpResponseMessage.Content?.Headers)) is not null;
5251
}
5352

5453
/// <summary>
@@ -143,9 +142,7 @@ private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders head
143142
}
144143
else
145144
{
146-
string? versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader)
147-
? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
148-
: null;
145+
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content.Headers);
149146
if (versionId is null)
150147
{
151148
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName));
@@ -154,7 +151,7 @@ private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders head
154151
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
155152

156153
var cloudEvent = new CloudEvent(version, extensionAttributes);
157-
foreach (var header in headers)
154+
foreach (var header in headers.Concat(content.Headers))
158155
{
159156
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
160157
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
@@ -345,5 +342,10 @@ private static bool HasCloudEventsContentType(HttpContent content) =>
345342

346343
private static bool HasCloudEventsBatchContentType(HttpContent content) =>
347344
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
345+
346+
private static string? MaybeGetVersionId(HttpHeaders? headers) =>
347+
headers is not null && headers.Contains(HttpUtilities.SpecVersionHttpHeader)
348+
? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
349+
: null;
348350
}
349351
}

test/CloudNative.CloudEvents.UnitTests/Http/HttpClientExtensionsTest.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Newtonsoft.Json.Linq;
99
using System;
1010
using System.Collections.Generic;
11-
using System.Globalization;
1211
using System.Linq;
1312
using System.Net;
1413
using System.Net.Http;
@@ -40,6 +39,20 @@ public class HttpClientExtensionsTest : HttpTestBase
4039
"Structured",
4140
new StringContent("content is ignored", Encoding.UTF8, "application/cloudevents+json"),
4241
null
42+
},
43+
{
44+
"Binary with header in content",
45+
new StringContent("header is in the content", Encoding.UTF8, "application/json")
46+
{
47+
Headers =
48+
{
49+
{ "ce-specversion", "1.0" },
50+
{ "ce-type", "test-type" },
51+
{ "ce-id", "test-id" },
52+
{ "ce-source", "//test" }
53+
}
54+
},
55+
null
4356
}
4457
};
4558

@@ -438,6 +451,32 @@ public async Task ToHttpContent_Batch()
438451
AssertBatchesEqual(batch, parsedBatch);
439452
}
440453

454+
[Theory]
455+
[InlineData(ContentMode.Binary)]
456+
[InlineData(ContentMode.Structured)]
457+
public async Task RoundtripRequest(ContentMode contentMode)
458+
{
459+
var cloudEvent = new CloudEvent().PopulateRequiredAttributes();
460+
var formatter = new JsonEventFormatter();
461+
var content = cloudEvent.ToHttpContent(contentMode, formatter);
462+
var request = new HttpRequestMessage { Content = content };
463+
var parsed = await request.ToCloudEventAsync(formatter);
464+
AssertCloudEventsEqual(cloudEvent, parsed);
465+
}
466+
467+
[Theory]
468+
[InlineData(ContentMode.Binary)]
469+
[InlineData(ContentMode.Structured)]
470+
public async Task RoundtripResponse(ContentMode contentMode)
471+
{
472+
var cloudEvent = new CloudEvent().PopulateRequiredAttributes();
473+
var formatter = new JsonEventFormatter();
474+
var content = cloudEvent.ToHttpContent(contentMode, formatter);
475+
var request = new HttpResponseMessage { Content = content };
476+
var parsed = await request.ToCloudEventAsync(formatter);
477+
AssertCloudEventsEqual(cloudEvent, parsed);
478+
}
479+
441480
internal static void CopyHeaders(IDictionary<string, string>? source, HttpHeaders target)
442481
{
443482
if (source is null)

0 commit comments

Comments
 (0)