Implementations of new HttpContent sync methods. (#38635)
authorMarie Píchová <11718369+ManickaP@users.noreply.github.com>
Fri, 10 Jul 2020 06:52:35 +0000 (08:52 +0200)
committerGitHub <noreply@github.com>
Fri, 10 Jul 2020 06:52:35 +0000 (08:52 +0200)
21 files changed:
src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs
src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs
src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.netcoreapp.cs
src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs
src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.netcoreapp.cs
src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpClientJsonExtensionsTests.cs
src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpContentJsonExtensionsTests.cs
src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContentTests.cs
src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContentTests.netcoreapp.cs [new file with mode: 0644]
src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj
src/libraries/System.Net.Http.Json/tests/FunctionalTests/TestClasses.cs
src/libraries/System.Net.Http/ref/System.Net.Http.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs
src/libraries/System.Net.Http/tests/FunctionalTests/SyncHttpHandlerTest.cs
src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.net5.0.cs
src/libraries/System.Utf8String.Experimental/src/System.Utf8String.Experimental.csproj
src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.cs
src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.netcoreapp.cs [new file with mode: 0644]
src/libraries/System.Utf8String.Experimental/tests/System.Utf8String.Experimental.Tests.csproj
src/libraries/System.Utf8String.Experimental/tests/System/Net/Http/Utf8StringContentTests.cs
src/libraries/System.Utf8String.Experimental/tests/System/Net/Http/Utf8StringContentTests.netcoreapp.cs [new file with mode: 0644]

index 28091e6..7ffddea 100644 (file)
@@ -64,6 +64,9 @@ namespace System.Net.Http.Functional.Tests
             }
         }
 
+        private HttpRequestMessage CreateRequest(HttpMethod method, Uri uri, Version version) =>
+            new HttpRequestMessage(method, uri) { Version = version };
+
         [Theory]
         [MemberData(nameof(DecompressedResponse_MethodSpecified_DecompressedContentReturned_MemberData))]
         public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(
@@ -162,11 +165,17 @@ namespace System.Net.Http.Functional.Tests
         [Theory, MemberData(nameof(RemoteServersAndCompressionUris))]
         public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed(Configuration.Http.RemoteServer remoteServer, Uri uri)
         {
+            // Sync API supported only up to HTTP/1.1
+            if (!TestAsync && remoteServer.HttpVersion.Major >= 2)
+            {
+                return;
+            }
+
             HttpClientHandler handler = CreateHttpClientHandler();
             handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
             using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler))
             {
-                using (HttpResponseMessage response = await client.GetAsync(uri))
+                using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion)))
                 {
                     Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                     string responseContent = await response.Content.ReadAsStringAsync();
@@ -184,10 +193,16 @@ namespace System.Net.Http.Functional.Tests
         [Theory, MemberData(nameof(RemoteServersAndCompressionUris))]
         public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuration.Http.RemoteServer remoteServer, Uri uri)
         {
+            // Sync API supported only up to HTTP/1.1
+            if (!TestAsync && remoteServer.HttpVersion.Major >= 2)
+            {
+                return;
+            }
+
             HttpClientHandler handler = CreateHttpClientHandler();
             handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
             using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler))
-            using (HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead))
+            using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion), HttpCompletionOption.ResponseHeadersRead))
             {
                 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
 
@@ -236,7 +251,7 @@ namespace System.Net.Http.Functional.Tests
                         client.DefaultRequestHeaders.Add("Accept-Encoding", manualAcceptEncodingHeaderValues);
                     }
 
-                    Task<HttpResponseMessage> clientTask = client.GetAsync(url);
+                    Task<HttpResponseMessage> clientTask = client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, url, UseVersion));
                     Task<List<string>> serverTask = server.AcceptConnectionSendResponseAndCloseAsync();
                     await TaskTimeoutExtensions.WhenAllOrAnyFailed(new Task[] { clientTask, serverTask });
 
index 79605ba..9d0e62e 100644 (file)
@@ -109,6 +109,25 @@ namespace System.Net.Http.Functional.Tests
                 _expectedVersion = expectedVersion;
             }
 
+#if NETCOREAPP
+            protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
+            {
+                if (request.Version != _expectedVersion)
+                {
+                    throw new Exception($"Unexpected request version: expected {_expectedVersion}, saw {request.Version}");
+                }
+
+                HttpResponseMessage response = base.Send(request, cancellationToken);
+
+                if (response.Version != _expectedVersion)
+                {
+                    throw new Exception($"Unexpected response version: expected {_expectedVersion}, saw {response.Version}");
+                }
+
+                return response;
+            }
+#endif
+
             protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
             {
                 if (request.Version != _expectedVersion)
index d362d2d..0388fef 100644 (file)
@@ -12,6 +12,7 @@ namespace System.Net.Http.Json
 {
     public sealed partial class JsonContent : System.Net.Http.HttpContent
     {
+        protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
         protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { throw null; }
     }
 }
index 89623cc..3e28569 100644 (file)
@@ -51,7 +51,7 @@ namespace System.Net.Http.Json
             => new JsonContent(inputValue, inputType, mediaType, options);
 
         protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
-            => SerializeToStreamAsyncCore(stream, CancellationToken.None);
+            => SerializeToStreamAsyncCore(stream, async: true, CancellationToken.None);
 
         protected override bool TryComputeLength(out long length)
         {
@@ -59,7 +59,7 @@ namespace System.Net.Http.Json
             return false;
         }
 
-        private async Task SerializeToStreamAsyncCore(Stream targetStream, CancellationToken cancellationToken)
+        private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, CancellationToken cancellationToken)
         {
             Encoding? targetEncoding = GetEncoding(Headers.ContentType?.CharSet);
 
@@ -70,15 +70,34 @@ namespace System.Net.Http.Json
                 Stream transcodingStream = Encoding.CreateTranscodingStream(targetStream, targetEncoding, Encoding.UTF8, leaveOpen: true);
                 try
                 {
-                    await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+                    if (async)
+                    {
+                        await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly.
+                        // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574
+                        using var writer = new Utf8JsonWriter(transcodingStream);
+                        JsonSerializer.Serialize(writer, Value, ObjectType, _jsonSerializerOptions);
+                    }
                 }
                 finally
                 {
-                    // DisposeAsync will flush any partial write buffers. In practice our partial write
+                    // Dispose/DisposeAsync will flush any partial write buffers. In practice our partial write
                     // buffers should be empty as we expect JsonSerializer to emit only well-formed UTF-8 data.
-                    await transcodingStream.DisposeAsync().ConfigureAwait(false);
+                    if (async)
+                    {
+                        await transcodingStream.DisposeAsync().ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        transcodingStream.Dispose();
+                    }
                 }
 #else
+                Debug.Assert(async);
+
                 using (TranscodingWriteStream transcodingStream = new TranscodingWriteStream(targetStream, targetEncoding))
                 {
                     await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
@@ -91,7 +110,21 @@ namespace System.Net.Http.Json
             }
             else
             {
-                await JsonSerializer.SerializeAsync(targetStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+                if (async)
+                {
+                    await JsonSerializer.SerializeAsync(targetStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+                }
+                else
+                {
+#if NETCOREAPP
+                    // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly.
+                    // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574
+                    using var writer = new Utf8JsonWriter(targetStream);
+                    JsonSerializer.Serialize(writer, Value, ObjectType, _jsonSerializerOptions);
+#else
+                    Debug.Fail("Synchronous serialization is only supported since .NET 5.0");
+#endif
+                }
             }
         }
 
index 306029e..7520cd7 100644 (file)
@@ -9,7 +9,10 @@ namespace System.Net.Http.Json
 {
     public sealed partial class JsonContent
     {
+        protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken)
+            => SerializeToStreamAsyncCore(stream, async: false, cancellationToken).GetAwaiter().GetResult();
+
         protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
-            => SerializeToStreamAsyncCore(stream, cancellationToken);
+            => SerializeToStreamAsyncCore(stream, async: true, cancellationToken);
     }
 }
index 0bc97ea..7ad9f4c 100644 (file)
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Threading.Tasks;
@@ -13,17 +13,10 @@ namespace System.Net.Http.Json.Functional.Tests
 {
     public class HttpClientJsonExtensionsTests
     {
-        private static readonly JsonSerializerOptions s_defaultSerializerOptions
-            = new JsonSerializerOptions
-            {
-                PropertyNameCaseInsensitive = true,
-                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
-            };
-
         [Fact]
         public async Task TestGetFromJsonAsync()
         {
-            const string json = @"{""Name"":""David"",""Age"":24}";
+            string json = Person.Create().Serialize();
             HttpHeaderData header = new HttpHeaderData("Content-Type", "application/json");
             List<HttpHeaderData> headers = new List<HttpHeaderData> { header };
 
@@ -89,7 +82,7 @@ namespace System.Net.Http.Json.Functional.Tests
                 async server => {
                     HttpRequestData request = await server.HandleRequestAsync();
                     ValidateRequest(request);
-                    Person per = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
+                    Person per = JsonSerializer.Deserialize<Person>(request.Body, JsonOptions.DefaultSerializerOptions);
                     per.Validate();
                 });
         }
@@ -121,7 +114,7 @@ namespace System.Net.Http.Json.Functional.Tests
                 async server => {
                     HttpRequestData request = await server.HandleRequestAsync();
                     ValidateRequest(request);
-                    Person obj = JsonSerializer.Deserialize<Person>(request.Body, s_defaultSerializerOptions);
+                    Person obj = JsonSerializer.Deserialize<Person>(request.Body, JsonOptions.DefaultSerializerOptions);
                     obj.Validate();
                 });
         }
