Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ public class JsonEventFormatter : CloudEventFormatter
/// </summary>
protected JsonDocumentOptions DocumentOptions { get; }

/// <summary>
/// Whether non-UTF8 JSON should be transcoded to UTF-8 before parsing when the target framework supports it.
/// Derived classes may override this to exercise the fallback path.
/// </summary>
protected virtual bool UseTranscodingStreamForNonUtf8 => true;

/// <summary>
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
Expand Down Expand Up @@ -193,15 +199,20 @@ private async Task<JsonDocument> ReadDocumentAsync(Stream data, ContentType? con
if (encoding is not UTF8Encoding)
{
#if NET5_0_OR_GREATER
data = Encoding.CreateTranscodingStream(data, encoding, Encoding.UTF8);
#else
using var reader = new StreamReader(data, encoding);
var json = async
? await reader.ReadToEndAsync().ConfigureAwait(false)
: reader.ReadToEnd();

return JsonDocument.Parse(json, DocumentOptions);
if (UseTranscodingStreamForNonUtf8)
{
data = Encoding.CreateTranscodingStream(data, encoding, Encoding.UTF8);
}
else
#endif
{
using var reader = new StreamReader(data, encoding);
var json = async
? await reader.ReadToEndAsync().ConfigureAwait(false)
: reader.ReadToEnd();

return JsonDocument.Parse(json, DocumentOptions);
}
}

return async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ public async Task DecodeStructuredModeMessageAsync_Minimal(string charset)

[Theory]
[InlineData("utf-8")]
[InlineData("utf-16")]
[InlineData("iso-8859-1")]
public void DecodeStructuredModeMessage_Minimal(string charset)
{
Expand All @@ -556,6 +557,31 @@ public void DecodeStructuredModeMessage_Minimal(string charset)
Assert.Equal("test-type", cloudEvent.Type);
Assert.Equal("test-id", cloudEvent.Id);
Assert.Equal(SampleUri, cloudEvent.Source);
Assert.Equal(NonAsciiValue, cloudEvent["text"]);
}

[Theory]
[InlineData("utf-8")]
[InlineData("utf-16")]
[InlineData("iso-8859-1")]
public async Task DecodeStructuredModeMessage_Minimal_ForcedNonUtf8Fallback(string charset)
{
var obj = new JObject
{
["specversion"] = "1.0",
["type"] = "test-type",
["id"] = "test-id",
["source"] = SampleUriText,
["text"] = NonAsciiValue
};
var bytes = Encoding.GetEncoding(charset).GetBytes(obj.ToString());
var stream = new MemoryStream(bytes);
var formatter = new NonTranscodingJsonEventFormatter();
var cloudEvent = formatter.DecodeStructuredModeMessage(stream, new ContentType($"application/cloudevents+json; charset={charset}"), null);
Assert.Equal("test-type", cloudEvent.Type);
Assert.Equal("test-id", cloudEvent.Id);
Assert.Equal(SampleUri, cloudEvent.Source);
Assert.Equal(NonAsciiValue, cloudEvent["text"]);
}

[Fact]
Expand Down Expand Up @@ -1218,6 +1244,11 @@ private static IReadOnlyList<CloudEvent> DecodeBatchModeMessage(Newtonsoft.Json.
return formatter.DecodeBatchModeMessage(bytes, s_jsonCloudEventBatchContentType, null);
}

private sealed class NonTranscodingJsonEventFormatter : JsonEventFormatter
{
protected override bool UseTranscodingStreamForNonUtf8 => false;
}

private sealed class YearMonthDayConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
Expand Down