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 @@ -51,38 +51,37 @@ public int MaxRedirect
/// The proxy resolver returns the proxy URI for a given destination, or null if no proxy applies.
/// Defaults to <see cref="DefaultScrubSensitiveHeaders"/>.
/// </summary>
public Action<HttpRequestMessage, Uri, Uri, Func<Uri, Uri?>?> ScrubSensitiveHeaders { get; set; } = DefaultScrubSensitiveHeaders;
public Action<HttpRequestMessage, Uri, Func<Uri, Uri?>?> ScrubSensitiveHeaders { get; set; } = DefaultScrubSensitiveHeaders;

/// <summary>
/// The default implementation for scrubbing sensitive headers during redirects.
/// This method removes Authorization and Cookie headers when the host, scheme, or port changes,
/// and removes ProxyAuthorization headers when no proxy is configured or the proxy is bypassed for the new URI.
/// </summary>
/// <param name="request">The HTTP request message to modify.</param>
/// <param name="newRequest">The HTTP request message to modify.</param>
/// <param name="originalUri">The original request URI.</param>
/// <param name="newUri">The new redirect URI.</param>
/// <param name="proxyResolver">A function that returns the proxy URI for a destination, or null if no proxy applies. Can be null if no proxy is configured.</param>
public static void DefaultScrubSensitiveHeaders(HttpRequestMessage request, Uri originalUri, Uri newUri, Func<Uri, Uri?>? proxyResolver)
public static void DefaultScrubSensitiveHeaders(HttpRequestMessage newRequest, Uri originalUri, Func<Uri, Uri?>? proxyResolver)
{
if(request == null) throw new ArgumentNullException(nameof(request));
if(newRequest == null) throw new ArgumentNullException(nameof(newRequest));
if(originalUri == null) throw new ArgumentNullException(nameof(originalUri));
if(newUri == null) throw new ArgumentNullException(nameof(newUri));
var newUri = newRequest.RequestUri ?? throw new InvalidOperationException("The request URI cannot be null.");

// Remove Authorization and Cookie headers if http request's scheme, host, or port changes
var isDifferentOrigin = !newUri.Host.Equals(originalUri.Host, StringComparison.OrdinalIgnoreCase) ||
!newUri.Scheme.Equals(originalUri.Scheme, StringComparison.OrdinalIgnoreCase) ||
newUri.Port != originalUri.Port;
if(isDifferentOrigin)
{
request.Headers.Authorization = null;
request.Headers.Remove("Cookie");
newRequest.Headers.Authorization = null;
newRequest.Headers.Remove("Cookie");
}

// Remove ProxyAuthorization if no proxy is configured or the URL is bypassed
var isProxyInactive = proxyResolver == null || proxyResolver(newUri) == null;
if(isProxyInactive)
{
request.Headers.ProxyAuthorization = null;
newRequest.Headers.ProxyAuthorization = null;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/http/httpClient/Middleware/RedirectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/// <param name="request">The <see cref="HttpRequestMessage"/> to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>for the request.</param>
/// <returns>The <see cref="HttpResponseMessage"/>.</returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Check warning on line 44 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 29 to the 15 allowed.

Check warning on line 44 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 29 to the 15 allowed.

Check warning on line 44 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 29 to the 15 allowed.

Check warning on line 44 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 29 to the 15 allowed.

Check warning on line 44 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 29 to the 15 allowed.
{
if(request == null) throw new ArgumentNullException(nameof(request));

Expand Down Expand Up @@ -121,7 +121,7 @@

// Scrub sensitive headers before following the redirect
var proxyResolver = GetProxyResolver();
redirectOption.ScrubSensitiveHeaders(newRequest, request.RequestUri!, newRequest.RequestUri, proxyResolver);
redirectOption.ScrubSensitiveHeaders(newRequest, request.RequestUri!, proxyResolver);

// If scheme has changed. Ensure that this has been opted in for security reasons
if(!newRequest.RequestUri.Scheme.Equals(request.RequestUri?.Scheme) && !redirectOption.AllowRedirectOnSchemeChange)
Expand Down Expand Up @@ -157,7 +157,7 @@
}
}

private bool ShouldRedirect(HttpResponseMessage responseMessage, RedirectHandlerOption redirectOption)

Check warning on line 160 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'ShouldRedirect' a static method.

Check warning on line 160 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'ShouldRedirect' a static method.

Check warning on line 160 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'ShouldRedirect' a static method.

Check warning on line 160 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'ShouldRedirect' a static method.
{
return IsRedirect(responseMessage.StatusCode) && redirectOption.ShouldRedirect(responseMessage) && redirectOption.MaxRedirect > 0;
}
Expand Down Expand Up @@ -196,7 +196,7 @@
/// Traverses the handler chain to find the final handler and extract its proxy settings.
/// </summary>
/// <returns>The IWebProxy from the final handler, or null if not found.</returns>
private IWebProxy? GetProxyFromFinalHandler()

Check warning on line 199 in src/http/httpClient/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'GetProxyFromFinalHandler' a static method.
{
#if BROWSER
// Browser platform does not support proxy configuration
Expand Down
20 changes: 12 additions & 8 deletions tests/http/httpClient/Middleware/RedirectHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,12 +443,13 @@ public async Task RedirectWithDifferentHostShouldRemoveCustomHeadersWithCustomCa
// Arrange - Custom callback that removes additional headers on host change
var redirectOption = new RedirectHandlerOption
{
ScrubSensitiveHeaders = (request, originalUri, newUri, proxyResolver) =>
ScrubSensitiveHeaders = (request, originalUri, proxyResolver) =>
{
// Call default implementation first
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, newUri, proxyResolver);
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, proxyResolver);

// Then add custom logic for additional headers
var newUri = request.RequestUri ?? throw new InvalidOperationException("The request URI cannot be null.");
var isDifferentHostOrScheme = !newUri.Host.Equals(originalUri.Host, StringComparison.OrdinalIgnoreCase) ||
!newUri.Scheme.Equals(originalUri.Scheme, StringComparison.OrdinalIgnoreCase);
if(isDifferentHostOrScheme)
Expand Down Expand Up @@ -494,10 +495,11 @@ public async Task RedirectWithDifferentSchemeShouldRemoveCustomHeadersWithCustom
var redirectOption = new RedirectHandlerOption
{
AllowRedirectOnSchemeChange = true,
ScrubSensitiveHeaders = (request, originalUri, newUri, proxyResolver) =>
ScrubSensitiveHeaders = (request, originalUri, proxyResolver) =>
{
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, newUri, proxyResolver);
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, proxyResolver);

var newUri = request.RequestUri ?? throw new InvalidOperationException("The request URI cannot be null.");
var isDifferentHostOrScheme = !newUri.Host.Equals(originalUri.Host, StringComparison.OrdinalIgnoreCase) ||
!newUri.Scheme.Equals(originalUri.Scheme, StringComparison.OrdinalIgnoreCase);
if(isDifferentHostOrScheme)
Expand Down Expand Up @@ -533,10 +535,11 @@ public async Task RedirectWithSameHostAndSchemeCustomCallbackShouldKeepCustomHea
// Arrange - Custom callback that only removes headers on host/scheme change
var redirectOption = new RedirectHandlerOption
{
ScrubSensitiveHeaders = (request, originalUri, newUri, proxyResolver) =>
ScrubSensitiveHeaders = (request, originalUri, proxyResolver) =>
{
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, newUri, proxyResolver);
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, proxyResolver);

var newUri = request.RequestUri ?? throw new InvalidOperationException("The request URI cannot be null.");
var isDifferentHostOrScheme = !newUri.Host.Equals(originalUri.Host, StringComparison.OrdinalIgnoreCase) ||
!newUri.Scheme.Equals(originalUri.Scheme, StringComparison.OrdinalIgnoreCase);
if(isDifferentHostOrScheme)
Expand Down Expand Up @@ -612,11 +615,12 @@ public async Task CustomCallbackCanUseProxyResolverToKeepHeadersWhenProxyActive(
};
var redirectOption = new RedirectHandlerOption
{
ScrubSensitiveHeaders = (request, originalUri, newUri, proxyResolver) =>
ScrubSensitiveHeaders = (request, originalUri, proxyResolver) =>
{
// Call default implementation
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, newUri, proxyResolver);
RedirectHandlerOption.DefaultScrubSensitiveHeaders(request, originalUri, proxyResolver);

var newUri = request.RequestUri ?? throw new InvalidOperationException("The request URI cannot be null.");
// Only remove X-Api-Key if proxy is inactive (like ProxyAuthorization logic)
var isProxyInactive = proxyResolver == null || proxyResolver(newUri) == null;
var isDifferentHostOrScheme = !newUri.Host.Equals(originalUri.Host, StringComparison.OrdinalIgnoreCase) ||
Expand Down
Loading