From 7f67c65bb2bfde2b71933c4542d99e61c61c7de7 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 25 Jun 2021 18:12:30 +0200 Subject: [PATCH 1/3] - browser http response stream could be seekable - test WebAssemblyEnableStreamingResponse --- .../System/Net/Http/HttpClientHandlerTest.cs | 157 ++++++++++++------ 1 file changed, 103 insertions(+), 54 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index d9958e9c5209a..7ddd187eec7e0 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -937,7 +937,6 @@ await connection.WriteStringAsync( [InlineData(true)] [InlineData(false)] [InlineData(null)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -960,6 +959,11 @@ public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(boo await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; + if (PlatformDetection.IsBrowser) + { + request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); + } + using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) using (HttpResponseMessage response = await client.SendAsync(TestAsync, request, CancellationToken.None)) { @@ -974,17 +978,20 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK Assert.Throws(() => responseStream.Write(new Span(new byte[1]))); - Assert.Throws(() => { responseStream.WriteAsync(new Memory(new byte[1])); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new Memory(new byte[1]))); #endif - Assert.Throws(() => { responseStream.WriteAsync(new byte[1], 0, 1); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new byte[1], 0, 1)); Assert.Throws(() => responseStream.WriteByte(1)); // Invalid arguments @@ -998,11 +1005,14 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => Assert.Throws(() => { responseStream.CopyToAsync(Stream.Null, -1, default); }); Assert.Throws(() => { responseStream.CopyToAsync(nonWritableStream, 100, default); }); Assert.Throws(() => { responseStream.CopyToAsync(disposedStream, 100, default); }); - Assert.Throws(() => responseStream.Read(null, 0, 100)); - Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); - Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + if (PlatformDetection.IsNotBrowser) + { + Assert.Throws(() => responseStream.Read(null, 0, 100)); + Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); + Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + } Assert.Throws(() => responseStream.BeginRead(null, 0, 100, null, null)); Assert.Throws(() => responseStream.BeginRead(new byte[1], -1, 1, null, null)); Assert.ThrowsAny(() => responseStream.BeginRead(new byte[1], 2, 1, null, null)); @@ -1018,62 +1028,99 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => // Various forms of reading var buffer = new byte[1]; - Assert.Equal('h', responseStream.ReadByte()); + if (PlatformDetection.IsBrowser) + { +#if !NETFRAMEWORK + Assert.Equal('h', await responseStream.ReadByteAsync()); + Assert.Equal('e', await responseStream.ReadByteAsync()); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer)); + Assert.Equal((byte)'o', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); + + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); +#endif + } + else + { + Assert.Equal('h', responseStream.ReadByte()); + Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal((byte)'e', buffer[0]); - Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); - Assert.Equal((byte)'e', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal((byte)'l', buffer[0]); - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, responseStream.Read(new Span(buffer))); + Assert.Equal(1, responseStream.Read(new Span(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'o', buffer[0]); + Assert.Equal((byte)'o', buffer[0]); - Assert.Equal(1, responseStream.Read(buffer, 0, 1)); - Assert.Equal((byte)' ', buffer[0]); + Assert.Equal(1, responseStream.Read(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); - // Doing any of these 0-byte reads causes the connection to fail. - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); #endif - Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(Span.Empty)); + Assert.Equal(0, responseStream.Read(Span.Empty)); #endif - Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); - - // And copying - var ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); - - // Read and copy again once we've exhausted all data - ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - responseStream.CopyTo(ms); - Assert.Equal(0, ms.Length); - Assert.Equal(-1, responseStream.ReadByte()); - Assert.Equal(0, responseStream.Read(buffer, 0, 1)); + Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); + + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + responseStream.CopyTo(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(-1, responseStream.ReadByte()); + Assert.Equal(0, responseStream.Read(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(new Span(buffer))); + Assert.Equal(0, responseStream.Read(new Span(buffer))); #endif - Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); #endif - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + } } } }, async server => @@ -1103,7 +1150,6 @@ await server.AcceptConnectionAsync(async connection => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1123,14 +1169,17 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.False(responseStream.CanSeek); + Assert.Equal(PlatformDetection.IsBrowser, responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK From 443428b3b8ded614fbaf748594e9f2f14957115b Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 25 Jun 2021 18:15:28 +0200 Subject: [PATCH 2/3] fix --- .../Common/tests/System/Net/Http/HttpClientHandlerTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 7ddd187eec7e0..35d35e22c2898 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -961,7 +961,9 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; if (PlatformDetection.IsBrowser) { +#if !NETFRAMEWORK request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); +#endif } using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) From 80b8d7b6ca04165f8bd152dab9b36a62b97bc62d Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 28 Jun 2021 16:48:53 +0200 Subject: [PATCH 3/3] feedback --- .../System/Net/Http/HttpClientHandlerTest.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 35d35e22c2898..98c23bdcde0c0 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -934,10 +934,12 @@ await connection.WriteStringAsync( } [Theory] - [InlineData(true)] - [InlineData(false)] - [InlineData(null)] - public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + [InlineData(null, false)] + public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked, bool enableWasmStreaming) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) { @@ -961,9 +963,12 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; if (PlatformDetection.IsBrowser) { + if (enableWasmStreaming) + { #if !NETFRAMEWORK - request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); + request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); #endif + } } using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) @@ -976,7 +981,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.False(responseStream.CanSeek); + Assert.Equal(PlatformDetection.IsBrowser && !enableWasmStreaming, responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null));