From 59417ef2d756d3ce1a3d113d87af5bb132ed7da4 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Mon, 25 Jan 2021 16:30:29 -0800 Subject: [PATCH] Get rid of StreamWriter usage in HTTP loopback server and fix HTTP/1.1 loopback implementation of SendResponseBodyAsync (#47364) Get rid of StreamWriter usage in HTTP loopback server and fix HTTP/1.1 loopback implementation of SendResponseBodyAsync Co-authored-by: Geoffrey Kizer --- .../Net/Http/HttpClientHandlerTest.Asynchrony.cs | 2 +- .../Net/Http/HttpClientHandlerTest.Cancellation.cs | 4 ++-- .../Http/HttpClientHandlerTest.Decompression.cs | 4 ++-- ...tpClientHandlerTest.MaxResponseHeadersLength.cs | 2 +- .../tests/System/Net/Http/HttpClientHandlerTest.cs | 3 +-- .../tests/System/Net/Http/HttpProtocolTests.cs | 10 +++++----- .../Common/tests/System/Net/Http/LoopbackServer.cs | 22 ++++++++++++++-------- .../tests/System/Net/Http/ResponseStreamTest.cs | 19 +++++++++---------- .../HttpClientHandlerTest.Connect.cs | 2 +- .../HttpClientHandlerTest.Headers.cs | 12 +++++++++--- .../HttpClientHandlerTest.ResponseDrain.cs | 4 ++-- .../FunctionalTests/HttpClientMiniStressTest.cs | 2 +- .../tests/FunctionalTests/HttpClientTest.cs | 5 ++--- .../FunctionalTests/SocketsHttpHandlerTest.cs | 6 +++--- .../tests/LoopbackHelper.cs | 2 +- 15 files changed, 54 insertions(+), 45 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs index 44c4a39..6834f83 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs @@ -59,7 +59,7 @@ namespace System.Net.Http.Functional.Tests await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAsync(); - await connection.Writer.WriteAsync( + await connection.WriteStringAsync( LoopbackServer.GetContentModeResponse( contentMode, string.Concat(Enumerable.Repeat('s', 10_000)), diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index 72c02b6..d246ca5 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -351,10 +351,10 @@ namespace System.Net.Http.Functional.Tests Task serverTask1 = server.AcceptConnectionAsync(async connection1 => { await connection1.ReadRequestHeaderAsync(); - await connection1.Writer.WriteAsync($"HTTP/1.1 200 OK\r\nConnection: close\r\nDate: {DateTimeOffset.UtcNow:R}\r\n"); + await connection1.WriteStringAsync($"HTTP/1.1 200 OK\r\nConnection: close\r\nDate: {DateTimeOffset.UtcNow:R}\r\n"); serverAboutToBlock.SetResult(true); await blockServerResponse.Task; - await connection1.Writer.WriteAsync("Content-Length: 5\r\n\r\nhello"); + await connection1.WriteStringAsync("Content-Length: 5\r\n\r\nhello"); }); Task get1 = client.GetAsync(url); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index ae0384d..fe808e7 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -101,7 +101,7 @@ namespace System.Net.Http.Functional.Tests await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAsync(); - await connection.Writer.WriteAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n"); + await connection.WriteStringAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n"); using (Stream compressedStream = compress(connection.Stream)) { await compressedStream.WriteAsync(expectedContent); @@ -163,7 +163,7 @@ namespace System.Net.Http.Functional.Tests await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAsync(); - await connection.Writer.WriteAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n"); + await connection.WriteStringAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n"); await connection.Stream.WriteAsync(compressedContent); }); }); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs index 3117f52..2c834e9 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs @@ -86,7 +86,7 @@ namespace System.Net.Http.Functional.Tests { while (!cts.IsCancellationRequested) { - await connection.Writer.WriteAsync(new string('s', 16000)); + await connection.WriteStringAsync(new string('s', 16000)); await Task.Delay(1); } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 2cb88d3..885a202 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -969,12 +969,11 @@ namespace System.Net.Http.Functional.Tests Task serverTask = server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); - TextWriter writer = connection.Writer; try { while (!cts.IsCancellationRequested) // infinite to make sure implementation doesn't OOM { - await writer.WriteAsync(new string(' ', 10000)); + await connection.WriteStringAsync(new string(' ', 10000)); await Task.Delay(1); } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs index 4eb602c..ec7bb19 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs @@ -442,18 +442,18 @@ namespace System.Net.Http.Functional.Tests { await connection.ReadRequestHeaderAsync(); - await connection.Writer.WriteAsync($"HTTP/1.1 200 OK{lineEnding}Transfer-Encoding: chunked{lineEnding}{lineEnding}"); + await connection.WriteStringAsync($"HTTP/1.1 200 OK{lineEnding}Transfer-Encoding: chunked{lineEnding}{lineEnding}"); for (int bytesSent = 0; bytesSent < expectedData.Length;) { int bytesRemaining = expectedData.Length - bytesSent; int bytesToSend = rand.Next(1, Math.Min(bytesRemaining, maxChunkSize + 1)); - await connection.Writer.WriteAsync(bytesToSend.ToString("X") + lineEnding); + await connection.WriteStringAsync(bytesToSend.ToString("X") + lineEnding); await connection.Stream.WriteAsync(new Memory(expectedData, bytesSent, bytesToSend)); - await connection.Writer.WriteAsync(lineEnding); + await connection.WriteStringAsync(lineEnding); bytesSent += bytesToSend; } - await connection.Writer.WriteAsync($"0{lineEnding}"); - await connection.Writer.WriteAsync(lineEnding); + await connection.WriteStringAsync($"0{lineEnding}"); + await connection.WriteStringAsync(lineEnding); }); }); } diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs index 845bd19..b44ae2d 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs @@ -397,7 +397,6 @@ namespace System.Net.Test.Common private const int BufferSize = 4000; private Socket _socket; private Stream _stream; - private StreamWriter _writer; private byte[] _readBuffer; private int _readStart; private int _readEnd; @@ -409,8 +408,6 @@ namespace System.Net.Test.Common _socket = socket; _stream = stream; - _writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true }; - _readBuffer = new byte[BufferSize]; _readStart = 0; _readEnd = 0; @@ -418,7 +415,6 @@ namespace System.Net.Test.Common public Socket Socket => _socket; public Stream Stream => _stream; - public StreamWriter Writer => _writer; public static async Task CreateAsync(Socket socket, Stream stream, Options httpOptions) { @@ -620,7 +616,6 @@ namespace System.Net.Test.Common } catch (Exception) { } - _writer.Dispose(); _stream.Dispose(); _socket?.Dispose(); } @@ -673,9 +668,20 @@ namespace System.Net.Test.Common return lines; } + public async Task WriteStringAsync(string s) + { + byte[] bytes = Encoding.ASCII.GetBytes(s); + await _stream.WriteAsync(bytes); + } + public async Task SendResponseAsync(string response) { - await _writer.WriteAsync(response).ConfigureAwait(false); + await WriteStringAsync(response); + } + + public async Task SendResponseAsync(byte[] response) + { + await _stream.WriteAsync(response); } public async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, string content = null) @@ -686,7 +692,7 @@ namespace System.Net.Test.Common public async Task> ReadRequestHeaderAndSendCustomResponseAsync(string response) { List lines = await ReadRequestHeaderAsync().ConfigureAwait(false); - await _writer.WriteAsync(response).ConfigureAwait(false); + await WriteStringAsync(response); return lines; } @@ -892,7 +898,7 @@ namespace System.Net.Test.Common public override async Task SendResponseBodyAsync(byte[] body, bool isFinal = true, int requestId = 0) { - await SendResponseAsync(Encoding.UTF8.GetString(body)).ConfigureAwait(false); + await SendResponseAsync(body).ConfigureAwait(false); } public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") diff --git a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs index 1851e0b..3e0e30e 100644 --- a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs @@ -321,15 +321,14 @@ namespace System.Net.Http.Functional.Tests } // Write response header - TextWriter writer = connection.Writer; - await writer.WriteAsync("HTTP/1.1 200 OK\r\n").ConfigureAwait(false); - await writer.WriteAsync($"Date: {DateTimeOffset.UtcNow:R}\r\n").ConfigureAwait(false); - await writer.WriteAsync("Content-Type: text/plain\r\n").ConfigureAwait(false); + await connection.WriteStringAsync("HTTP/1.1 200 OK\r\n").ConfigureAwait(false); + await connection.WriteStringAsync($"Date: {DateTimeOffset.UtcNow:R}\r\n").ConfigureAwait(false); + await connection.WriteStringAsync("Content-Type: text/plain\r\n").ConfigureAwait(false); if (!string.IsNullOrEmpty(transferHeader)) { - await writer.WriteAsync(transferHeader).ConfigureAwait(false); + await connection.WriteStringAsync(transferHeader).ConfigureAwait(false); } - await writer.WriteAsync("\r\n").ConfigureAwait(false); + await connection.WriteStringAsync("\r\n").ConfigureAwait(false); // Write response body if (transferType == TransferType.Chunked) @@ -337,16 +336,16 @@ namespace System.Net.Http.Functional.Tests string chunkSizeInHex = string.Format( "{0:x}\r\n", content.Length + (transferError == TransferError.ChunkSizeTooLarge ? 42 : 0)); - await writer.WriteAsync(chunkSizeInHex).ConfigureAwait(false); - await writer.WriteAsync($"{content}\r\n").ConfigureAwait(false); + await connection.WriteStringAsync(chunkSizeInHex).ConfigureAwait(false); + await connection.WriteStringAsync($"{content}\r\n").ConfigureAwait(false); if (transferError != TransferError.MissingChunkTerminator) { - await writer.WriteAsync("0\r\n\r\n").ConfigureAwait(false); + await connection.WriteStringAsync("0\r\n\r\n").ConfigureAwait(false); } } else { - await writer.WriteAsync($"{content}").ConfigureAwait(false); + await connection.WriteStringAsync($"{content}").ConfigureAwait(false); } })); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Connect.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Connect.cs index 9a178da..16587b3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Connect.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Connect.cs @@ -52,7 +52,7 @@ namespace System.Net.Http.Functional.Tests TextReader clientReader = new StreamReader(clientStream); TextWriter clientWriter = new StreamWriter(clientStream) { AutoFlush = true }; - TextWriter serverWriter = connection.Writer; + TextWriter serverWriter = new StreamWriter(connection.Stream, leaveOpen: true) { AutoFlush = true }; const string helloServer = "hello server"; const string helloClient = "hello client"; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index 53e81d9..f3f264d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -391,10 +391,16 @@ namespace System.Net.Http.Functional.Tests }, async server => { - await server.HandleRequestAsync(headers: new[] + // The client may detect the bad header and close the connection before we are done sending the response. + // So, eat any IOException that occurs here. + try { - new HttpHeaderData("", "foo") - }); + await server.HandleRequestAsync(headers: new[] + { + new HttpHeaderData("", "foo") + }); + } + catch (IOException) { } }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ResponseDrain.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ResponseDrain.cs index 8bd2083..6c17426 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ResponseDrain.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ResponseDrain.cs @@ -128,7 +128,7 @@ namespace System.Net.Http.Functional.Tests await connection.ReadRequestHeaderAsync().ConfigureAwait(false); foreach (char c in response) { - await connection.Writer.WriteAsync(c); + await connection.WriteStringAsync(c.ToString()); } // Process the second request. @@ -197,7 +197,7 @@ namespace System.Net.Http.Functional.Tests await connection.ReadRequestHeaderAsync(); try { - await connection.Writer.WriteAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: false)); + await connection.WriteStringAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: false)); } catch (Exception) { } // Eat errors from client disconnect. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs index e42252e..fb54f71 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs @@ -88,7 +88,7 @@ namespace System.Net.Http.Functional.Tests while (!string.IsNullOrEmpty(await connection.ReadLineAsync().ConfigureAwait(false))); Assert.Equal(numBytes, await connection.ReadBlockAsync(postData, 0, numBytes)); - await connection.Writer.WriteAsync(responseText).ConfigureAwait(false); + await connection.WriteStringAsync(responseText).ConfigureAwait(false); connection.Socket.Shutdown(SocketShutdown.Send); }); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 3fda220..2176fce 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -1065,7 +1065,7 @@ namespace System.Net.Http.Functional.Tests cts.Cancel(); for (int i = 0; i < 100; ++i) { - await connection.Writer.WriteLineAsync(content); + await connection.WriteStringAsync(content); await Task.Delay(TimeSpan.FromSeconds(0.1)); } } @@ -1090,8 +1090,7 @@ namespace System.Net.Http.Functional.Tests await connection.SendResponseAsync(headers: new[] { new HttpHeaderData("Content-Length", (Content.Length * 100).ToString()) }); for (int i = 0; i < 100; ++i) { - await connection.Writer.WriteLineAsync(Content); - await connection.Writer.FlushAsync(); + await connection.WriteStringAsync(Content); await Task.Delay(TimeSpan.FromSeconds(0.1)); } }); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 2369bc3..724f4a3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -408,7 +408,7 @@ namespace System.Net.Http.Functional.Tests await connection.ReadRequestHeaderAsync(); try { - await connection.Writer.WriteAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: false)); + await connection.WriteStringAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: false)); } catch (Exception) { } // Eat errors from client disconnect. @@ -460,7 +460,7 @@ namespace System.Net.Http.Functional.Tests try { // Write out only part of the response - await connection.Writer.WriteAsync(response.Substring(0, response.Length / 2)); + await connection.WriteStringAsync(response.Substring(0, response.Length / 2)); } catch (Exception) { } // Eat errors from client disconnect. @@ -1324,7 +1324,7 @@ namespace System.Net.Http.Functional.Tests Task serverTask1 = server.AcceptConnectionAsync(async connection => { - await connection.Writer.WriteAsync(LoopbackServer.GetHttpResponse(connectionClose: false) + "here is a bunch of garbage"); + await connection.WriteStringAsync(LoopbackServer.GetHttpResponse(connectionClose: false) + "here is a bunch of garbage"); await releaseServer.Task; // keep connection alive on the server side }); await client.GetStringAsync(uri); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/LoopbackHelper.cs b/src/libraries/System.Net.WebSockets.Client/tests/LoopbackHelper.cs index 12c364b..82ae0bf 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/LoopbackHelper.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/LoopbackHelper.cs @@ -42,7 +42,7 @@ namespace System.Net.WebSockets.Client.Tests if (serverResponse != null) { // We received a valid WebSocket opening handshake. Send the appropriate response. - await connection.Writer.WriteAsync(serverResponse).ConfigureAwait(false); + await connection.WriteStringAsync(serverResponse).ConfigureAwait(false); return results; } -- 2.7.4