IIS UrlRewrite Rule Application

This commit is contained in:
Justin Kotalik 2016-07-27 14:47:32 -07:00
parent bb4f5420d3
commit 5052a94cf7
109 changed files with 1295 additions and 770 deletions

View File

@ -1,4 +1,8 @@
using Microsoft.AspNetCore.Builder;
// 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.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite;
@ -9,7 +13,7 @@ namespace RewriteSample
{
public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnv)
{
app.UseRewriter(new UrlRewriteOptions()
app.UseRewriter(new RewriteOptions()
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
.ImportFromModRewrite(hostingEnv, "Rewrite.txt"));
app.Run(context => context.Response.WriteAsync(context.Request.Path));
@ -21,6 +25,7 @@ namespace RewriteSample
var host = new WebHostBuilder()
.UseKestrel()
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.Build();
host.Run();

View File

@ -1,19 +1,12 @@
<rewrite>
<rules>
<rule name="Fail bad requests">
<match url=".*"/>
<rule name="Query String Rewrite">
<match url="page\.asp$" />
<conditions>
<add input="{HTTP_HOST}" pattern="localhost" negate="true" />
<add input="{QUERY_STRING}" pattern="p1=(\d+)" />
<add input="##{C:1}##_{QUERY_STRING}" pattern="##([^#]+)##_.*p2=(\d+)" />
</conditions>
<action type="AbortRequest" />
</rule>
<rule name="Redirect from blog">
<match url="^blog/([_0-9a-z-]+)/([0-9]+)" />
<action type="Redirect" url="article/{R:2}/{R:1}" redirectType="Found" />
</rule>
<rule name="Rewrite to article.aspx">
<match url="^article/([0-9]+)/([_0-9a-z-]+)" />
<action type="Rewrite" url="article.aspx?id={R:1}&amp;title={R:2}" />
<action type="rewrite" url="newpage.aspx?param1={C:1}&amp;param2={C:2}" appendQueryString="false"/>
</rule>
</rules>
</rewrite>

View File

@ -4,21 +4,21 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite
{
/// <summary>
/// The builder to a list of rules for <see cref="UrlRewriteOptions"/> and <see cref="UrlRewriteMiddleware"/>
/// The builder to a list of rules for <see cref="RewriteOptions"/> and <see cref="RewriteMiddleware"/>
/// </summary>
public static class UrlRewriteOptionsAddRulesExtensions
public static class CodeRewriteExtensions
{
/// <summary>
/// Adds a rule to the current rules.
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="rule">A rule to be added to the current rules.</param>
public static UrlRewriteOptions AddRule(this UrlRewriteOptions options, Rule rule)
public static RewriteOptions AddRule(this RewriteOptions options, Rule rule)
{
options.Rules.Add(rule);
return options;
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="rules">A list of rules.</param>
public static UrlRewriteOptions AddRules(this UrlRewriteOptions options, List<Rule> rules)
public static RewriteOptions AddRules(this RewriteOptions options, List<Rule> rules)
{
options.Rules.AddRange(rules);
return options;
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="newPath">The string to replace the path with (with capture parameters).</param>
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns>
public static UrlRewriteOptions RewritePath(this UrlRewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
public static RewriteOptions RewritePath(this RewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
{
options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
return options;
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="options">The Url rewrite options.</param>
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns>
public static UrlRewriteOptions RewriteScheme(this UrlRewriteOptions options, bool stopRewriteOnSuccess = false)
public static RewriteOptions RewriteScheme(this RewriteOptions options, bool stopRewriteOnSuccess = false)
{
options.Rules.Add(new SchemeRule {OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
return options;
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="newPath">The string to replace the path with (with capture parameters).</param>
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns>
public static UrlRewriteOptions RedirectPath(this UrlRewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
public static RewriteOptions RedirectPath(this RewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
{
options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = Transformation.Redirect });
return options;
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="options">The Url rewrite options.</param>
/// <param name="sslPort">The port to redirect the scheme to.</param>
/// <returns></returns>
public static UrlRewriteOptions RedirectScheme(this UrlRewriteOptions options, int? sslPort)
public static RewriteOptions RedirectScheme(this RewriteOptions options, int? sslPort)
{
options.Rules.Add(new SchemeRule { SSLPort = sslPort, OnCompletion = Transformation.Redirect });
return options;
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="transform"></param>
/// <param name="description"></param>
/// <returns></returns>
public static UrlRewriteOptions CustomRule(this UrlRewriteOptions options, Func<UrlRewriteContext, RuleResult> onApplyRule, Transformation transform, string description = null)
public static RewriteOptions CustomRule(this RewriteOptions options, Func<RewriteContext, RuleResult> onApplyRule, Transformation transform, string description = null)
{
options.Rules.Add(new FunctionalRule { OnApplyRule = onApplyRule, OnCompletion = transform});
return options;

View File

@ -3,12 +3,12 @@
using System;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class FunctionalRule : Rule
{
public Func<UrlRewriteContext, RuleResult> OnApplyRule { get; set; }
public Func<RewriteContext, RuleResult> OnApplyRule { get; set; }
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
public override RuleResult ApplyRule(UrlRewriteContext context) => OnApplyRule(context);
public override RuleResult ApplyRule(RewriteContext context) => OnApplyRule(context);
}
}

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class Condition
{

View File

@ -3,7 +3,7 @@
using System;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class ConditionBuilder
{

View File

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.Operands;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Represents the ConditionPattern for a mod_rewrite rule.
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
/// <param name="previous">The previous condition results (for backreferences).</param>
/// <param name="testString">The testString created from the <see cref="Pattern"/>.</param>
/// <returns>If the testString satisfies the condition</returns>
public bool? CheckConditionExpression(UrlRewriteContext context, Match previous, string testString)
public bool? CheckConditionExpression(RewriteContext context, Match previous, string testString)
{
return Operand.CheckOperation(previous, testString, context.FileProvider) ^ Invert;
}

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public enum ConditionFlagType
{

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
// TODO Refactor Condition Flags and Rule Flags under base flag class
public class ConditionFlags

View File

@ -4,7 +4,7 @@
using System;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Parses the "CondPattern" portion of the RewriteCond.

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Parses the TestString segment of the mod_rewrite condition.

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public enum ConditionType
{

View File

@ -3,10 +3,9 @@
using System;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.Operands;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Converts a parsed expression into a mod_rewrite condition.

View File

@ -4,9 +4,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
///

View File

@ -3,7 +3,7 @@
using System;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Parses the flags

View File

@ -6,7 +6,7 @@ using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public class IntegerOperand : Operand
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public enum IntegerOperationType
{

View File

@ -4,7 +4,7 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public abstract class Operand
{

View File

@ -5,7 +5,7 @@ using System;
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public class PropertyOperand : Operand
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public enum PropertyOperationType
{

View File

@ -4,7 +4,7 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public class RegexOperand : Operand
{

View File

@ -5,7 +5,7 @@ using System;
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public class StringOperand : Operand
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Operands
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
{
public enum StringOperationType
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public enum OperationType
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class ParsedModRewriteExpression
{

View File

@ -1,14 +1,12 @@
// 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.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.ModRewrite;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// A Pattern segment contains a portion of the test string/ substitution segment with a type associated.

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class RuleBuilder
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public enum RuleFlagType
{

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class RuleFlags
{

View File

@ -3,7 +3,7 @@
using System;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public static class RuleRegexParser
{

View File

@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// mod_rewrite lookups for specific string constants.

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
/// <summary>
/// Tokenizes a mod_rewrite rule, delimited by spaces.

View File

@ -6,9 +6,8 @@ using System.Collections.Generic;
using Microsoft.Net.Http.Headers;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class ModRewriteRule : Rule
{
@ -28,7 +27,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
Description = description;
}
public override RuleResult ApplyRule(UrlRewriteContext context)
public override RuleResult ApplyRule(RewriteContext context)
{
// 1. Figure out which section of the string to match for the initial rule.
var results = InitialRule.Operand.RegexOperation.Match(context.HttpContext.Request.Path.ToString());
@ -36,7 +35,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
string flagRes = null;
if (CheckMatchResult(results.Success))
{
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
if (Flags.HasFlag(RuleFlagType.EscapeBackreference))
@ -49,7 +48,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
if (!CheckCondition(context, results, previous))
{
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
// TODO add chained flag
@ -82,12 +81,12 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
if (Flags.HasFlag(RuleFlagType.Forbidden))
{
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
return new RuleResult { Result = RuleTerminiation.ResponseComplete };
return RuleResult.ResponseComplete;
}
else if (Flags.HasFlag(RuleFlagType.Gone))
{
context.HttpContext.Response.StatusCode = StatusCodes.Status410Gone;
return new RuleResult { Result = RuleTerminiation.ResponseComplete };
return RuleResult.ResponseComplete;
}
else if (result == "-")
{
@ -124,7 +123,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
context.HttpContext.Response.Headers[HeaderNames.Location] = "/" + result + context.HttpContext.Request.QueryString;
}
}
return new RuleResult { Result = RuleTerminiation.ResponseComplete };
return RuleResult.ResponseComplete;
}
else
{
@ -145,11 +144,11 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
}
if (Flags.HasFlag(RuleFlagType.Last) || Flags.HasFlag(RuleFlagType.End))
{
return new RuleResult { Result = RuleTerminiation.StopRules };
return RuleResult.StopRules;
}
else
{
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
}
}
@ -163,7 +162,7 @@ namespace Microsoft.AspNetCore.Rewrite.ModRewrite
return !(result.Value ^ InitialRule.Invert);
}
private bool CheckCondition(UrlRewriteContext context, Match results, Match previous)
private bool CheckCondition(RewriteContext context, Match results, Match previous)
{
if (Conditions == null)
{

View File

@ -3,14 +3,14 @@
using System.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class PathRule : Rule
{
public Regex MatchPattern { get; set; }
public string OnMatch { get; set; }
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
public override RuleResult ApplyRule(UrlRewriteContext context)
public override RuleResult ApplyRule(RewriteContext context)
{
var matches = MatchPattern.Match(context.HttpContext.Request.Path);
if (matches.Success)
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
path,
req.QueryString);
context.HttpContext.Response.Redirect(newUrl);
return new RuleResult { Result = RuleTerminiation.ResponseComplete };
return RuleResult.ResponseComplete;
}
else
{
@ -35,14 +35,14 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
}
if (OnCompletion == Transformation.TerminatingRewrite)
{
return new RuleResult { Result = RuleTerminiation.StopRules };
return RuleResult.StopRules;
}
else
{
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
}
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
}
}

View File

@ -1,11 +1,11 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public abstract class Rule
{
public abstract RuleResult ApplyRule(UrlRewriteContext context);
public abstract RuleResult ApplyRule(RewriteContext context);
}
}

View File

@ -1,9 +1,9 @@
// 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 Microsoft.AspNetCore.Rewrite.Operands;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class RuleExpression
{

View File

@ -0,0 +1,14 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class RuleResult
{
public static RuleResult Continue = new RuleResult { Result = RuleTerminiation.Continue };
public static RuleResult ResponseComplete = new RuleResult { Result = RuleTerminiation.ResponseComplete };
public static RuleResult StopRules = new RuleResult { Result = RuleTerminiation.StopRules };
public RuleTerminiation Result { get; set; }
}
}

View File

@ -1,9 +1,12 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class ModRewriteCreatorTest
public enum RuleTerminiation
{
Continue,
ResponseComplete,
StopRules
}
}

View File

@ -4,13 +4,13 @@
using System.Text;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class SchemeRule : Rule
{
public int? SSLPort { get; set; }
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
public override RuleResult ApplyRule(UrlRewriteContext context)
public override RuleResult ApplyRule(RewriteContext context)
{
// TODO this only does http to https, add more features in the future.
@ -34,11 +34,11 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
context.HttpContext.Request.Host = host;
if (OnCompletion == Transformation.TerminatingRewrite)
{
return new RuleResult { Result = RuleTerminiation.StopRules };
return RuleResult.StopRules;
}
else
{
return new RuleResult { Result = RuleTerminiation.Continue };
return RuleResult.Continue;
}
}
@ -46,9 +46,9 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
var newUrl = new StringBuilder().Append("https://").Append(host).Append(req.PathBase).Append(req.Path).Append(req.QueryString);
context.HttpContext.Response.Redirect(newUrl.ToString());
return new RuleResult { Result = RuleTerminiation.ResponseComplete };
return RuleResult.ResponseComplete;
}
return new RuleResult { Result = RuleTerminiation.Continue }; ;
return RuleResult.Continue;
}
}
}

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal
{
public enum Transformation
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public enum ActionType
{

View File

@ -0,0 +1,16 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class Condition
{
public Pattern Input { get; set; }
public UrlMatch Match { get; set; }
public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Input.Evaluate(context.HttpContext, ruleMatch, condMatch);
return Match.Evaluate(pattern, context);
}
}
}

View File

@ -0,0 +1,27 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class Conditions
{
public List<Condition> ConditionList { get; set; } = new List<Condition>();
public LogicalGrouping MatchType { get; set; } // default is MatchAll
public bool TrackingAllCaptures { get; set; }
public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch)
{
MatchResults prevCond = null;
var success = true;
foreach (var condition in ConditionList)
{
var res = condition.Evaluate(context, ruleMatch, prevCond);
success = (MatchType == LogicalGrouping.MatchAll ? (success && res.Success) : (success || res.Success));
prevCond = res;
}
return new MatchResults { Success = success, BackReference = prevCond?.BackReference };
}
}
}

View File

@ -3,11 +3,9 @@
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
/// <summary>
/// </summary>

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public enum LogicalGrouping
{

View File

@ -0,0 +1,13 @@
// 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.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class MatchResults
{
public GroupCollection BackReference { get; set; }
public bool Success { get; set; }
}
}

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public enum MatchType
{

View File

@ -1,14 +1,10 @@
// 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.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class Condition
public class ParsedCondition
{
public Pattern Input { get; set; }
public Regex MatchPattern { get; set; }
public bool Negate { get; set; }
public bool IgnoreCase { get; set; } = true;
public MatchType MatchType { get; set; } = MatchType.Pattern;

View File

@ -1,14 +1,14 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class UrlAction
public class ParsedUrlAction
{
public ActionType Type { get; set; }
public Pattern Url { get; set; }
public bool AppendQueryString { get; set; }
public bool LogRewrittenUrl { get; set; }
public bool AppendQueryString { get; set; } = true;
public bool LogRewrittenUrl { get; set; } // Ignoring this flag.
public RedirectType RedirectType { get; set; } = RedirectType.Permanent;
}
}

View File

@ -1,10 +1,11 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class RuleResult
public class ParsedUrlMatch
{
public RuleTerminiation Result { get; set; }
public bool IgnoreCase { get; set; }
public bool Negate { get; set; }
}
}

View File

@ -3,23 +3,23 @@
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class Pattern
{
public IList<PatternSegment> PatternSegments { get; }
public Pattern(List<PatternSegment> patternSegments)
{
PatternSegments = patternSegments;
}
public string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var strBuilder = new StringBuilder();
// TODO consider thread static for string builder - DAVID PERF
foreach (var pattern in PatternSegments)
{
strBuilder.Append(pattern.Evaluate(context, ruleMatch, condMatch));

View File

@ -1,15 +1,13 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public abstract class PatternSegment
{
// Match from prevRule, Match from prevCond
public abstract string Evaluate(HttpContext context, Match ruleMatch, Match condMatch);
public abstract string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
}
}

View File

@ -1,10 +1,9 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class ConditionMatchSegment : PatternSegment
{
@ -15,9 +14,9 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Index = index;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return condMatch?.Groups[Index]?.Value;
return condMatch?.BackReference[Index]?.Value;
}
}
}

View File

@ -1,10 +1,9 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class HeaderSegment : PatternSegment
{
@ -15,7 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Header = header;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Request.Headers[Header];
}

View File

@ -1,14 +1,13 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class IsHttpsSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Request.IsHttps ? "ON" : "OFF";
}

View File

@ -1,10 +1,9 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class LiteralSegment : PatternSegment
{
@ -15,7 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Literal = literal;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return Literal;
}

View File

@ -1,14 +1,13 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class LocalAddressSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Connection.LocalIpAddress?.ToString();
}

View File

@ -1,14 +1,13 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class QueryStringSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Request.QueryString.ToString();
}

View File

@ -1,14 +1,13 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class RemoteAddressSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Connection.RemoteIpAddress?.ToString();
}

View File

@ -2,14 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class RemotePortSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
}

View File

@ -0,0 +1,15 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class RequestFileNameSegment : PatternSegment
{
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Request.Path;
}
}
}

View File

@ -1,10 +1,9 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class RuleMatchSegment : PatternSegment
{
@ -15,9 +14,9 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Index = index;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return ruleMatch?.Groups[Index]?.Value;
return ruleMatch?.BackReference[Index]?.Value;
}
}
}

View File

@ -1,10 +1,9 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class ToLowerSegment : PatternSegment
{
@ -15,7 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Pattern = pattern;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
return pattern.ToLowerInvariant();

View File

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class UrlEncodeSegment : PatternSegment
{
@ -16,7 +15,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Pattern = pattern;
}
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
return UrlEncoder.Default.Encode(pattern);

View File

@ -1,16 +1,15 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{
public class UrlSegment : PatternSegment
{
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.Request.Path.ToString();
return context.Request.Path;
}
}
}

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public enum PatternSyntax
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public enum RedirectType
{

View File

@ -1,11 +1,10 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public static class RewriteTags
{
// TODO More strings to be added later once further implementations are added.
public const string Rewrite = "rewrite";
public const string GlobalRules = "globalRules";
public const string Rules = "rules";
@ -27,7 +26,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
public const string Input = "input";
public const string Pattern = "pattern";
public const string Type = "type";
public const string AppendQuery = "appendQuery";
public const string AppendQuery = "appendQueryString";
public const string LogRewrittenUrl = "logRewrittenUrl";
public const string RedirectType = "redirectType";
}

View File

@ -2,16 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public static class ServerVariables
{
{
public static PatternSegment FindServerVariable(string serverVariable)
{
switch(serverVariable)
@ -53,6 +50,8 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
throw new NotImplementedException();
case "REMOTE_PORT":
return new RemotePortSegment();
case "REQUEST_FILENAME":
return new RequestFileNameSegment();
default:
throw new FormatException("Unrecognized server variable.");
}

View File

@ -0,0 +1,14 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public abstract class UrlAction
{
public Pattern Url { get; set; }
public abstract RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
}
}

View File

@ -0,0 +1,42 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
{
public class RedirectAction : UrlAction
{
public int StatusCode { get; }
public RedirectAction(int statusCode, Pattern pattern)
{
StatusCode = statusCode;
Url = pattern;
}
public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
context.Response.StatusCode = StatusCode;
// url can either contain the full url or the path and query
// always add to location header.
// TODO check for false positives
var split = pattern.IndexOf('?');
if (split >= 0)
{
var query = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
// not using the response.redirect here because status codes may be 301, 302, 307, 308
context.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
}
else
{
context.Response.Headers[HeaderNames.Location] = pattern;
}
return RuleResult.ResponseComplete;
}
}
}

View File

@ -0,0 +1,29 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
{
public class RedirectClearQueryAction : UrlAction
{
public int StatusCode { get; }
public RedirectClearQueryAction(int statusCode, Pattern pattern)
{
StatusCode = statusCode;
Url = pattern;
}
public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
context.Response.StatusCode = StatusCode;
// we are clearing the query, so just put the pattern in the location header
context.Response.Headers[HeaderNames.Location] = pattern;
return RuleResult.ResponseComplete;
}
}
}

View File

@ -0,0 +1,60 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
{
public class RewriteAction : UrlAction
{
public RuleTerminiation Result { get; }
public bool ClearQuery { get; }
public RewriteAction(RuleTerminiation result, Pattern pattern, bool clearQuery)
{
Result = result;
Url = pattern;
ClearQuery = clearQuery;
}
public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
if (ClearQuery)
{
context.Request.QueryString = new QueryString();
}
// TODO PERF, substrings, object creation, etc.
if (pattern.IndexOf("://") >= 0)
{
string scheme = null;
var host = new HostString();
var path = new PathString();
var query = new QueryString();
var fragment = new FragmentString();
UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment);
context.Request.Scheme = scheme;
context.Request.Host = host;
context.Request.Path = path;
context.Request.QueryString = query.Add(context.Request.QueryString);
}
else
{
var split = pattern.IndexOf('?');
if (split >= 0)
{
context.Request.Path = new PathString("/" + pattern.Substring(0, split));
context.Request.QueryString = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
}
else
{
context.Request.Path = new PathString("/" + pattern);
}
}
return new RuleResult { Result = Result };
}
}
}

View File

@ -0,0 +1,17 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
{
public class VoidAction : UrlAction
{
// Explicitly say that nothing happens
public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return RuleResult.Continue;
}
}
}

View File

@ -0,0 +1,11 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public abstract class UrlMatch
{
public bool Negate { get; set; }
public abstract MatchResults Evaluate(string input, RewriteContext context);
}
}

View File

@ -0,0 +1,24 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
{
public class ExactMatch : UrlMatch
{
public bool IgnoreCase { get; }
public string StringMatch { get; }
public ExactMatch(bool ignoreCase, string input, bool negate)
{
IgnoreCase = ignoreCase;
StringMatch = input;
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var pathMatch = string.Compare(pattern, StringMatch, IgnoreCase);
return new MatchResults { Success = ((pathMatch == 0) != Negate) };
}
}
}

View File

@ -0,0 +1,19 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
{
public class IsDirectoryMatch : UrlMatch
{
public IsDirectoryMatch( bool negate)
{
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var res = context.FileProvider.GetFileInfo(pattern).IsDirectory;
return new MatchResults { Success = (res != Negate) };
}
}
}

View File

@ -0,0 +1,19 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
{
public class IsFileMatch : UrlMatch
{
public IsFileMatch(bool negate)
{
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var res = context.FileProvider.GetFileInfo(pattern).Exists;
return new MatchResults { Success = (res != Negate) };
}
}
}

View File

@ -0,0 +1,27 @@
// 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.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
{
public class RegexMatch : UrlMatch
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
public Regex Match { get; }
public RegexMatch(Regex match, bool negate)
{
Match = match;
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var res = Match.Match(pattern);
return new MatchResults { BackReference = res.Groups, Success = (res.Success != Negate)};
}
}
}

View File

@ -0,0 +1,316 @@
// 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.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public static class UrlRewriteFileParser
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
public static List<UrlRewriteRule> Parse(TextReader reader)
{
var temp = XDocument.Load(reader);
var xmlRoot = temp.Descendants(RewriteTags.Rewrite).FirstOrDefault();
if (xmlRoot != null)
{
var result = new List<UrlRewriteRule>();
// TODO Global rules are currently not treated differently than normal rules, fix.
// See: https://github.com/aspnet/BasicMiddleware/issues/59
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, isGlobalRule: true);
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, isGlobalRule: false);
return result;
}
return null;
}
private static void ParseRules(XElement rules, List<UrlRewriteRule> result, bool isGlobalRule)
{
if (rules == null)
{
return;
}
foreach (var rule in rules.Elements(RewriteTags.Rule))
{
var res = new UrlRewriteRule();
SetRuleAttributes(rule, res);
CreateUrlAction(rule.Element(RewriteTags.Action), res, isGlobalRule);
if (res.Enabled)
{
result.Add(res);
}
}
}
private static void SetRuleAttributes(XElement rule, UrlRewriteRule res)
{
res.Name = rule.Attribute(RewriteTags.Name)?.Value;
bool enabled;
if (bool.TryParse(rule.Attribute(RewriteTags.Enabled)?.Value, out enabled))
{
res.Enabled = enabled;
}
PatternSyntax patternSyntax;
if (Enum.TryParse(rule.Attribute(RewriteTags.PatternSyntax)?.Value, out patternSyntax))
{
res.PatternSyntax = patternSyntax;
}
bool stopProcessing;
if (bool.TryParse(rule.Attribute(RewriteTags.StopProcessing)?.Value, out stopProcessing))
{
res.StopProcessing = stopProcessing;
}
CreateMatch(rule.Element(RewriteTags.Match), res);
CreateConditions(rule.Element(RewriteTags.Conditions), res);
}
private static void CreateMatch(XElement match, UrlRewriteRule res)
{
if (match == null)
{
throw new FormatException("Rules must have an associated match.");
}
var matchRes = new ParsedUrlMatch();
bool parBool;
if (bool.TryParse(match.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
{
matchRes.IgnoreCase = parBool;
}
if (bool.TryParse(match.Attribute(RewriteTags.Negate)?.Value, out parBool))
{
matchRes.Negate = parBool;
}
var parsedInputString = match.Attribute(RewriteTags.Url)?.Value;
switch (res.PatternSyntax)
{
case PatternSyntax.ECMAScript:
{
if (matchRes.IgnoreCase)
{
var regex = new Regex(parsedInputString, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
res.InitialMatch = new RegexMatch(regex, matchRes.Negate);
}
else
{
var regex = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
res.InitialMatch = new RegexMatch(regex, matchRes.Negate);
}
}
break;
case PatternSyntax.WildCard:
throw new NotImplementedException("Wildcard syntax is not supported.");
case PatternSyntax.ExactMatch:
res.InitialMatch = new ExactMatch(matchRes.IgnoreCase, parsedInputString, matchRes.Negate);
break;
}
}
private static void CreateConditions(XElement conditions, UrlRewriteRule res)
{
// This is to avoid nullptr exception on referencing conditions.
res.Conditions = new Conditions();
if (conditions == null)
{
return;
}
LogicalGrouping grouping;
if (Enum.TryParse(conditions.Attribute(RewriteTags.MatchType)?.Value, out grouping))
{
res.Conditions.MatchType = grouping;
}
bool parBool;
if (bool.TryParse(conditions.Attribute(RewriteTags.TrackingAllCaptures)?.Value, out parBool))
{
res.Conditions.TrackingAllCaptures = parBool;
}
foreach (var cond in conditions.Elements(RewriteTags.Add))
{
CreateCondition(cond, res);
}
}
private static void CreateCondition(XElement condition, UrlRewriteRule res)
{
var parsedCondRes = new ParsedCondition();
bool parBool;
if (bool.TryParse(condition.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
{
parsedCondRes.IgnoreCase = parBool;
}
if (bool.TryParse(condition.Attribute(RewriteTags.Negate)?.Value, out parBool))
{
parsedCondRes.Negate = parBool;
}
MatchType matchType;
if (Enum.TryParse(condition.Attribute(RewriteTags.MatchType)?.Value, out matchType))
{
parsedCondRes.MatchType = matchType;
}
var parsedString = condition.Attribute(RewriteTags.Input)?.Value;
if (parsedString == null)
{
throw new FormatException("Null input for condition");
}
var input = InputParser.ParseInputString(parsedString);
switch (res.PatternSyntax)
{
case PatternSyntax.ECMAScript:
{
switch (parsedCondRes.MatchType)
{
case MatchType.Pattern:
{
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
if (parsedString == null)
{
throw new FormatException("Pattern match does not have an associated pattern attribute in condition.");
}
Regex regex = null;
if (parsedCondRes.IgnoreCase)
{
regex = new Regex(parsedString, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
}
else
{
regex = new Regex(parsedString, RegexOptions.Compiled, RegexTimeout);
}
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, parsedCondRes.Negate) });
}
break;
case MatchType.IsDirectory:
{
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new IsDirectoryMatch(parsedCondRes.Negate) });
}
break;
case MatchType.IsFile:
{
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new IsFileMatch(parsedCondRes.Negate) });
}
break;
default:
throw new FormatException("Unrecognized matchType.");
}
}
break;
case PatternSyntax.WildCard:
throw new NotImplementedException("Wildcard syntax is not supported.");
case PatternSyntax.ExactMatch:
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
if (parsedString == null)
{
throw new FormatException("Pattern match does not have an associated pattern attribute in condition.");
}
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new ExactMatch(parsedCondRes.IgnoreCase, parsedString, parsedCondRes.Negate) });
break;
default:
throw new FormatException("Unrecognized pattern syntax.");
}
}
private static void CreateUrlAction(XElement urlAction, UrlRewriteRule res, bool globalRule)
{
if (urlAction == null)
{
throw new FormatException("Action is a required element of a rule.");
}
var actionRes = new ParsedUrlAction();
ActionType actionType;
if (Enum.TryParse(urlAction.Attribute(RewriteTags.Type)?.Value, out actionType))
{
actionRes.Type = actionType;
}
bool parseBool;
if (bool.TryParse(urlAction.Attribute(RewriteTags.AppendQuery)?.Value, out parseBool))
{
actionRes.AppendQueryString = parseBool;
}
if (bool.TryParse(urlAction.Attribute(RewriteTags.LogRewrittenUrl)?.Value, out parseBool))
{
actionRes.LogRewrittenUrl = parseBool;
}
RedirectType redirectType;
if (Enum.TryParse(urlAction.Attribute(RewriteTags.RedirectType)?.Value, out redirectType))
{
actionRes.RedirectType = redirectType;
}
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
CreateUrlActionFromParsedAction(actionRes, globalRule, res);
}
public static void CreateUrlActionFromParsedAction(ParsedUrlAction actionRes, bool globalRule, UrlRewriteRule res)
{
switch (actionRes.Type)
{
case ActionType.None:
res.Action = new VoidAction();
break;
case ActionType.Rewrite:
if (actionRes.AppendQueryString)
{
res.Action = new RewriteAction(res.StopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, actionRes.Url, clearQuery: false);
}
else
{
res.Action = new RewriteAction(res.StopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, actionRes.Url, clearQuery: true);
}
break;
case ActionType.Redirect:
if (actionRes.AppendQueryString)
{
res.Action = new RedirectAction((int)actionRes.RedirectType, actionRes.Url);
}
else
{
res.Action = new RedirectClearQueryAction((int)actionRes.RedirectType, actionRes.Url);
}
break;
case ActionType.AbortRequest:
throw new FormatException("Abort requests are not supported.");
case ActionType.CustomResponse:
// TODO
throw new FormatException("Custom Responses are not supported");
}
}
}
}