index c0edaba..99d5f1b 100644 (file)
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Collections.Generic;
@@ -154,7 +154,7 @@ namespace System.Net.Http.Json.Functional.Tests
         [Fact]
         public async Task TestGetFromJsonAsyncTextPlainUtf16Async()
         {
-            const string json = @"{""Name"":""David"",""Age"":24}";
+            string json = Person.Create().Serialize();
             await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
                 async (handler, uri) =>
                 {
index 95fcb80..a23a7c8 100644 (file)
@@ -1,8 +1,9 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Net.Http.Headers;
 using System.Net.Test.Common;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
@@ -10,8 +11,9 @@ using Xunit;
 
 namespace System.Net.Http.Json.Functional.Tests
 {
-    public class JsonContentTests
+    public abstract class JsonContentTestsBase
     {
+        protected abstract Task<HttpResponseMessage> SendAsync(HttpClient client, HttpRequestMessage request);
 
         private class Foo { }
         private class Bar { }
@@ -80,7 +82,7 @@ namespace System.Net.Http.Json.Functional.Tests
 
                         var request = new HttpRequestMessage(HttpMethod.Post, uri);
                         request.Content = content;
-                        await client.SendAsync(request);
+                        await SendAsync(client, request);
                     }
                 },
                 async server => {
@@ -112,7 +114,7 @@ namespace System.Net.Http.Json.Functional.Tests
                         var request = new HttpRequestMessage(HttpMethod.Post, uri);
                         MediaTypeHeaderValue mediaType = MediaTypeHeaderValue.Parse("foo/bar; charset=utf-8");
                         request.Content = JsonContent.Create(Person.Create(), mediaType: mediaType);
-                        await client.SendAsync(request);
+                        await SendAsync(client, request);
                     }
                 },
                 async server => {
@@ -159,7 +161,7 @@ namespace System.Net.Http.Json.Functional.Tests
         }
 
         [Fact]
-        public static async Task ValidateUtf16IsTranscodedAsync()
+        public async Task ValidateUtf16IsTranscodedAsync()
         {
             await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
                 async (handler, uri) =>
@@ -170,7 +172,7 @@ namespace System.Net.Http.Json.Functional.Tests
                         MediaTypeHeaderValue mediaType = MediaTypeHeaderValue.Parse("application/json; charset=utf-16");
                         // Pass new options to avoid using the Default Web Options that use camelCase.
                         request.Content = JsonContent.Create(Person.Create(), mediaType: mediaType, options: new JsonSerializerOptions());
-                        await client.SendAsync(request);
+                        await SendAsync(client, request);
                     }
                 },
                 async server => {
@@ -193,7 +195,7 @@ namespace System.Net.Http.Json.Functional.Tests
                         EnsureDefaultOptions dummyObj = new EnsureDefaultOptions();
                         var request = new HttpRequestMessage(HttpMethod.Post, uri);
                         request.Content = JsonContent.Create(dummyObj);
-                        await client.SendAsync(request);
+                        await SendAsync(client, request);
                     }
                 },
                 server => server.HandleRequestAsync());
@@ -213,7 +215,7 @@ namespace System.Net.Http.Json.Functional.Tests
                         content.Headers.ContentType = null;
 
                         request.Content = content;
-                        await client.SendAsync(request);
+                        await SendAsync(client, request);
                     }
                 },
                 async server => {
@@ -222,4 +224,9 @@ namespace System.Net.Http.Json.Functional.Tests
                 });
         }
     }
+
+    public class JsonContentTests_Async : JsonContentTestsBase
+    {
+        protected override Task<HttpResponseMessage> SendAsync(HttpClient client, HttpRequestMessage request) => client.SendAsync(request);
+    }
 }
diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContentTests.netcoreapp.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContentTests.netcoreapp.cs
new file mode 100644 (file)
index 0000000..dcbece5
--- /dev/null
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Net.Http.Headers;
+using System.Net.Test.Common;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Net.Http.Json.Functional.Tests
+{
+    public class JsonContentTests_Sync : JsonContentTestsBase
+    {
+        protected override Task<HttpResponseMessage> SendAsync(HttpClient client, HttpRequestMessage request) => Task.Run(() => client.Send(request));
+
+        [Fact]
+        public void JsonContent_CopyTo_Succeeds()
+        {
+            Person person = Person.Create();
+            using JsonContent content = JsonContent.Create(person);
+            using MemoryStream stream = new MemoryStream();
+            // HttpContent.CopyTo internally calls overriden JsonContent.SerializeToStream, which is the targeted method of this test.
+            content.CopyTo(stream, context: null, cancellationToken: default);
+            stream.Seek(0, SeekOrigin.Begin);
+            using StreamReader reader = new StreamReader(stream);
+            string json = reader.ReadToEnd();
+            Assert.Equal(person.Serialize(JsonOptions.DefaultSerializerOptions), json);
+        }
+    }
+}
index 8d3e902..2653fcb 100644 (file)
@@ -8,6 +8,9 @@
     <Compile Include="JsonContentTests.cs" />
     <Compile Include="TestClasses.cs" />
   </ItemGroup>
+  <ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
+    <Compile Include="JsonContentTests.netcoreapp.cs" />
+  </ItemGroup>
   <ItemGroup>
     <Compile Include="$(CommonTestPath)System\Net\Capability.Security.cs"
              Link="Common\System\Net\Capability.Security.cs" />
index 2318193..2ca9ed4 100644 (file)
@@ -13,25 +13,37 @@ namespace System.Net.Http.Json.Functional.Tests
         public int Age { get; set; }
         public string Name { get; set; }
         public Person Parent { get; set; }
+        public string PlaceOfBirth { get; set; }
 
         public void Validate()
         {
-            Assert.Equal("David", Name);
-            Assert.Equal(24, Age);
+            Assert.Equal("R. Daneel Olivaw", Name);
+            Assert.Equal(19_230, Age);
+            Assert.Equal("Horní Dolní", PlaceOfBirth);
             Assert.Null(Parent);
         }
 
         public static Person Create()
         {
-            return new Person { Name = "David", Age = 24 };
+            return new Person { Name = "R. Daneel Olivaw", Age = 19_230, PlaceOfBirth = "Horní Dolní"};
         }
 
-        public string Serialize()
+        public string Serialize(JsonSerializerOptions options = null)
         {
-            return JsonSerializer.Serialize(this);
+            return JsonSerializer.Serialize(this, options);
         }
     }
 
+    internal static class JsonOptions
+    {
+        public static readonly JsonSerializerOptions DefaultSerializerOptions
+            = new JsonSerializerOptions
+            {
+                PropertyNameCaseInsensitive = true,
+                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+            };
+    }
+
     internal class EnsureDefaultOptionsConverter : JsonConverter<EnsureDefaultOptions>
     {
         public override EnsureDefaultOptions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
index e3d9247..f656514 100644 (file)
@@ -10,6 +10,7 @@ namespace System.Net.Http
     {
         public ByteArrayContent(byte[] content) { }
         public ByteArrayContent(byte[] content, int offset, int count) { }
+        protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
         protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
         protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
         protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context) { throw null; }
@@ -251,6 +252,7 @@ namespace System.Net.Http
         public MultipartContent(string subtype) { }
         public MultipartContent(string subtype, string boundary) { }
         public virtual void Add(System.Net.Http.HttpContent content) { }
+        protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
         protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
         protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
         protected override void Dispose(bool disposing) { }
@@ -273,6 +275,7 @@ namespace System.Net.Http
     public sealed partial class ReadOnlyMemoryContent : System.Net.Http.HttpContent
     {
         public ReadOnlyMemoryContent(System.ReadOnlyMemory<byte> content) { }
+        protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
         protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
         protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
         protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context) { throw null; }
@@ -312,6 +315,7 @@ namespace System.Net.Http
     {
         public StreamContent(System.IO.Stream content) { }
         public StreamContent(System.IO.Stream content, int bufferSize) { }
+        protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
         protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
         protected override void Dispose(bool disposing) { }
         protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
index 1380b9c..2fcd31d 100644 (file)
@@ -121,6 +121,12 @@ namespace System.Net.Http
 
             protected abstract Stream GetDecompressedStream(Stream originalStream);
 
+            protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken)
+            {
+                using Stream decompressedStream = CreateContentReadStream(cancellationToken);
+                decompressedStream.CopyTo(stream);
+            }
+
             protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
                 SerializeToStreamAsync(stream, context, CancellationToken.None);
 
index 8eea9e9..7ecfee6 100644 (file)
@@ -41,6 +41,12 @@ namespace System.Net.Http.Functional.Tests
         protected override bool TestAsync => false;
     }
 
