-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Description
Is there an existing issue for this?
- I have searched the existing issuesTo pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
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<ActionResult> 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<JsonContentResult>(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<Program> 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<TStartup> : WebApplicationFactory<TStartup>, 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<GrpcConfig>(context.Configuration.GetSection(nameof(GrpcConfig)));
services.AddScoped<SignInManager<AppUser>>();
});
}
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.<SendAsync>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)
<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
<<SendAsync>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.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
.NET Version
9.0.203
Anything else?
.NET SDK:
Version: 9.0.203
Commit: dc7acfa194
Workload version: 9.0.200-manifests.12d79ccf
MSBuild version: 17.13.20+a4ef1e90f
Runtime Environment:
OS Name: Windows
OS Version: 10.0.22631
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\9.0.203\
.NET workloads installed:
[ios]
Installation Source: VS 17.13.35931.197
Manifest Version: 18.2.9180/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.ios\18.2.9180\WorkloadManifest.json
Install Type: Msi
[maui-windows]
Installation Source: VS 17.13.35931.197
Manifest Version: 9.0.14/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.maui\9.0.14\WorkloadManifest.json
Install Type: Msi
[maccatalyst]
Installation Source: VS 17.13.35931.197
Manifest Version: 18.2.9180/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.maccatalyst\18.2.9180\WorkloadManifest.json
Install Type: Msi
[android]
Installation Source: VS 17.13.35931.197
Manifest Version: 35.0.39/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.android\35.0.39\WorkloadManifest.json
Install Type: Msi
[aspire]
Installation Source: VS 17.13.35931.197
Manifest Version: 8.2.2/8.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.2\WorkloadManifest.json
Install Type: Msi
Configured to use loose manifests when installing new manifests.
Host:
Version: 9.0.4
Architecture: x64
Commit: f57e6dc747
.NET SDKs installed:
6.0.202 [C:\Program Files\dotnet\sdk]
6.0.401 [C:\Program Files\dotnet\sdk]
7.0.100 [C:\Program Files\dotnet\sdk]
9.0.100 [C:\Program Files\dotnet\sdk]
9.0.203 [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.0 [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.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.4 [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.0 [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.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.4 [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.0 [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.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.4 [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
Activity
martincostello commentedon May 9, 2025
Looking at the code provided, I would say that the exception is really just saying "the system under test didn't reply in under 100s, so things cancelled".
As there's no code for that system under test provided, I can't give any insight into why that might be the case.
The test setup seems reasonable, though I would change two things:
khteh commentedon May 9, 2025
Are you referring to
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTests");
? What do you mean "first class APIs"? Is it something new in ASP.Net Core 9? Never heard of it. Bear it mind that the code has to run on both Windows 11 (VS2022) and Ubuntu (VSCode).Not sure what you mean. Code here: https://github.com/khteh/AspNetCoreWebApi/blob/master/test/Web.Api.IntegrationTests/CustomWebApplicationFactory.cs
martincostello commentedon May 9, 2025
By first class APIs I mean the
UseEnvironment(string)
method, which IIRC, was added in .NET Core 3.0.Here's an example of its use: example. This code works on any operating system.
For logging I mean if the service provider has logging configured already, then you don't need to explicitly register a service for
ILogger<T>
.martincostello commentedon May 9, 2025
I had a quick look at the link to the repo you provided, and there's a lot of code there to pick through to try and see an issue.
I would suggest as a starting point wiring up the logging to output to xunit (I maintain a project to do exactly that) as you already have references to
ITestOutputHelper
in your test anyway, and then use that to see if anything useful is output that helps you diagnose what's taking so long. Here's an example of using it (in a whole repo dedicated to showing how to useWebApplicationFactory<T>
to test an app): example.khteh commentedon May 9, 2025
This only provides 3 hard-coded options https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.environments?view=net-9.0-pp How does that compare with
launchSettings.json
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-9.0#development-and-launchsettingsjson?khteh commentedon May 9, 2025
There are many build errors when integrating the nuget package to my
CustomWebApplicationFactory<TStartup>
class. Among them are the conflictIAsyncLifetime
has withxunit.core
andxunit.v3.core
. And thethis
doesn't work with the template class parameterTStartup
.martincostello commentedon May 9, 2025
The class does yes, but the documentation I linked you to shows it takes a string, so you can pass it whatever you like and not use the referenced separate class of constants.
martincostello commentedon May 9, 2025
If you read the documentation you'll see there are two packages. Use the right one for the version of xunit you are using.
The documentation also shows other ways to configure the logging than the one in the specific usage I linked you to.
khteh commentedon May 9, 2025
How can I check / verify if the service already has logging configured in the
WebApplicationFactory
subclass'sConfigureWebHost
method?Even when it does already have one, I just want a simple logging to the console instead of the complex logging system used by the full-blown web application using Serilog.martincostello commentedon May 9, 2025
AddLogging()
adds logging, soILogger<T>
will be registered by that.Xunit captures the console output by default, so you won't see any console output unless you specifically configure xunit itself to do that. That's why I recommended using the xunit logging package, which logs to the xunit output which is only emitted when a test fails.
khteh commentedon May 9, 2025
This doesn't work.
string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
inProgram.cs
reads a null string. I defined the following profile inlaunchSettings.json
but to no avail!14 remaining items
khteh commentedon May 9, 2025
Where does
Console.WriteLine($"{nameof(InitializeAsync)} completes");
output go in VS2022? I checked "Debug" and "Tests" outputs but don't see anything in therekhteh commentedon May 9, 2025
I have the following in
ConfigureWebHost()
:martincostello commentedon May 9, 2025
If it's not in the Debug window, then it's probably being captured by xunit.
We've reached the limits of how much I can help you with debugging your app now. There should be plenty of examples and documentation in the README/repo for my xunit logging library to help you with getting the test output wired-up.
khteh commentedon May 9, 2025
This line of code in README:
Is
ITestOutputHelper
available inConfigureWebHost()
? Otherwise how to add that intoAddXUnit
?martincostello commentedon May 9, 2025
You need to route it through from the test class to wherever you need to use it. Take a look at the sample for inspiration.
khteh commentedon May 9, 2025
I only see this in my Output window:
martincostello commentedon May 9, 2025
If you're using the xunit output, you need to look at the test output in Test Explorer.
martincostello commentedon May 9, 2025
If you don't see it, then possible reasons could be:
khteh commentedon May 9, 2025
[-]`WebApplicationFactory.HttpClient` throws ` Flush was canceled on underlying PipeWriter` during test[/-][+]`WebApplicationFactory.HttpClient` throws ` Flush was canceled on underlying PipeWriter` during test. `HttpClient` fails to reach controller endpoint.[/+][-]`WebApplicationFactory.HttpClient` throws ` Flush was canceled on underlying PipeWriter` during test. `HttpClient` fails to reach controller endpoint.[/-][+]`HttpClient` fails to reach controller endpoint during both Debug and Run sessions in integration tests.[/+][-]`HttpClient` fails to reach controller endpoint during both Debug and Run sessions in integration tests.[/-][+]`HttpClient.PostAsync` fails to reach controller endpoint during both Debug and Run sessions in integration tests.[/+]khteh commentedon Jun 5, 2025
On Ubuntu VSCode, after the timeout failure reaching the controller POST endpoint, I tried to restart the debug session and hit this exception which is informative to tell what's happening: