Skip to content
This repository was archived by the owner on Dec 20, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
30b4b75
#55 add rewrite map support to IIS
david-peden-q2 Nov 4, 2016
2adedfc
add tests for rewrite maps
david-peden-q2 Nov 4, 2016
7d0ad0f
code review feedback
david-peden-q2 Nov 8, 2016
d428eb9
ensure lookups in rewrite maps are case-insensitive
david-peden-q2 Dec 5, 2016
5faa822
code review feedback
david-peden-q2 Dec 5, 2016
224665a
code review feedback
david-peden-q2 Dec 12, 2016
24f2ad7
code review feedback
david-peden-q2 Dec 12, 2016
87c900a
code review feedback
david-peden-q2 Dec 12, 2016
c2b536d
code review feedback
david-peden-q2 Dec 12, 2016
162114e
Merge branch 'dev' into rewritemaps
david-peden-q2 Jan 9, 2017
bfae468
fix breakages after merging aspnet/basicmiddleware
david-peden-q2 Jan 9, 2017
1ed4bc1
Merge branch 'dev' into rewritemaps
david-peden-q2 Jan 10, 2017
883a1b2
revert version number change per code review feedback
david-peden-q2 Jan 10, 2017
a88996d
add extension method and initial failing tests
david-peden-q2 Jan 11, 2017
2ccb462
add support for adding externally generated rewrite maps
david-peden-q2 Jan 13, 2017
7a8ac6b
add IISRewriteMapCollection
david-peden-q2 Jan 19, 2017
9eb1b74
code review feedback
david-peden-q2 Jan 30, 2017
43fbf3b
add overloads to eliminate optional parameters per ms code standards
david-peden-q2 Jan 30, 2017
7d1cc11
remove extra ability to layer in external rewrite maps per code revie…
david-peden-q2 Feb 1, 2017
461d587
fix whitespace
david-peden-q2 Feb 2, 2017
ad5b72c
merge dev in
david-peden-q2 Feb 7, 2017
435fed7
fix breakages after merge
david-peden-q2 Feb 7, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextR
return options;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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;

namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{
public class IISRewriteMap
{
private readonly Dictionary<string, string> _map = new Dictionary<string, string>();

public IISRewriteMap(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(nameof(name));
}
Name = name;
}

public string Name { get; }

public string this[string key]
{
get
{
string value;
return _map.TryGetValue(key, out value) ? value : null;
}
set
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException(nameof(key));
}
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(nameof(value));
}
_map[key] = value;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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 System.Collections;
using System.Collections.Generic;

namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{
public class IISRewriteMapCollection : IEnumerable<IISRewriteMap>
{
private readonly Dictionary<string, IISRewriteMap> _rewriteMaps = new Dictionary<string, IISRewriteMap>();

public void Add(IISRewriteMap rewriteMap)
{
if (rewriteMap != null)
{
_rewriteMaps[rewriteMap.Name] = rewriteMap;
}
}

public int Count => _rewriteMaps.Count;

public IISRewriteMap this[string key]
{
get
{
IISRewriteMap value;
return _rewriteMaps.TryGetValue(key, out value) ? value : null;
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return _rewriteMaps.Values.GetEnumerator();
}

public IEnumerator<IISRewriteMap> GetEnumerator()
{
return _rewriteMaps.Values.GetEnumerator();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ public class InputParser
private const char Colon = ':';
private const char OpenBrace = '{';
private const char CloseBrace = '}';
private readonly IISRewriteMapCollection _rewriteMaps;

public InputParser()
{
}

public InputParser(IISRewriteMapCollection rewriteMaps)
{
_rewriteMaps = rewriteMaps;
}

/// <summary>
/// Creates a pattern, which is a template to create a new test string to
Expand All @@ -31,7 +41,7 @@ public Pattern ParseInputString(string testString, bool global)
return ParseString(context, global);
}

private static Pattern ParseString(ParserContext context, bool global)
private Pattern ParseString(ParserContext context, bool global)
{
var results = new List<PatternSegment>();
while (context.Next())
Expand Down Expand Up @@ -60,7 +70,7 @@ private static Pattern ParseString(ParserContext context, bool global)
return new Pattern(results);
}

private static void ParseParameter(ParserContext context, IList<PatternSegment> results, bool global)
private void ParseParameter(ParserContext context, IList<PatternSegment> results, bool global)
{
context.Mark();
// Four main cases:
Expand Down Expand Up @@ -128,6 +138,13 @@ private static void ParseParameter(ParserContext context, IList<PatternSegment>
return;
}
default:
var rewriteMap = _rewriteMaps?[parameter];
if (rewriteMap != null)
{
var pattern = ParseString(context, global);
results.Add(new RewriteMapSegment(rewriteMap, pattern));
return;
}
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(parameter, context.Index));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright and license header needed here

using System.Linq;
using System.Xml.Linq;

namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{
public static class RewriteMapParser
{
public static IISRewriteMapCollection Parse(XElement xmlRoot)
{
if (xmlRoot == null)
{
throw new ArgumentNullException(nameof(xmlRoot));
}

var mapsElement = xmlRoot.Descendants(RewriteTags.RewriteMaps).SingleOrDefault();
if (mapsElement == null)
{
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Should we throw here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. It's valid not to have a rewrite map. Am I missing your point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was talking about. It just seems like and odd scenario

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i could move the test for the element outside of the parser and never end up in that scenario but this seems to be the preferred style throughout the project. i definitely don't think it should throw, though. is the structure what's objectionable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it should stay here. If its a valid scenario it can stay

}

var rewriteMaps = new IISRewriteMapCollection();
foreach (var mapElement in mapsElement.Elements(RewriteTags.RewriteMap))
{
var map = new IISRewriteMap(mapElement.Attribute(RewriteTags.Name)?.Value);
foreach (var addElement in mapElement.Elements(RewriteTags.Add))
{
map[addElement.Attribute(RewriteTags.Key).Value.ToLowerInvariant()] = addElement.Attribute(RewriteTags.Value).Value;
}
rewriteMaps.Add(map);
}

return rewriteMaps;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class RewriteTags
public const string GlobalRules = "globalRules";
public const string IgnoreCase = "ignoreCase";
public const string Input = "input";
public const string Key = "key";
public const string LogicalGrouping = "logicalGrouping";
public const string LogRewrittenUrl = "logRewrittenUrl";
public const string Match = "match";
Expand All @@ -22,13 +23,16 @@ public static class RewriteTags
public const string Negate = "negate";
public const string Pattern = "pattern";
public const string PatternSyntax = "patternSyntax";
public const string Rewrite = "rewrite";
public const string RedirectType = "redirectType";
public const string Rewrite = "rewrite";
public const string RewriteMap = "rewriteMap";
public const string RewriteMaps = "rewriteMaps";
public const string Rule = "rule";
public const string Rules = "rules";
public const string StopProcessing = "stopProcessing";
public const string TrackAllCaptures = "trackAllCaptures";
public const string Type = "type";
public const string Url = "url";
public const string Value = "value";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{
public class UrlRewriteFileParser
{
private readonly InputParser _inputParser = new InputParser();
private InputParser _inputParser;

/// <summary>
/// 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)
{
var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo);
var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault();

if (xmlRoot != null)
if (xmlRoot == null)
{
var result = new List<IISUrlRewriteRule>();
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true);
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, global: false);
return result;
return null;
}
return null;

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

var result = new List<IISUrlRewriteRule>();
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true);
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, global: false);
return result;
}

private void ParseRules(XElement rules, IList<IISUrlRewriteRule> result, bool global)
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 Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;

namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class RewriteMapSegment : PatternSegment
{
private readonly IISRewriteMap _rewriteMap;
private readonly Pattern _pattern;

public RewriteMapSegment(IISRewriteMap rewriteMap, Pattern pattern)
{
_rewriteMap = rewriteMap;
_pattern = pattern;
}

public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences)
{
var key = _pattern.Evaluate(context, ruleBackReferences, conditionBackReferences).ToLowerInvariant();
return _rewriteMap[key];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
using Microsoft.Extensions.Logging.Testing;
using Xunit;

namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
Expand Down Expand Up @@ -88,11 +91,48 @@ public void FormatExceptionsOnBadSyntax(string testString)
Assert.Throws<FormatException>(() => new InputParser().ParseInputString(testString, global: false));
}

private RewriteContext CreateTestRewriteContext()
[Fact]
public void Should_throw_FormatException_if_no_rewrite_maps_are_defined()
{
Assert.Throws<FormatException>(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", global: false));
}

[Fact]
public void Should_throw_FormatException_if_rewrite_map_not_found()
{
const string definedMapName = "testMap";
const string undefinedMapName = "apiMap";
var map = new IISRewriteMap(definedMapName);
var maps = new IISRewriteMapCollection { map };
Assert.Throws<FormatException>(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", global: false));
}

[Fact]
public void Should_parse_RewriteMapSegment_and_successfully_evaluate_result()
{
const string expectedMapName = "apiMap";
const string expectedKey = "api.test.com";
const string expectedValue = "test.com/api";
var map = new IISRewriteMap(expectedMapName);
map[expectedKey] = expectedValue;
var maps = new IISRewriteMapCollection { map };

var inputString = $"{{{expectedMapName}:{{R:1}}}}";
var pattern = new InputParser(maps).ParseInputString(inputString, global: false);
Assert.Equal(1, pattern.PatternSegments.Count);

var segment = pattern.PatternSegments.Single();
var rewriteMapSegment = segment as RewriteMapSegment;
Assert.NotNull(rewriteMapSegment);

var result = rewriteMapSegment.Evaluate(CreateTestRewriteContext(), CreateRewriteMapRuleMatch(expectedKey).BackReferences, CreateRewriteMapConditionMatch(inputString).BackReferences);
Assert.Equal(expectedValue, result);
}

private RewriteContext CreateTestRewriteContext()
{
var context = new DefaultHttpContext();
return new RewriteContext { HttpContext = context, StaticFileProvider = null };
return new RewriteContext { HttpContext = context, StaticFileProvider = null, Logger = new NullLogger() };
}

private BackReferenceCollection CreateTestRuleBackReferences()
Expand All @@ -106,5 +146,17 @@ private BackReferenceCollection CreateTestCondBackReferences()
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
return new BackReferenceCollection(match.Groups);
}

private MatchResults CreateRewriteMapRuleMatch(string input)
{
var match = Regex.Match(input, "([^/]*)/?(.*)");
return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success };
}

private MatchResults CreateRewriteMapConditionMatch(string input)
{
var match = Regex.Match(input, "(.+)");
return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success };
}
}
}
Loading