TestServer returns error with response when server ends with p… (#19249)

This commit is contained in:
James Newton-King 2020-02-25 13:39:06 +13:00 committed by GitHub
parent 87037b84e0
commit 0b04260b13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 18 deletions

View File

@ -111,9 +111,14 @@ namespace Microsoft.AspNetCore.TestHost
{ {
await _application.ProcessRequestAsync(_testContext); await _application.ProcessRequestAsync(_testContext);
// Determine whether request body was complete when the delegate exited.
// This could throw an error if there was a pending server read. Needs to
// happen before completing the response so the response returns the error.
var requestBodyInProgress = RequestBodyReadInProgress();
// Matches Kestrel server: response is completed before request is drained // Matches Kestrel server: response is completed before request is drained
await CompleteResponseAsync(); await CompleteResponseAsync();
await CompleteRequestAsync(); await CompleteRequestAsync(requestBodyInProgress);
_application.DisposeContext(_testContext, exception: null); _application.DisposeContext(_testContext, exception: null);
} }
catch (Exception ex) catch (Exception ex)
@ -160,18 +165,8 @@ namespace Microsoft.AspNetCore.TestHost
CancelRequestBody(); CancelRequestBody();
} }
private async Task CompleteRequestAsync() private async Task CompleteRequestAsync(bool requestBodyInProgress)
{ {
bool requestBodyInProgress;
try
{
requestBodyInProgress = !_requestPipe.Reader.TryRead(out var result) || !result.IsCompleted;
}
catch (Exception ex)
{
throw new InvalidOperationException("An error occurred when completing the request. Request delegate may have finished while there is a pending read of the request body.", ex);
}
if (requestBodyInProgress) if (requestBodyInProgress)
{ {
// If request is still in progress then abort it. // If request is still in progress then abort it.
@ -188,6 +183,18 @@ namespace Microsoft.AspNetCore.TestHost
// Potential future improvement: add logging that the request timed out // Potential future improvement: add logging that the request timed out
} }
private bool RequestBodyReadInProgress()
{
try
{
return !_requestPipe.Reader.TryRead(out var result) || !result.IsCompleted;
}
catch (Exception ex)
{
throw new InvalidOperationException("An error occurred when completing the request. Request delegate may have finished while there is a pending read of the request body.", ex);
}
}
internal async Task CompleteResponseAsync() internal async Task CompleteResponseAsync()
{ {
_pipelineFinished = true; _pipelineFinished = true;

View File

@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.TestHost
private bool _aborted; private bool _aborted;
private Exception _abortException; private Exception _abortException;
private readonly object _abortLock = new object();
private readonly Action _abortRequest; private readonly Action _abortRequest;
private readonly Action _readComplete; private readonly Action _readComplete;
private readonly Pipe _pipe; private readonly Pipe _pipe;
@ -124,16 +125,24 @@ namespace Microsoft.AspNetCore.TestHost
internal void Abort(Exception innerException) internal void Abort(Exception innerException)
{ {
Contract.Requires(innerException != null); Contract.Requires(innerException != null);
_aborted = true;
_abortException = innerException; lock (_abortLock)
{
_abortException = innerException;
_aborted = true;
}
_pipe.Reader.CancelPendingRead(); _pipe.Reader.CancelPendingRead();
} }
private void CheckAborted() private void CheckAborted()
{ {
if (_aborted) lock (_abortLock)
{ {
throw new IOException(string.Empty, _abortException); if (_aborted)
{
throw new IOException(string.Empty, _abortException);
}
} }
} }

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -386,7 +385,6 @@ namespace Microsoft.AspNetCore.TestHost
} }
[Fact] [Fact]
[Flaky("<No longer used; tracked in Kusto>", FlakyOn.All)]
public async Task ClientStreaming_ResponseCompletesWithPendingRead_ThrowError() public async Task ClientStreaming_ResponseCompletesWithPendingRead_ThrowError()
{ {
// Arrange // Arrange