From e7b147dfe3e27146e92364b3eafca04a2271afd4 Mon Sep 17 00:00:00 2001 From: David Wambugu Date: Tue, 24 Sep 2024 12:44:38 +0300 Subject: [PATCH] Port Update message writer and reader to ignore Message info from DI (#3058) to ODL 7x --- .../ODataMessageReader.cs | 28 +++--- .../ODataMessageWriter.cs | 26 ++--- .../MessageWritterConcurrencyTests.cs | 94 +++++++++++++++++++ 3 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 test/FunctionalTests/Microsoft.OData.Core.Tests/MessageWritterConcurrencyTests.cs diff --git a/src/Microsoft.OData.Core/ODataMessageReader.cs b/src/Microsoft.OData.Core/ODataMessageReader.cs index 9413378482..a6a730f0c4 100644 --- a/src/Microsoft.OData.Core/ODataMessageReader.cs +++ b/src/Microsoft.OData.Core/ODataMessageReader.cs @@ -935,24 +935,18 @@ private ODataMessageInfo GetOrCreateMessageInfo(Stream messageStream, bool isAsy { if (this.messageInfo == null) { - if (this.container == null) + this.messageInfo = new ODataMessageInfo { - this.messageInfo = new ODataMessageInfo(); - } - else - { - this.messageInfo = this.container.GetRequiredService(); - } - - this.messageInfo.Encoding = this.encoding; - this.messageInfo.IsResponse = this.readingResponse; - this.messageInfo.IsAsync = isAsync; - this.messageInfo.MediaType = this.contentType; - this.messageInfo.Model = this.model; - this.messageInfo.PayloadUriConverter = this.payloadUriConverter; - this.messageInfo.Container = this.container; - this.messageInfo.MessageStream = messageStream; - this.messageInfo.PayloadKind = this.readerPayloadKind; + Encoding = this.encoding, + IsResponse = this.readingResponse, + IsAsync = isAsync, + MediaType = this.contentType, + Model = this.model, + PayloadUriConverter = this.payloadUriConverter, + Container = this.container, + MessageStream = messageStream, + PayloadKind = this.readerPayloadKind + }; } return this.messageInfo; diff --git a/src/Microsoft.OData.Core/ODataMessageWriter.cs b/src/Microsoft.OData.Core/ODataMessageWriter.cs index 0e8cb08a8c..261b4fb4fe 100644 --- a/src/Microsoft.OData.Core/ODataMessageWriter.cs +++ b/src/Microsoft.OData.Core/ODataMessageWriter.cs @@ -1257,23 +1257,17 @@ private ODataMessageInfo GetOrCreateMessageInfo(Stream messageStream, bool isAsy { if (this.messageInfo == null) { - if (this.container == null) + this.messageInfo = new ODataMessageInfo { - this.messageInfo = new ODataMessageInfo(); - } - else - { - this.messageInfo = this.container.GetRequiredService(); - } - - this.messageInfo.Encoding = this.encoding; - this.messageInfo.IsResponse = this.writingResponse; - this.messageInfo.IsAsync = isAsync; - this.messageInfo.MediaType = this.mediaType; - this.messageInfo.Model = this.model; - this.messageInfo.PayloadUriConverter = this.payloadUriConverter; - this.messageInfo.Container = this.container; - this.messageInfo.MessageStream = messageStream; + Encoding = this.encoding, + IsResponse = this.writingResponse, + IsAsync = isAsync, + MediaType = this.mediaType, + Model = this.model, + PayloadUriConverter = this.payloadUriConverter, + Container = this.container, + MessageStream = messageStream + }; } return this.messageInfo; diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/MessageWritterConcurrencyTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/MessageWritterConcurrencyTests.cs new file mode 100644 index 0000000000..95de21c117 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/MessageWritterConcurrencyTests.cs @@ -0,0 +1,94 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System.IO; +using System.Threading.Tasks; +using System; +using Xunit; +using System.Linq; +using Microsoft.OData.Tests; +using Microsoft.Test.OData.DependencyInjection; + +namespace Microsoft.OData.Core.Tests +{ + public class MessageWriterConcurrencyTests + { + /// + /// Verifies that concurrent message writer does not interleave execution and isolates the underlying streams. + /// + /// A task for the asynchronous test + [Fact] + public async Task VerifyConcurrentResultsAreIsolatedAsync() + { + TestContainerBuilder containerBuilder = new TestContainerBuilder(); + containerBuilder.AddDefaultODataServices(); + IServiceProvider serviceProvider = containerBuilder.BuildContainer(); + + string content1 = string.Concat(Enumerable.Repeat('A', 1000_000)); + string content2 = string.Concat(Enumerable.Repeat('B', 1000_000)); + + await TaskUtils.CompletedTask; + for (int i = 0; i < 1000; i++) + { + string[] values = await Task.WhenAll(WritePayloadAsync(content1, serviceProvider), WritePayloadAsync(content2, serviceProvider)); + Assert.Equal(content1.Length, values[0].Length); + Assert.Equal(content2.Length, values[1].Length); + + Assert.Equal(content1, values[0]); + Assert.Equal(content2, values[1]); + } + } + + + /// + /// A helper function that writes to a stream using the message writer and returns the content that is present in the stream. + /// + /// String content to write. + /// A service provider with the default configurations. + /// A task that resolves to the string present in the output stream. + private static async Task WritePayloadAsync(string content, IServiceProvider serviceProvider) + { + using (Stream outputStream = new MemoryStream()) + { + + var message = new InMemoryMessage + { + Stream = outputStream, + Container = serviceProvider + }; + + var responseMessage = new ODataResponseMessage(message, writing: true, enableMessageStreamDisposal: true, maxMessageSize: -1); +#if NETCOREAPP3_1_OR_GREATER + + await using (ODataMessageWriter writer = new ODataMessageWriter(responseMessage)) + { +#else + using (ODataMessageWriter writer = new ODataMessageWriter(responseMessage)) + { +#endif + await Task.Yield(); + + await writer.WriteValueAsync(content); + + outputStream.Position = 0; + StreamReader reader = new StreamReader(outputStream); + + + await Task.Yield(); + + string response = await reader.ReadToEndAsync(); +#if NETCOREAPP3_1_OR_GREATER + + await writer.DisposeAsync(); +#else + writer.Dispose(); +#endif + return response; + } + } + } + } +} \ No newline at end of file