dotnet / dotnet/aspnetcore

`HttpClient.PostAsync` fails to reach controller endpoint during both Debug and Run sessions in integration tests.

Open
#61,844 45 comments 0 reactions 0 assignees View on GitHub
area-mvc feature-mvc-testing
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug
`HttpClient.PostAync` does not reach controller endpoint at all. `GetAsync` and `DeleteAsync` are OK. The application web UI and swagger UI work perfectly. It seems that `HttpClient` doesn't manage to reach the controller endpoint somehow. Same behaviour observed in running a debug session of the application and sending a POST request from Postman. The POST request from Postman seems like taking forever and never hit the controller endpoint breakpoint. Debug console log:
```
[06/06/2025 10:57:19 +08:00] Debug { Id: 1, Name: "AuthenticationFailed" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Failed to authenticate HTTPS connection. {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware", ConnectionId="0HND4KKA9F3V6", ThreadId=18, ExceptionDetail={["Type"]="System.IO.IOException", ["HResult"]=-2146232800, ["Message"]="Received an unexpected EOF or 0 bytes from the transport stream.", ["Source"]="System.Net.Security", ["TargetSite"]="Void MoveNext()"}}
System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
at System.Net.Security.SslStream.ReceiveHandshakeFrameAsync[TIOAdapter](CancellationToken cancellationToken)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware.OnConnectionAsync(ConnectionContext context)

[06/06/2025 10:57:19 +08:00] Debug { Id: 2, Name: "ConnectionStop" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection id ""0HND4KKA9F3V6"" stopped. {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Connections", ThreadId=18}

[06/06/2025 10:57:19 +08:00] Debug { Id: 7, Name: "ConnectionWriteFin" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection id ""0HND4KKA9F3V6"" sending FIN because: ""The Socket transport's send loop completed gracefully."" {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets", ThreadId=18}

[06/06/2025 10:57:19 +08:00] Debug { Id: 3, Name: "HttpsConnectionEstablished" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection "0HND4KKA9F3V7" established using the following protocol: "Tls13" {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware", ThreadId=8}

Exception thrown: 'System.InvalidOperationException' in Microsoft.AspNetCore.Server.Kestrel.Core.dll
4
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll
```
Irconically, once I cancel the request on the Postman side, it hits the contoller POST method/endpoint and the request completes successfully without seeing anything unusual at the Debug Console. Of course, the Postman won't show any response since the request was already cancelled.

`Program.cs`:
```
builder.Services.AddControllersWithViews();
app.MapControllers();
```
Controller:
```
[Route("api/[controller]")]
[ApiController]
public class AccountsController : ControllerBase {
[HttpPost("register")]
[Consumes("application/json")]
public async Task Register([FromBody] Models.Request.RegisterUserRequest request)
{
if (!ModelState.IsValid)
{
_logger.LogError($"{nameof(Register)} invalid model state!");
return BadRequest(ModelState);
}
_logger.LogDebug($"{nameof(Register)}");
RegisterUserResponse response = await _mediator.Send(new RegisterUserCommand(request.FirstName, request.LastName, request.Email, request.UserName, request.Password));
_logger.LogDebug($"{nameof(Register)} response: {JsonSerializer.Serialize(response)}");
return _mapper.Map(response);
}
}
```
Controller integration test code:
```
[Collection("Controller Test Collection")]
public class AccountsControllerIntegrationTests
{
private readonly HttpClient _client;
private readonly ITestOutputHelper _output;
public AccountsControllerIntegrationTests(ITestOutputHelper output, CustomWebApplicationFactory factory) => (_output, _client) = (output, factory.Client);
[Fact]
public async Task CanRegisterUserWithValidAccountDetails()
{
var httpResponse = await _client.PostAsync("/api/accounts/register", new StringContent(System.Text.Json.JsonSerializer.Serialize(new Models.Request.RegisterUserRequest("John", "Doe", "jdoe@gmail.com", "johndoe", "Pa$$word1")), Encoding.UTF8, "application/json"));
...
}
```
`CustomWebApplicationFactory`:
```
public class CustomWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TStartup : class
{
public HttpClient Client { get; set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("IntegrationTests");
builder.ConfigureLogging((p) => p.SetMinimumLevel(LogLevel.Debug));
builder.ConfigureServices((context, services) =>
{
// Create a new service provider.
services.Configure(context.Configuration.GetSection(nameof(GrpcConfig)));
services.AddScoped>();
});
}
public async ValueTask InitializeAsync()
{
Client = CreateClient(new WebApplicationFactoryClientOptions
{
BaseAddress = new Uri("https://localhost:4433")
});
}
```
### Expected Behavior

What do you think?

### Steps To Reproduce

_No response_

### Exceptions (if any)

```
Message: 
System.Threading.Tasks.TaskCanceledException : The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
---- System.TimeoutException : Flush was canceled on underlying PipeWriter.
-------- System.OperationCanceledException : Flush was canceled on underlying PipeWriter.

Stack Trace: 
HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
AccountsControllerIntegrationTests.CanChangePasswordWithValidAccountDetails() line 133
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
----- Inner Stack Trace -----
ThrowHelper.ThrowOperationCanceledException_FlushCanceled()
PipeWriter.CopyFromAsync(Stream source, CancellationToken cancellationToken)
<b__0>d.MoveNext()
--- End of stack trace from previous location ---
<g__RunRequestAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
ClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
CookieContainerHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
```

### .NET Version

10.0.0

### Anything else?

```
.NET SDK:
Version: 10.0.100
Commit: b0f34d51fc
Workload version: 10.0.100-manifests.5fb86115
MSBuild version: 18.0.2+b0f34d51f

Runtime Environment:
OS Name: Windows
OS Version: 10.0.26200
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\10.0.100\

.NET workloads installed:
[maui-windows]
Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
Manifest Version: 10.0.0/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maui\10.0.0\WorkloadManifest.json
Install Type: Msi

[maccatalyst]
Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
Manifest Version: 26.1.10494/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maccatalyst\26.1.10494\WorkloadManifest.json
Install Type: Msi

[android]
Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
Manifest Version: 36.1.2/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.android\36.1.2\WorkloadManifest.json
Install Type: Msi

[ios]
Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
Manifest Version: 26.1.10494/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.ios\26.1.10494\WorkloadManifest.json
Install Type: Msi

Configured to use workload sets when installing new manifests.
No workload sets are installed. Run "dotnet workload restore" to install a workload set.

Host:
Version: 10.0.0
Architecture: x64
Commit: b0f34d51fc

.NET SDKs installed:
6.0.202 [C:\Program Files\dotnet\sdk]
6.0.401 [C:\Program Files\dotnet\sdk]
6.0.428 [C:\Program Files\dotnet\sdk]
7.0.120 [C:\Program Files\dotnet\sdk]
9.0.112 [C:\Program Files\dotnet\sdk]
9.0.308 [C:\Program Files\dotnet\sdk]
10.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
Not set

global.json file:
Not found

Learn more:
https://aka.ms/dotnet/info

Download .NET:
https://aka.ms/dotnet/download
```

Contributor guide

Open the contributing guide

Research direction

Start with CustomWebApplicationFactory.InitializeAsync and the AccountsControllerIntegrationTests POST test, then review Program.cs and AccountsController alongside the Kestrel TLS and timeout logs. Reproduce the 100-second cancellation and determine why the POST does not complete; done should include a verified explanation and regression coverage or a documented resolution.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.