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
Expand Up @@ -16,6 +16,7 @@ public void Defaults_work_correctly()
Assert.Equal(32, options.MaxBodyCaptureSizeKb);
Assert.Null(options.AllowLocalCompareTargets);
Assert.False(options.AllowUiInProduction);
Assert.True(options.CaptureOutgoingHttpClientRequests);
Assert.Empty(options.IgnorePaths);
Assert.Equal(["Authorization", "Cookie", "Set-Cookie"], options.RedactedHeaders);
Assert.Empty(options.RedactedQueryParameters);
Expand All @@ -34,6 +35,7 @@ public void Custom_options_are_registered_and_used()
options.MaxBodyCaptureSizeKb = 4;
options.AllowLocalCompareTargets = true;
options.IgnorePaths = ["/health"];
options.CaptureOutgoingHttpClientRequests = false;
options.RedactedHeaders = ["X-Api-Key"];
options.RedactedQueryParameters = ["token"];
options.RedactedJsonFields = ["password"];
Expand All @@ -48,6 +50,7 @@ public void Custom_options_are_registered_and_used()
Assert.Equal(4, options.MaxBodyCaptureSizeKb);
Assert.True(options.AllowLocalCompareTargets);
Assert.Equal(["/health"], options.IgnorePaths);
Assert.False(options.CaptureOutgoingHttpClientRequests);
Assert.Equal(["X-Api-Key"], options.RedactedHeaders);
Assert.Equal(["token"], options.RedactedQueryParameters);
Assert.Equal(["password"], options.RedactedJsonFields);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Net;
using DebugProbe.AspNetCore.Tests.Infrastructure;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace DebugProbe.AspNetCore.Tests.Handlers;

public class OutgoingHttpClientCaptureOptionsTests
{
[Fact]
public async Task Disabled_outgoing_capture_does_not_store_outgoing_traces()
{
await using var app = await DebugProbeTestApp.CreateAsync(
endpoints =>
{
endpoints.MapGet("/proxy", async (IHttpClientFactory httpClientFactory) =>
{
var client = httpClientFactory.CreateClient("outgoing");
var body = await client.GetStringAsync("https://api.example.test/ping");

return Results.Text(body);
});
},
configureOptions: options => options.CaptureOutgoingHttpClientRequests = false,
configureServices: services =>
{
services.AddHttpClient("outgoing")
.ConfigurePrimaryHttpMessageHandler(() =>
new StubHandler(_ => new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("pong")
}));
});

var response = await app.Client.GetAsync("/proxy");

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("pong", await response.Content.ReadAsStringAsync());

var entry = app.SingleEntry;
Assert.Equal("GET", entry.Method);
Assert.Equal("/proxy", entry.Path);
Assert.Empty(entry.OutgoingRequests);
}

private sealed class StubHandler(Func<HttpRequestMessage, HttpResponseMessage> send) : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(send(request));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ private DebugProbeTestApp(IHost host)
public static async Task<DebugProbeTestApp> CreateAsync(
Action<IEndpointRouteBuilder> mapEndpoints,
Action<DebugProbeOptions>? configureOptions = null,
Action<IApplicationBuilder>? configureAfterDebugProbe = null)
Action<IApplicationBuilder>? configureAfterDebugProbe = null,
Action<IServiceCollection>? configureServices = null)
{
var host = await new HostBuilder()
.ConfigureWebHost(webHost =>
Expand All @@ -42,6 +43,7 @@ public static async Task<DebugProbeTestApp> CreateAsync(
{
services.AddRouting();
services.AddDebugProbe(configureOptions);
configureServices?.Invoke(services);
});
webHost.Configure(app =>
{
Expand Down
15 changes: 9 additions & 6 deletions DebugProbe.AspNetCore/Extensions/DebugProbeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,18 @@ public static IServiceCollection AddDebugProbe(this IServiceCollection services,

services.AddHttpClient();

services.AddTransient<DebugProbeHttpClientHandler>();

services.ConfigureAll<HttpClientFactoryOptions>(options =>
if (options.CaptureOutgoingHttpClientRequests)
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
services.AddTransient<DebugProbeHttpClientHandler>();

services.ConfigureAll<HttpClientFactoryOptions>(httpClientOptions =>
{
builder.AdditionalHandlers.Add(builder.Services.GetRequiredService<DebugProbeHttpClientHandler>());
httpClientOptions.HttpMessageHandlerBuilderActions.Add(builder =>
{
builder.AdditionalHandlers.Add(builder.Services.GetRequiredService<DebugProbeHttpClientHandler>());
});
});
});
}

return services;
}
Expand Down
6 changes: 6 additions & 0 deletions DebugProbe.AspNetCore/Options/DebugProbeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public class DebugProbeOptions
/// </summary>
public bool AllowUiInProduction { get; set; }

/// <summary>
/// Captures outgoing requests made through IHttpClientFactory.
/// Defaults to true.
/// </summary>
public bool CaptureOutgoingHttpClientRequests { get; set; } = true;

/// <summary>
/// Additional request paths to ignore.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion DebugProbe.AspNetCore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ builder.Services.AddDebugProbe(options =>

options.AllowUiInProduction = false;

options.CaptureOutgoingHttpClientRequests = true;

options.IgnorePaths =
[
"/api/auth/login",
Expand Down Expand Up @@ -89,7 +91,7 @@ app.UseDebugProbe();
- Configurable body capture limits
- Ignored path configuration for noisy or sensitive endpoints
- Configurable redaction for sensitive headers, query parameters, and JSON fields
- Outgoing `HttpClient` request tracing
- Optional outgoing `HttpClient` request tracing

## Trace Compare

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ builder.Services.AddDebugProbe(options =>

options.AllowUiInProduction = false;

options.CaptureOutgoingHttpClientRequests = true;

options.IgnorePaths =
[
"/api/auth/login",
Expand Down Expand Up @@ -89,7 +91,7 @@ app.UseDebugProbe();
- Configurable body capture limits
- Ignored path configuration for noisy or sensitive endpoints
- Configurable redaction for sensitive headers, query parameters, and JSON fields
- Outgoing `HttpClient` request tracing
- Optional outgoing `HttpClient` request tracing

## Trace Compare

Expand Down
Loading