Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
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
106 changes: 106 additions & 0 deletions src/System.Net.Http/tests/FunctionalTests/LoopbackServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace System.Net.Http.Functional.Tests
{
public class LoopbackServer
{
public enum TransferType
{
None = 0,
ContentLength,
Chunked
}

public enum TransferError
{
None = 0,
ContentLengthTooLarge,
ChunkSizeTooLarge,
MissingChunkTerminator
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a flags enum so that multiple failure conditions can be specified, or are they mutually exclusive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only two that could be combined as flags would be ChunkSizeTooLarge and MissingChunkTerminator, i.e. both of the invalid conditions could be present. But I currently don't test for that. ContentLengthTooLarge is basically mutually exclusive with the other two chunk related errors. I didn't think it was necessary to test every combination of the chunked failures.


public static Task StartServer(
TransferType transferType,
TransferError transferError,
out IPEndPoint serverEndPoint)
{
var server = new TcpListener(IPAddress.Loopback, 0);
Task serverTask = ((Func<Task>)async delegate {
server.Start();
using (var client = await server.AcceptSocketAsync())
using (var stream = new NetworkStream(client))
using (var reader = new StreamReader(stream, Encoding.ASCII))
{
// Read past request headers.
string line;
while (!string.IsNullOrEmpty(line = reader.ReadLine())) ;

// Determine response transfer headers.
string transferHeader = null;
string content = "This is some response content.";
if (transferType == TransferType.ContentLength)
{
if (transferError == TransferError.ContentLengthTooLarge)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(if you change the enum as questioned, these comparisons will of course need to change, too)

{
transferHeader = $"Content-Length: {content.Length + 42}\r\n";
}
else
{
transferHeader = $"Content-Length: {content.Length}\r\n";
}
}
else if (transferType == TransferType.Chunked)
{
transferHeader = "Transfer-Encoding: chunked\r\n";
}

// Write response.
using (var writer = new StreamWriter(stream, Encoding.ASCII))
{
writer.Write("HTTP/1.1 200 OK\r\n");
writer.Write($"Date: {DateTimeOffset.UtcNow:R}\r\n");
writer.Write("Content-Type: text/plain\r\n");

if (!string.IsNullOrEmpty(transferHeader))
{
writer.Write(transferHeader);
}

writer.Write("\r\n");
if (transferType == TransferType.Chunked)
{
string chunkSizeInHex = string.Format(
"{0:x}\r\n",
content.Length + (transferError == TransferError.ChunkSizeTooLarge ? 42 : 0));
writer.Write(chunkSizeInHex);
writer.Write($"{content}\r\n");
if (transferError != TransferError.MissingChunkTerminator)
{
writer.Write("0\r\n\r\n");
}
}
else
{
writer.Write($"{content}\r\n");
}
writer.Flush();
}

client.Shutdown(SocketShutdown.Both);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is preexisting from my change, but it's there any chance anything above throws, and if so, what happens if this shutdown doesn't happen? Just wondering if this should be in a finally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If something throws, then the StartServer task will fault. And the await serverTaskin the tests themselves will throw an exception. I don't think there is a chance of the tests hanging, so it probably isn't necessary to use a finally around the .Shutdown()

}
})();

serverEndPoint = (IPEndPoint)server.LocalEndpoint;
return serverTask;
}
}
}
74 changes: 37 additions & 37 deletions src/System.Net.Http/tests/FunctionalTests/ResponseStreamTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,51 +92,51 @@ await client.GetAsync(HttpTestServers.RemoteEchoServer, HttpCompletionOption.Res
}

[ActiveIssue(6231, PlatformID.Windows)]
[Fact]
public async Task ReadAsStreamAsync_IncompleteContentLengthResponse_ThrowsIOException()
[Theory]
[InlineData(LoopbackServer.TransferType.ContentLength, LoopbackServer.TransferError.ContentLengthTooLarge)]
[InlineData(LoopbackServer.TransferType.Chunked, LoopbackServer.TransferError.MissingChunkTerminator)]
[InlineData(LoopbackServer.TransferType.Chunked, LoopbackServer.TransferError.ChunkSizeTooLarge)]
public async Task ReadAsStreamAsync_InvalidServerResponse_ThrowsIOException(
LoopbackServer.TransferType transferType,
LoopbackServer.TransferError transferError)
{
var server = new TcpListener(IPAddress.Loopback, 0);
Task serverTask = ((Func<Task>)async delegate {
server.Start();
using (var client = await server.AcceptSocketAsync())
using (var stream = new NetworkStream(client))
using (var reader = new StreamReader(stream, Encoding.ASCII))
{
// Read request
string line;
while (!string.IsNullOrEmpty(line = reader.ReadLine())) ;

// Write response, with a potentially invalid content length
using (var writer = new StreamWriter(stream, Encoding.ASCII))
{
string content = "This is some response content.";
writer.Write("HTTP/1.1 200 OK\r\n");
writer.Write($"Date: {DateTimeOffset.UtcNow:R}\r\n");
writer.Write("Content-Type: text/plain\r\n");
writer.Write($"Content-Length: {content.Length + 42}\r\n"); // incorrect length
writer.Write("\r\n");
writer.Write(content);
writer.Flush();
}

client.Shutdown(SocketShutdown.Both);
}
})();
IPEndPoint serverEndPoint;
Task serverTask = LoopbackServer.StartServer(transferType, transferError, out serverEndPoint);

await Assert.ThrowsAsync<IOException>(() => ReadAsStreamHelper(serverEndPoint));

await serverTask;
}

[Theory]
[InlineData(LoopbackServer.TransferType.None, LoopbackServer.TransferError.None)]
[InlineData(LoopbackServer.TransferType.ContentLength, LoopbackServer.TransferError.None)]
[InlineData(LoopbackServer.TransferType.Chunked, LoopbackServer.TransferError.None)]
public async Task ReadAsStreamAsync_ValidServerResponse_Success(
LoopbackServer.TransferType transferType,
LoopbackServer.TransferError transferError)
{
IPEndPoint serverEndPoint;
Task serverTask = LoopbackServer.StartServer(transferType, transferError, out serverEndPoint);

await ReadAsStreamHelper(serverEndPoint);

await serverTask;
}

private async Task ReadAsStreamHelper(IPEndPoint serverEndPoint)
{
using (var client = new HttpClient())
{
var local = (IPEndPoint)server.LocalEndpoint;
using (var response = await client.GetAsync(new Uri($"http://localhost:{((IPEndPoint)server.LocalEndpoint).Port}/"), HttpCompletionOption.ResponseHeadersRead))
using (var response = await client.GetAsync(
new Uri($"http://{serverEndPoint.Address}:{(serverEndPoint).Port}/"),
HttpCompletionOption.ResponseHeadersRead))
using (var stream = await response.Content.ReadAsStreamAsync())
{
await Assert.ThrowsAsync<IOException>(async () => {
var buffer = new byte[1];
while (await stream.ReadAsync(buffer, 0, 1) > 0) ;
});
var buffer = new byte[1];
while (await stream.ReadAsync(buffer, 0, 1) > 0) ;
}
}

await serverTask;
}

// These methods help to validate the response body since the endpoint will echo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Compile Include="HttpMethodTest.cs" />
<Compile Include="HttpRequestMessageTest.cs" />
<Compile Include="HttpResponseMessageTest.cs" />
<Compile Include="LoopbackServer.cs" />
<Compile Include="MessageProcessingHandlerTest.cs" />
<Compile Include="MultipartContentTest.cs" />
<Compile Include="MultipartFormDataContentTest.cs" />
Expand Down