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

View File

@ -1,19 +1,12 @@
<rewrite> <rewrite>
<rules> <rules>
<rule name="Fail bad requests"> <rule name="Query String Rewrite">
<match url=".*"/> <match url="page\.asp$" />
<conditions> <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> </conditions>
<action type="AbortRequest" /> <action type="rewrite" url="newpage.aspx?param1={C:1}&amp;param2={C:2}" appendQueryString="false"/>
</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}" />
</rule> </rule>
</rules> </rules>
</rewrite> </rewrite>

View File

@ -4,21 +4,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction; using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite namespace Microsoft.AspNetCore.Rewrite
{ {
/// <summary> /// <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> /// </summary>
public static class UrlRewriteOptionsAddRulesExtensions public static class CodeRewriteExtensions
{ {
/// <summary> /// <summary>
/// Adds a rule to the current rules. /// Adds a rule to the current rules.
/// </summary> /// </summary>
/// <param name="options">The UrlRewrite options.</param> /// <param name="options">The UrlRewrite options.</param>
/// <param name="rule">A rule to be added to the current rules.</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); options.Rules.Add(rule);
return options; return options;
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// </summary> /// </summary>
/// <param name="options">The UrlRewrite options.</param> /// <param name="options">The UrlRewrite options.</param>
/// <param name="rules">A list of rules.</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); options.Rules.AddRange(rules);
return options; 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="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> /// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns> /// <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 }); options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
return options; return options;
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="options">The Url rewrite options.</param> /// <param name="options">The Url rewrite options.</param>
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param> /// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns> /// <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 }); options.Rules.Add(new SchemeRule {OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
return options; 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="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> /// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
/// <returns></returns> /// <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 }); options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = Transformation.Redirect });
return options; return options;
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="options">The Url rewrite options.</param> /// <param name="options">The Url rewrite options.</param>
/// <param name="sslPort">The port to redirect the scheme to.</param> /// <param name="sslPort">The port to redirect the scheme to.</param>
/// <returns></returns> /// <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 }); options.Rules.Add(new SchemeRule { SSLPort = sslPort, OnCompletion = Transformation.Redirect });
return options; return options;
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <param name="transform"></param> /// <param name="transform"></param>
/// <param name="description"></param> /// <param name="description"></param>
/// <returns></returns> /// <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}); options.Rules.Add(new FunctionalRule { OnApplyRule = onApplyRule, OnCompletion = transform});
return options; return options;

View File

@ -3,12 +3,12 @@
using System; using System;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction namespace Microsoft.AspNetCore.Rewrite.Internal
{ {
public class FunctionalRule : Rule 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 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. // 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. // 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 public class Condition
{ {

View File

@ -3,7 +3,7 @@
using System; using System;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{ {
public class ConditionBuilder 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.RegularExpressions; 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> /// <summary>
/// Represents the ConditionPattern for a mod_rewrite rule. /// 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="previous">The previous condition results (for backreferences).</param>
/// <param name="testString">The testString created from the <see cref="Pattern"/>.</param> /// <param name="testString">The testString created from the <see cref="Pattern"/>.</param>
/// <returns>If the testString satisfies the condition</returns> /// <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; return Operand.CheckOperation(previous, testString, context.FileProvider) ^ Invert;
} }

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum ConditionFlagType
{ {

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; 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 // TODO Refactor Condition Flags and Rule Flags under base flag class
public class ConditionFlags public class ConditionFlags

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum ConditionType
{ {

View File

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

View File

@ -4,9 +4,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; 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> /// <summary>
/// ///

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum IntegerOperationType
{ {

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum PropertyOperationType
{ {

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum StringOperationType
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum OperationType
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public class ParsedModRewriteExpression
{ {

View File

@ -1,14 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.ModRewrite;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{ {
/// <summary> /// <summary>
/// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate /// 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. // 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. // 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> /// <summary>
/// A Pattern segment contains a portion of the test string/ substitution segment with a type associated. /// 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;
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.Rewrite.ModRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{ {
public class RuleBuilder public class RuleBuilder
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum RuleFlagType
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,14 +3,14 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction namespace Microsoft.AspNetCore.Rewrite.Internal
{ {
public class PathRule : Rule public class PathRule : Rule
{ {
public Regex MatchPattern { get; set; } public Regex MatchPattern { get; set; }
public string OnMatch { get; set; } public string OnMatch { get; set; }
public Transformation OnCompletion { get; set; } = Transformation.Rewrite; 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); var matches = MatchPattern.Match(context.HttpContext.Request.Path);
if (matches.Success) if (matches.Success)
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
path, path,
req.QueryString); req.QueryString);
context.HttpContext.Response.Redirect(newUrl); context.HttpContext.Response.Redirect(newUrl);
return new RuleResult { Result = RuleTerminiation.ResponseComplete }; return RuleResult.ResponseComplete;
} }
else else
{ {
@ -35,14 +35,14 @@ namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction
} }
if (OnCompletion == Transformation.TerminatingRewrite) if (OnCompletion == Transformation.TerminatingRewrite)
{ {
return new RuleResult { Result = RuleTerminiation.StopRules }; return RuleResult.StopRules;
} }
else 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. // 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. // 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 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. // 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. // 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 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. // 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. // 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 System.Text;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.RuleAbstraction namespace Microsoft.AspNetCore.Rewrite.Internal
{ {
public class SchemeRule : Rule public class SchemeRule : Rule
{ {
public int? SSLPort { get; set; } public int? SSLPort { get; set; }
public Transformation OnCompletion { get; set; } = Transformation.Rewrite; 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. // 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; context.HttpContext.Request.Host = host;
if (OnCompletion == Transformation.TerminatingRewrite) if (OnCompletion == Transformation.TerminatingRewrite)
{ {
return new RuleResult { Result = RuleTerminiation.StopRules }; return RuleResult.StopRules;
} }
else 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); var newUrl = new StringBuilder().Append("https://").Append(host).Append(req.PathBase).Append(req.Path).Append(req.QueryString);
context.HttpContext.Response.Redirect(newUrl.ToString()); 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. // 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. // 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 public enum Transformation
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Encodings.Web; using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{ {
/// <summary> /// <summary>
/// </summary> /// </summary>

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 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. // 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. // 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 public enum MatchType
{ {

View File

@ -1,14 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
{ {
public class Condition public class ParsedCondition
{ {
public Pattern Input { get; set; }
public Regex MatchPattern { get; set; }
public bool Negate { get; set; } public bool Negate { get; set; }
public bool IgnoreCase { get; set; } = true; public bool IgnoreCase { get; set; } = true;
public MatchType MatchType { get; set; } = MatchType.Pattern; public MatchType MatchType { get; set; } = MatchType.Pattern;

View File

@ -1,14 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 ActionType Type { get; set; }
public Pattern Url { get; set; } public Pattern Url { get; set; }
public bool AppendQueryString { get; set; } public bool AppendQueryString { get; set; } = true;
public bool LogRewrittenUrl { get; set; } public bool LogRewrittenUrl { get; set; } // Ignoring this flag.
public RedirectType RedirectType { get; set; } = RedirectType.Permanent; public RedirectType RedirectType { get; set; } = RedirectType.Permanent;
} }
} }

View File

@ -1,10 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{ {
public class Pattern public class Pattern
{ {
public IList<PatternSegment> PatternSegments { get; } public IList<PatternSegment> PatternSegments { get; }
public Pattern(List<PatternSegment> patternSegments) public Pattern(List<PatternSegment> patternSegments)
{ {
PatternSegments = 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(); var strBuilder = new StringBuilder();
// TODO consider thread static for string builder - DAVID PERF
foreach (var pattern in PatternSegments) foreach (var pattern in PatternSegments)
{ {
strBuilder.Append(pattern.Evaluate(context, ruleMatch, condMatch)); strBuilder.Append(pattern.Evaluate(context, ruleMatch, condMatch));

View File

@ -1,15 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{ {
public abstract class PatternSegment public abstract class PatternSegment
{ {
// Match from prevRule, Match from prevCond // 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. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class ConditionMatchSegment : PatternSegment public class ConditionMatchSegment : PatternSegment
{ {
@ -15,9 +14,9 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Index = index; 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. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class HeaderSegment : PatternSegment public class HeaderSegment : PatternSegment
{ {
@ -15,7 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Header = header; 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]; return context.Request.Headers[Header];
} }

View File

@ -1,14 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class IsHttpsSegment : PatternSegment 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"; return context.Request.IsHttps ? "ON" : "OFF";
} }

View File

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

View File

@ -1,14 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class LocalAddressSegment : PatternSegment 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(); return context.Connection.LocalIpAddress?.ToString();
} }

View File

@ -1,14 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class QueryStringSegment : PatternSegment 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(); return context.Request.QueryString.ToString();
} }

View File

@ -1,14 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class RemoteAddressSegment : PatternSegment 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(); 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class RemotePortSegment : PatternSegment 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); 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. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class RuleMatchSegment : PatternSegment public class RuleMatchSegment : PatternSegment
{ {
@ -15,9 +14,9 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Index = index; 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. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class ToLowerSegment : PatternSegment public class ToLowerSegment : PatternSegment
{ {
@ -15,7 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Pattern = pattern; 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); var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
return pattern.ToLowerInvariant(); 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. // 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.Encodings.Web;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class UrlEncodeSegment : PatternSegment public class UrlEncodeSegment : PatternSegment
{ {
@ -16,7 +15,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
Pattern = pattern; 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); var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
return UrlEncoder.Default.Encode(pattern); return UrlEncoder.Default.Encode(pattern);

View File

@ -1,16 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
{ {
public class UrlSegment : PatternSegment 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. // 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. // 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 public enum PatternSyntax
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public enum RedirectType
{ {

View File

@ -1,11 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public static class RewriteTags
{ {
// TODO More strings to be added later once further implementations are added.
public const string Rewrite = "rewrite"; public const string Rewrite = "rewrite";
public const string GlobalRules = "globalRules"; public const string GlobalRules = "globalRules";
public const string Rules = "rules"; public const string Rules = "rules";
@ -27,7 +26,7 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
public const string Input = "input"; public const string Input = "input";
public const string Pattern = "pattern"; public const string Pattern = "pattern";
public const string Type = "type"; public const string Type = "type";
public const string AppendQuery = "appendQuery"; public const string AppendQuery = "appendQueryString";
public const string LogRewrittenUrl = "logRewrittenUrl"; public const string LogRewrittenUrl = "logRewrittenUrl";
public const string RedirectType = "redirectType"; 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Globalization; using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{ {
public static class ServerVariables public static class ServerVariables
{ {
public static PatternSegment FindServerVariable(string serverVariable) public static PatternSegment FindServerVariable(string serverVariable)
{ {
switch(serverVariable) switch(serverVariable)
@ -53,6 +50,8 @@ namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
throw new NotImplementedException(); throw new NotImplementedException();
case "REMOTE_PORT": case "REMOTE_PORT":
return new RemotePortSegment(); return new RemotePortSegment();
case "REQUEST_FILENAME":
return new RequestFileNameSegment();
default: default:
throw new FormatException("Unrecognized server variable."); 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> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>0e7ca1a7-1dc3-4ce6-b9c7-1688fe1410f1</ProjectGuid> <ProjectGuid>0e7ca1a7-1dc3-4ce6-b9c7-1688fe1410f1</ProjectGuid>
<RootNamespace>Microsoft.AspNetCore.Rewrite</RootNamespace> <RootNamespace>Microsoft.AspNetCore.Rewrite</RootNamespace>
@ -17,5 +17,5 @@
<PropertyGroup> <PropertyGroup>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Targets" Condition="'$(VSToolsPath)' != ''" />
</Project> </Project>

View File

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

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Rewrite
/// <summary> /// <summary>
/// The UrlRewrite Context contains the HttpContext of the request and the file provider to check conditions. /// The UrlRewrite Context contains the HttpContext of the request and the file provider to check conditions.
/// </summary> /// </summary>
public class UrlRewriteContext public class RewriteContext
{ {
public HttpContext HttpContext { get; set; } public HttpContext HttpContext { get; set; }
public IFileProvider FileProvider { 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction; using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite namespace Microsoft.AspNetCore.Rewrite
@ -13,19 +13,19 @@ namespace Microsoft.AspNetCore.Rewrite
/// <summary> /// <summary>
/// Represents a middleware that rewrites urls imported from mod_rewrite, UrlRewrite, and code. /// Represents a middleware that rewrites urls imported from mod_rewrite, UrlRewrite, and code.
/// </summary> /// </summary>
public class UrlRewriteMiddleware public class RewriteMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly UrlRewriteOptions _options; private readonly RewriteOptions _options;
private readonly IFileProvider _fileProvider; private readonly IFileProvider _fileProvider;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="UrlRewriteMiddleware"/> /// Creates a new instance of <see cref="RewriteMiddleware"/>
/// </summary> /// </summary>
/// <param name="next">The delegate representing the next middleware in the request pipeline.</param> /// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
/// <param name="hostingEnv">The Hosting Environment.</param> /// <param name="hostingEnv">The Hosting Environment.</param>
/// <param name="options">The middleware options, containing the rules to apply.</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) if (next == null)
{ {
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Rewrite
{ {
throw new ArgumentNullException(nameof(context)); 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) foreach (var rule in _options.Rules)
{ {
// Apply the rule // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Rewrite.RuleAbstraction; using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Rewrite namespace Microsoft.AspNetCore.Rewrite
{ {
/// <summary> /// <summary>
/// Options for the <see cref="UrlRewriteMiddleware"/> /// Options for the <see cref="RewriteMiddleware"/>
/// </summary> /// </summary>
public class UrlRewriteOptions public class RewriteOptions
{ {
/// <summary> /// <summary>
/// The ordered list of rules to apply to the context. /// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; 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 public static class UrlRewriteExtensions
{ {
/// <summary> /// <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> /// </summary>
/// <param name="app"></param> /// <param name="options">The UrlRewrite options.</param>
/// <param name="options">Options for urlrewrite.</param> /// <param name="hostingEnv"></param>
/// <returns></returns> /// <param name="filePath">The path to the file containing urlrewrite rules.</param>
public static IApplicationBuilder UseRewriter(this IApplicationBuilder app, UrlRewriteOptions options) public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
{ {
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null) if (options == null)
{ {
throw new ArgumentNullException(nameof(options)); 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. // 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. // 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; using Xunit;
namespace Microsoft.AspNetCore.Rewrite namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{ {
public class ConditionActionTest public class ConditionActionTest
{ {

View File

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

View File

@ -1,11 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 Microsoft.AspNetCore.Rewrite.Operands; using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
using Xunit; 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 // This file tests an input of a list of tokens and verifies that the appropriate condition is obtained
public class ModRewriteConditionBuilderTest public class ModRewriteConditionBuilderTest

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