Skip to content

Source server variables from IIS when running ANCM in-proc #10022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Middleware/Middleware.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<Compile Include="Microsoft.AspNetCore.Rewrite.netcoreapp3.0.cs" />
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
<Reference Include="Microsoft.AspNetCore.Server.IIS" />
<Reference Include="Microsoft.Extensions.Configuration.Abstractions" />
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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; }
}
Expand Down Expand Up @@ -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
{
Expand All @@ -457,7 +457,7 @@ public enum UriMatchPart
public partial class UrlRewriteFileParser
{
public UrlRewriteFileParser() { }
public System.Collections.Generic.IList<Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.IISUrlRewriteRule> Parse(System.IO.TextReader reader) { throw null; }
public System.Collections.Generic.IList<Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite.IISUrlRewriteRule> Parse(System.IO.TextReader reader, bool alwaysUseManagedServerVariables) { throw null; }
}
public partial class UrlRewriteRuleBuilder
{
Expand Down
15 changes: 9 additions & 6 deletions src/Middleware/Rewrite/src/IISUrlRewriteOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -19,7 +20,8 @@ public static class IISUrlRewriteOptionsExtensions
/// <param name="options">The <see cref="RewriteOptions"/></param>
/// <param name="fileProvider">The <see cref="IFileProvider"/> </param>
/// <param name="filePath">The path to the file containing UrlRewrite rules.</param>
public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFileProvider fileProvider, string filePath)
/// <param name="alwaysUseManagedServerVariables">Server variables are by default sourced from the server if it supports the <see cref="IServerVariablesFeature"/> feature. Use <c>true</c> to disable that behavior</param>
public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFileProvider fileProvider, string filePath, bool alwaysUseManagedServerVariables = false)
{
if (options == null)
{
Expand All @@ -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);
}
}

Expand All @@ -44,7 +46,8 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IFile
/// </summary>
/// <param name="options">The <see cref="RewriteOptions"/></param>
/// <param name="reader">The text reader stream.</param>
public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextReader reader)
/// <param name="alwaysUseManagedServerVariables">Server variables are by default sourced from the server if it supports the <see cref="IServerVariablesFeature"/> feature. Use <c>true</c> to disable that behavior</param>
public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextReader reader, bool alwaysUseManagedServerVariables = false)
{
if (options == null)
{
Expand All @@ -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)
{
Expand All @@ -66,4 +69,4 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextR
return options;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -98,7 +100,7 @@ private void ParseParameter(ParserContext context, IList<PatternSegment> 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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,58 +15,89 @@ public static class ServerVariables
/// <param name="serverVariable">The server variable</param>
/// <param name="context">The parser context which is utilized when an exception is thrown</param>
/// <param name="uriMatchPart">Indicates whether the full URI or the path should be evaluated for URL segments</param>
/// <param name="alwaysUseManagedServerVariables">Determines whether server variables are sourced from the managed server</param>
/// <exception cref="FormatException">Thrown when the server variable is unknown</exception>
/// <returns>The matching <see cref="PatternSegment"/></returns>
public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart)
public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart, bool alwaysUseManagedServerVariables)
{
Func<PatternSegment> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,7 +20,8 @@ public class UrlRewriteFileParser
/// Parse an IIS rewrite section into a list of <see cref="IISUrlRewriteRule"/>s.
/// </summary>
/// <param name="reader">The reader containing the rewrite XML</param>
public IList<IISUrlRewriteRule> Parse(TextReader reader)
/// <param name="alwaysUseManagedServerVariables">Determines whether server variables will be sourced from the managed server</param>
public IList<IISUrlRewriteRule> Parse(TextReader reader, bool alwaysUseManagedServerVariables)
{
var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo);
var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault();
Expand All @@ -30,7 +31,7 @@ public IList<IISUrlRewriteRule> Parse(TextReader reader)
return null;
}

_inputParser = new InputParser(RewriteMapParser.Parse(xmlRoot));
_inputParser = new InputParser(RewriteMapParser.Parse(xmlRoot), alwaysUseManagedServerVariables);

var result = new List<IISUrlRewriteRule>();
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<PatternSegment> _fallbackThunk;

public IISServerVariableSegment(string variableName, Func<PatternSegment> 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);
}
}
}
Loading