From bed294c554f0958c774613bc3ba31aff8f810917 Mon Sep 17 00:00:00 2001 From: David Peden Date: Wed, 8 Feb 2017 12:22:55 -0600 Subject: [PATCH] Added support for custom responses (#200) --- BasicMiddleware.sln | 2 +- .../RewriteMiddlewareLoggingExtensions.cs | 11 + .../InvalidUrlRewriteFormatException.cs | 34 +++ .../Internal/IISUrlRewrite/RewriteTags.cs | 4 + .../IISUrlRewrite/UrlRewriteFileParser.cs | 127 +++++----- .../IISUrlRewrite/UrlRewriteRuleBuilder.cs | 28 +-- .../UrlActions/CustomResponseAction.cs | 45 ++++ .../{VoidAction.cs => NoneAction.cs} | 4 +- .../FormatExceptionHandlingTests.cs | 218 +----------------- ...dUrlRewriteFormatExceptionHandlingTests.cs | 218 ++++++++++++++++++ .../IISUrlRewrite/MiddleWareTests.cs | 27 +++ 11 files changed, 423 insertions(+), 295 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InvalidUrlRewriteFormatException.cs create mode 100644 src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/CustomResponseAction.cs rename src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/{VoidAction.cs => NoneAction.cs} (87%) create mode 100644 test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs diff --git a/BasicMiddleware.sln b/BasicMiddleware.sln index 91ffb2b952..2fbc9f4838 100644 --- a/BasicMiddleware.sln +++ b/BasicMiddleware.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26127.0 +VisualStudioVersion = 15.0.26127.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpOverrides", "src\Microsoft.AspNetCore.HttpOverrides\Microsoft.AspNetCore.HttpOverrides.csproj", "{517308C3-B477-4B01-B461-CAB9C10B6928}" EndProject diff --git a/src/Microsoft.AspNetCore.Rewrite/Extensions/RewriteMiddlewareLoggingExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/Extensions/RewriteMiddlewareLoggingExtensions.cs index eaa1b0e345..3ad1e699dd 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Extensions/RewriteMiddlewareLoggingExtensions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Extensions/RewriteMiddlewareLoggingExtensions.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite.Logging private static readonly Action _redirectSummary; private static readonly Action _rewriteSummary; private static readonly Action _abortedRequest; + private static readonly Action _customResponse; static RewriteMiddlewareLoggingExtensions() { @@ -76,6 +77,11 @@ namespace Microsoft.AspNetCore.Rewrite.Logging LogLevel.Debug, 11, "Request to {requestedUrl} was aborted"); + + _customResponse = LoggerMessage.Define( + LogLevel.Debug, + 12, + "Request to {requestedUrl} was ended"); } public static void RewriteMiddlewareRequestContinueResults(this ILogger logger, string currentUrl) @@ -132,5 +138,10 @@ namespace Microsoft.AspNetCore.Rewrite.Logging { _abortedRequest(logger, requestedUrl, null); } + + public static void CustomResponse(this ILogger logger, string requestedUrl) + { + _customResponse(logger, requestedUrl, null); + } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InvalidUrlRewriteFormatException.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InvalidUrlRewriteFormatException.cs new file mode 100644 index 0000000000..8e2bac3937 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InvalidUrlRewriteFormatException.cs @@ -0,0 +1,34 @@ +// 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.Xml; +using System.Xml.Linq; + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public class InvalidUrlRewriteFormatException : FormatException + { + public int LineNumber { get; } + public int LinePosition { get; } + + public InvalidUrlRewriteFormatException(XElement element, string message) + : base(FormatMessage(element, message)) + { + } + + public InvalidUrlRewriteFormatException(XElement element, string message, Exception innerException) + : base(FormatMessage(element, message), innerException) + { + var xmlLineInfo = (IXmlLineInfo)element; + LineNumber = xmlLineInfo.LineNumber; + LinePosition = xmlLineInfo.LinePosition; + } + + private static string FormatMessage(XElement element, string message) + { + var xmlLineInfo = (IXmlLineInfo)element; + return Resources.FormatError_UrlRewriteParseError(message, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/RewriteTags.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/RewriteTags.cs index 42a8e5aa83..d6d482bdd4 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/RewriteTags.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/RewriteTags.cs @@ -29,6 +29,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite public const string RewriteMaps = "rewriteMaps"; public const string Rule = "rule"; public const string Rules = "rules"; + public const string StatusCode = "statusCode"; + public const string SubStatusCode = "subStatusCode"; + public const string StatusDescription = "statusDescription"; + public const string StatusReason = "statusReason"; public const string StopProcessing = "stopProcessing"; public const string TrackAllCaptures = "trackAllCaptures"; public const string Type = "type"; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs index cb96c8b03a..0cf093b091 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using System.Xml.Linq; +using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { @@ -74,13 +74,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var match = rule.Element(RewriteTags.Match); if (match == null) { - ThrowUrlFormatException(rule, "Cannot have rule without match"); + throw new InvalidUrlRewriteFormatException(rule, "Condition must have an associated match"); } var action = rule.Element(RewriteTags.Action); if (action == null) { - ThrowUrlFormatException(rule, "Rule does not have an associated action attribute"); + throw new InvalidUrlRewriteFormatException(rule, "Rule does not have an associated action attribute"); } ParseMatch(match, builder, patternSyntax); @@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var parsedInputString = match.Attribute(RewriteTags.Url)?.Value; if (parsedInputString == null) { - ThrowUrlFormatException(match, "Match must have Url Attribute"); + throw new InvalidUrlRewriteFormatException(match, "Match must have Url Attribute"); } var ignoreCase = ParseBool(match, RewriteTags.IgnoreCase, defaultValue: true); @@ -127,70 +127,77 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite if (parsedInputString == null) { - ThrowUrlFormatException(condition, "Conditions must have an input attribute"); + throw new InvalidUrlRewriteFormatException(condition, "Conditions must have an input attribute"); } var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value; - try - { - var input = _inputParser.ParseInputString(parsedInputString, global); - builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); - } - catch (FormatException formatException) - { - ThrowUrlFormatException(condition, formatException.Message, formatException); - } + var input = _inputParser.ParseInputString(parsedInputString, global); + builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); } private void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing, bool global) { var actionType = ParseEnum(urlAction, RewriteTags.Type, ActionType.None); - var redirectType = ParseEnum(urlAction, RewriteTags.RedirectType, RedirectType.Permanent); - var appendQuery = ParseBool(urlAction, RewriteTags.AppendQueryString, defaultValue: true); - - string url = string.Empty; - if (urlAction.Attribute(RewriteTags.Url) != null) + UrlAction action; + switch (actionType) { - url = urlAction.Attribute(RewriteTags.Url).Value; - if (string.IsNullOrEmpty(url)) - { - ThrowUrlFormatException(urlAction, "Url attribute cannot contain an empty string"); - } + case ActionType.None: + action = new NoneAction(stopProcessing ? RuleResult.SkipRemainingRules : RuleResult.ContinueRules); + break; + case ActionType.Rewrite: + case ActionType.Redirect: + var url = string.Empty; + if (urlAction.Attribute(RewriteTags.Url) != null) + { + url = urlAction.Attribute(RewriteTags.Url).Value; + if (string.IsNullOrEmpty(url)) + { + throw new InvalidUrlRewriteFormatException(urlAction, "Url attribute cannot contain an empty string"); + } + } + + var urlPattern = _inputParser.ParseInputString(url, global); + var appendQuery = ParseBool(urlAction, RewriteTags.AppendQueryString, defaultValue: true); + + if (actionType == ActionType.Rewrite) + { + action = new RewriteAction(stopProcessing ? RuleResult.SkipRemainingRules : RuleResult.ContinueRules, urlPattern, appendQuery); + } + else + { + var redirectType = ParseEnum(urlAction, RewriteTags.RedirectType, RedirectType.Permanent); + action = new RedirectAction((int)redirectType, urlPattern, appendQuery); + } + break; + case ActionType.AbortRequest: + action = new AbortAction(); + break; + case ActionType.CustomResponse: + int statusCode; + if (!int.TryParse(urlAction.Attribute(RewriteTags.StatusCode)?.Value, out statusCode)) + { + throw new InvalidUrlRewriteFormatException(urlAction, "A valid status code is required"); + } + + if (statusCode < 200 || statusCode > 999) + { + throw new NotSupportedException("Status codes must be between 200 and 999 (inclusive)"); + } + + if (!string.IsNullOrEmpty(urlAction.Attribute(RewriteTags.SubStatusCode)?.Value)) + { + throw new NotSupportedException("Substatus codes are not supported"); + } + + var statusReason = urlAction.Attribute(RewriteTags.StatusReason)?.Value; + var statusDescription = urlAction.Attribute(RewriteTags.StatusDescription)?.Value; + + action = new CustomResponseAction(statusCode) { StatusReason = statusReason, StatusDescription = statusDescription }; + break; + default: + throw new NotSupportedException($"The action type {actionType} wasn't recognized"); } - - try - { - var input = _inputParser.ParseInputString(url, global); - builder.AddUrlAction(input, actionType, appendQuery, stopProcessing, (int)redirectType); - } - catch (FormatException formatException) - { - ThrowUrlFormatException(urlAction, formatException.Message, formatException); - } - } - - private static void ThrowUrlFormatException(XElement element, string message) - { - var lineInfo = (IXmlLineInfo)element; - var line = lineInfo.LineNumber; - var col = lineInfo.LinePosition; - throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col)); - } - - private static void ThrowUrlFormatException(XElement element, string message, Exception ex) - { - var lineInfo = (IXmlLineInfo)element; - var line = lineInfo.LineNumber; - var col = lineInfo.LinePosition; - throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col), ex); - } - - private static void ThrowParameterFormatException(XElement element, string message) - { - var lineInfo = (IXmlLineInfo)element; - var line = lineInfo.LineNumber; - var col = lineInfo.LinePosition; - throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col)); + builder.AddUrlAction(action); } private bool ParseBool(XElement element, string rewriteTag, bool defaultValue) @@ -203,7 +210,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } else if (!bool.TryParse(attribute.Value, out result)) { - ThrowParameterFormatException(element, $"The {rewriteTag} parameter '{attribute.Value}' was not recognized"); + throw new InvalidUrlRewriteFormatException(element, $"The {rewriteTag} parameter '{attribute.Value}' was not recognized"); } return result; } @@ -219,7 +226,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } else if(!Enum.TryParse(attribute.Value, ignoreCase: true, result: out enumResult)) { - ThrowParameterFormatException(element, $"The {rewriteTag} parameter '{attribute.Value}' was not recognized"); + throw new InvalidUrlRewriteFormatException(element, $"The {rewriteTag} parameter '{attribute.Value}' was not recognized"); } return enumResult; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs index 26951d5f9c..8cf927e7b2 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite @@ -33,31 +31,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures, global); } - public void AddUrlAction( - Pattern url, - ActionType actionType = ActionType.None, - bool appendQueryString = true, - bool stopProcessing = false, - int statusCode = StatusCodes.Status301MovedPermanently) + public void AddUrlAction(UrlAction action) { - switch (actionType) + if (action == null) { - case ActionType.None: - _action = new VoidAction(stopProcessing ? RuleResult.SkipRemainingRules : RuleResult.ContinueRules); - break; - case ActionType.Rewrite: - _action = new RewriteAction(stopProcessing ? RuleResult.SkipRemainingRules : RuleResult.ContinueRules, - url, appendQueryString); - break; - case ActionType.Redirect: - _action = new RedirectAction(statusCode, url, appendQueryString); - break; - case ActionType.AbortRequest: - _action = new AbortAction(); - break; - case ActionType.CustomResponse: - throw new NotImplementedException("Custom Responses are not implemented"); + throw new ArgumentNullException(nameof(action), "Rules must contain an action"); } + _action = action; } public void AddUrlMatch(string input, bool ignoreCase = true, bool negate = false, PatternSyntax patternSyntax = PatternSyntax.ECMAScript) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/CustomResponseAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/CustomResponseAction.cs new file mode 100644 index 0000000000..b7cc36eeda --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/CustomResponseAction.cs @@ -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.Text; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Rewrite.Logging; + +namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions +{ + public class CustomResponseAction : UrlAction + { + public int StatusCode { get; } + public string StatusReason { get; set; } + public string StatusDescription { get; set; } + + public CustomResponseAction(int statusCode) + { + StatusCode = statusCode; + } + + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) + { + var response = context.HttpContext.Response; + response.StatusCode = StatusCode; + + if (!string.IsNullOrEmpty(StatusReason)) + { + context.HttpContext.Features.Get().ReasonPhrase = StatusReason; + } + + if (!string.IsNullOrEmpty(StatusDescription)) + { + var content = Encoding.UTF8.GetBytes(StatusDescription); + response.ContentLength = content.Length; + response.ContentType = "text/plain; charset=utf-8"; + response.Body.Write(content, 0, content.Length); + } + + context.Result = RuleResult.EndResponse; + + context.Logger?.CustomResponse(context.HttpContext.Request.GetEncodedUrl()); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/NoneAction.cs similarity index 87% rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/NoneAction.cs index 95a092a0f8..4ba171bb5e 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/NoneAction.cs @@ -3,11 +3,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { - public class VoidAction : UrlAction + public class NoneAction : UrlAction { public RuleResult Result { get; } - public VoidAction(RuleResult result) + public NoneAction(RuleResult result) { Result = result; } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs index c14f062e64..f6771dddfe 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs @@ -12,60 +12,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite { [Theory] [InlineData( -@" - - - - -", - "Could not parse the UrlRewrite file. Message: 'Cannot have rule without match'. Line number '3': '10'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'Missing close brace for parameter at string index: '1''. Line number '5': '14'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'Match must have Url Attribute'. Line number '4': '14'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'Missing close brace for parameter at string index: '6''. Line number '6': '18'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'Conditions must have an input attribute'. Line number '6': '18'.")] - [InlineData( @" @@ -77,7 +23,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite ", - "Could not parse the UrlRewrite file. Message: 'Match does not have an associated pattern attribute in condition'. Line number '6': '18'.")] + "Match does not have an associated pattern attribute in condition")] [InlineData( @" @@ -90,174 +36,30 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite ", - "Could not parse the UrlRewrite file. Message: 'Match does not have an associated pattern attribute in condition'. Line number '6': '18'.")] + "Match does not have an associated pattern attribute in condition")] [InlineData( @" - - - -", - "Could not parse the UrlRewrite file. Message: 'Url attribute cannot contain an empty string'. Line number '5': '14'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The redirectType parameter 'foo' was not recognized'. Line number '5': '14'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The type parameter 'foo' was not recognized'. Line number '5': '14'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The logicalGrouping parameter 'foo' was not recognized'. Line number '5': '14'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The patternSyntax parameter 'foo' was not recognized'. Line number '3': '10'.")] - [InlineData( -@" - - - - + - + ", - "Could not parse the UrlRewrite file. Message: 'The matchType parameter 'foo' was not recognized'. Line number '6': '18'.")] + "Missing close brace for parameter at string index: '6'")] [InlineData( @" - - - - - - + + + ", - "Could not parse the UrlRewrite file. Message: 'The enabled parameter 'foo' was not recognized'. Line number '3': '10'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The stopProcessing parameter 'foo' was not recognized'. Line number '3': '10'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The ignoreCase parameter 'foo' was not recognized'. Line number '4': '14'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The ignoreCase parameter 'foo' was not recognized'. Line number '6': '18'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The negate parameter 'foo' was not recognized'. Line number '4': '14'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The negate parameter 'foo' was not recognized'. Line number '6': '18'.")] - [InlineData( -@" - - - - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The trackAllCaptures parameter 'foo' was not recognized'. Line number '5': '14'.")] - [InlineData( -@" - - - - - - -", - "Could not parse the UrlRewrite file. Message: 'The appendQueryString parameter 'foo' was not recognized'. Line number '5': '14'.")] + "Missing close brace for parameter at string index: '1'")] public void ThrowFormatExceptionWithCorrectMessage(string input, string expected) { // Arrange, Act, Assert @@ -265,4 +67,4 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite Assert.Equal(expected, ex.Message); } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs new file mode 100644 index 0000000000..5c2e26fc5f --- /dev/null +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InvalidUrlRewriteFormatExceptionHandlingTests.cs @@ -0,0 +1,218 @@ +// 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.Rewrite.Internal.IISUrlRewrite; +using Xunit; + +namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite +{ + public class InvalidUrlRewriteFormatExceptionHandlingTests + { + [Theory] + [InlineData( + @" + + + + +", + "Could not parse the UrlRewrite file. Message: 'Condition must have an associated match'. Line number '3': '10'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'Match must have Url Attribute'. Line number '4': '14'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'Conditions must have an input attribute'. Line number '6': '18'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'Url attribute cannot contain an empty string'. Line number '5': '14'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The redirectType parameter 'foo' was not recognized'. Line number '5': '14'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The type parameter 'foo' was not recognized'. Line number '5': '14'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The logicalGrouping parameter 'foo' was not recognized'. Line number '5': '14'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The patternSyntax parameter 'foo' was not recognized'. Line number '3': '10'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The matchType parameter 'foo' was not recognized'. Line number '6': '18'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The enabled parameter 'foo' was not recognized'. Line number '3': '10'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The stopProcessing parameter 'foo' was not recognized'. Line number '3': '10'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The ignoreCase parameter 'foo' was not recognized'. Line number '4': '14'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The ignoreCase parameter 'foo' was not recognized'. Line number '6': '18'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The negate parameter 'foo' was not recognized'. Line number '4': '14'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The negate parameter 'foo' was not recognized'. Line number '6': '18'.")] + [InlineData( + @" + + + + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The trackAllCaptures parameter 'foo' was not recognized'. Line number '5': '14'.")] + [InlineData( + @" + + + + + + +", + "Could not parse the UrlRewrite file. Message: 'The appendQueryString parameter 'foo' was not recognized'. Line number '5': '14'.")] + public void ThrowInvalidUrlRewriteFormatExceptionWithCorrectMessage(string input, string expected) + { + // Arrange, Act, Assert + var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input))); + Assert.Equal(expected, ex.Message); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs index ed5d95ffe8..006c88f4e7 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -516,5 +517,31 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite Assert.Equal(expectedRewrittenUri, response); } + + [Fact] + public async Task Invoke_CustomResponse() + { + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" + + + + + + + ")); + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRewriter(options); + }); + var server = new TestServer(builder); + + var response = await server.CreateClient().GetAsync("article/10/hey"); + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + Assert.Equal("reason", response.ReasonPhrase); + Assert.Equal("description", content); + } } }