Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>disable</Nullable>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>d0d8591a-b364-4f96-921f-d24a0e638079</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.10.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Threading;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

builder.Services.AddHttpClient("TestClient", (sp, httpClient) =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
Expand All @@ -18,15 +9,17 @@
httpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
});

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();
builder.Services.AddHttpClient("ResilientClient", httpClient =>
{
httpClient.BaseAddress = new Uri("http://localhost:5000");
})
.AddStandardResilienceHandler(options =>
{
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(2);
options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(8);
});

app.MapControllers();
var app = builder.Build();

app.MapGet("/api/delay-4-seconds", async (CancellationToken cancellationToken) => await Task.Delay(TimeSpan.FromSeconds(4), cancellationToken));

Expand All @@ -36,7 +29,7 @@

try
{
var response = await httpClient.GetAsync("/api/delay-4-seconds");
using var response = await httpClient.GetAsync("/api/delay-4-seconds");

return Results.Ok();
}
Expand All @@ -53,7 +46,7 @@

try
{
var response = await httpClient.GetAsync($"/api/delay-4-seconds", cancellationToken);
using var response = await httpClient.GetAsync($"/api/delay-4-seconds", cancellationToken);

return Results.Ok();
}
Expand All @@ -71,7 +64,7 @@

try
{
var response = await httpClient.GetAsync("/api/delay-4-seconds", tokenSource.Token);
using var response = await httpClient.GetAsync("/api/delay-4-seconds", tokenSource.Token);

return Results.Ok();
}
Expand All @@ -83,4 +76,40 @@
}
});

app.MapGet("/api/test-timeout-vs-cancellation", async (IHttpClientFactory httpClientFactory, CancellationToken cancellationToken) =>
{
var httpClient = httpClientFactory.CreateClient("TestClient");

try
{
using var response = await httpClient.GetAsync("/api/delay-4-seconds", cancellationToken);

return Results.Ok();
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException)
{
return Results.Text("The request timed out");
}
catch (OperationCanceledException)
{
return Results.Text("The caller canceled the request");
}
});

app.MapGet("/api/test-resilience-timeout", async (IHttpClientFactory httpClientFactory) =>
{
var httpClient = httpClientFactory.CreateClient("ResilientClient");

try
{
using var response = await httpClient.GetAsync("/api/delay-4-seconds");

return Results.Ok();
}
catch (Exception ex)
{
return Results.Text($"{ex.GetType().Name} after {httpClient.Timeout}");
}
});

app.Run();
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,30 @@ public async Task GivenCustomTokenIsCombinedWithGlobalAndRequestToken_WhenTheReq
// Assert
await Assert.ThrowsAsync<TaskCanceledException>(() => client.GetAsync("/api/test-combined-timeout", cts.Token));
}

[Fact]
public async Task WhenTheTimeoutElapses_ThenTheOperationCanceledExceptionNestsATimeoutException()
{
// Arrange
var client = _factory.CreateClient();

// Act
var responseMessage = await client.GetStringAsync("/api/test-timeout-vs-cancellation");

// Assert
Assert.Equal("The request timed out", responseMessage);
}

[Fact]
public async Task WhenTheResilienceHandlerTimesOut_ThenItThrowsTimeoutRejectedExceptionAndDisablesHttpClientTimeout()
{
// Arrange
var client = _factory.CreateClient();

// Act
var responseMessage = await client.GetStringAsync("/api/test-resilience-timeout");

// Assert
Assert.Equal("TimeoutRejectedException after -00:00:00.0010000", responseMessage);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand All @@ -10,11 +10,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading