// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Rewrite.IISUrlRewrite;
using Microsoft.AspNetCore.Rewrite.UrlActions;
using Microsoft.AspNetCore.Rewrite.UrlMatches;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
{
public class MiddlewareTests
{
[Fact]
public async Task Invoke_RedirectPathToPathAndQuery()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Response.Headers[HeaderNames.Location]));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("article/10/hey");
Assert.Equal("/article.aspx?id=10&title=hey", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_RewritePathToPathAndQuery()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.Path + context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync("/article/10/hey");
Assert.Equal("/article.aspx?id=10&title=hey", response);
}
[Fact]
public async Task Invoke_RewriteBasedOnQueryStringParameters()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.Path + context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync("page.asp?p2=321&p1=123");
Assert.Equal("/newpage.aspx?param1=123¶m2=321", response);
}
[Fact]
public async Task Invoke_RedirectToLowerCase()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Response.Headers[HeaderNames.Location]));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("HElLo");
Assert.Equal("/hello", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_RedirectRemoveTrailingSlash()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("hey/hello/");
Assert.Equal("/hey/hello", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_RedirectAddTrailingSlash()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("hey/hello");
Assert.Equal("/hey/hello/", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_RedirectToHttps()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync(new Uri("http://example.com"));
Assert.Equal("https://example.com/", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_RewriteToHttps()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(
context.Request.Scheme +
"://" +
context.Request.Host +
context.Request.Path +
context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync(new Uri("http://example.com"));
Assert.Equal("https://example.com/", response);
}
[Fact]
public async Task Invoke_ReverseProxyToAnotherSite()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(
context.Request.Scheme +
"://" +
context.Request.Host +
context.Request.Path +
context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync(new Uri("http://example.com/"));
Assert.Equal("http://internalserver/", response);
}
[Fact]
public async Task Invoke_CaptureEmptyStringInRegexAssertRedirectLocationHasForwardSlash()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(
context.Request.Scheme +
"://" +
context.Request.Host +
context.Request.Path +
context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync(new Uri("http://example.com/"));
Assert.Equal("/", response.Headers.Location.OriginalString);
}
[Fact]
public async Task Invoke_CaptureEmptyStringInRegexAssertRewriteLocationHasForwardSlash()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(
context.Request.Path +
context.Request.QueryString));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync(new Uri("http://example.com/"));
Assert.Equal("/", response);
}
[Fact]
public async Task Invoke_CaptureEmptyStringInRegexAssertLocationHeaderContainsPathBase()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(
context.Request.Path +
context.Request.QueryString));
});
var server = new TestServer(builder) { BaseAddress = new Uri("http://localhost:5000/foo") };
var response = await server.CreateClient().GetAsync("");
Assert.Equal("/foo", response.Headers.Location.OriginalString);
}
[Theory]
[InlineData("IsFile")]
[InlineData("isfile")]
[InlineData("IsDirectory")]
[InlineData("isdirectory")]
public async Task VerifyIsFileAndIsDirectoryParsing(string matchType)
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader($@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("hey/hello");
Assert.Equal("/hey/hello/", response.Headers.Location.OriginalString);
}
[Fact]
public async Task VerifyTrackAllCaptures()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("article/23?p1=123&p2=abc");
Assert.Equal("/blogposts/article/abc?p1=123&p2=abc", response.Headers.Location.OriginalString);
}
[Fact]
public async Task VerifyTrackAllCapturesRuleAndConditionCapture()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("article/23?p1=123&p2=abc");
Assert.Equal("/blog/article/23/abc?p1=123&p2=abc", response.Headers.Location.OriginalString);
}
[Fact]
public async Task ThrowIndexOutOfRangeExceptionWithCorrectMessage()
{
// Arrange, Act, Assert
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var ex = await Assert.ThrowsAsync(() => server.CreateClient().GetAsync("article/23?p1=123&p2=abc"));
Assert.Equal("Cannot access back reference at index 9. Only 5 back references were captured.", ex.Message);
}
[Fact]
public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri_ParsedRule()
{
// arrange
var xml = @"
";
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(xml));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.GetEncodedUrl()));
});
var server = new TestServer(builder);
// act
var response = await server.CreateClient().GetStringAsync($"http://localhost/{Guid.NewGuid()}/foo/bar");
// assert
Assert.Equal("http://www.test.com/foo/bar", response);
}
[Theory]
[InlineData("http://fetch.environment.local/dev/path", "http://1.1.1.1/path")]
[InlineData("http://fetch.environment.local/qa/path", "http://fetch.environment.local/qa/path")]
public async Task Invoke_ReverseProxyToAnotherSiteUsingXmlConfiguredRewriteMap(string requestUri, string expectedRewrittenUri)
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.GetEncodedUrl()));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync(new Uri(requestUri));
Assert.Equal(expectedRewrittenUri, response);
}
[Fact]
public async Task Invoke_CustomResponse()
{
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"
"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetAsync("article/10/hey");
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
Assert.Equal("reason", response.ReasonPhrase);
Assert.Equal("description", content);
}
[Theory]
[InlineData(@"^http://localhost(/.*)", "http://localhost/foo/bar", (int)UriMatchPart.Path)]
[InlineData(@"^http://localhost(/.*)", "http://www.test.com/foo/bar", (int)UriMatchPart.Full)]
public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri_CodedRule(string conditionInputPattern, string expectedResult, int uriMatchPart)
{
// arrange
var inputParser = new InputParser();
var ruleBuilder = new UrlRewriteRuleBuilder
{
Name = "test",
Global = false
};
ruleBuilder.AddUrlMatch(".*");
var condition = new UriMatchCondition(
inputParser,
"{REQUEST_URI}",
conditionInputPattern,
(UriMatchPart)uriMatchPart,
ignoreCase: true,
negate: false);
ruleBuilder.ConfigureConditionBehavior(LogicalGrouping.MatchAll, trackAllCaptures: true);
ruleBuilder.AddUrlCondition(condition);
var action = new RewriteAction(
RuleResult.SkipRemainingRules,
inputParser.ParseInputString(@"http://www.test.com{C:1}", (UriMatchPart)uriMatchPart),
queryStringAppend: false);
ruleBuilder.AddUrlAction(action);
var options = new RewriteOptions().Add(ruleBuilder.Build());
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.GetEncodedUrl()));
});
var server = new TestServer(builder);
// act
var response = await server.CreateClient().GetStringAsync("http://localhost/foo/bar");
// assert
Assert.Equal(expectedResult, response);
}
}
}