fix: Use content headers when parsing HTTP requests/responses
Fixes #221 Signed-off-by: Jon Skeet <jonskeet@google.com>
This commit is contained in:
parent
3ce4aa08f2
commit
39b691c24a
|
@ -6,7 +6,6 @@ using CloudNative.CloudEvents.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
|
@ -36,7 +35,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(httpRequestMessage, nameof(httpRequestMessage));
|
Validation.CheckNotNull(httpRequestMessage, nameof(httpRequestMessage));
|
||||||
return HasCloudEventsContentType(httpRequestMessage.Content) ||
|
return HasCloudEventsContentType(httpRequestMessage.Content) ||
|
||||||
httpRequestMessage.Headers.Contains(HttpUtilities.SpecVersionHttpHeader);
|
(MaybeGetVersionId(httpRequestMessage.Headers) ?? MaybeGetVersionId(httpRequestMessage.Content?.Headers)) is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -48,7 +47,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(httpResponseMessage, nameof(httpResponseMessage));
|
Validation.CheckNotNull(httpResponseMessage, nameof(httpResponseMessage));
|
||||||
return HasCloudEventsContentType(httpResponseMessage.Content) ||
|
return HasCloudEventsContentType(httpResponseMessage.Content) ||
|
||||||
httpResponseMessage.Headers.Contains(HttpUtilities.SpecVersionHttpHeader);
|
(MaybeGetVersionId(httpResponseMessage.Headers) ?? MaybeGetVersionId(httpResponseMessage.Content?.Headers)) is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -143,9 +142,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string? versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader)
|
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content.Headers);
|
||||||
? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
|
|
||||||
: null;
|
|
||||||
if (versionId is 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));
|
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 @@ namespace CloudNative.CloudEvents.Http
|
||||||
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
|
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
|
||||||
|
|
||||||
var cloudEvent = new CloudEvent(version, extensionAttributes);
|
var cloudEvent = new CloudEvent(version, extensionAttributes);
|
||||||
foreach (var header in headers)
|
foreach (var header in headers.Concat(content.Headers))
|
||||||
{
|
{
|
||||||
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
|
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
|
||||||
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
||||||
|
@ -345,5 +342,10 @@ namespace CloudNative.CloudEvents.Http
|
||||||
|
|
||||||
private static bool HasCloudEventsBatchContentType(HttpContent content) =>
|
private static bool HasCloudEventsBatchContentType(HttpContent content) =>
|
||||||
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
|
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
|
||||||
|
|
||||||
|
private static string? MaybeGetVersionId(HttpHeaders? headers) =>
|
||||||
|
headers is not null && headers.Contains(HttpUtilities.SpecVersionHttpHeader)
|
||||||
|
? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,6 @@ using CloudNative.CloudEvents.UnitTests;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
@ -40,6 +39,20 @@ namespace CloudNative.CloudEvents.Http.UnitTests
|
||||||
"Structured",
|
"Structured",
|
||||||
new StringContent("content is ignored", Encoding.UTF8, "application/cloudevents+json"),
|
new StringContent("content is ignored", Encoding.UTF8, "application/cloudevents+json"),
|
||||||
null
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Binary with header in content",
|
||||||
|
new StringContent("header is in the content", Encoding.UTF8, "application/json")
|
||||||
|
{
|
||||||
|
Headers =
|
||||||
|
{
|
||||||
|
{ "ce-specversion", "1.0" },
|
||||||
|
{ "ce-type", "test-type" },
|
||||||
|
{ "ce-id", "test-id" },
|
||||||
|
{ "ce-source", "//test" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -438,6 +451,32 @@ namespace CloudNative.CloudEvents.Http.UnitTests
|
||||||
AssertBatchesEqual(batch, parsedBatch);
|
AssertBatchesEqual(batch, parsedBatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(ContentMode.Binary)]
|
||||||
|
[InlineData(ContentMode.Structured)]
|
||||||
|
public async Task RoundtripRequest(ContentMode contentMode)
|
||||||
|
{
|
||||||
|
var cloudEvent = new CloudEvent().PopulateRequiredAttributes();
|
||||||
|
var formatter = new JsonEventFormatter();
|
||||||
|
var content = cloudEvent.ToHttpContent(contentMode, formatter);
|
||||||
|
var request = new HttpRequestMessage { Content = content };
|
||||||
|
var parsed = await request.ToCloudEventAsync(formatter);
|
||||||
|
AssertCloudEventsEqual(cloudEvent, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(ContentMode.Binary)]
|
||||||
|
[InlineData(ContentMode.Structured)]
|
||||||
|
public async Task RoundtripResponse(ContentMode contentMode)
|
||||||
|
{
|
||||||
|
var cloudEvent = new CloudEvent().PopulateRequiredAttributes();
|
||||||
|
var formatter = new JsonEventFormatter();
|
||||||
|
var content = cloudEvent.ToHttpContent(contentMode, formatter);
|
||||||
|
var request = new HttpResponseMessage { Content = content };
|
||||||
|
var parsed = await request.ToCloudEventAsync(formatter);
|
||||||
|
AssertCloudEventsEqual(cloudEvent, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
internal static void CopyHeaders(IDictionary<string, string>? source, HttpHeaders target)
|
internal static void CopyHeaders(IDictionary<string, string>? source, HttpHeaders target)
|
||||||
{
|
{
|
||||||
if (source is null)
|
if (source is null)
|
||||||
|
|
Loading…
Reference in New Issue