|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Routing.Matching;
// End-to-end tests for the host matching functionality
public abstract class HostMatcherPolicyIntegrationTestBase
{
protected abstract bool HasDynamicMetadata { get; }
[Fact]
public async Task Match_Host()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithPort()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_Host_Unicode()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "æon.contoso.com", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "æon.contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithPort_IncorrectPort()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com:1111");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
[Fact]
public async Task Match_HostWithPort_IncorrectHost()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "www.contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
[Fact]
public async Task Match_HostWithWildcard_Unicode()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "æon.contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithWildcard_NoSubdomain()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
[Fact]
public async Task Match_HostWithWildcard_Subdomain()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "www.contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithWildcard_MultipleSubdomains()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "www.blog.contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithWildcard_PrefixNotInSubdomain()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "mycontoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
[Fact]
public async Task Match_HostAndHostWithWildcard_NoSubdomain()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:8080", "*.contoso.com:8080", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com:8080");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_Host_CaseInsensitive()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "Contoso.COM", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithPort_InferHttpPort()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:80", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com", "http");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithPort_InferHttpsPort()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:443", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com", "https");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_HostWithPort_NoHostHeader()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "contoso.com:443", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", null, "https");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
[Fact]
public async Task Match_Port_NoHostHeader_InferHttpsPort()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*:443", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", null, "https");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_NoMetadata_MatchesAnyHost()
{
// Arrange
var endpoint = CreateEndpoint("/hello");
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_EmptyHostList_MatchesAnyHost()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_WildcardHost_MatchesAnyHost()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_WildcardHostAndWildcardPort_MatchesAnyHost()
{
// Arrange
var endpoint = CreateEndpoint("/hello", hosts: new string[] { "*:*", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint);
}
[Fact]
public async Task Match_CatchAllRouteWithMatchingHost_Success()
{
// Arrange
var endpoint = CreateEndpoint("/{**path}", hosts: new string[] { "contoso.com", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "contoso.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertMatch(httpContext, endpoint, new { path = "hello" });
}
[Fact]
public async Task Match_CatchAllRouteFailureHost_NoMatch()
{
// Arrange
var endpoint = CreateEndpoint("/{**path}", hosts: new string[] { "contoso.com", });
var matcher = CreateMatcher(endpoint);
var httpContext = CreateContext("/hello", "nomatch.com");
// Act
await matcher.MatchAsync(httpContext);
// Assert
MatcherAssert.AssertNotMatch(httpContext);
}
private static Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{
var services = new ServiceCollection()
.AddOptions()
.AddLogging()
.AddRouting()
.BuildServiceProvider();
var builder = services.GetRequiredService<DfaMatcherBuilder>();
for (var i = 0; i < endpoints.Length; i++)
{
builder.AddEndpoint(endpoints[i]);
}
return builder.Build();
}
internal static HttpContext CreateContext(
string path,
string host,
string scheme = null)
{
var httpContext = new DefaultHttpContext();
if (host != null)
{
httpContext.Request.Host = new HostString(host);
}
httpContext.Request.Path = path;
httpContext.Request.Scheme = scheme;
return httpContext;
}
internal RouteEndpoint CreateEndpoint(
string template,
object defaults = null,
object constraints = null,
int order = 0,
string[] hosts = null)
{
var metadata = new List<object>();
if (hosts != null)
{
metadata.Add(new HostAttribute(hosts ?? Array.Empty<string>()));
}
if (HasDynamicMetadata)
{
metadata.Add(new DynamicEndpointMetadata());
}
var displayName = "endpoint: " + template + " " + string.Join(", ", hosts ?? new[] { "*:*" });
return new RouteEndpoint(
TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, constraints),
order,
new EndpointMetadataCollection(metadata),
displayName);
}
internal (Matcher matcher, RouteEndpoint endpoint) CreateMatcher(string template)
{
var endpoint = CreateEndpoint(template);
return (CreateMatcher(endpoint), endpoint);
}
private class DynamicEndpointMetadata : IDynamicEndpointMetadata
{
public bool IsDynamic => true;
}
}
|