Skip to content

Commit

Permalink
Adds AllowRedirectOnSchemeChange to configure ability to redirect to …
Browse files Browse the repository at this point in the history
…different schemes
  • Loading branch information
andrueastman committed Sep 8, 2021
1 parent 7f9ba26 commit fee7d2d
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -59,10 +59,11 @@ public void RedirectHandler_HttpMessageHandlerConstructor()
public void RedirectHandler_RedirectOptionConstructor()
{
// Assert
using RedirectHandler redirect = new RedirectHandler(new RedirectHandlerOption { MaxRedirect = 2 });
using RedirectHandler redirect = new RedirectHandler(new RedirectHandlerOption { MaxRedirect = 2, AllowRedirectOnSchemeChange = true});
Assert.Null(redirect.InnerHandler);
Assert.NotNull(redirect.RedirectOption);
Assert.Equal(2, redirect.RedirectOption.MaxRedirect);
Assert.True(redirect.RedirectOption.AllowRedirectOnSchemeChange);
Assert.IsType<RedirectHandler>(redirect);
}

Expand Down Expand Up @@ -151,7 +152,7 @@ public async Task RedirectWithDifferentHostShouldRemoveAuthHeader(HttpStatusCode
[InlineData(HttpStatusCode.Found)] // 302
[InlineData(HttpStatusCode.TemporaryRedirect)] // 307
[InlineData(HttpStatusCode.PermanentRedirect)] // 308
public async Task RedirectWithDifferentSchemeShouldRemoveAuthHeader(HttpStatusCode statusCode)
public async Task RedirectWithDifferentSchemeThrowsInvalidOperationExceptionIfAllowRedirectOnSchemeChangeIsDisabled(HttpStatusCode statusCode)
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://example.org/foo");
Expand All @@ -160,6 +161,28 @@ public async Task RedirectWithDifferentSchemeShouldRemoveAuthHeader(HttpStatusCo
redirectResponse.Headers.Location = new Uri("http://example.org/bar");
this._testHttpMessageHandler.SetHttpResponse(redirectResponse, new HttpResponseMessage(HttpStatusCode.OK));// sets the mock response
// Act
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await this._invoker.SendAsync(httpRequestMessage, CancellationToken.None));
// Assert
Assert.Contains("Redirects with changing schemes not allowed by default", exception.Message);
Assert.Equal("Scheme changed from https to http.", exception.InnerException?.Message);
Assert.IsType<InvalidOperationException>(exception);
}

[Theory]
[InlineData(HttpStatusCode.MovedPermanently)] // 301
[InlineData(HttpStatusCode.Found)] // 302
[InlineData(HttpStatusCode.TemporaryRedirect)] // 307
[InlineData(HttpStatusCode.PermanentRedirect)] // 308
public async Task RedirectWithDifferentSchemeShouldRemoveAuthHeaderIfAllowRedirectOnSchemeChangeIsEnabled(HttpStatusCode statusCode)
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://example.org/foo");
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("fooAuth", "aparam");
var redirectResponse = new HttpResponseMessage(statusCode);
redirectResponse.Headers.Location = new Uri("http://example.org/bar");
this._redirectHandler.RedirectOption.AllowRedirectOnSchemeChange = true;// Enable redirects on scheme change
this._testHttpMessageHandler.SetHttpResponse(redirectResponse, new HttpResponseMessage(HttpStatusCode.OK));// sets the mock response
// Act
var response = await _invoker.SendAsync(httpRequestMessage, new CancellationToken());
// Assert
Assert.NotSame(response.RequestMessage, httpRequestMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,10 @@ public int MaxRedirect
/// A delegate that's called to determine whether a response should be redirected or not. The delegate method should accept <see cref="HttpResponseMessage"/> as it's parameter and return a <see cref="bool"/>. This defaults to true.
/// </summary>
public Func<HttpResponseMessage, bool> ShouldRedirect { get; set; } = (response) => true;

/// <summary>
/// A boolean value to determine if we redirects are allowed if the scheme changes(e.g. https to http). Defaults to false.
/// </summary>
public bool AllowRedirectOnSchemeChange { get; set; } = false;
}
}
8 changes: 8 additions & 0 deletions http/dotnet/httpclient/src/Middleware/RedirectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
newRequest.Headers.Authorization = null;
}

// If scheme has changed. Ensure that this has been opted in for security reasons
if(!newRequest.RequestUri.Scheme.Equals(httpRequestMessage.RequestUri?.Scheme) && !RedirectOption.AllowRedirectOnSchemeChange)
{
throw new InvalidOperationException(
$"Redirects with changing schemes not allowed by default. You can change this by modifying the {nameof(RedirectOption.AllowRedirectOnSchemeChange)} option",
new Exception($"Scheme changed from {httpRequestMessage.RequestUri?.Scheme} to {newRequest.RequestUri.Scheme}."));
}

// Send redirect request to get response
response = await base.SendAsync(newRequest, cancellationToken);

Expand Down

0 comments on commit fee7d2d

Please sign in to comment.