diff --git a/src/Middleware/Middleware.sln b/src/Middleware/Middleware.sln index 44be9e8d3bec..bccbab07c9e2 100644 --- a/src/Middleware/Middleware.sln +++ b/src/Middleware/Middleware.sln @@ -281,6 +281,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{179A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeaderPropagationSample", "HeaderPropagation\samples\HeaderPropagationSample\HeaderPropagationSample.csproj", "{CDE2E736-A034-4748-98C4-0DEDAAC8063D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IIS", "..\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj", "{B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1527,6 +1529,18 @@ Global {CDE2E736-A034-4748-98C4-0DEDAAC8063D}.Release|x64.Build.0 = Release|Any CPU {CDE2E736-A034-4748-98C4-0DEDAAC8063D}.Release|x86.ActiveCfg = Release|Any CPU {CDE2E736-A034-4748-98C4-0DEDAAC8063D}.Release|x86.Build.0 = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|x64.Build.0 = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Debug|x86.Build.0 = Debug|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|Any CPU.Build.0 = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|x64.ActiveCfg = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|x64.Build.0 = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|x86.ActiveCfg = Release|Any CPU + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1648,6 +1662,7 @@ Global {8CDBD9C6-96D8-4987-AFCD-D248FBC7F02D} = {0437D207-864E-429C-92B4-9D08D290188C} {179A159B-87EA-4353-BE92-4FB6CC05BC7D} = {0437D207-864E-429C-92B4-9D08D290188C} {CDE2E736-A034-4748-98C4-0DEDAAC8063D} = {179A159B-87EA-4353-BE92-4FB6CC05BC7D} + {B9BE1823-B555-4AAB-AEBC-C8C3F48C8861} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA} diff --git a/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.csproj b/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.csproj index a4014b3f9491..587668329e38 100644 --- a/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.csproj +++ b/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.netcoreapp3.0.cs b/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.netcoreapp3.0.cs index 180d85f51d16..d807634f4979 100644 --- a/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.netcoreapp3.0.cs +++ b/src/Middleware/Rewrite/ref/Microsoft.AspNetCore.Rewrite.netcoreapp3.0.cs @@ -18,8 +18,8 @@ public static partial class ApacheModRewriteOptionsExtensions } public static partial class IISUrlRewriteOptionsExtensions { - public static Microsoft.AspNetCore.Rewrite.RewriteOptions AddIISUrlRewrite(this Microsoft.AspNetCore.Rewrite.RewriteOptions options, Microsoft.Extensions.FileProviders.IFileProvider fileProvider, string filePath) { throw null; } - public static Microsoft.AspNetCore.Rewrite.RewriteOptions AddIISUrlRewrite(this Microsoft.AspNetCore.Rewrite.RewriteOptions options, System.IO.TextReader reader) { throw null; } + public static Microsoft.AspNetCore.Rewrite.RewriteOptions AddIISUrlRewrite(this Microsoft.AspNetCore.Rewrite.RewriteOptions options, Microsoft.Extensions.FileProviders.IFileProvider fileProvider, string filePath, bool alwaysUseManagedServerVariables = false) { throw null; } + public static Microsoft.AspNetCore.Rewrite.RewriteOptions AddIISUrlRewrite(this Microsoft.AspNetCore.Rewrite.RewriteOptions options, System.IO.TextReader reader, bool alwaysUseManagedServerVariables = false) { throw null; } } public partial interface IRule { @@ -366,7 +366,7 @@ public virtual void ApplyRule(Microsoft.AspNetCore.Rewrite.RewriteContext contex public partial class InputParser { public InputParser() { } - public InputParser(Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.IISRewriteMapCollection rewriteMaps) { } + public InputParser(Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.IISRewriteMapCollection rewriteMaps, bool alwaysUseManagedServerVariables) { } public Microsoft.AspNetCore.Rewrite.Internal.Pattern ParseInputString(string testString) { throw null; } public Microsoft.AspNetCore.Rewrite.Internal.Pattern ParseInputString(string testString, Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.UriMatchPart uriMatchPart) { throw null; } } @@ -443,7 +443,7 @@ public static partial class RewriteTags } public static partial class ServerVariables { - public static Microsoft.AspNetCore.Rewrite.Internal.PatternSegment FindServerVariable(string serverVariable, Microsoft.AspNetCore.Rewrite.Internal.ParserContext context, Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.UriMatchPart uriMatchPart) { throw null; } + public static Microsoft.AspNetCore.Rewrite.Internal.PatternSegment FindServerVariable(string serverVariable, Microsoft.AspNetCore.Rewrite.Internal.ParserContext context, Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.UriMatchPart uriMatchPart, bool alwaysUseManagedServerVariables) { throw null; } } public partial class UriMatchCondition : Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.Condition { @@ -457,7 +457,7 @@ public enum UriMatchPart public partial class UrlRewriteFileParser { public UrlRewriteFileParser() { } - public System.Collections.Generic.IList Parse(System.IO.TextReader reader) { throw null; } + public System.Collections.Generic.IList Parse(System.IO.TextReader reader, bool alwaysUseManagedServerVariables) { throw null; } } public partial class UrlRewriteRuleBuilder { diff --git a/src/Middleware/Rewrite/src/IISUrlRewriteOptionsExtensions.cs b/src/Middleware/Rewrite/src/IISUrlRewriteOptionsExtensions.cs index e31819b1116b..600a3c065621 100644 --- a/src/Middleware/Rewrite/src/IISUrlRewriteOptionsExtensions.cs +++ b/src/Middleware/Rewrite/src/IISUrlRewriteOptionsExtensions.cs @@ -1,8 +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. using System; using System.IO; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; using Microsoft.Extensions.FileProviders; @@ -19,7 +20,8 @@ public static class IISUrlRewriteOptionsExtensions /// The /// The /// The path to the file containing UrlRewrite rules. - public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFileProvider fileProvider, string filePath) + /// Server variables are by default sourced from the server if it supports the feature. Use true to disable that behavior + public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFileProvider fileProvider, string filePath, bool alwaysUseManagedServerVariables = false) { if (options == null) { @@ -35,7 +37,7 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFile using (var stream = file.CreateReadStream()) { - return AddIISUrlRewrite(options, new StreamReader(stream)); + return AddIISUrlRewrite(options, new StreamReader(stream), alwaysUseManagedServerVariables); } } @@ -44,7 +46,8 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFile /// /// The /// The text reader stream. - public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextReader reader) + /// Server variables are by default sourced from the server if it supports the feature. Use true to disable that behavior + public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextReader reader, bool alwaysUseManagedServerVariables = false) { if (options == null) { @@ -56,7 +59,7 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextR throw new ArgumentException(nameof(reader)); } - var rules = new UrlRewriteFileParser().Parse(reader); + var rules = new UrlRewriteFileParser().Parse(reader, alwaysUseManagedServerVariables); foreach (var rule in rules) { @@ -66,4 +69,4 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextR return options; } } -} \ No newline at end of file +} diff --git a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/InputParser.cs b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/InputParser.cs index 53c63dfb6dd5..a68ef08bd503 100644 --- a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/InputParser.cs +++ b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/InputParser.cs @@ -1,4 +1,4 @@ -// 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. using System; @@ -14,14 +14,16 @@ public class InputParser private const char OpenBrace = '{'; private const char CloseBrace = '}'; private readonly IISRewriteMapCollection _rewriteMaps; + private readonly bool _alwaysUseManagedServerVariables; public InputParser() { } - public InputParser(IISRewriteMapCollection rewriteMaps) + public InputParser(IISRewriteMapCollection rewriteMaps, bool alwaysUseManagedServerVariables) { _rewriteMaps = rewriteMaps; + _alwaysUseManagedServerVariables = alwaysUseManagedServerVariables; } /// @@ -98,7 +100,7 @@ private void ParseParameter(ParserContext context, IList results { // This is just a server variable, so we do a lookup and verify the server variable exists. parameter = context.Capture(); - results.Add(ServerVariables.FindServerVariable(parameter, context, uriMatchPart)); + results.Add(ServerVariables.FindServerVariable(parameter, context, uriMatchPart, _alwaysUseManagedServerVariables)); return; } else if (context.Current == Colon) diff --git a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/ServerVariables.cs b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/ServerVariables.cs index 35e6ce12375f..cc991ca6642a 100644 --- a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/ServerVariables.cs +++ b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/ServerVariables.cs @@ -1,4 +1,4 @@ -// 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. using System; @@ -15,58 +15,89 @@ public static class ServerVariables /// The server variable /// The parser context which is utilized when an exception is thrown /// Indicates whether the full URI or the path should be evaluated for URL segments + /// Determines whether server variables are sourced from the managed server /// Thrown when the server variable is unknown /// The matching - public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart) + public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart, bool alwaysUseManagedServerVariables) { + Func managedVariableThunk = default; + switch (serverVariable) { // TODO Add all server variables here. case "ALL_RAW": - throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + managedVariableThunk = () => throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + break; case "APP_POOL_ID": - throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + managedVariableThunk = () => throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + break; case "CONTENT_LENGTH": - return new HeaderSegment(HeaderNames.ContentLength); + managedVariableThunk = () => new HeaderSegment(HeaderNames.ContentLength); + break; case "CONTENT_TYPE": - return new HeaderSegment(HeaderNames.ContentType); + managedVariableThunk = () => new HeaderSegment(HeaderNames.ContentType); + break; case "HTTP_ACCEPT": - return new HeaderSegment(HeaderNames.Accept); + managedVariableThunk = () => new HeaderSegment(HeaderNames.Accept); + break; case "HTTP_COOKIE": - return new HeaderSegment(HeaderNames.Cookie); + managedVariableThunk = () => new HeaderSegment(HeaderNames.Cookie); + break; case "HTTP_HOST": - return new HeaderSegment(HeaderNames.Host); + managedVariableThunk = () => new HeaderSegment(HeaderNames.Host); + break; case "HTTP_REFERER": - return new HeaderSegment(HeaderNames.Referer); + managedVariableThunk = () => new HeaderSegment(HeaderNames.Referer); + break; case "HTTP_USER_AGENT": - return new HeaderSegment(HeaderNames.UserAgent); + managedVariableThunk = () => new HeaderSegment(HeaderNames.UserAgent); + break; case "HTTP_CONNECTION": - return new HeaderSegment(HeaderNames.Connection); + managedVariableThunk = () => new HeaderSegment(HeaderNames.Connection); + break; case "HTTP_URL": - return new UrlSegment(uriMatchPart); + managedVariableThunk = () => new UrlSegment(uriMatchPart); + break; case "HTTPS": - return new IsHttpsUrlSegment(); + managedVariableThunk = () => new IsHttpsUrlSegment(); + break; case "LOCAL_ADDR": - return new LocalAddressSegment(); + managedVariableThunk = () => new LocalAddressSegment(); + break; case "HTTP_PROXY_CONNECTION": - throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + managedVariableThunk = () => throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + break; case "QUERY_STRING": - return new QueryStringSegment(); + managedVariableThunk = () => new QueryStringSegment(); + break; case "REMOTE_ADDR": - return new RemoteAddressSegment(); + managedVariableThunk = () => new RemoteAddressSegment(); + break; case "REMOTE_HOST": - throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + managedVariableThunk = () => throw new NotSupportedException(Resources.FormatError_UnsupportedServerVariable(serverVariable)); + break; case "REMOTE_PORT": - return new RemotePortSegment(); + managedVariableThunk = () => new RemotePortSegment(); + break; case "REQUEST_FILENAME": - return new RequestFileNameSegment(); + managedVariableThunk = () => new RequestFileNameSegment(); + break; case "REQUEST_METHOD": - return new RequestMethodSegment(); + managedVariableThunk = () => new RequestMethodSegment(); + break; case "REQUEST_URI": - return new UrlSegment(uriMatchPart); + managedVariableThunk = () => new UrlSegment(uriMatchPart); + break; default: throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index)); } + + if (alwaysUseManagedServerVariables) + { + return managedVariableThunk(); + } + + return new IISServerVariableSegment(serverVariable, managedVariableThunk); } } } diff --git a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/UrlRewriteFileParser.cs b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/UrlRewriteFileParser.cs index 6bb08f41f1d2..97cc7ce4715b 100644 --- a/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/UrlRewriteFileParser.cs +++ b/src/Middleware/Rewrite/src/Internal/IISUrlRewrite/UrlRewriteFileParser.cs @@ -1,4 +1,4 @@ -// 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. using System; @@ -20,7 +20,8 @@ public class UrlRewriteFileParser /// Parse an IIS rewrite section into a list of s. /// /// The reader containing the rewrite XML - public IList Parse(TextReader reader) + /// Determines whether server variables will be sourced from the managed server + public IList Parse(TextReader reader, bool alwaysUseManagedServerVariables) { var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo); var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault(); @@ -30,7 +31,7 @@ public IList Parse(TextReader reader) return null; } - _inputParser = new InputParser(RewriteMapParser.Parse(xmlRoot)); + _inputParser = new InputParser(RewriteMapParser.Parse(xmlRoot), alwaysUseManagedServerVariables); var result = new List(); ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true); diff --git a/src/Middleware/Rewrite/src/Internal/PatternSegments/IISServerVariableSegment.cs b/src/Middleware/Rewrite/src/Internal/PatternSegments/IISServerVariableSegment.cs new file mode 100644 index 000000000000..30504d54a4f1 --- /dev/null +++ b/src/Middleware/Rewrite/src/Internal/PatternSegments/IISServerVariableSegment.cs @@ -0,0 +1,25 @@ +// 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.Server.IIS; + +namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments +{ + internal class IISServerVariableSegment : PatternSegment + { + private readonly string _variableName; + private readonly Func _fallbackThunk; + + public IISServerVariableSegment(string variableName, Func fallbackThunk) + { + _variableName = variableName; + _fallbackThunk = fallbackThunk; + } + + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) + { + return context.HttpContext.GetIISServerVariable(_variableName) ?? _fallbackThunk().Evaluate(context, ruleBackReferences, conditionBackReferences); + } + } +} diff --git a/src/Middleware/Rewrite/src/Microsoft.AspNetCore.Rewrite.csproj b/src/Middleware/Rewrite/src/Microsoft.AspNetCore.Rewrite.csproj index d203b74371d3..be6f19ceb739 100644 --- a/src/Middleware/Rewrite/src/Microsoft.AspNetCore.Rewrite.csproj +++ b/src/Middleware/Rewrite/src/Microsoft.AspNetCore.Rewrite.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/FileParserTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/FileParserTests.cs index 18f1a246d0f8..e4ae35323f1d 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/FileParserTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/FileParserTests.cs @@ -1,4 +1,4 @@ -// 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. using System.Collections.Generic; @@ -35,7 +35,7 @@ public void RuleParse_ParseTypicalRule() pattern: "article.aspx?id={R:1}&title={R:2}")); // act - var res = new UrlRewriteFileParser().Parse(new StringReader(xml)); + var res = new UrlRewriteFileParser().Parse(new StringReader(xml), false); // assert AssertUrlRewriteRuleEquality(expected, res); @@ -72,7 +72,7 @@ public void RuleParse_ParseSingleRuleWithSingleCondition() pattern: "article.aspx?id={R:1}&title={R:2}")); // act - var res = new UrlRewriteFileParser().Parse(new StringReader(xml)); + var res = new UrlRewriteFileParser().Parse(new StringReader(xml), false); // assert AssertUrlRewriteRuleEquality(expected, res); @@ -121,7 +121,7 @@ public void RuleParse_ParseMultipleRules() pattern: "article.aspx?id={R:1}&title={R:2}")); // act - var res = new UrlRewriteFileParser().Parse(new StringReader(xml)); + var res = new UrlRewriteFileParser().Parse(new StringReader(xml), false); // assert AssertUrlRewriteRuleEquality(expected, res); @@ -150,7 +150,7 @@ public void Should_parse_global_rules() "; // act - var rules = new UrlRewriteFileParser().Parse(new StringReader(xml)); + var rules = new UrlRewriteFileParser().Parse(new StringReader(xml), false); // assert Assert.Equal(2, rules.Count); @@ -219,4 +219,4 @@ private void AssertUrlRewriteRuleEquality(IList actual, IList } } } -} \ No newline at end of file +} diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/FormatExceptionHandlingTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/FormatExceptionHandlingTests.cs index f6771dddfed0..20ad022d5afa 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/FormatExceptionHandlingTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/FormatExceptionHandlingTests.cs @@ -1,4 +1,4 @@ -// 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. using System; @@ -63,8 +63,8 @@ public class FormatExceptionHandlingTests public void ThrowFormatExceptionWithCorrectMessage(string input, string expected) { // Arrange, Act, Assert - var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input))); + var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input), false)); Assert.Equal(expected, ex.Message); } } -} \ No newline at end of file +} diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/InputParserTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/InputParserTests.cs index ebc32becdc29..0439bc4fa0f9 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/InputParserTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/InputParserTests.cs @@ -1,4 +1,4 @@ -// 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. using System; @@ -94,7 +94,7 @@ public void FormatExceptionsOnBadSyntax(string testString) [Fact] public void Should_throw_FormatException_if_no_rewrite_maps_are_defined() { - Assert.Throws(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", UriMatchPart.Path)); + Assert.Throws(() => new InputParser(null, false).ParseInputString("{apiMap:{R:1}}", UriMatchPart.Path)); } [Fact] @@ -104,7 +104,7 @@ public void Should_throw_FormatException_if_rewrite_map_not_found() const string undefinedMapName = "apiMap"; var map = new IISRewriteMap(definedMapName); var maps = new IISRewriteMapCollection { map }; - Assert.Throws(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", UriMatchPart.Path)); + Assert.Throws(() => new InputParser(maps, false).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", UriMatchPart.Path)); } [Fact] @@ -118,7 +118,7 @@ public void Should_parse_RewriteMapSegment_and_successfully_evaluate_result() var maps = new IISRewriteMapCollection { map }; var inputString = $"{{{expectedMapName}:{{R:1}}}}"; - var pattern = new InputParser(maps).ParseInputString(inputString, UriMatchPart.Path); + var pattern = new InputParser(maps, false).ParseInputString(inputString, UriMatchPart.Path); Assert.Equal(1, pattern.PatternSegments.Count); var segment = pattern.PatternSegments.Single(); diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs index 5c2e26fc5ff0..f144104a67fe 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs @@ -211,8 +211,8 @@ public class InvalidUrlRewriteFormatExceptionHandlingTests public void ThrowInvalidUrlRewriteFormatExceptionWithCorrectMessage(string input, string expected) { // Arrange, Act, Assert - var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input))); + var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input), false)); Assert.Equal(expected, ex.Message); } } -} \ No newline at end of file +} diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/ServerVariableTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/ServerVariableTests.cs index b56a8c78548f..274ef848e5e8 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/ServerVariableTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/ServerVariableTests.cs @@ -1,14 +1,16 @@ -// 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. +using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Rewrite.Internal; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; using Microsoft.Net.Http.Headers; using Xunit; -namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite +namespace Microsoft.AspNetCore.Rewrite.Tests.IISUrlRewrite { public class ServerVariableTests { @@ -32,13 +34,105 @@ public void CheckServerVariableParsingAndApplication(string variable, string exp { // Arrange and Act var testParserContext = new ParserContext("test"); - var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, uriMatchPart); - var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); + var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, uriMatchPart, true); + var lookup = serverVar.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); // Assert Assert.Equal(expected, lookup); } - private RewriteContext CreateTestHttpContext() + [Theory] + [InlineData("CONTENT_LENGTH", "20", UriMatchPart.Path)] + [InlineData("CONTENT_TYPE", "text/xml", UriMatchPart.Path)] + [InlineData("HTTP_ACCEPT", "other-accept", UriMatchPart.Path)] + [InlineData("HTTP_COOKIE", "other-cookie", UriMatchPart.Path)] + [InlineData("HTTP_HOST", "otherexample.com", UriMatchPart.Path)] + [InlineData("HTTP_REFERER", "other-referer", UriMatchPart.Path)] + [InlineData("HTTP_USER_AGENT", "other-useragent", UriMatchPart.Path)] + [InlineData("HTTP_CONNECTION", "other-connection", UriMatchPart.Path)] + [InlineData("HTTP_URL", "http://otherexample.com/other-foo?bar=2", UriMatchPart.Full)] + [InlineData("HTTP_URL", "http://otherexample.com/other-foo?bar=2", UriMatchPart.Path)] + [InlineData("QUERY_STRING", "bar=2", UriMatchPart.Path)] + [InlineData("REQUEST_FILENAME", "/other-foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "/other-foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "/other-foo", UriMatchPart.Full)] + [InlineData("REQUEST_METHOD", "POST", UriMatchPart.Full)] + public void CheckServerVariableFeatureHasPrecedenceWhenEnabled(string variable, string expected, UriMatchPart uriMatchPart) + { + // Arrange and Act + var testParserContext = new ParserContext("test"); + var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, uriMatchPart, false); + var httpContext = CreateTestHttpContext(); + httpContext.Features.Set(new TestServerVariablesFeature(new Dictionary + { + ["CONTENT_LENGTH"] = "20", + ["CONTENT_TYPE"] = "text/xml", + ["HTTP_ACCEPT"] = "other-accept", + ["HTTP_COOKIE"] = "other-cookie", + ["HTTP_HOST"] = "otherexample.com", + ["HTTP_REFERER"] = "other-referer", + ["HTTP_USER_AGENT"] = "other-useragent", + ["HTTP_CONNECTION"] = "other-connection", + ["HTTP_URL"] = "http://otherexample.com/other-foo?bar=2", + ["QUERY_STRING"] = "bar=2", + ["REQUEST_FILENAME"] = "/other-foo", + ["REQUEST_URI"] = "/other-foo", + ["REQUEST_METHOD"] = "POST" + })); + + var rewriteContext = CreateTestRewriteContext(httpContext); + var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); + + // Assert + Assert.Equal(expected, lookup); + } + + [Theory] + [InlineData("CONTENT_LENGTH", "10", UriMatchPart.Path)] + [InlineData("CONTENT_TYPE", "json", UriMatchPart.Path)] + [InlineData("HTTP_ACCEPT", "accept", UriMatchPart.Path)] + [InlineData("HTTP_COOKIE", "cookie", UriMatchPart.Path)] + [InlineData("HTTP_HOST", "example.com", UriMatchPart.Path)] + [InlineData("HTTP_REFERER", "referer", UriMatchPart.Path)] + [InlineData("HTTP_USER_AGENT", "useragent", UriMatchPart.Path)] + [InlineData("HTTP_CONNECTION", "connection", UriMatchPart.Path)] + [InlineData("HTTP_URL", "/foo", UriMatchPart.Path)] + [InlineData("HTTP_URL", "http://example.com/foo?bar=1", UriMatchPart.Full)] + [InlineData("QUERY_STRING", "bar=1", UriMatchPart.Path)] + [InlineData("REQUEST_FILENAME", "/foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "/foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "http://example.com/foo?bar=1", UriMatchPart.Full)] + [InlineData("REQUEST_METHOD", "GET", UriMatchPart.Full)] + public void CheckServerVariableFeatureIsntUsedWhenDisabled(string variable, string expected, UriMatchPart uriMatchPart) + { + // Arrange and Act + var testParserContext = new ParserContext("test"); + var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, uriMatchPart, true); + var httpContext = CreateTestHttpContext(); + httpContext.Features.Set(new TestServerVariablesFeature(new Dictionary + { + ["CONTENT_LENGTH"] = "20", + ["CONTENT_TYPE"] = "text/xml", + ["HTTP_ACCEPT"] = "other-accept", + ["HTTP_COOKIE"] = "other-cookie", + ["HTTP_HOST"] = "otherexample.com", + ["HTTP_REFERER"] = "other-referer", + ["HTTP_USER_AGENT"] = "other-useragent", + ["HTTP_CONNECTION"] = "other-connection", + ["HTTP_URL"] = "http://otherexample.com/other-foo?bar=2", + ["QUERY_STRING"] = "bar=2", + ["REQUEST_FILENAME"] = "/other-foo", + ["REQUEST_URI"] = "/other-foo", + ["REQUEST_METHOD"] = "POST" + })); + + var rewriteContext = CreateTestRewriteContext(httpContext); + var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); + + // Assert + Assert.Equal(expected, lookup); + } + + private HttpContext CreateTestHttpContext() { var context = new DefaultHttpContext(); context.Request.Method = HttpMethods.Get; @@ -53,7 +147,13 @@ private RewriteContext CreateTestHttpContext() context.Request.Headers[HeaderNames.Referer] = "referer"; context.Request.Headers[HeaderNames.UserAgent] = "useragent"; context.Request.Headers[HeaderNames.Connection] = "connection"; - return new RewriteContext { HttpContext = context }; + + return context; + } + + private RewriteContext CreateTestRewriteContext(HttpContext context = null) + { + return new RewriteContext { HttpContext = context ?? CreateTestHttpContext() }; } private MatchResults CreateTestRuleMatch() @@ -74,7 +174,7 @@ private void EmptyQueryStringCheck() var context = new DefaultHttpContext(); var rewriteContext = new RewriteContext { HttpContext = context }; var testParserContext = new ParserContext("test"); - var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext, UriMatchPart.Path); + var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext, UriMatchPart.Path, true); var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); Assert.Equal(string.Empty, lookup); diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/TestServerVariablesFeature.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/TestServerVariablesFeature.cs new file mode 100644 index 000000000000..372039679ada --- /dev/null +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/TestServerVariablesFeature.cs @@ -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. + +using System.Collections.Generic; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Rewrite.Tests.IISUrlRewrite +{ + public class TestServerVariablesFeature : IServerVariablesFeature + { + private readonly Dictionary _variables; + + public TestServerVariablesFeature(Dictionary variables) + { + _variables = variables; + } + + public string this[string variableName] + { + get => _variables[variableName]; + set => _variables[variableName] = value; + } + } +} diff --git a/src/Middleware/Rewrite/test/IISUrlRewrite/UrlRewriteApplicationTests.cs b/src/Middleware/Rewrite/test/IISUrlRewrite/UrlRewriteApplicationTests.cs index ecc753a620c5..d1b9a83936cc 100644 --- a/src/Middleware/Rewrite/test/IISUrlRewrite/UrlRewriteApplicationTests.cs +++ b/src/Middleware/Rewrite/test/IISUrlRewrite/UrlRewriteApplicationTests.cs @@ -1,4 +1,4 @@ -// 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. using System.IO; @@ -23,7 +23,7 @@ public void ApplyRule_AssertStopProcessingFlagWillTerminateOnNoAction() "); - var rules = new UrlRewriteFileParser().Parse(xml); + var rules = new UrlRewriteFileParser().Parse(xml, false); Assert.Equal(1, rules.Count); var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; @@ -42,7 +42,7 @@ public void ApplyRule_AssertNoTerminateFlagWillNotTerminateOnNoAction() "); - var rules = new UrlRewriteFileParser().Parse(xml); + var rules = new UrlRewriteFileParser().Parse(xml, false); Assert.Equal(1, rules.Count); var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; @@ -64,7 +64,7 @@ public void ApplyRule_TrackAllCaptures() "); - var rules = new UrlRewriteFileParser().Parse(xml); + var rules = new UrlRewriteFileParser().Parse(xml, false); Assert.Equal(1, rules.Count); Assert.True(rules[0].Conditions.TrackAllCaptures);