Fix TestServer hang with duplex streaming requests (#17158)

This commit is contained in:
James Newton-King 2020-01-16 08:08:41 +13:00 committed by Andrew Stanton-Nurse
parent ad8ecf96a1
commit 34294a3e7a
2 changed files with 53 additions and 13 deletions

View File

@ -110,8 +110,10 @@ namespace Microsoft.AspNetCore.TestHost
try
{
await _application.ProcessRequestAsync(_testContext);
await CompleteRequestAsync();
// Matches Kestrel server: response is completed before request is drained
await CompleteResponseAsync();
await CompleteRequestAsync();
_application.DisposeContext(_testContext, exception: null);
}
catch (Exception ex)
@ -171,18 +173,9 @@ namespace Microsoft.AspNetCore.TestHost
await _requestPipe.Reader.CompleteAsync();
}
if (_sendRequestStreamTask != null)
{
try
{
// Ensure duplex request is either completely read or has been aborted.
await _sendRequestStreamTask;
}
catch (OperationCanceledException)
{
// Request was canceled, likely because it wasn't read before the request ended.
}
}
// Don't wait for request to drain. It could block indefinitely. In a real server
// we would wait for a timeout and then kill the socket.
// Potential future improvement: add logging that the request timed out
}
internal async Task CompleteResponseAsync()

View File

@ -384,6 +384,53 @@ namespace Microsoft.AspNetCore.TestHost
await writeTask;
}
[Fact]
public async Task ClientStreaming_ResponseCompletesWithoutResponseBodyWrite()
{
// Arrange
var requestStreamTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
RequestDelegate appDelegate = ctx =>
{
ctx.Response.Headers["test-header"] = "true";
return Task.CompletedTask;
};
Stream requestStream = null;
var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate));
var server = new TestServer(builder);
var client = server.CreateClient();
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345");
httpRequest.Version = new Version(2, 0);
httpRequest.Content = new PushContent(async stream =>
{
requestStream = stream;
await requestStreamTcs.Task;
});
// Act
var response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).WithTimeout();
var responseContent = await response.Content.ReadAsStreamAsync().WithTimeout();
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("true", response.Headers.GetValues("test-header").Single());
// Read response
byte[] buffer = new byte[1024];
var length = await responseContent.ReadAsync(buffer).AsTask().WithTimeout();
Assert.Equal(0, length);
// Writing to request stream will fail because server is complete
await Assert.ThrowsAnyAsync<Exception>(() => requestStream.WriteAsync(buffer).AsTask());
// Unblock request
requestStreamTcs.TrySetResult(null);
}
[Fact]
public async Task ClientStreaming_ServerAbort()
{