View File

@ -0,0 +1,41 @@
// 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.
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class UrlRewriteRule : Rule
{
public string Name { get; set; }
public bool Enabled { get; set; } = true;
public PatternSyntax PatternSyntax { get; set; }
public bool StopProcessing { get; set; }
public UrlMatch InitialMatch { get; set; }
public Conditions Conditions { get; set; }
public UrlAction Action { get; set; }
public override RuleResult ApplyRule(RewriteContext context)
{
if (!Enabled)
{
return RuleResult.Continue;
}
// Due to the path string always having a leading slash,
// remove it from the path before regex comparison
var initMatchRes = InitialMatch.Evaluate(context.HttpContext.Request.Path.ToString().Substring(1), context);
if (!initMatchRes.Success)
{
return RuleResult.Continue;
}
var condMatchRes = Conditions.Evaluate(context, initMatchRes);
if (!condMatchRes.Success)
{
return RuleResult.Continue;
}
// at this point we know the rule passed, evaluate the replacement.
return Action.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
}
}
}

View File

@ -5,7 +5,7 @@
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>0e7ca1a7-1dc3-4ce6-b9c7-1688fe1410f1</ProjectGuid>
<RootNamespace>Microsoft.AspNetCore.Rewrite</RootNamespace>
@ -17,5 +17,5 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -4,7 +4,7 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Rewrite.ModRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
namespace Microsoft.AspNetCore.Rewrite
{
@ -16,16 +16,16 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="options">The UrlRewrite options.</param>
/// <param name="hostingEnv"></param>
/// <param name="filePath">The path to the file containing mod_rewrite rules.</param>
public static UrlRewriteOptions ImportFromModRewrite(this UrlRewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
{
if (options == null)
{
throw new ArgumentNullException("UrlRewriteOptions is null");
throw new ArgumentNullException(nameof(options));
}
if (hostingEnv == null)
{
throw new ArgumentNullException("HostingEnvironment is null");
throw new ArgumentNullException(nameof(hostingEnv));
}
if (string.IsNullOrEmpty(filePath))
@ -46,36 +46,39 @@ namespace Microsoft.AspNetCore.Rewrite
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="reader">Text reader containing a stream of mod_rewrite rules.</param>
public static UrlRewriteOptions ImportFromModRewrite(this UrlRewriteOptions options, TextReader reader)
public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, TextReader reader)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
options.Rules.AddRange(FileParser.Parse(reader));
return options;
}
/// <summary>
/// Adds a mod_rewrite rule to the current rules.
/// Additional properties (conditions, flags) for the rule can be added through the action.
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="rule">The literal string of a mod_rewrite rule:
/// "RewriteRule Pattern Substitution [Flags]"</param>
/// <param name="action">Action to perform on the <see cref="RuleBuilder"/> </param>
public static UrlRewriteOptions AddModRewriteRule(this UrlRewriteOptions options, string rule, Action<RuleBuilder> action)
public static RewriteOptions AddModRewriteRule(this RewriteOptions options, string rule)
{
var builder = new RuleBuilder(rule);
action(builder);
options.Rules.Add(builder.Build());
return options;
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (rule == null)
{
throw new ArgumentNullException(nameof(rule));
}
/// <summary>
/// Adds a mod_rewrite rule to the current rules.
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="rule">The literal string of a mod_rewrite rule:
/// "RewriteRule Pattern Substitution [Flags]"</param>
public static UrlRewriteOptions AddModRewriteRule(this UrlRewriteOptions options, string rule)
{
var builder = new RuleBuilder(rule);
options.Rules.Add(builder.Build());
return options;

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <summary>
/// The UrlRewrite Context contains the HttpContext of the request and the file provider to check conditions.
/// </summary>
public class UrlRewriteContext
public class RewriteContext
{
public HttpContext HttpContext { get; set; }
public IFileProvider FileProvider { get; set; }

View File

@ -0,0 +1,35 @@
// 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 Microsoft.AspNetCore.Rewrite;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods for the <see cref="RewriteMiddleware"/>
/// </summary>
public static class RewriteExtensions
{
/// <summary>
/// Checks if a given Url matches rules and conditions, and modifies the HttpContext on match.
/// </summary>
/// <param name="app"></param>
/// <param name="options">Options for urlrewrite.</param>
/// <returns></returns>
public static IApplicationBuilder UseRewriter(this IApplicationBuilder app, RewriteOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
// put middleware in pipeline
return app.UseMiddleware<RewriteMiddleware>(options);
}
}
}

View File

@ -5,7 +5,7 @@ using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite
@ -13,19 +13,19 @@ namespace Microsoft.AspNetCore.Rewrite
/// <summary>
/// Represents a middleware that rewrites urls imported from mod_rewrite, UrlRewrite, and code.
/// </summary>
public class UrlRewriteMiddleware
public class RewriteMiddleware
{
private readonly RequestDelegate _next;
private readonly UrlRewriteOptions _options;
private readonly RewriteOptions _options;
private readonly IFileProvider _fileProvider;
/// <summary>
/// Creates a new instance of <see cref="UrlRewriteMiddleware"/>
/// Creates a new instance of <see cref="RewriteMiddleware"/>
/// </summary>
/// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
/// <param name="hostingEnv">The Hosting Environment.</param>
/// <param name="options">The middleware options, containing the rules to apply.</param>
public UrlRewriteMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, UrlRewriteOptions options)
public RewriteMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, RewriteOptions options)
{
if (next == null)
{
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Rewrite
{
throw new ArgumentNullException(nameof(context));
}
var urlContext = new UrlRewriteContext { HttpContext = context, FileProvider = _fileProvider };
var urlContext = new RewriteContext { HttpContext = context, FileProvider = _fileProvider };
foreach (var rule in _options.Rules)
{
// Apply the rule

View File

@ -2,15 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite
{
/// <summary>
/// Options for the <see cref="UrlRewriteMiddleware"/>
/// Options for the <see cref="RewriteMiddleware"/>
/// </summary>
public class UrlRewriteOptions
public class RewriteOptions
{
/// <summary>
/// The ordered list of rules to apply to the context.

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
{
public enum RuleTerminiation
{
Continue,
ResponseComplete,
StopRules
}
}

View File

@ -1,14 +0,0 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
{
public class Conditions
{
public List<Condition> ConditionList { get; set; } = new List<Condition>();
public LogicalGrouping MatchType { get; set; } // default is MatchAll
public bool TrackingAllCaptures { get; set; }
}
}

View File

@ -1,14 +0,0 @@
// 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.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
{
public class InitialMatch
{
public Regex Url { get; set; } // TODO must be a non-empty string, throw in check after parsing?
public bool IgnoreCase { get; set; } = true;
public bool Negate { get; set; }
}
}

View File

@ -1,44 +0,0 @@
// 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.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Rewrite.UrlRewrite;
namespace Microsoft.AspNetCore.Rewrite
{
public static class UrlRewriteExtensions
{
/// <summary>
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="hostingEnv"></param>
/// <param name="filePath">The path to the file containing urlrewrite rules.</param>
public static UrlRewriteOptions ImportFromUrlRewrite(this UrlRewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
{
if (options == null)
{
throw new ArgumentNullException("UrlRewriteOptions is null");
}
if (hostingEnv == null)
{
throw new ArgumentNullException("HostingEnvironment is null");
}
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException(nameof(filePath));
}
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
using (var stream = File.OpenRead(path))
{
options.Rules.AddRange(UrlRewriteFileParser.Parse(new StreamReader(stream)));
};
return options;
}
}
}

View File

@ -1,228 +0,0 @@
// 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.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
{
// TODO rename
public static class UrlRewriteFileParser
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
public static List<UrlRewriteRule> Parse(TextReader reader)
{
var temp = XDocument.Load(reader);
var xmlRoot = temp.Descendants(RewriteTags.Rewrite);
var rules = new List<UrlRewriteRule>();
if (xmlRoot != null)
{
// there is a valid rewrite block, go through each rule and process
GetGlobalRules(xmlRoot.Descendants(RewriteTags.GlobalRules), rules);
GetRules(xmlRoot.Descendants(RewriteTags.Rules), rules);
}
return rules;
}
private static void GetGlobalRules(IEnumerable<XElement> globalRules, List<UrlRewriteRule> result)
{
foreach (var rule in globalRules.Elements(RewriteTags.Rule) ?? Enumerable.Empty<XElement>())
{
var res = new UrlRewriteRule();
SetRuleAttributes(rule, res);
// TODO handle full url with global rules - may or may not support
res.Action = CreateUrlAction(rule.Element(RewriteTags.Action));
result.Add(res);
}
}
private static void GetRules(IEnumerable<XElement> rules, List<UrlRewriteRule> result)
{
// TODO Better null check?
foreach (var rule in rules.Elements(RewriteTags.Rule) ?? Enumerable.Empty<XElement>())
{
var res = new UrlRewriteRule();
SetRuleAttributes(rule, res);
res.Action = CreateUrlAction(rule.Element(RewriteTags.Action));
result.Add(res);
}
}
private static void SetRuleAttributes(XElement rule, UrlRewriteRule res)
{
if (rule == null)
{
return;
}
res.Name = rule.Attribute(RewriteTags.Name)?.Value;
bool enabled;
if (bool.TryParse(rule.Attribute(RewriteTags.Enabled)?.Value, out enabled))
{
res.Enabled = enabled;
}
PatternSyntax patternSyntax;
if (Enum.TryParse(rule.Attribute(RewriteTags.PatternSyntax)?.Value, out patternSyntax))
{
res.PatternSyntax = patternSyntax;
}
bool stopProcessing;
if (bool.TryParse(rule.Attribute(RewriteTags.StopProcessing)?.Value, out stopProcessing))
{
res.StopProcessing = stopProcessing;
}
res.Match = CreateMatch(rule.Element(RewriteTags.Match));
res.Conditions = CreateConditions(rule.Element(RewriteTags.Conditions));
}
private static InitialMatch CreateMatch(XElement match)
{
if (match == null)
{
return null;
}
var matchRes = new InitialMatch();
bool parBool;
if (bool.TryParse(match.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
{
matchRes.IgnoreCase = parBool;
}
if (bool.TryParse(match.Attribute(RewriteTags.Negate)?.Value, out parBool))
{
matchRes.Negate = parBool;
}
var parsedInputString = match.Attribute(RewriteTags.Url)?.Value;
if (matchRes.IgnoreCase)
{
matchRes.Url = new Regex(parsedInputString, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout);
}
else
{
matchRes.Url = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
}
return matchRes;
}
private static Conditions CreateConditions(XElement conditions)
{
var condRes = new Conditions();
if (conditions == null)
{
return condRes; // TODO make sure no null exception on Conditions
}
LogicalGrouping grouping;
if (Enum.TryParse(conditions.Attribute(RewriteTags.MatchType)?.Value, out grouping))
{
condRes.MatchType = grouping;
}
bool parBool;
if (bool.TryParse(conditions.Attribute(RewriteTags.TrackingAllCaptures)?.Value, out parBool))
{
condRes.TrackingAllCaptures = parBool;
}
foreach (var cond in conditions.Elements(RewriteTags.Add))
{
condRes.ConditionList.Add(CreateCondition(cond));
}
return condRes;
}
private static Condition CreateCondition(XElement condition)
{
if (condition == null)
{
return null;
}
var condRes = new Condition();
bool parBool;
if (bool.TryParse(condition.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
{
condRes.IgnoreCase = parBool;
}
if (bool.TryParse(condition.Attribute(RewriteTags.Negate)?.Value, out parBool))
{
condRes.Negate = parBool;
}
MatchType matchType;
if (Enum.TryParse(condition.Attribute(RewriteTags.MatchPattern)?.Value, out matchType))
{
condRes.MatchType = matchType;
}
var parsedInputString = condition.Attribute(RewriteTags.Input)?.Value;
if (parsedInputString != null)
{
condRes.Input = InputParser.ParseInputString(parsedInputString);
}
parsedInputString = condition.Attribute(RewriteTags.Pattern)?.Value;
if (condRes.IgnoreCase)
{
condRes.MatchPattern = new Regex(parsedInputString, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout);
}
else
{
condRes.MatchPattern = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
}
return condRes;
}
private static UrlAction CreateUrlAction(XElement urlAction)
{
if (urlAction == null)
{
throw new FormatException("Action is a required element of a rule.");
}
var actionRes = new UrlAction();
ActionType actionType;
if (Enum.TryParse(urlAction.Attribute(RewriteTags.Type)?.Value, out actionType))
{
actionRes.Type = actionType;
}
bool parseBool;
if (bool.TryParse(urlAction.Attribute(RewriteTags.AppendQuery)?.Value, out parseBool))
{
actionRes.AppendQueryString = parseBool;
}
if (bool.TryParse(urlAction.Attribute(RewriteTags.LogRewrittenUrl)?.Value, out parseBool))
{
actionRes.LogRewrittenUrl = parseBool;
}
RedirectType redirectType;
if (Enum.TryParse(urlAction.Attribute(RewriteTags.RedirectType)?.Value, out redirectType))
{
actionRes.RedirectType = redirectType;
}
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
return actionRes;
}
}
}

View File

@ -1,25 +0,0 @@
// 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 Microsoft.AspNetCore.Rewrite.RuleAbstraction;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
{
public class UrlRewriteRule : Rule
{
public string Name { get; set; }
public bool Enabled { get; set; } = true;
public PatternSyntax PatternSyntax { get; set; }
public bool StopProcessing { get; set; }
public InitialMatch Match { get; set; }
public Conditions Conditions { get; set; }
public UrlAction Action { get; set; }
public override RuleResult ApplyRule(UrlRewriteContext context)
{
// TODO
throw new NotImplementedException();
}
}
}

View File

@ -2,34 +2,67 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Rewrite;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
namespace Microsoft.AspNetCore.Builder
namespace Microsoft.AspNetCore.Rewrite
{
/// <summary>
/// Extension methods for the <see cref="UrlRewriteMiddleware"/>
/// </summary>
public static class UrlRewriteExtensions
{
/// <summary>
/// Checks if a given Url matches rules and conditions, and modifies the HttpContext on match.
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
/// </summary>
/// <param name="app"></param>
/// <param name="options">Options for urlrewrite.</param>
/// <returns></returns>
public static IApplicationBuilder UseRewriter(this IApplicationBuilder app, UrlRewriteOptions options)
/// <param name="options">The UrlRewrite options.</param>
/// <param name="hostingEnv"></param>
/// <param name="filePath">The path to the file containing urlrewrite rules.</param>
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
// put middleware in pipeline
return app.UseMiddleware<UrlRewriteMiddleware>(options);
if (hostingEnv == null)
{
throw new ArgumentNullException(nameof(hostingEnv));
}
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException(nameof(filePath));
}
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
using (var stream = File.OpenRead(path))
{
options.Rules.AddRange(UrlRewriteFileParser.Parse(new StreamReader(stream)));
};
return options;
}
/// <summary>
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
/// </summary>
/// <param name="options">The UrlRewrite options.</param>
/// <param name="stream">The text reader stream.</param>
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, TextReader stream)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (stream == null)
{
throw new ArgumentException(nameof(stream));
}
using (stream)
{
options.Rules.AddRange(UrlRewriteFileParser.Parse(stream));
};
return options;
}
}
}

View File

@ -1,10 +1,10 @@
// 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 Microsoft.AspNetCore.Rewrite.ModRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite
namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
public class ConditionActionTest
{

View File

@ -3,10 +3,10 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Rewrite.ModRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite
namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
public class FlagParserTest
{

View File

@ -1,11 +1,11 @@
// 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 Microsoft.AspNetCore.Rewrite.ModRewrite;
using Microsoft.AspNetCore.Rewrite.Operands;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
using Xunit;
namespace RewriteTest
namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
// This file tests an input of a list of tokens and verifies that the appropriate condition is obtained
public class ModRewriteConditionBuilderTest

Some files were not shown because too many files have changed in this diff Show More