mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Implementing and testing request-to-shell mapping in RunningShellTable
Trailing host and leading path equality is required (by ordinal-ignore-case) Longest stated host name is primary quality consideration for selection Longest stated path prefix is secondary quality consideration, for shells of equal stated host names Single unqualified shell matches only if all qualified shells fail first statment 'Default' unqualified (no stated host/path) shell takes precidence over other unqualified shells No match occurs if 'Default' is qualified, multiple other shells are unqualified, and no qualified match occurs --HG-- branch : dev
This commit is contained in:
166
src/Orchard.Tests/Environment/RunningShellTableTests.cs
Normal file
166
src/Orchard.Tests/Environment/RunningShellTableTests.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Tests.Stubs;
|
||||
|
||||
namespace Orchard.Tests.Environment {
|
||||
[TestFixture]
|
||||
public class RunningShellTableTests {
|
||||
[Test]
|
||||
public void NoShellsGiveNoMatch() {
|
||||
var table = new RunningShellTable();
|
||||
var match = table.Match(new StubHttpContext());
|
||||
Assert.That(match, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultShellMatchesByDefault() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default" };
|
||||
table.Add(settings);
|
||||
var match = table.Match(new StubHttpContext());
|
||||
Assert.That(match, Is.SameAs(settings));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnotherShellMatchesByHostHeader() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlHost = "a.example.com" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "a.example.com"));
|
||||
Assert.That(match, Is.SameAs(settingsA));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultStillCatchesWhenOtherShellsMiss() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlHost = "a.example.com" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "b.example.com"));
|
||||
Assert.That(match, Is.SameAs(settings));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultWontFallbackIfItHasCriteria() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default", RequestUrlHost = "www.example.com" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlHost = "a.example.com" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "b.example.com"));
|
||||
Assert.That(match, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultWillCatchRequestsIfItMatchesCriteria() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default", RequestUrlHost = "www.example.com" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlHost = "a.example.com" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "www.example.com"));
|
||||
Assert.That(match, Is.EqualTo(settings));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NonDefaultCatchallWillFallbackIfNothingElseMatches() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default", RequestUrlHost = "www.example.com" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "b.example.com"));
|
||||
Assert.That(match, Is.EqualTo(settingsA));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultCatchallIsFallbackEvenWhenOthersAreUnqualified() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha" };
|
||||
var settingsB = new ShellSettings { Name = "Beta", RequestUrlHost = "b.example.com" };
|
||||
var settingsG = new ShellSettings { Name = "Gamma" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
table.Add(settingsB);
|
||||
table.Add(settingsG);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "a.example.com"));
|
||||
Assert.That(match, Is.EqualTo(settings));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ThereIsNoFallbackIfMultipleSitesAreUnqualifiedButDefaultIsNotOneOfThem() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default", RequestUrlHost = "www.example.com" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha" };
|
||||
var settingsB = new ShellSettings { Name = "Beta", RequestUrlHost = "b.example.com" };
|
||||
var settingsG = new ShellSettings { Name = "Gamma" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
table.Add(settingsB);
|
||||
table.Add(settingsG);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "a.example.com"));
|
||||
Assert.That(match, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PathAlsoCausesMatch() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default" };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlPrefix = "~/foo" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
var match = table.Match(new StubHttpContext("~/foo/bar", "a.example.com"));
|
||||
Assert.That(match, Is.SameAs(settingsA));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PathAndHostMustBothMatch() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settings = new ShellSettings { Name = "Default", RequestUrlHost = "www.example.com", };
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" };
|
||||
var settingsB = new ShellSettings { Name = "Beta", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/bar" };
|
||||
var settingsG = new ShellSettings { Name = "Gamma", RequestUrlHost = "wiki.example.com" };
|
||||
var settingsD = new ShellSettings { Name = "Delta", RequestUrlPrefix = "~/Quux" };
|
||||
table.Add(settings);
|
||||
table.Add(settingsA);
|
||||
table.Add(settingsB);
|
||||
table.Add(settingsG);
|
||||
table.Add(settingsD);
|
||||
|
||||
Assert.That(table.Match(new StubHttpContext("~/foo/bar", "wiki.example.com")), Is.SameAs(settingsA));
|
||||
Assert.That(table.Match(new StubHttpContext("~/bar/foo", "wiki.example.com")), Is.SameAs(settingsB));
|
||||
Assert.That(table.Match(new StubHttpContext("~/baaz", "wiki.example.com")), Is.SameAs(settingsG));
|
||||
Assert.That(table.Match(new StubHttpContext("~/foo/bar", "www.example.com")), Is.SameAs(settings));
|
||||
Assert.That(table.Match(new StubHttpContext("~/bar/foo", "www.example.com")), Is.SameAs(settings));
|
||||
Assert.That(table.Match(new StubHttpContext("~/baaz", "www.example.com")), Is.SameAs(settings));
|
||||
Assert.That(table.Match(new StubHttpContext("~/foo/bar", "a.example.com")), Is.Null);
|
||||
|
||||
Assert.That(table.Match(new StubHttpContext("~/quux/quad", "wiki.example.com")), Is.SameAs(settingsG));
|
||||
Assert.That(table.Match(new StubHttpContext("~/quux/quad", "www.example.com")), Is.SameAs(settings));
|
||||
Assert.That(table.Match(new StubHttpContext("~/quux/quad", "a.example.com")), Is.SameAs(settingsD));
|
||||
Assert.That(table.Match(new StubHttpContext("~/yarg", "wiki.example.com")), Is.SameAs(settingsG));
|
||||
Assert.That(table.Match(new StubHttpContext("~/yarg", "www.example.com")), Is.SameAs(settings));
|
||||
Assert.That(table.Match(new StubHttpContext("~/yarg", "a.example.com")), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PathAloneWillMatch() {
|
||||
var table = (IRunningShellTable)new RunningShellTable();
|
||||
var settingsA = new ShellSettings { Name = "Alpha", RequestUrlPrefix = "~/foo" };
|
||||
table.Add(settingsA);
|
||||
|
||||
Assert.That(table.Match(new StubHttpContext("~/foo/bar", "wiki.example.com")), Is.SameAs(settingsA));
|
||||
Assert.That(table.Match(new StubHttpContext("~/bar/foo", "wiki.example.com")), Is.Null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@ using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Autofac;
|
||||
using Autofac.Integration.Web;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
@@ -109,19 +110,35 @@ namespace Orchard.Tests.Mvc.Routes {
|
||||
public void MatchingRouteToActiveShellTableWillLimitTheAbilityToMatchRoutes() {
|
||||
Init();
|
||||
|
||||
var routeA = new Route("foo", new MvcRouteHandler());
|
||||
var routeB = new Route("bar", new MvcRouteHandler());
|
||||
var routeC = new Route("quux", new MvcRouteHandler());
|
||||
var routeFoo = new Route("foo", new MvcRouteHandler());
|
||||
|
||||
_settingsA.RequestUrlHost = "a.example.com";
|
||||
_containerA.Resolve<IRoutePublisher>().Publish(
|
||||
new[] {new RouteDescriptor {Priority = 0, Route = routeA}});
|
||||
new[] {new RouteDescriptor {Priority = 0, Route = routeFoo}});
|
||||
|
||||
_settingsB.RequestUrlHost = "b.example.com";
|
||||
_containerB.Resolve<IRoutePublisher>().Publish(
|
||||
new[] {new RouteDescriptor {Priority = 0, Route = routeB}});
|
||||
new[] {new RouteDescriptor {Priority = 0, Route = routeFoo}});
|
||||
|
||||
var httpContext = new StubHttpContext("~/foo");
|
||||
var routeData = _routes.GetRouteData(httpContext);
|
||||
Assert.That(routeData, Is.Null);
|
||||
|
||||
var httpContextA = new StubHttpContext("~/foo", "a.example.com");
|
||||
var routeDataA = _routes.GetRouteData(httpContextA);
|
||||
Assert.That(routeDataA, Is.Not.Null);
|
||||
Assert.That(routeDataA.DataTokens.ContainsKey("IContainerProvider"), Is.True);
|
||||
var routeContainerProviderA = (IContainerProvider)routeDataA.DataTokens["IContainerProvider"];
|
||||
Assert.That(routeContainerProviderA.ApplicationContainer.Resolve<IRouteProvider>(), Is.SameAs(_containerA.Resolve<IRouteProvider>()));
|
||||
Assert.That(routeContainerProviderA.ApplicationContainer.Resolve<IRouteProvider>(), Is.Not.SameAs(_containerB.Resolve<IRouteProvider>()));
|
||||
|
||||
var httpContextB = new StubHttpContext("~/foo", "b.example.com");
|
||||
var routeDataB = _routes.GetRouteData(httpContextB);
|
||||
Assert.That(routeDataB, Is.Not.Null);
|
||||
Assert.That(routeDataB.DataTokens.ContainsKey("IContainerProvider"), Is.True);
|
||||
var routeContainerProviderB = (IContainerProvider)routeDataA.DataTokens["IContainerProvider"];
|
||||
Assert.That(routeContainerProviderB.ApplicationContainer.Resolve<IRouteProvider>(), Is.SameAs(_containerB.Resolve<IRouteProvider>()));
|
||||
Assert.That(routeContainerProviderB.ApplicationContainer.Resolve<IRouteProvider>(), Is.Not.SameAs(_containerA.Resolve<IRouteProvider>()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -158,6 +158,7 @@
|
||||
<Compile Include="Data\StubLocator.cs" />
|
||||
<Compile Include="Environment\AutofacUtil\AutofacTests.cs" />
|
||||
<Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyTests.cs" />
|
||||
<Compile Include="Environment\RunningShellTableTests.cs" />
|
||||
<Compile Include="Environment\Utility\Build.cs" />
|
||||
<Compile Include="Environment\Configuration\AppDataFolderTests.cs" />
|
||||
<Compile Include="Environment\Configuration\DefaultTenantManagerTests.cs" />
|
||||
|
@@ -1,10 +1,12 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Tests.Stubs {
|
||||
public class StubHttpContext : HttpContextBase {
|
||||
private readonly string _appRelativeCurrentExecutionFilePath;
|
||||
private readonly string _hostHeader;
|
||||
private readonly IDictionary _items = new Dictionary<object, object>();
|
||||
|
||||
public StubHttpContext() {
|
||||
@@ -15,23 +17,29 @@ namespace Orchard.Tests.Stubs {
|
||||
_appRelativeCurrentExecutionFilePath = appRelativeCurrentExecutionFilePath;
|
||||
}
|
||||
|
||||
public StubHttpContext(string appRelativeCurrentExecutionFilePath, string hostHeader) {
|
||||
_appRelativeCurrentExecutionFilePath = appRelativeCurrentExecutionFilePath;
|
||||
_hostHeader = hostHeader;
|
||||
}
|
||||
|
||||
public override HttpRequestBase Request {
|
||||
get { return new StupHttpRequest(_appRelativeCurrentExecutionFilePath); }
|
||||
get { return new StubHttpRequest(this); }
|
||||
}
|
||||
|
||||
public override IDictionary Items {
|
||||
get { return _items; }
|
||||
}
|
||||
|
||||
public class StupHttpRequest : HttpRequestBase {
|
||||
private readonly string _appRelativeCurrentExecutionFilePath;
|
||||
class StubHttpRequest : HttpRequestBase {
|
||||
private readonly StubHttpContext _httpContext;
|
||||
private NameValueCollection _serverVariables;
|
||||
|
||||
public StupHttpRequest(string appRelativeCurrentExecutionFilePath) {
|
||||
_appRelativeCurrentExecutionFilePath = appRelativeCurrentExecutionFilePath;
|
||||
public StubHttpRequest(StubHttpContext httpContext) {
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
public override string AppRelativeCurrentExecutionFilePath {
|
||||
get { return _appRelativeCurrentExecutionFilePath; }
|
||||
get { return _httpContext._appRelativeCurrentExecutionFilePath; }
|
||||
}
|
||||
|
||||
public override string ApplicationPath {
|
||||
@@ -41,6 +49,14 @@ namespace Orchard.Tests.Stubs {
|
||||
public override string PathInfo {
|
||||
get { return ""; }
|
||||
}
|
||||
|
||||
public override NameValueCollection ServerVariables {
|
||||
get {
|
||||
return _serverVariables = _serverVariables
|
||||
?? new NameValueCollection { { "HTTP_HOST", _httpContext._hostHeader } };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,12 +14,40 @@ namespace Orchard.Environment {
|
||||
|
||||
public class RunningShellTable : IRunningShellTable {
|
||||
private IEnumerable<ShellSettings> _shells = Enumerable.Empty<ShellSettings>();
|
||||
private IEnumerable<IGrouping<string, ShellSettings>> _shellsByHost = Enumerable.Empty<ShellSettings>().GroupBy(x => default(string));
|
||||
private ShellSettings _fallback;
|
||||
|
||||
public void Add(ShellSettings settings) {
|
||||
_shells = _shells
|
||||
.Where(s=>s.Name != settings.Name)
|
||||
.Where(s => s.Name != settings.Name)
|
||||
.Concat(new[] { settings })
|
||||
.ToArray();
|
||||
|
||||
var qualified =
|
||||
_shells.Where(x => !string.IsNullOrEmpty(x.RequestUrlHost) || !string.IsNullOrEmpty(x.RequestUrlPrefix));
|
||||
|
||||
var unqualified =
|
||||
_shells.Where(x => string.IsNullOrEmpty(x.RequestUrlHost) && string.IsNullOrEmpty(x.RequestUrlPrefix));
|
||||
|
||||
_shellsByHost = qualified
|
||||
.GroupBy(s => s.RequestUrlHost ?? "")
|
||||
.OrderByDescending(g => g.Key.Length);
|
||||
|
||||
if (unqualified.Count() == 1) {
|
||||
// only one shell had no request url criteria
|
||||
_fallback = unqualified.Single();
|
||||
}
|
||||
else if (unqualified.Any()) {
|
||||
// two or more shells had no request criteria.
|
||||
// this is technically a misconfiguration - so fallback to the default shell
|
||||
// if it's one which will catch all requests
|
||||
_fallback = unqualified.SingleOrDefault(x => x.Name == "Default");
|
||||
}
|
||||
else {
|
||||
// no shells are unqualified - a request that does not match a shell's spec
|
||||
// will not be mapped to routes coming from orchard
|
||||
_fallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ShellSettings> List() {
|
||||
@@ -27,7 +55,23 @@ namespace Orchard.Environment {
|
||||
}
|
||||
|
||||
public ShellSettings Match(HttpContextBase httpContext) {
|
||||
return _shells.SingleOrDefault(settings => settings.Name == "Default");
|
||||
var host = httpContext.Request.ServerVariables.Get("HTTP_HOST") ?? "";
|
||||
|
||||
var hostLength = host.IndexOf(':');
|
||||
if (hostLength != -1)
|
||||
host = host.Substring(0, hostLength);
|
||||
|
||||
var appRelativePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
|
||||
|
||||
var mostQualifiedMatch = _shellsByHost
|
||||
.Where(group => host.EndsWith(group.Key, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany(group => group
|
||||
.OrderByDescending(settings => (settings.RequestUrlPrefix ?? "").Length))
|
||||
.Where(settings => appRelativePath.StartsWith(settings.RequestUrlPrefix ?? "", StringComparison.OrdinalIgnoreCase))
|
||||
.FirstOrDefault();
|
||||
|
||||
return mostQualifiedMatch ?? _fallback;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user