+    public sealed class SyncHttpHandler_HttpClientHandler_Decompression_Tests : HttpClientHandler_Decompression_Test
+    {
+        public SyncHttpHandler_HttpClientHandler_Decompression_Tests(ITestOutputHelper output) : base(output) { }
+        protected override bool TestAsync => false;
+    }
+
     public sealed class SyncHttpHandler_IdnaProtocolTests : IdnaProtocolTests
     {
         public SyncHttpHandler_IdnaProtocolTests(ITestOutputHelper output) : base(output) { }
index fdccb75..85f2598 100644 (file)
@@ -19,6 +19,8 @@ namespace System.Net.Http
 {
     public sealed partial class Utf8StringContent : System.Net.Http.HttpContent
     {
+        protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
+        protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
         protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { throw null; }
     }
 }
index 3c93df3..4496422 100644 (file)
     <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Searching.cs"
              Link="System\Text\Utf8Span.Searching.cs" />
   </ItemGroup>
+  <ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
+    <Compile Include="System\Net\Http\Utf8StringContent.netcoreapp.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
     <Reference Include="System.Buffers" />
     <Reference Include="System.Memory" />
index a6e3452..3efd25f 100644 (file)
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
 
 namespace System.Net.Http
 {
-    public sealed class Utf8StringContent : HttpContent
+    public sealed partial class Utf8StringContent : HttpContent
     {
         private const string DefaultMediaType = "text/plain";
 
@@ -42,7 +42,7 @@ namespace System.Net.Http
             Task.FromResult<Stream>(new Utf8StringStream(_content));
 
 #if NETSTANDARD2_0
-        protected async override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
+        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context)
         {
             ReadOnlyMemory<byte> buffer = _content.AsMemoryBytes();
             if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
diff --git a/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.netcoreapp.cs b/src/libraries/System.Utf8String.Experimental/src/System/Net/Http/Utf8StringContent.netcoreapp.cs
new file mode 100644 (file)
index 0000000..d8a7b51
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.IO;
+using System.Net.Http.Headers;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Net.Http
+{
+    public sealed partial class Utf8StringContent
+    {
+        protected override Stream CreateContentReadStream(CancellationToken cancellationToken) =>
+            new Utf8StringStream(_content);
+
+        protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken) =>
+            stream.Write(_content.AsBytes());
+    }
+}
index 73e5276..1edb4b7 100644 (file)
@@ -37,6 +37,7 @@
     <Compile Include="System\Utf8TestUtilities.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(IsPrerelease)' != 'false' and '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
+    <Compile Include="System\Net\Http\Utf8StringContentTests.netcoreapp.cs" />
     <Compile Include="System\MemoryTests.netcoreapp.cs" />
     <Compile Include="System\ReflectionTests.netcoreapp.cs" />
     <Compile Include="System\Utf8ExtensionsTests.netcoreapp.cs" />
index eedac58..9ea0a4c 100644 (file)
@@ -32,7 +32,7 @@ namespace System.Net.Http.Tests
         }
 
         [Fact]
-        public static async Task Ctor_GetStream()
+        public static async Task Ctor_CopyToAsync_GetStream()
         {
             MemoryStream memoryStream = new MemoryStream();
 
diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Net/Http/Utf8StringContentTests.netcoreapp.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Net/Http/Utf8StringContentTests.netcoreapp.cs
new file mode 100644 (file)
index 0000000..8df8b1e
--- /dev/null
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+using static System.Tests.Utf8TestUtilities;
+
+namespace System.Net.Http.Tests
+{
+    public partial class Utf8StringContentTests
+    {
+        [Fact]
+        public static void Ctor_CopyTo_GetStream()
+        {
+            var memoryStream = new MemoryStream();
+
+            new Utf8StringContent(u8("Hello")).CopyTo(memoryStream, default, default);
+
+            Assert.Equal(u8("Hello").ToByteArray(), memoryStream.ToArray());
+        }
+
+        [Fact]
+        public static void Ctor_ReadAsStream()
+        {
+            var content = new Utf8StringContent(u8("Hello"));
+            Stream stream = content.ReadAsStream();
+
+            var memoryStream = new MemoryStream();
+            stream.CopyTo(memoryStream);
+            memoryStream.Seek(0, SeekOrigin.Begin);
+            Assert.Equal(u8("Hello").ToByteArray(), memoryStream.ToArray());
+        }
+    }
+}