├── License.txt ├── src ├── UrlRewriter.snk ├── Errors │ ├── IRewriteErrorHandler.cs │ └── DefaultErrorHandler.cs ├── Actions │ ├── GoneAction.cs │ ├── NotFoundAction.cs │ ├── ForbiddenAction.cs │ ├── NotImplementedAction.cs │ ├── MethodNotAllowedAction.cs │ ├── IRewriteAction.cs │ ├── SetStatusAction.cs │ ├── SetLocationAction.cs │ ├── RewriteAction.cs │ ├── AddHeaderAction.cs │ ├── SetPropertyAction.cs │ ├── SetCookieAction.cs │ ├── RedirectAction.cs │ ├── SetAppSettingPropertyAction.cs │ └── ConditionalAction.cs ├── urlrewriter.net.nuspec ├── Parsers │ ├── UnlessConditionActionParser.cs │ ├── IRewriteConditionParser.cs │ ├── ExistsConditionParser.cs │ ├── MethodConditionParser.cs │ ├── AddressConditionParser.cs │ ├── UrlMatchConditionParser.cs │ ├── HeaderMatchConditionParser.cs │ ├── PropertyMatchConditionParser.cs │ ├── IRewriteActionParser.cs │ ├── GoneActionParser.cs │ ├── NotFoundActionParser.cs │ ├── ForbiddenActionParser.cs │ ├── NotAllowedActionParser.cs │ ├── NotImplementedActionParser.cs │ ├── RedirectActionParser.cs │ ├── SetStatusActionParser.cs │ ├── SetAppSettingPropertyActionParser.cs │ ├── AddHeaderActionParser.cs │ ├── SetCookieActionParser.cs │ ├── SetPropertyActionParser.cs │ ├── RewriteActionParser.cs │ ├── RewriteActionParserBase.cs │ └── IfConditionActionParser.cs ├── FormRewriterControlAdapter.cs ├── RewriteProcessing.cs ├── Transforms │ ├── IRewriteTransform.cs │ ├── DecodeTransform.cs │ ├── EncodeTransform.cs │ ├── Base64DecodeTransform.cs │ ├── Base64Transform.cs │ ├── LowerTransform.cs │ ├── UpperTransform.cs │ └── StaticMappingTransform.cs ├── Conditions │ ├── IRewriteCondition.cs │ ├── MatchCondition.cs │ ├── NegativeCondition.cs │ ├── MethodCondition.cs │ ├── AddressCondition.cs │ ├── ExistsCondition.cs │ ├── PropertyMatchCondition.cs │ └── UrlMatchCondition.cs ├── Properties │ └── AssemblyInfo.cs ├── Configuration │ ├── RewriterConfigurationSectionHandler.cs │ ├── IConfigurationManager.cs │ ├── ConditionParserPipeline.cs │ ├── ConfigurationManagerFacade.cs │ ├── TransformFactory.cs │ ├── IRewriterConfiguration.cs │ └── ActionParserFactory.cs ├── Extensions │ └── ListExtensions.cs ├── Utilities │ ├── Message.cs │ ├── MessageProvider.cs │ ├── TypeHelper.cs │ ├── IHttpContext.cs │ ├── Constants.cs │ └── IPRange.cs ├── Logging │ ├── IRewriteLogger.cs │ ├── NullLogger.cs │ ├── TraceLogger.cs │ └── DebugLogger.cs ├── RewriteFormHtmlTextWriter.cs ├── RewriterHttpModule.cs ├── IRewriteContext.cs ├── Form.cs └── UrlRewriter.ndoc ├── Local.vs2008.testrunconfig ├── Local.vs2010.testrunconfig ├── test ├── README.txt ├── Mocks │ ├── MockRewriteCondition.cs │ ├── MockRewriteAction.cs │ ├── MockConfigurationManager.cs │ └── MockRewriteContext.cs ├── Actions │ ├── GoneActionTest.cs │ ├── NotFoundActionTest.cs │ ├── ForbiddenActionTest.cs │ ├── NotImplementedActionTest.cs │ ├── MethodNotAllowedActionTest.cs │ ├── SetStatusActionTest.cs │ ├── AddHeaderActionTest.cs │ ├── SetCookieActionTest.cs │ ├── SetPropertyActionTest.cs │ ├── SetAppSettingPropertyActionTest.cs │ ├── ConditionalActionTest.cs │ ├── RewriteActionTest.cs │ └── RedirectActionTest.cs ├── Properties │ └── AssemblyInfo.cs ├── Assert │ └── ExceptionAssert.cs └── App.config ├── README.md ├── Intelligencia.UrlRewriter.vs2008.sln └── Intelligencia.UrlRewriter.vs2010.sln /License.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethyates/urlrewriter/HEAD/License.txt -------------------------------------------------------------------------------- /src/UrlRewriter.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethyates/urlrewriter/HEAD/src/UrlRewriter.snk -------------------------------------------------------------------------------- /Local.vs2008.testrunconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is a default test run configuration for a local test run. 4 | 5 | 6 | -------------------------------------------------------------------------------- /Local.vs2010.testrunconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is a default test run configuration for a local test run. 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Errors/IRewriteErrorHandler.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | 11 | namespace Intelligencia.UrlRewriter 12 | { 13 | /// 14 | /// Interface for rewriter error handlers. 15 | /// 16 | public interface IRewriteErrorHandler 17 | { 18 | /// 19 | /// Handles the error. 20 | /// 21 | /// The HTTP context. 22 | void HandleError(HttpContext context); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Actions/GoneAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Returns a 410 Gone HTTP status code. 15 | /// 16 | public sealed class GoneAction : SetStatusAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | public GoneAction() 22 | : base(HttpStatusCode.Gone) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Actions/NotFoundAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Returns a 404 Not Found HTTP status code. 15 | /// 16 | public sealed class NotFoundAction : SetStatusAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | public NotFoundAction() 22 | : base(HttpStatusCode.NotFound) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Actions/ForbiddenAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Returns a 403 Forbidden HTTP status code. 15 | /// 16 | public sealed class ForbiddenAction : SetStatusAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | public ForbiddenAction() 22 | : base(HttpStatusCode.Forbidden) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Actions/NotImplementedAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Returns a 501 Not Implemented HTTP status code. 15 | /// 16 | public sealed class NotImplementedAction : SetStatusAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | public NotImplementedAction() 22 | : base(HttpStatusCode.NotImplemented) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Actions/MethodNotAllowedAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Returns a 405 Method Not Allowed HTTP status code. 15 | /// 16 | public sealed class MethodNotAllowedAction : SetStatusAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | public MethodNotAllowedAction() 22 | : base(HttpStatusCode.MethodNotAllowed) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/urlrewriter.net.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UrlRewriter.Net 5 | 2.1.0 6 | Seth Yates, Stewart Rae 7 | Seth Yates 8 | false 9 | http://urlrewriter.net/ 10 | http://www.opensource.org/licenses/mit-license.php 11 | Easy URL Rewriting for .NET 12 | Easy URL Rewriting for .NET 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Parsers/UnlessConditionActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using Intelligencia.UrlRewriter.Utilities; 10 | 11 | namespace Intelligencia.UrlRewriter.Parsers 12 | { 13 | /// 14 | /// Parses the IFNOT node. 15 | /// 16 | public class UnlessConditionActionParser : IfConditionActionParser 17 | { 18 | /// 19 | /// The name of the action. 20 | /// 21 | public override string Name 22 | { 23 | get { return Constants.ElementUnless; } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/README.txt: -------------------------------------------------------------------------------- 1 | README.TXT for Unit Test Assembly 2 | ================================= 3 | 4 | !IMPORTANT NOTE! 5 | 6 | If you are using Visual Studio 2010 and the solution file Intelligencia.UrlRewriter.vs2010.sln, 7 | to get the unit test running you need to add the following app setting to your local 8 | devenv.exe.config file: 9 | 10 | 11 | 12 | 13 | 14 | The config file should be in the same directory as the Visual Studio executable file (devenv.exe), 15 | usually at the following location: 16 | 17 | C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE 18 | 19 | More info: http://go.microsoft.com/fwlink/?LinkId=201405 20 | 21 | 22 | Please note that this file is ALSO used to test the "exists" conditition. -------------------------------------------------------------------------------- /src/FormRewriterControlAdapter.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web.UI; 10 | using System.Web.UI.Adapters; 11 | 12 | namespace Intelligencia.UrlRewriter 13 | { 14 | /// 15 | /// ControlAdapter for rewriting form actions 16 | /// 17 | public class FormRewriterControlAdapter : ControlAdapter 18 | { 19 | /// 20 | /// Renders the control. 21 | /// 22 | /// The writer to write to 23 | protected override void Render(HtmlTextWriter writer) 24 | { 25 | base.Render(new RewriteFormHtmlTextWriter(writer)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/RewriteProcessing.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter 11 | { 12 | /// 13 | /// Processing flag. Tells the rewriter how to continue processing (or not). 14 | /// 15 | public enum RewriteProcessing 16 | { 17 | /// 18 | /// Continue processing at the next rule. 19 | /// 20 | ContinueProcessing, 21 | 22 | /// 23 | /// Halt processing. 24 | /// 25 | StopProcessing, 26 | 27 | /// 28 | /// Restart processing at the first rule. 29 | /// 30 | RestartProcessing 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Transforms/IRewriteTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter 11 | { 12 | /// 13 | /// Interface for transforming replacements. 14 | /// 15 | public interface IRewriteTransform 16 | { 17 | /// 18 | /// Applies a transformation to the input string. 19 | /// 20 | /// The input string. 21 | /// The transformed string. 22 | string ApplyTransform(string input); 23 | 24 | /// 25 | /// The name of the transform. 26 | /// 27 | string Name { get; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Mocks/MockRewriteCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Intelligencia.UrlRewriter.Mocks 4 | { 5 | /// 6 | /// A mock IRewriteCondition 7 | /// 8 | public class MockRewriteCondition : IRewriteCondition 9 | { 10 | /// 11 | /// Constructor. 12 | /// 13 | /// The IRewriteCondition.IsMatch result 14 | public MockRewriteCondition(bool result) 15 | { 16 | _result = result; 17 | } 18 | 19 | /// 20 | /// Mock implementation of IRewriteCondition.IsMatch 21 | /// 22 | /// 23 | /// 24 | public bool IsMatch(IRewriteContext context) 25 | { 26 | return _result; 27 | } 28 | 29 | private bool _result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Conditions/IRewriteCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter 11 | { 12 | /// 13 | /// Interface for conditions. Conditions must be thread-safe as there is a single 14 | /// instance of each condition. This means that you must not make any changes to fields/properties 15 | /// on the condition once its created. 16 | /// 17 | public interface IRewriteCondition 18 | { 19 | /// 20 | /// Determines if the condition matches. 21 | /// 22 | /// The rewrite context. 23 | /// True if the condition is met. 24 | bool IsMatch(IRewriteContext context); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/Mocks/MockRewriteAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Intelligencia.UrlRewriter.Mocks 4 | { 5 | /// 6 | /// A mock IRewriteAction. 7 | /// 8 | public class MockRewriteAction : IRewriteAction 9 | { 10 | /// 11 | /// Constructor. 12 | /// 13 | /// The IRewriteAction.Execute result 14 | public MockRewriteAction(RewriteProcessing result) 15 | { 16 | _result = result; 17 | } 18 | 19 | /// 20 | /// Mock implementation of IRewriteAction.Execute 21 | /// 22 | /// 23 | /// 24 | public RewriteProcessing Execute(IRewriteContext context) 25 | { 26 | return _result; 27 | } 28 | 29 | private RewriteProcessing _result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System.Reflection; 9 | using System; 10 | using System.Runtime.InteropServices; 11 | using System.Security; 12 | 13 | [assembly: AssemblyTitle("UrlRewriter 2.1")] 14 | [assembly: AssemblyDescription("An extendible, rule-based URL Rewriter for .NET")] 15 | [assembly: AssemblyConfiguration("")] 16 | [assembly: AssemblyCompany("Intelligencia")] 17 | [assembly: AssemblyProduct("UrlRewriter")] 18 | [assembly: AssemblyCopyright("(C) 2011 Intelligencia, (C) 2011 Seth Yates")] 19 | [assembly: AssemblyTrademark("")] 20 | [assembly: AssemblyCulture("")] 21 | [assembly: AssemblyVersion("2.1.0.2")] 22 | [assembly: AssemblyDelaySign(false)] 23 | [assembly: AssemblyKeyName("")] 24 | [assembly: AllowPartiallyTrustedCallers] 25 | [assembly: ComVisible(false)] 26 | [assembly: CLSCompliant(true)] 27 | -------------------------------------------------------------------------------- /src/Parsers/IRewriteConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | 11 | namespace Intelligencia.UrlRewriter 12 | { 13 | /// 14 | /// Interface defining a parser which parses an XML node and returns the correct 15 | /// IRewriteCondition instance based on the node. 16 | /// 17 | public interface IRewriteConditionParser 18 | { 19 | /// 20 | /// Parses the node if possible. The parser may be called on a condition 21 | /// that it cannot parse, if it is registered on a common verb 22 | /// which is shared by several condition parsers (e.g., and). 23 | /// 24 | /// The node to parse. 25 | /// The condition parsed. If the parser could not parse the node, 26 | /// it must return null. 27 | IRewriteCondition Parse(XmlNode node); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Configuration/RewriterConfigurationSectionHandler.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | 12 | namespace Intelligencia.UrlRewriter.Configuration 13 | { 14 | /// 15 | /// Configuration section handler for the rewriter section. 16 | /// 17 | public sealed class RewriterConfigurationSectionHandler : IConfigurationSectionHandler 18 | { 19 | /// 20 | /// Creates the settings object. 21 | /// 22 | /// The parent node. 23 | /// The configuration context. 24 | /// The section. 25 | /// The settings object. 26 | object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section) 27 | { 28 | return section; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Transforms/DecodeTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Url decodes the input. 16 | /// 17 | public sealed class DecodeTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | return HttpUtility.UrlDecode(input); 27 | } 28 | 29 | /// 30 | /// The name of the action. 31 | /// 32 | public string Name 33 | { 34 | get { return Constants.TransformDecode; } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Transforms/EncodeTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Url encodes the input. 16 | /// 17 | public sealed class EncodeTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | return HttpUtility.UrlEncode(input); 27 | } 28 | 29 | /// 30 | /// The name of the action. 31 | /// 32 | public string Name 33 | { 34 | get { return Constants.TransformEncode; } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Configuration/IConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Specialized; 10 | 11 | namespace Intelligencia.UrlRewriter.Configuration 12 | { 13 | /// 14 | /// Interface for a facade to the ASP.NET ConfigurationManager. 15 | /// Useful for plugging out the ConfigurationManager in unit tests. 16 | /// 17 | public interface IConfigurationManager 18 | { 19 | /// 20 | /// Retrieves a configuration section from the web application's config file. 21 | /// 22 | /// The configuration section name 23 | /// The configuration section class instance 24 | object GetSection(string sectionName); 25 | 26 | /// 27 | /// Gets the AppSettings from the web application's config file. 28 | /// 29 | NameValueCollection AppSettings { get; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Transforms/Base64DecodeTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Base 64 encodes the input. 16 | /// 17 | public sealed class Base64Transform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | return Convert.ToBase64String(Encoding.UTF8.GetBytes(input)); 27 | } 28 | 29 | /// 30 | /// The name of the action. 31 | /// 32 | public string Name 33 | { 34 | get { return Constants.TransformBase64Decode; } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Transforms/Base64Transform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Base 64 encodes the input. 16 | /// 17 | public sealed class Base64DecodeTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | return Encoding.UTF8.GetString(Convert.FromBase64String(input)); 27 | } 28 | 29 | /// 30 | /// The name of the action. 31 | /// 32 | public string Name 33 | { 34 | get { return Constants.TransformBase64; } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Configuration/ConditionParserPipeline.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter.Configuration 14 | { 15 | /// 16 | /// Pipeline for creating the Condition parsers. 17 | /// 18 | public class ConditionParserPipeline : List 19 | { 20 | /* 21 | /// 22 | /// Adds a parser. 23 | /// 24 | /// The parser type. 25 | public void AddParser(string parserType) 26 | { 27 | AddParser((IRewriteConditionParser)TypeHelper.Activate(parserType, null)); 28 | } 29 | 30 | /// 31 | /// Adds a parser. 32 | /// 33 | /// The parser. 34 | public void AddParser(IRewriteConditionParser parser) 35 | { 36 | Add(parser); 37 | } 38 | */ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Intelligencia.UrlRewriter 5 | { 6 | /// 7 | /// Extension methods for Lists. 8 | /// 9 | public static class ListExtensions 10 | { 11 | /// 12 | /// Returns true provided all the conditions in the list are met for the given context. 13 | /// 14 | /// The list of conditions 15 | /// The rewrite context 16 | /// True if all the conditions are met 17 | public static bool IsMatch(this IList conditions, IRewriteContext context) 18 | { 19 | if (conditions == null) 20 | { 21 | throw new ArgumentNullException("conditions"); 22 | } 23 | 24 | // Ensure all the conditions are met, i.e. return false if any are not met. 25 | foreach (IRewriteCondition condition in conditions) 26 | { 27 | if (!condition.IsMatch(context)) 28 | { 29 | return false; 30 | } 31 | } 32 | 33 | return true; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Transforms/LowerTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Threading; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Transforms the input to lower case. 16 | /// 17 | public sealed class LowerTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | if (input == null) 27 | { 28 | throw new ArgumentNullException("input"); 29 | } 30 | 31 | return input.ToLower(Thread.CurrentThread.CurrentCulture); 32 | } 33 | 34 | /// 35 | /// The name of the action. 36 | /// 37 | public string Name 38 | { 39 | get { return Constants.TransformLower; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Transforms/UpperTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Threading; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Transforms 13 | { 14 | /// 15 | /// Transforms the input to upper case. 16 | /// 17 | public sealed class UpperTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Applies a transformation to the input string. 21 | /// 22 | /// The input string. 23 | /// The transformed string. 24 | public string ApplyTransform(string input) 25 | { 26 | if (input == null) 27 | { 28 | throw new ArgumentNullException("input"); 29 | } 30 | 31 | return input.ToUpper(Thread.CurrentThread.CurrentCulture); 32 | } 33 | 34 | /// 35 | /// The name of the action. 36 | /// 37 | public string Name 38 | { 39 | get { return Constants.TransformUpper; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Parsers/ExistsConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Conditions; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter.Parsers 14 | { 15 | /// 16 | /// Parser for exists conditions. 17 | /// 18 | public sealed class ExistsConditionParser : IRewriteConditionParser 19 | { 20 | /// 21 | /// Parses the condition. 22 | /// 23 | /// The node to parse. 24 | /// The condition parsed, or null if nothing parsed. 25 | public IRewriteCondition Parse(XmlNode node) 26 | { 27 | if (node == null) 28 | { 29 | throw new ArgumentNullException("node"); 30 | } 31 | 32 | string exists = node.GetOptionalAttribute(Constants.AttrExists); 33 | if (exists == null) 34 | { 35 | return null; 36 | } 37 | 38 | return new ExistsCondition(exists); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Parsers/MethodConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Conditions; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter.Parsers 14 | { 15 | /// 16 | /// Parser for method conditions. 17 | /// 18 | public sealed class MethodConditionParser : IRewriteConditionParser 19 | { 20 | /// 21 | /// Parses the condition. 22 | /// 23 | /// The node to parse. 24 | /// The condition parsed, or null if nothing parsed. 25 | public IRewriteCondition Parse(XmlNode node) 26 | { 27 | if (node == null) 28 | { 29 | throw new ArgumentNullException("node"); 30 | } 31 | 32 | string method = node.GetOptionalAttribute(Constants.AttrMethod); 33 | if (method == null) 34 | { 35 | return null; 36 | } 37 | 38 | return new MethodCondition(method); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Parsers/AddressConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Conditions; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter.Parsers 14 | { 15 | /// 16 | /// Parser for address conditions. 17 | /// 18 | public sealed class AddressConditionParser : IRewriteConditionParser 19 | { 20 | /// 21 | /// Parses the condition. 22 | /// 23 | /// The node to parse. 24 | /// The condition parsed, or null if nothing parsed. 25 | public IRewriteCondition Parse(XmlNode node) 26 | { 27 | if (node == null) 28 | { 29 | throw new ArgumentNullException("node"); 30 | } 31 | 32 | string address = node.GetOptionalAttribute(Constants.AttrAddress); 33 | if (address == null) 34 | { 35 | return null; 36 | } 37 | 38 | return new AddressCondition(address); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Parsers/UrlMatchConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Conditions; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter.Parsers 14 | { 15 | /// 16 | /// Parser for URL match conditions. 17 | /// 18 | public sealed class UrlMatchConditionParser : IRewriteConditionParser 19 | { 20 | /// 21 | /// Parses the condition. 22 | /// 23 | /// The node to parse. 24 | /// The condition parsed, or null if nothing parsed. 25 | public IRewriteCondition Parse(XmlNode node) 26 | { 27 | if (node == null) 28 | { 29 | throw new ArgumentNullException("node"); 30 | } 31 | 32 | string urlPattern = node.GetOptionalAttribute(Constants.AttrUrl); 33 | if (urlPattern == null) 34 | { 35 | return null; 36 | } 37 | 38 | return new UrlMatchCondition(urlPattern); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Actions/IRewriteAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter 11 | { 12 | /// 13 | /// Interface for executable actions. Actions must be thread-safe as there is a single 14 | /// instance of each action. This means that you must not make any changes to fields/properties 15 | /// on the action once its created. 16 | /// 17 | public interface IRewriteAction 18 | { 19 | /// 20 | /// Executes the action. 21 | /// 22 | /// 23 | /// It is important to set the correct properties on the context 24 | /// (e.g., StatusCode, Location), rather than directly implementing the action 25 | /// (e.g., RewritePath). This allows for the correct pipeline processing of 26 | /// all the specified rules. 27 | /// 28 | /// The Processing directive determines how the rewriter should continue 29 | /// processing after this action has executed. 30 | /// The context to execute the action on. 31 | RewriteProcessing Execute(IRewriteContext context); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Errors/DefaultErrorHandler.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | 11 | namespace Intelligencia.UrlRewriter.Errors 12 | { 13 | /// 14 | /// The default error handler. 15 | /// 16 | public class DefaultErrorHandler : IRewriteErrorHandler 17 | { 18 | /// 19 | /// Constructor. 20 | /// 21 | /// URL of the error page. 22 | public DefaultErrorHandler(string url) 23 | { 24 | if (url == null) 25 | { 26 | throw new ArgumentNullException("url"); 27 | } 28 | 29 | _url = url; 30 | } 31 | 32 | /// 33 | /// Handles the error by rewriting to the error page URL. 34 | /// 35 | /// The context. 36 | public void HandleError(HttpContext context) 37 | { 38 | if (context == null) 39 | { 40 | throw new ArgumentNullException("context"); 41 | } 42 | 43 | context.Server.Execute(_url); 44 | } 45 | 46 | private string _url; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Configuration/ConfigurationManagerFacade.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Specialized; 10 | using System.Configuration; 11 | 12 | namespace Intelligencia.UrlRewriter.Configuration 13 | { 14 | /// 15 | /// A naive pass-through implementation of the IConfigurationManager facade that proxys calls to the 16 | /// ASP.NET ConfigurationManager. 17 | /// 18 | public class ConfigurationManagerFacade : IConfigurationManager 19 | { 20 | /// 21 | /// Retrieves a configuration section from the web application's config file. 22 | /// 23 | /// The configuration section name 24 | /// The configuration section class instance 25 | public object GetSection(string sectionName) 26 | { 27 | return ConfigurationManager.GetSection(sectionName); 28 | } 29 | 30 | /// 31 | /// Gets the AppSettings from the web application's config file. 32 | /// 33 | public NameValueCollection AppSettings 34 | { 35 | get { return ConfigurationManager.AppSettings; } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Utilities/Message.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Utilities 11 | { 12 | /// 13 | /// Message ids 14 | /// 15 | internal enum Message 16 | { 17 | AttributeNotAllowed, 18 | ElementNotAllowed, 19 | ElementNoAttributes, 20 | ElementNoElements, 21 | MappedValuesNotAllowed, 22 | ValueOfProcessingAttribute, 23 | AttributeRequired, 24 | FullTypeNameRequiresAssemblyName, 25 | AssemblyNameRequired, 26 | TypeNameRequired, 27 | MapAlreadyDefined, 28 | InvalidTypeSpecified, 29 | InputIsNotHex, 30 | AddressesNotOfSameType, 31 | ProductName, 32 | StoppingBecauseOfRule, 33 | RestartingBecauseOfRule, 34 | ResultNotFound, 35 | CallingErrorHandler, 36 | RewritingXtoY, 37 | RedirectingXtoY, 38 | TooManyRestarts, 39 | InvalidHttpStatusCode, 40 | StartedProcessing, 41 | AttributeCannotBeBlank, 42 | InvalidBooleanAttribute, 43 | InvalidIntegerAttribute, 44 | MissingConfigFileSection, 45 | MappingNotFound, 46 | TransformFunctionNotFound 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Parsers/HeaderMatchConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Conditions; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for header match conditions. 18 | /// 19 | public sealed class HeaderMatchConditionParser : IRewriteConditionParser 20 | { 21 | /// 22 | /// Parses the condition. 23 | /// 24 | /// The node to parse. 25 | /// The condition parsed, or null if nothing parsed. 26 | public IRewriteCondition Parse(XmlNode node) 27 | { 28 | if (node == null) 29 | { 30 | throw new ArgumentNullException("node"); 31 | } 32 | 33 | string header = node.GetOptionalAttribute(Constants.AttrHeader); 34 | if (header == null) 35 | { 36 | return null; 37 | } 38 | 39 | string match = node.GetRequiredAttribute(Constants.AttrMatch, true); 40 | return new PropertyMatchCondition(header, match); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Parsers/PropertyMatchConditionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Conditions; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for property match conditions. 18 | /// 19 | public sealed class PropertyMatchConditionParser : IRewriteConditionParser 20 | { 21 | /// 22 | /// Parses the condition. 23 | /// 24 | /// The node to parse. 25 | /// The condition parsed, or null if nothing parsed. 26 | public IRewriteCondition Parse(XmlNode node) 27 | { 28 | if (node == null) 29 | { 30 | throw new ArgumentNullException("node"); 31 | } 32 | 33 | string property = node.GetOptionalAttribute(Constants.AttrProperty); 34 | if (property == null) 35 | { 36 | return null; 37 | } 38 | 39 | string match = node.GetRequiredAttribute(Constants.AttrMatch, true); 40 | 41 | return new PropertyMatchCondition(property, match); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/Actions/GoneActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class GoneActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Act 15 | GoneAction action = new GoneAction(); 16 | 17 | // Assert 18 | Assert.AreEqual(HttpStatusCode.Gone, action.StatusCode); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | GoneAction action = new GoneAction(); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsStatusCode_ReturnsStopProcessing() 34 | { 35 | // Arrange 36 | GoneAction action = new GoneAction(); 37 | IRewriteContext context = new MockRewriteContext(); 38 | 39 | // Act 40 | RewriteProcessing result = action.Execute(context); 41 | 42 | // Assert 43 | Assert.AreEqual(HttpStatusCode.Gone, context.StatusCode); 44 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Conditions/MatchCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text.RegularExpressions; 10 | 11 | namespace Intelligencia.UrlRewriter.Conditions 12 | { 13 | /// 14 | /// Base class for MatchConditions. 15 | /// 16 | public abstract class MatchCondition : IRewriteCondition 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// Pattern to match. 22 | protected MatchCondition(string pattern) 23 | { 24 | if (pattern == null) 25 | { 26 | throw new ArgumentNullException("pattern"); 27 | } 28 | 29 | _pattern = new Regex(pattern, RegexOptions.IgnoreCase); 30 | } 31 | 32 | /// 33 | /// The pattern to match. 34 | /// 35 | public Regex Pattern 36 | { 37 | get { return _pattern; } 38 | } 39 | 40 | /// 41 | /// Determines if the condition is matched. 42 | /// 43 | /// The rewriting context. 44 | /// True if the condition is met. 45 | public abstract bool IsMatch(IRewriteContext context); 46 | 47 | private Regex _pattern; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/Actions/NotFoundActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class NotFoundActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Act 15 | NotFoundAction action = new NotFoundAction(); 16 | 17 | // Assert 18 | Assert.AreEqual(HttpStatusCode.NotFound, action.StatusCode); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | NotFoundAction action = new NotFoundAction(); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsStatusCode_ReturnsStopProcessing() 34 | { 35 | // Arrange 36 | NotFoundAction action = new NotFoundAction(); 37 | IRewriteContext context = new MockRewriteContext(); 38 | 39 | // Act 40 | RewriteProcessing result = action.Execute(context); 41 | 42 | // Assert 43 | Assert.AreEqual(HttpStatusCode.NotFound, context.StatusCode); 44 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Conditions/NegativeCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Conditions 11 | { 12 | /// 13 | /// Performs a negation of the given conditions. 14 | /// 15 | public sealed class NegativeCondition : IRewriteCondition 16 | { 17 | /// 18 | /// Default constructor. 19 | /// 20 | /// 21 | public NegativeCondition(IRewriteCondition chainedCondition) 22 | { 23 | if (chainedCondition == null) 24 | { 25 | throw new ArgumentNullException("chainedCondition"); 26 | } 27 | 28 | _chainedCondition = chainedCondition; 29 | } 30 | 31 | /// 32 | /// Determines if the condition is matched. 33 | /// 34 | /// The rewriting context. 35 | /// True if the condition is met. 36 | public bool IsMatch(IRewriteContext context) 37 | { 38 | if (context == null) 39 | { 40 | throw new ArgumentNullException("context"); 41 | } 42 | 43 | return !_chainedCondition.IsMatch(context); 44 | } 45 | 46 | private IRewriteCondition _chainedCondition; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Intelligencia.UrlRewriter.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Intelligencia.UrlRewriter.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM componenets. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("dc05d49d-8884-4833-ba4b-ea07adf75888")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /test/Actions/ForbiddenActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class ForbiddenActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Act 15 | ForbiddenAction action = new ForbiddenAction(); 16 | 17 | // Assert 18 | Assert.AreEqual(HttpStatusCode.Forbidden, action.StatusCode); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | ForbiddenAction action = new ForbiddenAction(); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsStatusCode_ReturnsStopProcessing() 34 | { 35 | // Arrange 36 | ForbiddenAction action = new ForbiddenAction(); 37 | IRewriteContext context = new MockRewriteContext(); 38 | 39 | // Act 40 | RewriteProcessing result = action.Execute(context); 41 | 42 | // Assert 43 | Assert.AreEqual(HttpStatusCode.Forbidden, context.StatusCode); 44 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Actions/NotImplementedActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class NotImplementedActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Act 15 | NotImplementedAction action = new NotImplementedAction(); 16 | 17 | // Assert 18 | Assert.AreEqual(HttpStatusCode.NotImplemented, action.StatusCode); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | NotImplementedAction action = new NotImplementedAction(); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsStatusCode_ReturnsStopProcessing() 34 | { 35 | // Arrange 36 | NotImplementedAction action = new NotImplementedAction(); 37 | IRewriteContext context = new MockRewriteContext(); 38 | 39 | // Act 40 | RewriteProcessing result = action.Execute(context); 41 | 42 | // Assert 43 | Assert.AreEqual(HttpStatusCode.NotImplemented, context.StatusCode); 44 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Actions/MethodNotAllowedActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class MethodNotAllowedActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Act 15 | MethodNotAllowedAction action = new MethodNotAllowedAction(); 16 | 17 | // Assert 18 | Assert.AreEqual(HttpStatusCode.MethodNotAllowed, action.StatusCode); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | MethodNotAllowedAction action = new MethodNotAllowedAction(); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsStatusCode_ReturnsStopProcessing() 34 | { 35 | // Arrange 36 | MethodNotAllowedAction action = new MethodNotAllowedAction(); 37 | IRewriteContext context = new MockRewriteContext(); 38 | 39 | // Act 40 | RewriteProcessing result = action.Execute(context); 41 | 42 | // Assert 43 | Assert.AreEqual(HttpStatusCode.MethodNotAllowed, context.StatusCode); 44 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Conditions/MethodCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text.RegularExpressions; 10 | 11 | namespace Intelligencia.UrlRewriter.Conditions 12 | { 13 | /// 14 | /// Matches on the current method. 15 | /// 16 | public sealed class MethodCondition : MatchCondition 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// 22 | public MethodCondition(string pattern) 23 | : base(GetMethodPattern(pattern)) 24 | { 25 | } 26 | 27 | /// 28 | /// Determines if the condition is matched. 29 | /// 30 | /// The rewriting context. 31 | /// True if the condition is met. 32 | public override bool IsMatch(IRewriteContext context) 33 | { 34 | if (context == null) 35 | { 36 | throw new ArgumentNullException("context"); 37 | } 38 | 39 | return Pattern.IsMatch(context.HttpContext.HttpMethod); 40 | } 41 | 42 | private static string GetMethodPattern(string method) 43 | { 44 | // Convert the "GET,POST,*" pattern to a regex, e.g. "^GET|POST|.+$". 45 | return String.Format("^{0}$", Regex.Replace(method, @"[^a-zA-Z,\*]+", "").Replace(",", "|").Replace("*", ".+")); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Conditions/AddressCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Conditions 13 | { 14 | /// 15 | /// Matches on the current remote IP address. 16 | /// 17 | public sealed class AddressCondition : IRewriteCondition 18 | { 19 | /// 20 | /// Default constructor. 21 | /// 22 | /// 23 | public AddressCondition(string pattern) 24 | { 25 | if (pattern == null) 26 | { 27 | throw new ArgumentNullException("pattern"); 28 | } 29 | 30 | _range = IPRange.Parse(pattern); 31 | } 32 | 33 | /// 34 | /// Determines if the condition is matched. 35 | /// 36 | /// The rewriting context. 37 | /// True if the condition is met. 38 | public bool IsMatch(IRewriteContext context) 39 | { 40 | if (context == null) 41 | { 42 | throw new ArgumentNullException("context"); 43 | } 44 | 45 | string ipAddress = context.Properties[Constants.RemoteAddressHeader]; 46 | 47 | return (ipAddress != null && _range.InRange(IPAddress.Parse(ipAddress))); 48 | } 49 | 50 | private IPRange _range; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Actions/SetStatusAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Sets the StatusCode. 15 | /// 16 | public class SetStatusAction : IRewriteAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// The status code to set. 22 | public SetStatusAction(HttpStatusCode statusCode) 23 | { 24 | _statusCode = statusCode; 25 | } 26 | 27 | /// 28 | /// The status code. 29 | /// 30 | public HttpStatusCode StatusCode 31 | { 32 | get { return _statusCode; } 33 | } 34 | 35 | /// 36 | /// Executes the action. 37 | /// 38 | /// The rewriting context. 39 | public virtual RewriteProcessing Execute(IRewriteContext context) 40 | { 41 | if (context == null) 42 | { 43 | throw new ArgumentNullException("context"); 44 | } 45 | 46 | context.StatusCode = StatusCode; 47 | 48 | return ((int)StatusCode >= 300) 49 | ? RewriteProcessing.StopProcessing 50 | : RewriteProcessing.ContinueProcessing; 51 | } 52 | 53 | private HttpStatusCode _statusCode; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Transforms/StaticMappingTransform.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Specialized; 10 | 11 | namespace Intelligencia.UrlRewriter.Transforms 12 | { 13 | /// 14 | /// Default RewriteMapper, reads its maps from config. 15 | /// The mapping is CASE-INSENSITIVE. 16 | /// 17 | public sealed class StaticMappingTransform : IRewriteTransform 18 | { 19 | /// 20 | /// Default constructor. 21 | /// 22 | /// The name of the mapping. 23 | /// The mappings. 24 | public StaticMappingTransform(string name, StringDictionary map) 25 | { 26 | _name = name; 27 | _map = map; 28 | } 29 | 30 | /// 31 | /// Maps the specified value in the specified map to its replacement value. 32 | /// 33 | /// The value being mapped. 34 | /// The value mapped to, or null if no mapping could be performed. 35 | public string ApplyTransform(string input) 36 | { 37 | return _map[input]; 38 | } 39 | 40 | /// 41 | /// The name of the action. 42 | /// 43 | public string Name 44 | { 45 | get { return _name; } 46 | } 47 | 48 | private string _name; 49 | private StringDictionary _map; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Utilities/MessageProvider.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Resources; 11 | using System.Reflection; 12 | 13 | namespace Intelligencia.UrlRewriter.Utilities 14 | { 15 | /// 16 | /// Message provider. 17 | /// 18 | internal static class MessageProvider 19 | { 20 | /// 21 | /// Formats a string. 22 | /// 23 | /// The message ID 24 | /// The arguments 25 | /// The formatted string 26 | public static string FormatString(Message message, params object[] args) 27 | { 28 | string format; 29 | 30 | lock (_messageCache) 31 | { 32 | if (_messageCache.ContainsKey(message)) 33 | { 34 | format = _messageCache[message]; 35 | } 36 | else 37 | { 38 | format = _resources.GetString(message.ToString()); 39 | _messageCache.Add(message, format); 40 | } 41 | } 42 | 43 | return String.Format(format, args); 44 | } 45 | 46 | private static IDictionary _messageCache = new Dictionary(); 47 | private static ResourceManager _resources = new ResourceManager(Constants.Messages, Assembly.GetExecutingAssembly()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Parsers/IRewriteActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Configuration; 11 | 12 | namespace Intelligencia.UrlRewriter 13 | { 14 | /// 15 | /// Interface defining a parser which parses an XML node and returns the correct 16 | /// IRewriteAction instance based on the node. 17 | /// 18 | public interface IRewriteActionParser 19 | { 20 | /// 21 | /// Parses the node if possible. The parser may be called on an action node 22 | /// that it cannot parse, if it is registered on a common verb 23 | /// which is shared by several action parsers (e.g., set). 24 | /// 25 | /// The node to parse. 26 | /// The rewriter configuration. 27 | /// The action parsed. If the parser could not parse the node, 28 | /// it must return null. 29 | IRewriteAction Parse(XmlNode node, IRewriterConfiguration config); 30 | 31 | /// 32 | /// The name of the action. 33 | /// 34 | string Name { get; } 35 | 36 | /// 37 | /// Whether the action allows nested actions. 38 | /// 39 | bool AllowsNestedActions { get; } 40 | 41 | /// 42 | /// Whether the action allows attributes. 43 | /// 44 | bool AllowsAttributes { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Parsers/GoneActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Actions; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for gone actions. 18 | /// 19 | public sealed class GoneActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementGone; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return false; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | return new GoneAction(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Parsers/NotFoundActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Actions; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for not found actions. 18 | /// 19 | public sealed class NotFoundActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementNotFound; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return false; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | return new NotFoundAction(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Parsers/ForbiddenActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Configuration; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | using Intelligencia.UrlRewriter.Actions; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for forbidden actions. 18 | /// 19 | public sealed class ForbiddenActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementForbidden; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return false; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | return new ForbiddenAction(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Parsers/NotAllowedActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Actions; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for not allowed actions. 18 | /// 19 | public sealed class NotAllowedActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementNotAllowed; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return false; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | return new MethodNotAllowedAction(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Parsers/NotImplementedActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Actions; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Parser for forbidden actions. 18 | /// 19 | public sealed class NotImplementedActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementNotImplemented; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return false; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | return new NotImplementedAction(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UrlRewriter - a rule-based URL Rewriter for .NET. 2 | 3 | > Copyright (c)2011 Seth Yates 4 | > Author Seth Yates 5 | > Author Stewart Rae 6 | > Version 2.1 7 | 8 | Installation 9 | ============ 10 | 1. Open your web project, or create a new one. 11 | 2. Add a reference to the `Intelligencia.UrlRewriter` assembly. 12 | 3. Open the `web.config` file. 13 | 4. Add Configuration section handler: 14 | ```XML 15 | 16 |
20 | 21 | ``` 22 | This enables the URL Rewriter to read its configuration from the `rewriteRules` node in the `web.config` file. 23 | 24 | 5. Add UrlRewriter mapper HttpModule: 25 | 26 | ```XML 27 | 28 | 29 | 32 | 33 | 34 | ``` 35 | This enables the URL Rewriter to intercept web requests and rewrite URL requests. 36 | 37 | 6. Add some rules to your web.config file: 38 | 39 | ```XML 40 | 41 | 42 | 43 | 44 | ``` 45 | 46 | The syntax of the rewriter section is very powerful. Refer to the help file for more details 47 | of what is possible. The above rule assumes you have mapped all requests to the .NET runtime. 48 | For more information on how to do this, see http://urlrewriter.net/index.php/using/installation/ 49 | 50 | 7. Compile and test! 51 | -------------------------------------------------------------------------------- /src/Logging/IRewriteLogger.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Logging 11 | { 12 | /// 13 | /// Interface for logging info from the Rewriter. 14 | /// 15 | public interface IRewriteLogger 16 | { 17 | /// 18 | /// Writes a debug message. 19 | /// 20 | /// The message to write. 21 | void Debug(object message); 22 | 23 | /// 24 | /// Writes an informational message. 25 | /// 26 | /// The message to write. 27 | void Info(object message); 28 | 29 | /// 30 | /// Writes a warning message. 31 | /// 32 | /// The message to write. 33 | void Warn(object message); 34 | 35 | /// 36 | /// Writes an error. 37 | /// 38 | /// The message to write. 39 | void Error(object message); 40 | 41 | /// 42 | /// Writes an error. 43 | /// 44 | /// The message to write. 45 | /// The exception 46 | void Error(object message, Exception exception); 47 | 48 | /// 49 | /// Writes a fatal error. 50 | /// 51 | /// The message to write. 52 | /// The exception 53 | void Fatal(object message, Exception exception); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Conditions/ExistsCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | 11 | namespace Intelligencia.UrlRewriter.Conditions 12 | { 13 | /// 14 | /// Condition that tests the existence of a file. 15 | /// 16 | public class ExistsCondition : IRewriteCondition 17 | { 18 | /// 19 | /// Constructor. 20 | /// 21 | /// The file location 22 | public ExistsCondition(string location) 23 | { 24 | if (location == null) 25 | { 26 | throw new ArgumentNullException("location"); 27 | } 28 | 29 | _location = location; 30 | } 31 | 32 | /// 33 | /// Determines if the condition is matched. 34 | /// 35 | /// The rewriting context. 36 | /// True if the condition is met. 37 | public bool IsMatch(IRewriteContext context) 38 | { 39 | if (context == null) 40 | { 41 | throw new ArgumentNullException("context"); 42 | } 43 | 44 | try 45 | { 46 | string filename = context.HttpContext.MapPath(context.Expand(_location)); 47 | return File.Exists(filename) || Directory.Exists(filename); 48 | } 49 | catch 50 | { 51 | // An HTTP exception or an I/O exception indicates that the file definitely 52 | // does not exist. 53 | return false; 54 | } 55 | } 56 | 57 | private string _location; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Logging/NullLogger.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Logging 11 | { 12 | /// 13 | /// A logger which does nothing. 14 | /// 15 | public class NullLogger : IRewriteLogger 16 | { 17 | /// 18 | /// Writes a debug message. 19 | /// 20 | /// The message to write. 21 | public void Debug(object message) { } 22 | 23 | /// 24 | /// Writes an informational message. 25 | /// 26 | /// The message to write. 27 | public void Info(object message) { } 28 | 29 | /// 30 | /// Writes a warning message. 31 | /// 32 | /// The message to write. 33 | public void Warn(object message) { } 34 | 35 | /// 36 | /// Writes an error. 37 | /// 38 | /// The message to write. 39 | public void Error(object message) { } 40 | 41 | /// 42 | /// Writes an error. 43 | /// 44 | /// The message to write. 45 | /// The exception 46 | public void Error(object message, Exception exception) { } 47 | 48 | /// 49 | /// Writes a fatal error. 50 | /// 51 | /// The message to write. 52 | /// The exception 53 | public void Fatal(object message, Exception exception) { } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Actions/SetLocationAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Actions 11 | { 12 | /// 13 | /// Sets the Location. 14 | /// 15 | public abstract class SetLocationAction : IRewriteAction 16 | { 17 | /// 18 | /// Default constructor. 19 | /// 20 | /// The location (pattern) to set. 21 | protected SetLocationAction(string location) 22 | { 23 | if (location == null) 24 | { 25 | throw new ArgumentNullException("location"); 26 | } 27 | 28 | _location = location; 29 | } 30 | 31 | /// 32 | /// The location to set. This can include replacements referencing the matched pattern, 33 | /// for example $1, $2, ... $n and ${group} as well as ${ServerVariable} and mapping, e.g., 34 | /// ${MapName:$1}. 35 | /// 36 | public string Location 37 | { 38 | get { return _location; } 39 | } 40 | 41 | /// 42 | /// Executes the action. 43 | /// 44 | /// The rewriting context. 45 | public virtual RewriteProcessing Execute(IRewriteContext context) 46 | { 47 | if (context == null) 48 | { 49 | throw new ArgumentNullException("context"); 50 | } 51 | 52 | context.Location = context.ResolveLocation(context.Expand(Location)); 53 | 54 | return RewriteProcessing.StopProcessing; 55 | } 56 | 57 | private string _location; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Configuration/TransformFactory.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Configuration 13 | { 14 | /// 15 | /// Factory for creating transforms. 16 | /// 17 | public class TransformFactory 18 | { 19 | /* 20 | /// 21 | /// Adds a transform. 22 | /// 23 | /// The type of the transform. 24 | public void AddTransform(string transformType) 25 | { 26 | AddTransform((IRewriteTransform)TypeHelper.Activate(transformType, null)); 27 | } 28 | */ 29 | 30 | /// 31 | /// Adds a transform. 32 | /// 33 | /// The transform object. 34 | public void Add(IRewriteTransform transform) 35 | { 36 | if (transform == null) 37 | { 38 | throw new ArgumentNullException("transform"); 39 | } 40 | 41 | _transforms.Add(transform.Name, transform); 42 | } 43 | 44 | /// 45 | /// Gets a transform by name. 46 | /// 47 | /// The transform name. 48 | /// The transform object. 49 | public IRewriteTransform GetTransform(string name) 50 | { 51 | return (_transforms.ContainsKey(name)) 52 | ? _transforms[name] 53 | : null; 54 | } 55 | 56 | private IDictionary _transforms = new Dictionary(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Actions/RewriteAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Rewrites in-place. 15 | /// 16 | public sealed class RewriteAction : SetLocationAction, IRewriteCondition 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// The location to set. 22 | /// The processing directive. 23 | public RewriteAction(string location, RewriteProcessing processing) 24 | : base(location) 25 | { 26 | _processing = processing; 27 | } 28 | 29 | /// 30 | /// Executes the action. 31 | /// 32 | /// The rewrite context. 33 | public override RewriteProcessing Execute(IRewriteContext context) 34 | { 35 | base.Execute(context); 36 | return _processing; 37 | } 38 | 39 | /// 40 | /// Determines if the rewrite rule matches. 41 | /// 42 | /// The rewrite context. 43 | /// True if the rule matches. 44 | public bool IsMatch(IRewriteContext context) 45 | { 46 | return Conditions.IsMatch(context); 47 | } 48 | 49 | /// 50 | /// Conditions that must hold for the rule to fire. 51 | /// 52 | public IList Conditions 53 | { 54 | get { return _conditions; } 55 | } 56 | 57 | private IList _conditions = new List(); 58 | private RewriteProcessing _processing; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Intelligencia.UrlRewriter.vs2008.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 10.00 2 | # Visual Studio 2008 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0AE0A505-E466-4555-A21C-033DE6609F33}" 4 | ProjectSection(SolutionItems) = preProject 5 | Intelligencia.UrlRewriter.vs2008.vsmdi = Intelligencia.UrlRewriter.vs2008.vsmdi 6 | ..\License.txt = ..\License.txt 7 | Local.vs2008.testrunconfig = Local.vs2008.testrunconfig 8 | ..\README.md = ..\README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intelligencia.UrlRewriter", "src\Intelligencia.UrlRewriter.csproj", "{F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intelligencia.UrlRewriter.Tests", "test\Intelligencia.UrlRewriter.Tests.csproj", "{776B0ECF-0B91-441D-A154-D62DAC1FB27C}" 14 | EndProject 15 | Global 16 | GlobalSection(TestCaseManagementSettings) = postSolution 17 | CategoryFile = Intelligencia.UrlRewriter.vs2008.vsmdi 18 | EndGlobalSection 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /src/Actions/AddHeaderAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Actions 11 | { 12 | /// 13 | /// Action that adds a given header. 14 | /// 15 | public class AddHeaderAction : IRewriteAction 16 | { 17 | /// 18 | /// Default constructor. 19 | /// 20 | /// The header name. 21 | /// The header value. 22 | public AddHeaderAction(string header, string value) 23 | { 24 | if (header == null) 25 | { 26 | throw new ArgumentNullException("header"); 27 | } 28 | if (value == null) 29 | { 30 | throw new ArgumentNullException("value"); 31 | } 32 | 33 | _header = header; 34 | _value = value; 35 | } 36 | 37 | /// 38 | /// The header name. 39 | /// 40 | public string Header 41 | { 42 | get { return _header; } 43 | } 44 | 45 | /// 46 | /// The header value. 47 | /// 48 | public string Value 49 | { 50 | get { return _value; } 51 | } 52 | 53 | /// 54 | /// Executes the action. 55 | /// 56 | /// The rewrite context. 57 | public RewriteProcessing Execute(IRewriteContext context) 58 | { 59 | if (context == null) 60 | { 61 | throw new ArgumentNullException("context"); 62 | } 63 | 64 | context.ResponseHeaders.Add(Header, Value); 65 | 66 | return RewriteProcessing.ContinueProcessing; 67 | } 68 | 69 | private string _header; 70 | private string _value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/RewriteFormHtmlTextWriter.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | using System.Web.UI; 11 | 12 | namespace Intelligencia.UrlRewriter 13 | { 14 | /// 15 | /// The HTML Text Writer to use for rewriting form actions. 16 | /// 17 | public class RewriteFormHtmlTextWriter : HtmlTextWriter 18 | { 19 | /// 20 | /// Constructor 21 | /// 22 | /// The writer to use. 23 | public RewriteFormHtmlTextWriter(HtmlTextWriter writer) 24 | : base(writer) 25 | { 26 | InnerWriter = writer.InnerWriter; 27 | } 28 | 29 | /// 30 | /// Writes an attribute 31 | /// 32 | /// Name of the attribute 33 | /// Value of the attribute 34 | /// 35 | public override void WriteAttribute(string name, string value, bool fEncode) 36 | { 37 | // If the attribute we are writing is the "action" attribute, and we are not on a sub-control, 38 | // then replace the value to write with the raw URL of the request - which ensures that we'll 39 | // preserve the PathInfo value on postback scenarios 40 | if (name == "action") 41 | { 42 | if (HttpContext.Current.Items["ActionAlreadyWritten"] == null) 43 | { 44 | value = RewriterHttpModule.RawUrl; 45 | 46 | // Indicate that we've already rewritten the
's action attribute to prevent 47 | // us from rewriting a sub-control under the control 48 | HttpContext.Current.Items["ActionAlreadyWritten"] = true; 49 | } 50 | } 51 | 52 | base.WriteAttribute(name, value, fEncode); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Intelligencia.UrlRewriter.vs2010.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 11.00 2 | # Visual Studio 2010 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0AE0A505-E466-4555-A21C-033DE6609F33}" 4 | ProjectSection(SolutionItems) = preProject 5 | Intelligencia.UrlRewriter.vs2010.vsmdi = Intelligencia.UrlRewriter.vs2010.vsmdi 6 | ..\License.txt = ..\License.txt 7 | Local.vs2010.testrunconfig = Local.vs2010.testrunconfig 8 | ..\README.md = ..\README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intelligencia.UrlRewriter", "src\Intelligencia.UrlRewriter.csproj", "{F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Intelligencia.UrlRewriter.Tests", "test\Intelligencia.UrlRewriter.Tests.csproj", "{776B0ECF-0B91-441D-A154-D62DAC1FB27C}" 14 | EndProject 15 | Global 16 | GlobalSection(TestCaseManagementSettings) = postSolution 17 | CategoryFile = Intelligencia.UrlRewriter.vs2010.vsmdi 18 | EndGlobalSection 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {F8516C67-92BE-43FF-A8A9-B71A3EA4BE0D}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {776B0ECF-0B91-441D-A154-D62DAC1FB27C}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /src/Actions/SetPropertyAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Actions 11 | { 12 | /// 13 | /// Action that sets properties in the context. 14 | /// 15 | public class SetPropertyAction : IRewriteAction 16 | { 17 | /// 18 | /// Default constructor. 19 | /// 20 | /// The name of the variable. 21 | /// The name of the value. 22 | public SetPropertyAction(string name, string value) 23 | { 24 | if (name == null) 25 | { 26 | throw new ArgumentNullException("name"); 27 | } 28 | if (value == null) 29 | { 30 | throw new ArgumentNullException("value"); 31 | } 32 | 33 | _name = name; 34 | _value = value; 35 | } 36 | 37 | /// 38 | /// The name of the variable. 39 | /// 40 | public string Name 41 | { 42 | get { return _name; } 43 | } 44 | 45 | /// 46 | /// The value of the variable. 47 | /// 48 | public string Value 49 | { 50 | get { return _value; } 51 | } 52 | 53 | /// 54 | /// Executes the action. 55 | /// 56 | /// The rewrite context. 57 | public RewriteProcessing Execute(IRewriteContext context) 58 | { 59 | if (context == null) 60 | { 61 | throw new ArgumentNullException("context"); 62 | } 63 | 64 | context.Properties.Set(Name, context.Expand(Value)); 65 | 66 | return RewriteProcessing.ContinueProcessing; 67 | } 68 | 69 | private string _name; 70 | private string _value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Configuration/IRewriterConfiguration.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Collections.Specialized; 11 | using Intelligencia.UrlRewriter.Logging; 12 | 13 | namespace Intelligencia.UrlRewriter.Configuration 14 | { 15 | /// 16 | /// Interface for the rewriter's configuration. 17 | /// Useful for plugging out config in unit tests. 18 | /// 19 | public interface IRewriterConfiguration 20 | { 21 | /// 22 | /// The rules. 23 | /// 24 | IList Rules { get; } 25 | 26 | /// 27 | /// The action parser factory. 28 | /// 29 | ActionParserFactory ActionParserFactory { get; } 30 | 31 | /// 32 | /// The transform factory. 33 | /// 34 | TransformFactory TransformFactory { get; } 35 | 36 | /// 37 | /// The condition parser pipeline. 38 | /// 39 | ConditionParserPipeline ConditionParserPipeline { get; } 40 | 41 | /// 42 | /// Dictionary of error handlers. 43 | /// 44 | IDictionary ErrorHandlers { get; } 45 | 46 | /// 47 | /// Logger to use for logging information. 48 | /// 49 | IRewriteLogger Logger { get; set; } 50 | 51 | /// 52 | /// Collection of default document names to use if the result of a rewriting 53 | /// is a directory name. 54 | /// 55 | StringCollection DefaultDocuments { get; } 56 | 57 | /// 58 | /// Additional X-Powered-By header. 59 | /// 60 | string XPoweredBy { get; } 61 | 62 | /// 63 | /// The configuration manager instance. 64 | /// 65 | IConfigurationManager ConfigurationManager { get; } 66 | } 67 | } -------------------------------------------------------------------------------- /src/Actions/SetCookieAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// Action that sets a cookie. 15 | /// 16 | public class SetCookieAction : IRewriteAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// The cookie name. 22 | /// The cookie value. 23 | public SetCookieAction(string cookieName, string cookieValue) 24 | { 25 | if (cookieName == null) 26 | { 27 | throw new ArgumentNullException("cookieName"); 28 | } 29 | if (cookieValue == null) 30 | { 31 | throw new ArgumentNullException("cookieValue"); 32 | } 33 | 34 | _name = cookieName; 35 | _value = cookieValue; 36 | } 37 | 38 | /// 39 | /// The name of the variable. 40 | /// 41 | public string Name 42 | { 43 | get { return _name; } 44 | } 45 | 46 | /// 47 | /// The value of the variable. 48 | /// 49 | public string Value 50 | { 51 | get { return _value; } 52 | } 53 | 54 | /// 55 | /// Executes the action. 56 | /// 57 | /// The rewrite context. 58 | public RewriteProcessing Execute(IRewriteContext context) 59 | { 60 | if (context == null) 61 | { 62 | throw new ArgumentNullException("context"); 63 | } 64 | 65 | HttpCookie cookie = new HttpCookie(Name, Value); 66 | context.ResponseCookies.Add(cookie); 67 | 68 | return RewriteProcessing.ContinueProcessing; 69 | } 70 | 71 | private string _name; 72 | private string _value; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Configuration/ActionParserFactory.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using Intelligencia.UrlRewriter.Utilities; 11 | 12 | namespace Intelligencia.UrlRewriter.Configuration 13 | { 14 | /// 15 | /// Factory for creating the action parsers. 16 | /// 17 | public class ActionParserFactory 18 | { 19 | /* 20 | /// 21 | /// Adds a parser. 22 | /// 23 | /// The parser type. 24 | public void Add(string parserType) 25 | { 26 | Add((IRewriteActionParser)TypeHelper.Activate(parserType, null)); 27 | } 28 | */ 29 | 30 | /// 31 | /// Adds a parser. 32 | /// 33 | /// The parser. 34 | public void Add(IRewriteActionParser parser) 35 | { 36 | if (parser == null) 37 | { 38 | throw new ArgumentNullException("parser"); 39 | } 40 | 41 | IList list; 42 | 43 | if (_parsers.ContainsKey(parser.Name)) 44 | { 45 | list = _parsers[parser.Name]; 46 | } 47 | else 48 | { 49 | list = new List(); 50 | _parsers.Add(parser.Name, list); 51 | } 52 | 53 | list.Add(parser); 54 | } 55 | 56 | /// 57 | /// Returns a list of parsers for the given verb. 58 | /// 59 | /// The verb. 60 | /// A list of parsers 61 | public IList GetParsers(string verb) 62 | { 63 | return (_parsers.ContainsKey(verb)) 64 | ? _parsers[verb] 65 | : null; 66 | } 67 | 68 | private IDictionary> _parsers = new Dictionary>(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Parsers/RedirectActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Actions; 12 | using Intelligencia.UrlRewriter.Configuration; 13 | using Intelligencia.UrlRewriter.Utilities; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Parser for redirect actions. 19 | /// 20 | public sealed class RedirectActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementRedirect; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | 59 | string to = node.GetRequiredAttribute(Constants.AttrTo, true); 60 | bool permanent = node.GetBooleanAttribute(Constants.AttrPermanent) ?? true; 61 | 62 | RedirectAction action = new RedirectAction(to, permanent); 63 | ParseConditions(node, action.Conditions, false, config); 64 | return action; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Parsers/SetStatusActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | using System.Xml; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | using Intelligencia.UrlRewriter.Actions; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Action parser for the set-status action. 19 | /// 20 | public sealed class SetStatusActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementSet; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | if (config == null) 59 | { 60 | throw new ArgumentNullException("config"); 61 | } 62 | 63 | int? statusCode = node.GetIntegerAttribute(Constants.AttrStatus); 64 | if (!statusCode.HasValue) 65 | { 66 | return null; 67 | } 68 | 69 | return new SetStatusAction((HttpStatusCode)statusCode.Value); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Parsers/SetAppSettingPropertyActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using Intelligencia.UrlRewriter.Actions; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter.Parsers 15 | { 16 | /// 17 | /// Action parser for the set-appsetting property action. 18 | /// 19 | public sealed class SetAppSettingPropertyActionParser : RewriteActionParserBase 20 | { 21 | /// 22 | /// The name of the action. 23 | /// 24 | public override string Name 25 | { 26 | get { return Constants.ElementSetAppSetting; } 27 | } 28 | 29 | /// 30 | /// Whether the action allows nested actions. 31 | /// 32 | public override bool AllowsNestedActions 33 | { 34 | get { return false; } 35 | } 36 | 37 | /// 38 | /// Whether the action allows attributes. 39 | /// 40 | public override bool AllowsAttributes 41 | { 42 | get { return true; } 43 | } 44 | 45 | /// 46 | /// Parses the node. 47 | /// 48 | /// The node to parse. 49 | /// The rewriter configuration. 50 | /// The parsed action, or null if no action parsed. 51 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 52 | { 53 | if (node == null) 54 | { 55 | throw new ArgumentNullException("node"); 56 | } 57 | if (config == null) 58 | { 59 | throw new ArgumentNullException("config"); 60 | } 61 | 62 | string propertyName = node.GetRequiredAttribute(Constants.AttrProperty); 63 | string appSettingKey = node.GetRequiredAttribute(Constants.AttrKey); 64 | 65 | return new SetAppSettingPropertyAction(propertyName, appSettingKey); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Utilities/TypeHelper.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Utilities 11 | { 12 | /// 13 | /// Helper class for dealing with types. 14 | /// 15 | internal static class TypeHelper 16 | { 17 | /// 18 | /// Loads and activates a type 19 | /// 20 | /// The full name of the type to activate "TypeName, AssemblyName" 21 | /// Arguments to pass to the constructor 22 | /// The object 23 | public static object Activate(string fullTypeName, object[] args) 24 | { 25 | string[] components = fullTypeName.Split(new char[] { ',' }, 2); 26 | if (components.Length != 2) 27 | { 28 | throw new ArgumentOutOfRangeException("fullTypeName", fullTypeName, MessageProvider.FormatString(Message.FullTypeNameRequiresAssemblyName)); 29 | } 30 | 31 | return Activate(components[1].Trim(), components[0].Trim(), args); 32 | } 33 | 34 | /// 35 | /// Loads and activates a type 36 | /// 37 | /// The assembly name 38 | /// The type name 39 | /// Arguments to pass to the constructor 40 | /// The object 41 | public static object Activate(string assemblyName, string typeName, object[] args) 42 | { 43 | if (assemblyName.Length == 0) 44 | { 45 | throw new ArgumentOutOfRangeException("assemblyName", assemblyName, MessageProvider.FormatString(Message.AssemblyNameRequired)); 46 | } 47 | 48 | if (typeName.Length == 0) 49 | { 50 | throw new ArgumentOutOfRangeException("typeName", typeName, MessageProvider.FormatString(Message.TypeNameRequired)); 51 | } 52 | 53 | return AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assemblyName, typeName, false, 0, null, args, null, null, null); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/Actions/SetStatusActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class SetStatusActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_SetsStatusCode() 13 | { 14 | // Arrange 15 | HttpStatusCode code = HttpStatusCode.InternalServerError; 16 | 17 | // Act 18 | SetStatusAction action = new SetStatusAction(code); 19 | 20 | // Assert 21 | Assert.AreEqual(code, action.StatusCode); 22 | } 23 | 24 | [TestMethod] 25 | public void Execute_WithNullContext_Throws() 26 | { 27 | // Arrange 28 | SetStatusAction action = new SetStatusAction(HttpStatusCode.OK); 29 | IRewriteContext context = null; 30 | 31 | // Act/Assert 32 | ExceptionAssert.Throws(() => action.Execute(context)); 33 | } 34 | 35 | [TestMethod] 36 | public void Execute_WhenStatusCodeAccepted_SetsStatusCode_ReturnsContinueProcessing() 37 | { 38 | // Arrange 39 | HttpStatusCode code = HttpStatusCode.Accepted; 40 | SetStatusAction action = new SetStatusAction(code); 41 | IRewriteContext context = new MockRewriteContext(); 42 | 43 | // Act 44 | RewriteProcessing result = action.Execute(context); 45 | 46 | // Assert 47 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 48 | Assert.AreEqual(code, context.StatusCode); 49 | } 50 | 51 | [TestMethod] 52 | public void Execute_WhenStatusCodeError_SetsStatusCode_ReturnsStopProcessing() 53 | { 54 | // Arrange 55 | HttpStatusCode code = HttpStatusCode.InternalServerError; 56 | SetStatusAction action = new SetStatusAction(code); 57 | IRewriteContext context = new MockRewriteContext(); 58 | 59 | // Act 60 | RewriteProcessing result = action.Execute(context); 61 | 62 | // Assert 63 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 64 | Assert.AreEqual(code, context.StatusCode); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/Mocks/MockConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using Intelligencia.UrlRewriter.Configuration; 4 | 5 | namespace Intelligencia.UrlRewriter.Mocks 6 | { 7 | /// 8 | /// A mock IConfigurationManager. 9 | /// 10 | public class MockConfigurationManager : IConfigurationManager 11 | { 12 | /// 13 | /// Default constructor. 14 | /// 15 | public MockConfigurationManager() 16 | : this(null, null) 17 | { 18 | } 19 | 20 | /// 21 | /// Constructor. 22 | /// 23 | /// The IConfigurationManager.AppSettings collection 24 | public MockConfigurationManager(NameValueCollection appSettings) 25 | : this(null, appSettings) 26 | { 27 | } 28 | 29 | /// 30 | /// Constructor. 31 | /// 32 | /// The IConfigurationManager.GetSection result 33 | public MockConfigurationManager(object section) 34 | : this(section, null) 35 | { 36 | } 37 | 38 | /// 39 | /// Constructor. 40 | /// 41 | /// The IConfigurationManager.GetSection result 42 | /// The IConfigurationManager.AppSettings collection 43 | public MockConfigurationManager(object section, NameValueCollection appSettings) 44 | { 45 | _section = section; 46 | _appSettings = appSettings ?? new NameValueCollection(); 47 | } 48 | 49 | /// 50 | /// Mock implementation of IConfigurationManager.GetSection 51 | /// 52 | /// 53 | /// 54 | public object GetSection(string sectionName) 55 | { 56 | return _section; 57 | } 58 | 59 | /// 60 | /// Mock implementation of IConfigurationManager.AppSettings 61 | /// 62 | public NameValueCollection AppSettings 63 | { 64 | get { return _appSettings; } 65 | } 66 | 67 | private object _section; 68 | private NameValueCollection _appSettings; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Conditions/PropertyMatchCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text.RegularExpressions; 10 | 11 | namespace Intelligencia.UrlRewriter.Conditions 12 | { 13 | /// 14 | /// Performs a property match. 15 | /// 16 | public sealed class PropertyMatchCondition : MatchCondition 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// The property name 22 | /// The pattern 23 | public PropertyMatchCondition(string propertyName, string pattern) 24 | : base(pattern) 25 | { 26 | if (propertyName == null) 27 | { 28 | throw new ArgumentNullException("propertyName"); 29 | } 30 | if (pattern == null) 31 | { 32 | throw new ArgumentNullException("pattern"); 33 | } 34 | 35 | _propertyName = propertyName; 36 | } 37 | 38 | /// 39 | /// The property name. 40 | /// 41 | public string PropertyName 42 | { 43 | get { return _propertyName; } 44 | } 45 | 46 | /// 47 | /// Determines if the condition is matched. 48 | /// 49 | /// The rewriting context. 50 | /// True if the condition is met. 51 | public override bool IsMatch(IRewriteContext context) 52 | { 53 | if (context == null) 54 | { 55 | throw new ArgumentNullException("context"); 56 | } 57 | 58 | string property = context.Properties[PropertyName]; 59 | if (property != null) 60 | { 61 | Match match = Pattern.Match(property); 62 | if (match.Success) 63 | { 64 | context.LastMatch = match; 65 | } 66 | return match.Success; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | private string _propertyName = String.Empty; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Parsers/AddHeaderActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | using Intelligencia.UrlRewriter.Actions; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Action parser for the add-header action. 19 | /// 20 | public sealed class AddHeaderActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementAdd; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | if (config == null) 59 | { 60 | throw new ArgumentNullException("config"); 61 | } 62 | 63 | string headerName = node.GetOptionalAttribute(Constants.AttrHeader); 64 | if (headerName == null) 65 | { 66 | return null; 67 | } 68 | 69 | string headerValue = node.GetRequiredAttribute(Constants.AttrValue, true); 70 | 71 | return new AddHeaderAction(headerName, headerValue); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Parsers/SetCookieActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Configuration; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | using Intelligencia.UrlRewriter.Actions; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Action parser for the set-cookie action. 19 | /// 20 | public sealed class SetCookieActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementSet; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | if (config == null) 59 | { 60 | throw new ArgumentNullException("config"); 61 | } 62 | 63 | string cookieName = node.GetOptionalAttribute(Constants.AttrCookie); 64 | if (String.IsNullOrEmpty(cookieName)) 65 | { 66 | return null; 67 | } 68 | 69 | string cookieValue = node.GetRequiredAttribute(Constants.AttrValue, true); 70 | 71 | return new SetCookieAction(cookieName, cookieValue); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Parsers/SetPropertyActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Actions; 12 | using Intelligencia.UrlRewriter.Configuration; 13 | using Intelligencia.UrlRewriter.Utilities; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Action parser for the set-property action. 19 | /// 20 | public sealed class SetPropertyActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementSet; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | if (config == null) 59 | { 60 | throw new ArgumentNullException("config"); 61 | } 62 | 63 | string propertyName = node.GetOptionalAttribute(Constants.AttrProperty); 64 | if (String.IsNullOrEmpty(propertyName)) 65 | { 66 | return null; 67 | } 68 | 69 | string propertyValue = node.GetRequiredAttribute(Constants.AttrValue, true); 70 | 71 | return new SetPropertyAction(propertyName, propertyValue); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Actions/RedirectAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Net; 11 | 12 | namespace Intelligencia.UrlRewriter.Actions 13 | { 14 | /// 15 | /// Redirect using 302 temporary redirect. 16 | /// 17 | public sealed class RedirectAction : SetLocationAction, IRewriteCondition 18 | { 19 | /// 20 | /// Default constructor. 21 | /// 22 | /// The location to set. 23 | /// Whether the redirection is permanent. 24 | public RedirectAction(string location, bool permanent) 25 | : base(location) 26 | { 27 | if (location == null) 28 | { 29 | throw new ArgumentNullException("location"); 30 | } 31 | 32 | _permanent = permanent; 33 | } 34 | 35 | /// 36 | /// Executes the action. 37 | /// 38 | /// The rewriting context. 39 | public override RewriteProcessing Execute(IRewriteContext context) 40 | { 41 | if (context == null) 42 | { 43 | throw new ArgumentNullException("context"); 44 | } 45 | 46 | base.Execute(context); 47 | 48 | context.StatusCode = (_permanent) 49 | ? HttpStatusCode.Moved 50 | : HttpStatusCode.Found; 51 | 52 | return RewriteProcessing.StopProcessing; 53 | } 54 | 55 | /// 56 | /// Determines if the rewrite rule matches. 57 | /// 58 | /// 59 | /// 60 | public bool IsMatch(IRewriteContext context) 61 | { 62 | return Conditions.IsMatch(context); 63 | } 64 | 65 | /// 66 | /// Conditions that must hold for the rule to fire. 67 | /// 68 | public IList Conditions 69 | { 70 | get { return _conditions; } 71 | } 72 | 73 | private IList _conditions = new List(); 74 | private bool _permanent; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Actions/SetAppSettingPropertyAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Actions 11 | { 12 | /// 13 | /// Action that sets a property in the context from AppSettings, i.e the appSettings collection 14 | /// in web.config. 15 | /// 16 | public class SetAppSettingPropertyAction : IRewriteAction 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// The name of the variable. 22 | /// The name of the key in AppSettings. 23 | public SetAppSettingPropertyAction(string name, string appSettingsKey) 24 | { 25 | if (name == null) 26 | { 27 | throw new ArgumentNullException("name"); 28 | } 29 | if (appSettingsKey == null) 30 | { 31 | throw new ArgumentNullException("appSettingsKey"); 32 | } 33 | 34 | _name = name; 35 | _appSettingsKey = appSettingsKey; 36 | } 37 | 38 | /// 39 | /// The name of the variable. 40 | /// 41 | public string Name 42 | { 43 | get { return _name; } 44 | } 45 | 46 | /// 47 | /// The name of the key in AppSettings. 48 | /// 49 | public string AppSettingKey 50 | { 51 | get { return _appSettingsKey; } 52 | } 53 | 54 | /// 55 | /// Executes the action. 56 | /// 57 | /// The rewrite context. 58 | public RewriteProcessing Execute(IRewriteContext context) 59 | { 60 | if (context == null) 61 | { 62 | throw new ArgumentNullException("context"); 63 | } 64 | 65 | // If the value cannot be found in AppSettings, default to an empty string. 66 | string appSettingValue = context.ConfigurationManager.AppSettings[_appSettingsKey] ?? String.Empty; 67 | context.Properties.Set(Name, appSettingValue); 68 | 69 | return RewriteProcessing.ContinueProcessing; 70 | } 71 | 72 | private string _name; 73 | private string _appSettingsKey; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Conditions/UrlMatchCondition.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Text.RegularExpressions; 10 | 11 | namespace Intelligencia.UrlRewriter.Conditions 12 | { 13 | /// 14 | /// Matches on the current URL. 15 | /// 16 | public sealed class UrlMatchCondition : IRewriteCondition 17 | { 18 | /// 19 | /// Default constructor. 20 | /// 21 | /// 22 | public UrlMatchCondition(string pattern) 23 | { 24 | if (pattern == null) 25 | { 26 | throw new ArgumentNullException("pattern"); 27 | } 28 | 29 | _pattern = pattern; 30 | } 31 | 32 | /// 33 | /// Determines if the condition is matched. 34 | /// 35 | /// The rewriting context. 36 | /// True if the condition is met. 37 | public bool IsMatch(IRewriteContext context) 38 | { 39 | if (context == null) 40 | { 41 | throw new ArgumentNullException("context"); 42 | } 43 | 44 | Regex regex = GetRegex(context); 45 | 46 | Match match = regex.Match(context.Location); 47 | if (match.Success) 48 | { 49 | context.LastMatch = match; 50 | } 51 | 52 | return match.Success; 53 | } 54 | 55 | /// 56 | /// Gets regular expression to evaluate. 57 | /// 58 | private Regex GetRegex(IRewriteContext context) 59 | { 60 | // Use double-checked locking pattern to synchronise access to the regex. 61 | if (_regex == null) 62 | { 63 | lock (this) 64 | { 65 | if (_regex == null) 66 | { 67 | _regex = new Regex(context.ResolveLocation(_pattern), RegexOptions.IgnoreCase); 68 | } 69 | } 70 | } 71 | 72 | return _regex; 73 | } 74 | 75 | /// 76 | /// The pattern to match. 77 | /// 78 | public string Pattern 79 | { 80 | get { return _pattern; } 81 | } 82 | 83 | private string _pattern; 84 | private Regex _regex; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Actions/ConditionalAction.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace Intelligencia.UrlRewriter.Actions 12 | { 13 | /// 14 | /// A Conditional Action 15 | /// 16 | public class ConditionalAction : IRewriteAction, IRewriteCondition 17 | { 18 | /// 19 | /// Conditions that must hold for the rule to fire. 20 | /// 21 | public IList Conditions 22 | { 23 | get { return _conditions; } 24 | } 25 | 26 | /// 27 | /// Child rules. 28 | /// 29 | public IList Actions 30 | { 31 | get { return _actions; } 32 | } 33 | 34 | /// 35 | /// Determines if the action matches the current context. 36 | /// 37 | /// The context to match on. 38 | /// True if the condition matches. 39 | public virtual bool IsMatch(IRewriteContext context) 40 | { 41 | return Conditions.IsMatch(context); 42 | } 43 | 44 | /// 45 | /// Executes the rule. 46 | /// 47 | /// The rewrite context 48 | public virtual RewriteProcessing Execute(IRewriteContext context) 49 | { 50 | if (context == null) 51 | { 52 | throw new ArgumentNullException("context"); 53 | } 54 | 55 | // Execute the actions. 56 | for (int i = 0; i < Actions.Count; i++) 57 | { 58 | IRewriteCondition condition = Actions[i] as IRewriteCondition; 59 | if (condition == null || condition.IsMatch(context)) 60 | { 61 | IRewriteAction action = Actions[i]; 62 | RewriteProcessing processing = action.Execute(context); 63 | if (processing != RewriteProcessing.ContinueProcessing) 64 | { 65 | return processing; 66 | } 67 | } 68 | } 69 | 70 | return RewriteProcessing.ContinueProcessing; 71 | } 72 | 73 | private IList _actions = new List(); 74 | private IList _conditions = new List(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/RewriterHttpModule.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Web; 10 | using Intelligencia.UrlRewriter.Configuration; 11 | using Intelligencia.UrlRewriter.Utilities; 12 | 13 | namespace Intelligencia.UrlRewriter 14 | { 15 | /// 16 | /// Main HTTP Module for the URL Rewriter. 17 | /// Rewrites URL's based on patterns and conditions specified in the configuration file. 18 | /// This class cannot be inherited. 19 | /// 20 | public sealed class RewriterHttpModule : IHttpModule 21 | { 22 | /// 23 | /// Initialises the module. 24 | /// 25 | /// The application context. 26 | void IHttpModule.Init(HttpApplication context) 27 | { 28 | context.BeginRequest += BeginRequest; 29 | } 30 | 31 | /// 32 | /// Disposes of the module. 33 | /// 34 | void IHttpModule.Dispose() 35 | { 36 | } 37 | 38 | /// 39 | /// The raw URL for the current request, before any rewriting. 40 | /// 41 | public static string RawUrl 42 | { 43 | get { return _rewriter.RawUrl; } 44 | } 45 | 46 | /// 47 | /// Event handler for the "BeginRequest" event. 48 | /// 49 | /// The sender object 50 | /// Event args 51 | private void BeginRequest(object sender, EventArgs args) 52 | { 53 | // Add our PoweredBy header 54 | // HttpContext.Current.Response.AddHeader(Constants.HeaderXPoweredBy, Configuration.XPoweredBy); 55 | 56 | // Allow a bypass to be set up by the using application 57 | HttpContext context = HttpContext.Current; 58 | if (context.Items.Contains(@"Intelligencia.UrlRewriter.Bypass") && 59 | context.Items[@"Intelligencia.UrlRewriter.Bypass"] is bool && 60 | ((bool)context.Items[@"Intelligencia.UrlRewriter.Bypass"])) 61 | { 62 | // A bypass is set! 63 | return; 64 | } 65 | _rewriter.Rewrite(); 66 | } 67 | 68 | private static RewriterEngine _rewriter = new RewriterEngine( 69 | new HttpContextFacade(), 70 | new ConfigurationManagerFacade(), 71 | new RewriterConfiguration()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/Actions/AddHeaderActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class AddHeaderActionTest 9 | { 10 | [TestMethod] 11 | public void Constructor_WithNullHeader_Throws() 12 | { 13 | // Arrange 14 | string header = null; 15 | string value = "HeaderValue"; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => new AddHeaderAction(header, value)); 19 | } 20 | 21 | [TestMethod] 22 | public void Constructor_WithNullValue_Throws() 23 | { 24 | // Arrange 25 | string header = "HeaderName"; 26 | string value = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => new AddHeaderAction(header, value)); 30 | } 31 | 32 | [TestMethod] 33 | public void Constructor_SetsHeaderAndValue() 34 | { 35 | // Arrange 36 | string header = "HeaderName"; 37 | string value = "HeaderValue"; 38 | 39 | // Act 40 | AddHeaderAction action = new AddHeaderAction(header, value); 41 | 42 | // Assert 43 | Assert.AreEqual(header, action.Header); 44 | Assert.AreEqual(value, action.Value); 45 | } 46 | 47 | [TestMethod] 48 | public void Execute_WithNullContext_Throws() 49 | { 50 | // Arrange 51 | string header = "HeaderName"; 52 | string value = "HeaderValue"; 53 | IRewriteContext context = null; 54 | AddHeaderAction action = new AddHeaderAction(header, value); 55 | 56 | // Act/Assert 57 | ExceptionAssert.Throws(() => action.Execute(context)); 58 | } 59 | 60 | [TestMethod] 61 | public void Execute_SetsResponseHeader_ReturnsContinueProcessing() 62 | { 63 | // Arrange 64 | string header = "HeaderName"; 65 | string value = "HeaderValue"; 66 | IRewriteContext context = new MockRewriteContext(); 67 | AddHeaderAction action = new AddHeaderAction(header, value); 68 | 69 | // Act 70 | RewriteProcessing result = action.Execute(context); 71 | 72 | // Assert 73 | CollectionAssert.Contains(context.ResponseHeaders.Keys, header); 74 | Assert.AreEqual(value, context.ResponseHeaders[header]); 75 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/Actions/SetCookieActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class SetCookieActionTest 9 | { 10 | [TestMethod] 11 | public void Constructor_WithNullCookieName_Throws() 12 | { 13 | // Arrange 14 | string cookieName = null; 15 | string cookieValue = "CookieValue"; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => new SetCookieAction(cookieName, cookieValue)); 19 | } 20 | 21 | [TestMethod] 22 | public void Constructor_WithNullCookieValue_Throws() 23 | { 24 | // Arrange 25 | string cookieName = "CookieName"; 26 | string cookieValue = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => new SetCookieAction(cookieName, cookieValue)); 30 | } 31 | 32 | [TestMethod] 33 | public void Constructor_SetsNameAndValue() 34 | { 35 | // Arrange 36 | string cookieName = "CookieName"; 37 | string cookieValue = "CookieValue"; 38 | 39 | // Act 40 | SetCookieAction action = new SetCookieAction(cookieName, cookieValue); 41 | 42 | // Assert 43 | Assert.AreEqual(cookieName, action.Name); 44 | Assert.AreEqual(cookieValue, action.Value); 45 | } 46 | 47 | [TestMethod] 48 | public void Execute_WithNullContext_Throws() 49 | { 50 | // Arrange 51 | SetCookieAction action = new SetCookieAction("CookieName", "CookieValue"); 52 | IRewriteContext context = null; 53 | 54 | // Act/Assert 55 | ExceptionAssert.Throws(() => action.Execute(context)); 56 | } 57 | 58 | [TestMethod] 59 | public void Execute_SetsCookie_ReturnsContinueProcessing() 60 | { 61 | // Arrange 62 | string cookieName = "CookieName"; 63 | string cookieValue = "CookieValue"; 64 | SetCookieAction action = new SetCookieAction(cookieName, cookieValue); 65 | IRewriteContext context = new MockRewriteContext(); 66 | 67 | // Act 68 | RewriteProcessing result = action.Execute(context); 69 | 70 | // Assert 71 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 72 | CollectionAssert.Contains(context.ResponseCookies.Keys, cookieName); 73 | Assert.AreEqual(cookieValue, context.ResponseCookies[cookieName].Value); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/Actions/SetPropertyActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class SetPropertyActionTest 9 | { 10 | [TestMethod] 11 | public void Constructor_WithNullName_Throws() 12 | { 13 | // Arrange 14 | string propertyName = null; 15 | string propertyValue = "PropertyValue"; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => new SetPropertyAction(propertyName, propertyValue)); 19 | } 20 | 21 | [TestMethod] 22 | public void Constructor_WithNullValue_Throws() 23 | { 24 | // Arrange 25 | string propertyName = "PropertyName"; 26 | string propertyValue = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => new SetPropertyAction(propertyName, propertyValue)); 30 | } 31 | 32 | [TestMethod] 33 | public void Constructor_SetsNameAndValue() 34 | { 35 | // Arrange 36 | string propertyName = "PropertyName"; 37 | string propertyValue = "PropertyValue"; 38 | 39 | // Act 40 | SetPropertyAction action = new SetPropertyAction(propertyName, propertyValue); 41 | 42 | // Assert 43 | Assert.AreEqual(propertyName, action.Name); 44 | Assert.AreEqual(propertyValue, action.Value); 45 | } 46 | 47 | [TestMethod] 48 | public void Execute_WithNullContext_Throws() 49 | { 50 | // Arrange 51 | SetPropertyAction action = new SetPropertyAction("Name", "Value"); 52 | IRewriteContext context = null; 53 | 54 | // Act/Assert 55 | ExceptionAssert.Throws(() => action.Execute(context)); 56 | } 57 | 58 | [TestMethod] 59 | public void Execute_SetsProperty_ReturnsContinueProcessing() 60 | { 61 | // Arrange 62 | string propertyName = "PropertyName"; 63 | string propertyValue = "PropertyValue"; 64 | SetPropertyAction action = new SetPropertyAction(propertyName, propertyValue); 65 | IRewriteContext context = new MockRewriteContext(); 66 | 67 | // Act 68 | RewriteProcessing result = action.Execute(context); 69 | 70 | // Assert 71 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 72 | CollectionAssert.Contains(context.Properties.Keys, propertyName); 73 | Assert.AreEqual(propertyValue, context.Properties[propertyName]); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Logging/TraceLogger.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Diagnostics; 10 | 11 | namespace Intelligencia.UrlRewriter.Logging 12 | { 13 | /// 14 | /// A logger which writes to Trace, which may be read by a Trace Listener. 15 | /// 16 | public class TraceLogger : IRewriteLogger 17 | { 18 | /// 19 | /// Writes a debug message. 20 | /// 21 | /// The message to write. 22 | public void Debug(object message) 23 | { 24 | Trace.WriteLine(message); 25 | } 26 | 27 | /// 28 | /// Writes an informational message. 29 | /// 30 | /// The message to write. 31 | public void Info(object message) 32 | { 33 | Trace.WriteLine(message); 34 | } 35 | 36 | /// 37 | /// Writes a warning message. 38 | /// 39 | /// The message to write. 40 | public void Warn(object message) 41 | { 42 | Trace.WriteLine(message); 43 | } 44 | 45 | /// 46 | /// Writes an error. 47 | /// 48 | /// The message to write. 49 | public void Error(object message) 50 | { 51 | Trace.WriteLine(message); 52 | } 53 | 54 | /// 55 | /// Writes an error. 56 | /// 57 | /// The message to write. 58 | /// The exception 59 | public void Error(object message, Exception exception) 60 | { 61 | Trace.WriteLine(message); 62 | if (exception != null) 63 | { 64 | Trace.WriteLine(String.Format("Exception: {0}\r\nError Message: {1}", exception.GetType(), exception.Message)); 65 | } 66 | } 67 | 68 | /// 69 | /// Writes a fatal error. 70 | /// 71 | /// The message to write. 72 | /// The exception 73 | public void Fatal(object message, Exception exception) 74 | { 75 | Trace.WriteLine(message); 76 | if (exception != null) 77 | { 78 | Trace.WriteLine(String.Format("Exception: {0}\r\nError Message: {1}", exception.GetType(), exception.Message)); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Logging/DebugLogger.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | 10 | namespace Intelligencia.UrlRewriter.Logging 11 | { 12 | /// 13 | /// A logger which writes out to the Debug window. 14 | /// 15 | public class DebugLogger : IRewriteLogger 16 | { 17 | /// 18 | /// Writes a debug message. 19 | /// 20 | /// The message to write. 21 | public void Debug(object message) 22 | { 23 | System.Diagnostics.Debug.WriteLine(message); 24 | } 25 | 26 | /// 27 | /// Writes an informational message. 28 | /// 29 | /// The message to write. 30 | public void Info(object message) 31 | { 32 | System.Diagnostics.Debug.WriteLine(message); 33 | } 34 | 35 | /// 36 | /// Writes a warning message. 37 | /// 38 | /// The message to write. 39 | public void Warn(object message) 40 | { 41 | System.Diagnostics.Debug.WriteLine(message); 42 | } 43 | 44 | /// 45 | /// Writes an error. 46 | /// 47 | /// The message to write. 48 | public void Error(object message) 49 | { 50 | System.Diagnostics.Debug.WriteLine(message); 51 | } 52 | 53 | /// 54 | /// Writes an error. 55 | /// 56 | /// The message to write. 57 | /// The exception 58 | public void Error(object message, Exception exception) 59 | { 60 | System.Diagnostics.Debug.WriteLine(message); 61 | if (exception != null) 62 | { 63 | System.Diagnostics.Debug.WriteLine(String.Format("Exception: {0}\r\nError Message: {1}", exception.GetType(), exception.Message)); 64 | } 65 | } 66 | 67 | /// 68 | /// Writes a fatal error. 69 | /// 70 | /// The message to write. 71 | /// The exception 72 | public void Fatal(object message, Exception exception) 73 | { 74 | System.Diagnostics.Debug.WriteLine(message); 75 | if (exception != null) 76 | { 77 | System.Diagnostics.Debug.WriteLine(String.Format("Exception: {0}\r\nError Message: {1}", exception.GetType(), exception.Message)); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/IRewriteContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using System.Net; 3 | using System.Text.RegularExpressions; 4 | using System.Web; 5 | using Intelligencia.UrlRewriter.Configuration; 6 | using Intelligencia.UrlRewriter.Utilities; 7 | 8 | namespace Intelligencia.UrlRewriter 9 | { 10 | public interface IRewriteContext 11 | { 12 | /// 13 | /// The configuration manager. 14 | /// 15 | IConfigurationManager ConfigurationManager { get; } 16 | 17 | /// 18 | /// The current HTTP context. 19 | /// 20 | IHttpContext HttpContext { get; } 21 | 22 | /// 23 | /// The current location being rewritten. 24 | /// 25 | /// 26 | /// This property starts out as Request.RawUrl and is altered by various rewrite actions. 27 | /// 28 | string Location { get; set; } 29 | 30 | /// 31 | /// The properties for the context, including headers and cookie values. 32 | /// 33 | NameValueCollection Properties { get; } 34 | 35 | /// 36 | /// Output response headers. 37 | /// 38 | /// 39 | /// This collection is the collection of headers to add to the response. 40 | /// For the headers sent in the request, use the Properties property. 41 | /// 42 | NameValueCollection ResponseHeaders { get; } 43 | 44 | /// 45 | /// The status code to send in the response. 46 | /// 47 | HttpStatusCode StatusCode { get; set; } 48 | 49 | /// 50 | /// Collection of output cookies. 51 | /// 52 | /// 53 | /// This is the collection of cookies to send in the response. For the cookies 54 | /// received in the request, use the Properties property. 55 | /// 56 | HttpCookieCollection ResponseCookies { get; } 57 | 58 | /// 59 | /// Last matching pattern from a match (if any). 60 | /// 61 | Match LastMatch { get; set; } 62 | 63 | /// 64 | /// Expands the given input using the last match, properties, maps and transforms. 65 | /// 66 | /// The input to expand. 67 | /// The expanded form of the input. 68 | string Expand(string input); 69 | 70 | /// 71 | /// Resolves the location to an absolute reference. 72 | /// 73 | /// The application-referenced location. 74 | /// The absolute location. 75 | string ResolveLocation(string location); 76 | } 77 | } -------------------------------------------------------------------------------- /src/Parsers/RewriteActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Configuration; 11 | using Intelligencia.UrlRewriter.Actions; 12 | using Intelligencia.UrlRewriter.Configuration; 13 | using Intelligencia.UrlRewriter.Utilities; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Parser for rewrite actions. 19 | /// 20 | public sealed class RewriteActionParser : RewriteActionParserBase 21 | { 22 | /// 23 | /// The name of the action. 24 | /// 25 | public override string Name 26 | { 27 | get { return Constants.ElementRewrite; } 28 | } 29 | 30 | /// 31 | /// Whether the action allows nested actions. 32 | /// 33 | public override bool AllowsNestedActions 34 | { 35 | get { return false; } 36 | } 37 | 38 | /// 39 | /// Whether the action allows attributes. 40 | /// 41 | public override bool AllowsAttributes 42 | { 43 | get { return true; } 44 | } 45 | 46 | /// 47 | /// Parses the node. 48 | /// 49 | /// The node to parse. 50 | /// The rewriter configuration. 51 | /// The parsed action, or null if no action parsed. 52 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 53 | { 54 | if (node == null) 55 | { 56 | throw new ArgumentNullException("node"); 57 | } 58 | if (config == null) 59 | { 60 | throw new ArgumentNullException("config"); 61 | } 62 | 63 | string to = node.GetRequiredAttribute(Constants.AttrTo, true); 64 | 65 | RewriteProcessing processing = ParseProcessing(node); 66 | 67 | RewriteAction action = new RewriteAction(to, processing); 68 | ParseConditions(node, action.Conditions, false, config); 69 | 70 | return action; 71 | } 72 | 73 | private RewriteProcessing ParseProcessing(XmlNode node) 74 | { 75 | string processing = node.GetOptionalAttribute(Constants.AttrProcessing); 76 | if (processing == null) 77 | { 78 | // Default to ContinueProcessing if processing attribute is missing. 79 | return RewriteProcessing.ContinueProcessing; 80 | } 81 | 82 | switch (processing) 83 | { 84 | case Constants.AttrValueRestart: 85 | return RewriteProcessing.RestartProcessing; 86 | 87 | case Constants.AttrValueStop: 88 | return RewriteProcessing.StopProcessing; 89 | 90 | case Constants.AttrValueContinue: 91 | return RewriteProcessing.ContinueProcessing; 92 | 93 | default: 94 | throw new ConfigurationErrorsException(MessageProvider.FormatString(Message.ValueOfProcessingAttribute, processing, Constants.AttrValueContinue, Constants.AttrValueRestart, Constants.AttrValueStop), node); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Form.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Runtime.InteropServices; 10 | using System.Web.UI; 11 | using System.Web.UI.HtmlControls; 12 | using Intelligencia.UrlRewriter.Utilities; 13 | 14 | namespace Intelligencia.UrlRewriter 15 | { 16 | /// 17 | /// Replacement for <asp:form> to handle rewritten form postback. 18 | /// 19 | /// 20 | ///

This form should be used for pages that use the URL Rewriter and have 21 | /// forms that are posted back. If you use the normal ASP.NET HtmlForm, 22 | /// then the postback will not be able to correctly resolve the postback data to the form data. 23 | ///

24 | ///

This form is a direct replacement for the <asp:form> tag. 25 | ///

26 | ///

The following code demonstrates the usage of this control.

27 | /// 28 | /// <%@ Page language="c#" Codebehind="MyPage.aspx.cs" AutoEventWireup="false" Inherits="MyPage" %> 29 | /// <%@ Register TagPrefix="url" Namespace="Intelligencia.UrlRewriter" Assembly="Intelligencia.UrlRewriter" %> 30 | /// <html> 31 | /// ... 32 | /// <body> 33 | /// <url:form id="MyForm" runat="server"> 34 | /// ... 35 | /// </url:form> 36 | /// </body> 37 | /// </html> 38 | /// 39 | ///
40 | [ComVisible(false)] 41 | [ToolboxData("<{0}:Form runat=\"server\">")] 42 | public class Form : HtmlForm 43 | { 44 | /// 45 | /// Renders children of the form control. 46 | /// 47 | /// The output writer. 48 | /// 49 | protected override void RenderChildren(HtmlTextWriter writer) 50 | { 51 | writer.RenderBeginTag(HtmlTextWriterTag.Div); 52 | base.RenderChildren(writer); 53 | writer.RenderEndTag(); 54 | } 55 | 56 | /// 57 | /// Renders attributes. 58 | /// 59 | /// The output writer. 60 | /// 61 | protected override void RenderAttributes(HtmlTextWriter writer) 62 | { 63 | writer.WriteAttribute(Constants.AttrName, GetName()); 64 | Attributes.Remove(Constants.AttrName); 65 | 66 | writer.WriteAttribute(Constants.AttrMethod, GetMethod()); 67 | Attributes.Remove(Constants.AttrMethod); 68 | 69 | writer.WriteAttribute(Constants.AttrAction, GetAction(), true); 70 | Attributes.Remove(Constants.AttrAction); 71 | 72 | Attributes.Render(writer); 73 | 74 | if (ID != null) 75 | { 76 | writer.WriteAttribute(Constants.AttrID, GetID()); 77 | } 78 | } 79 | 80 | private string GetID() 81 | { 82 | return ClientID; 83 | } 84 | 85 | private string GetName() 86 | { 87 | return Name; 88 | } 89 | 90 | private string GetMethod() 91 | { 92 | return Method; 93 | } 94 | 95 | private string GetAction() 96 | { 97 | return RewriterHttpModule.RawUrl; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /test/Assert/ExceptionAssert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace Intelligencia.UrlRewriter 5 | { 6 | /// 7 | /// Useful assertions for actions that are expected to throw an exception. 8 | /// 9 | public static class ExceptionAssert 10 | { 11 | /// 12 | /// Executes an exception, expecting an exception to be thrown. 13 | /// Like Assert.Throws in NUnit. 14 | /// 15 | /// The action to execute 16 | /// The exception thrown by the action 17 | public static Exception Throws(Action action) 18 | { 19 | return Throws(action, null); 20 | } 21 | 22 | /// 23 | /// Executes an exception, expecting an exception to be thrown. 24 | /// Like Assert.Throws in NUnit. 25 | /// 26 | /// The action to execute 27 | /// The error message if the expected exception is not thrown 28 | /// The exception thrown by the action 29 | public static Exception Throws(Action action, string message) 30 | { 31 | try 32 | { 33 | action(); 34 | } 35 | catch (Exception ex) 36 | { 37 | // The action method has thrown the expected exception. 38 | // Return the exception, in case the unit test wants to perform further assertions on it. 39 | return ex; 40 | } 41 | 42 | // If we end up here, the expected exception was not thrown. Fail! 43 | throw new AssertFailedException(message ?? "Expected exception was not thrown."); 44 | } 45 | 46 | /// 47 | /// Executes an exception, expecting an exception of a specific type to be thrown. 48 | /// Like Assert.Throws in NUnit. 49 | /// 50 | /// The action to execute 51 | /// The exception thrown by the action 52 | public static T Throws(Action action) where T : Exception 53 | { 54 | return Throws(action, null); 55 | } 56 | 57 | /// 58 | /// Executes an exception, expecting an exception of a specific type to be thrown. 59 | /// Like Assert.Throws in NUnit. 60 | /// 61 | /// The action to execute 62 | /// The error message if the expected exception is not thrown 63 | /// The exception thrown by the action 64 | public static T Throws(Action action, string message) where T : Exception 65 | { 66 | try 67 | { 68 | action(); 69 | } 70 | catch (Exception ex) 71 | { 72 | T actual = ex as T; 73 | if (actual == null) 74 | { 75 | throw new AssertFailedException(message ?? String.Format("Expected exception of type {0} not thrown. Actual exception type was {1}.", typeof(T), ex.GetType())); 76 | } 77 | 78 | // The action method has thrown the expected exception of type 'T'. 79 | // Return the exception, in case the unit test wants to perform further assertions on it. 80 | return actual; 81 | } 82 | 83 | // If we end up here, the expected exception of type 'T' was not thrown. Fail! 84 | throw new AssertFailedException(message ?? String.Format("Expected exception of type {0} not thrown.", typeof(T))); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/Utilities/IHttpContext.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Specialized; 11 | using System.Net; 12 | using System.Web; 13 | 14 | namespace Intelligencia.UrlRewriter.Utilities 15 | { 16 | /// 17 | /// Interface for the HTTP context. 18 | /// Useful for plugging out the HttpContext.Current object in unit tests. 19 | /// 20 | public interface IHttpContext 21 | { 22 | /// 23 | /// Retrieves the application path. 24 | /// 25 | string ApplicationPath { get; } 26 | 27 | /// 28 | /// Retrieves the raw URL. 29 | /// 30 | string RawUrl { get; } 31 | 32 | /// 33 | /// Retrieves the current request URL. 34 | /// 35 | Uri RequestUrl { get; } 36 | 37 | /// 38 | /// Maps the given URL to the absolute local path. 39 | /// 40 | /// The URL to map. 41 | /// The absolute local file path relating to the URL. 42 | string MapPath(string url); 43 | 44 | /// 45 | /// Sets the status code for the response. 46 | /// 47 | /// The status code. 48 | void SetStatusCode(HttpStatusCode code); 49 | 50 | /// 51 | /// Rewrites the request to the new URL. 52 | /// 53 | /// The new URL to rewrite to. 54 | void RewritePath(string url); 55 | 56 | /// 57 | /// Sets the redirection location to the given URL. 58 | /// 59 | /// The URL of the redirection location. 60 | void SetRedirectLocation(string url); 61 | 62 | /// 63 | /// Adds a header to the response. 64 | /// 65 | /// The header name. 66 | /// The header value. 67 | void SetResponseHeader(string name, string value); 68 | 69 | /// 70 | /// Adds a cookie to the response. 71 | /// 72 | /// The cookie to add. 73 | void SetResponseCookie(HttpCookie cookie); 74 | 75 | /// 76 | /// Handles an error with the error handler. 77 | /// 78 | /// The error handler to use. 79 | void HandleError(IRewriteErrorHandler handler); 80 | 81 | /// 82 | /// The Items collection for the current request. 83 | /// 84 | IDictionary Items { get; } 85 | 86 | /// 87 | /// Retrieves the HTTP method used by the request (GET, POST, HEAD, PUT, DELETE). 88 | /// 89 | string HttpMethod { get; } 90 | 91 | /// 92 | /// Gets a collection of server variables. 93 | /// 94 | NameValueCollection ServerVariables { get; } 95 | 96 | /// 97 | /// Gets a collection of request headers. 98 | /// 99 | NameValueCollection RequestHeaders { get; } 100 | 101 | /// 102 | /// Gets a collection of request cookies. 103 | /// 104 | HttpCookieCollection RequestCookies { get; } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Utilities/Constants.cs: -------------------------------------------------------------------------------- 1 | // Version 2.0 2 | // 3 | // Copyright 2011 Intelligencia 4 | // Copyright 2011 Seth Yates 5 | // 6 | 7 | using System; 8 | 9 | namespace Intelligencia.UrlRewriter.Utilities 10 | { 11 | /// 12 | /// Constants for the parser. 13 | /// 14 | internal static class Constants 15 | { 16 | public const string Messages = "Intelligencia.UrlRewriter.Messages"; 17 | public const string RewriterNode = "rewriter"; 18 | public const string RemoteAddressHeader = "REMOTE_ADDR"; 19 | public const string AttributeAction = "{0}-{1}"; 20 | public const string HeaderXPoweredBy = "X-Powered-By"; 21 | 22 | public const string AttrID = "id"; 23 | public const string AttrAction = "action"; 24 | public const string AttrExists = "exists"; 25 | public const string AttrFile = "file"; 26 | public const string AttrAddress = "address"; 27 | public const string AttrHeader = "header"; 28 | public const string AttrMethod = "method"; 29 | public const string AttrMatch = "match"; 30 | public const string AttrValue = "value"; 31 | public const string AttrProperty = "property"; 32 | public const string AttrKey = "key"; 33 | public const string AttrStatus = "status"; 34 | public const string AttrCookie = "cookie"; 35 | public const string AttrRewrite = "rewrite"; 36 | public const string AttrRedirect = "redirect"; 37 | public const string AttrProcessing = "processing"; 38 | public const string AttrPermanent = "permanent"; 39 | public const string AttrValueContinue = "continue"; 40 | public const string AttrValueRestart = "restart"; 41 | public const string AttrValueStop = "stop"; 42 | public const string AttrFrom = "from"; 43 | public const string AttrName = "name"; 44 | public const string AttrTo = "to"; 45 | public const string AttrType = "type"; 46 | public const string AttrCode = "code"; 47 | public const string AttrUrl = "url"; 48 | public const string AttrParser = "parser"; 49 | public const string AttrTransform = "transform"; 50 | public const string AttrLogger = "logger"; 51 | 52 | public const string ElementIf = "if"; 53 | public const string ElementUnless = "unless"; 54 | public const string ElementAnd = "and"; 55 | public const string ElementAdd = "add"; 56 | public const string ElementSet = "set"; 57 | public const string ElementSetAppSetting = "set-appsetting"; 58 | public const string ElementErrorHandler = "error-handler"; 59 | public const string ElementForbidden = "forbidden"; 60 | public const string ElementNotImplemented = "not-implemented"; 61 | public const string ElementNotAllowed = "not-allowed"; 62 | public const string ElementGone = "gone"; 63 | public const string ElementNotFound = "not-found"; 64 | public const string ElementRewrite = "rewrite"; 65 | public const string ElementRedirect = "redirect"; 66 | public const string ElementMap = "map"; 67 | public const string ElementMapping = "mapping"; 68 | public const string ElementRegister = "register"; 69 | public const string ElementDefaultDocuments = "default-documents"; 70 | public const string ElementDocument = "document"; 71 | 72 | public const string TransformDecode = "decode"; 73 | public const string TransformEncode = "encode"; 74 | public const string TransformBase64 = "base64"; 75 | public const string TransformBase64Decode = "base64decode"; 76 | public const string TransformLower = "lower"; 77 | public const string TransformUpper = "upper"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/UrlRewriter.ndoc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/Parsers/RewriteActionParserBase.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Collections.Generic; 11 | using Intelligencia.UrlRewriter.Conditions; 12 | using Intelligencia.UrlRewriter.Configuration; 13 | using Intelligencia.UrlRewriter.Utilities; 14 | 15 | namespace Intelligencia.UrlRewriter.Parsers 16 | { 17 | /// 18 | /// Base class for rewrite actions. 19 | /// 20 | public abstract class RewriteActionParserBase : IRewriteActionParser 21 | { 22 | /// 23 | /// Parses the action. 24 | /// 25 | /// The node to parse. 26 | /// The rewriter configuration. 27 | /// The parsed action, null if no action parsed. 28 | public abstract IRewriteAction Parse(XmlNode node, IRewriterConfiguration config); 29 | 30 | /// 31 | /// The name of the action. 32 | /// 33 | public abstract string Name { get; } 34 | 35 | /// 36 | /// Whether the action allows nested actions. 37 | /// 38 | public abstract bool AllowsNestedActions { get; } 39 | 40 | /// 41 | /// Whether the action allows attributes. 42 | /// 43 | public abstract bool AllowsAttributes { get; } 44 | 45 | /// 46 | /// Parses conditions from the node. 47 | /// 48 | /// The node. 49 | /// Conditions list to add new conditions to. 50 | /// Whether the conditions should be negated. 51 | /// Rewriter configuration 52 | protected void ParseConditions(XmlNode node, IList conditions, bool negative, IRewriterConfiguration config) 53 | { 54 | if (config == null) 55 | { 56 | return; 57 | } 58 | if (node == null) 59 | { 60 | throw new ArgumentNullException("node"); 61 | } 62 | if (conditions == null) 63 | { 64 | throw new ArgumentNullException("conditions"); 65 | } 66 | 67 | // Parse attribute-based conditions. 68 | foreach (IRewriteConditionParser parser in config.ConditionParserPipeline) 69 | { 70 | IRewriteCondition condition = parser.Parse(node); 71 | if (condition != null) 72 | { 73 | if (negative) 74 | { 75 | condition = new NegativeCondition(condition); 76 | } 77 | 78 | conditions.Add(condition); 79 | } 80 | } 81 | 82 | // Now, process the nested conditions. 83 | XmlNode childNode = node.FirstChild; 84 | while (childNode != null) 85 | { 86 | if (childNode.NodeType == XmlNodeType.Element) 87 | { 88 | if (childNode.LocalName == Constants.ElementAnd) 89 | { 90 | ParseConditions(childNode, conditions, negative, config); 91 | 92 | XmlNode childNode2 = childNode.NextSibling; 93 | node.RemoveChild(childNode); 94 | childNode = childNode2; 95 | continue; 96 | } 97 | } 98 | 99 | childNode = childNode.NextSibling; 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /test/Actions/SetAppSettingPropertyActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class SetAppSettingPropertyActionTest 9 | { 10 | [TestMethod] 11 | public void Constructor_WithNullPropertyName_Throws() 12 | { 13 | // Arrange 14 | string propertyName = null; 15 | string appSettingKey = "AppSettingKey"; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => new SetAppSettingPropertyAction(propertyName, appSettingKey)); 19 | } 20 | 21 | [TestMethod] 22 | public void Constructor_WithNullAppSettingKey_Throws() 23 | { 24 | // Arrange 25 | string propertyName = "PropertyName"; 26 | string appSettingKey = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => new SetAppSettingPropertyAction(propertyName, appSettingKey)); 30 | } 31 | 32 | [TestMethod] 33 | public void Constructor_SetsNameAppSettingKey() 34 | { 35 | // Arrange 36 | string propertyName = "PropertyName"; 37 | string appSettingKey = "AppSettingKey"; 38 | 39 | // Act 40 | SetAppSettingPropertyAction action = new SetAppSettingPropertyAction(propertyName, appSettingKey); 41 | 42 | // Assert 43 | Assert.AreEqual(propertyName, action.Name); 44 | Assert.AreEqual(appSettingKey, action.AppSettingKey); 45 | } 46 | 47 | [TestMethod] 48 | public void Execute_WithNullContext_Throws() 49 | { 50 | // Arrange 51 | string propertyName = "PropertyName"; 52 | string appSettingKey = "AppSettingKey"; 53 | SetAppSettingPropertyAction action = new SetAppSettingPropertyAction(propertyName, appSettingKey); 54 | IRewriteContext context = null; 55 | 56 | // Act/Assert 57 | ExceptionAssert.Throws(() => action.Execute(context)); 58 | } 59 | 60 | [TestMethod] 61 | public void Execute_SetsPropertyAppSetting_ReturnsContinueProcessing() 62 | { 63 | // Arrange 64 | string propertyName = "PropertyName"; 65 | string appSettingKey = "AppSettingKey"; 66 | string appSettingValue = "AppSettingValue"; 67 | SetAppSettingPropertyAction action = new SetAppSettingPropertyAction(propertyName, appSettingKey); 68 | IRewriteContext context = new MockRewriteContext(); 69 | context.ConfigurationManager.AppSettings[appSettingKey] = appSettingValue; 70 | 71 | // Act 72 | RewriteProcessing result = action.Execute(context); 73 | 74 | // Assert 75 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 76 | CollectionAssert.Contains(context.Properties.Keys, propertyName); 77 | Assert.AreEqual(appSettingValue, context.Properties[propertyName]); 78 | } 79 | 80 | [TestMethod] 81 | public void Execute_WhenMissingAppSetting_SetsPropertyToEmptyString_ReturnsContinueProcessing() 82 | { 83 | // Arrange 84 | string propertyName = "PropertyName"; 85 | string appSettingKey = "MissingAppSettingKey"; 86 | SetAppSettingPropertyAction action = new SetAppSettingPropertyAction(propertyName, appSettingKey); 87 | IRewriteContext context = new MockRewriteContext(); 88 | 89 | // Act 90 | RewriteProcessing result = action.Execute(context); 91 | 92 | // Assert 93 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 94 | CollectionAssert.Contains(context.Properties.Keys, propertyName); 95 | Assert.AreEqual(String.Empty, context.Properties[propertyName]); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Parsers/IfConditionActionParser.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Xml; 10 | using System.Collections.Generic; 11 | using System.Configuration; 12 | using Intelligencia.UrlRewriter.Actions; 13 | using Intelligencia.UrlRewriter.Utilities; 14 | using Intelligencia.UrlRewriter.Configuration; 15 | 16 | namespace Intelligencia.UrlRewriter.Parsers 17 | { 18 | /// 19 | /// Parses the IF node. 20 | /// 21 | public class IfConditionActionParser : RewriteActionParserBase 22 | { 23 | /// 24 | /// The name of the action. 25 | /// 26 | public override string Name 27 | { 28 | get { return Constants.ElementIf; } 29 | } 30 | 31 | /// 32 | /// Whether the action allows nested actions. 33 | /// 34 | public override bool AllowsNestedActions 35 | { 36 | get { return true; } 37 | } 38 | 39 | /// 40 | /// Whether the action allows attributes. 41 | /// 42 | public override bool AllowsAttributes 43 | { 44 | get { return true; } 45 | } 46 | 47 | /// 48 | /// Parses the action. 49 | /// 50 | /// The node to parse. 51 | /// The rewriter configuration. 52 | /// The parsed action, null if no action parsed. 53 | public override IRewriteAction Parse(XmlNode node, IRewriterConfiguration config) 54 | { 55 | if (node == null) 56 | { 57 | throw new ArgumentNullException("node"); 58 | } 59 | if (config == null) 60 | { 61 | throw new ArgumentNullException("config"); 62 | } 63 | 64 | ConditionalAction rule = new ConditionalAction(); 65 | 66 | // Process the conditions on the element. 67 | bool negative = (node.LocalName == Constants.ElementUnless); 68 | ParseConditions(node, rule.Conditions, negative, config); 69 | 70 | // Next, process the actions on the element. 71 | ReadActions(node, rule.Actions, config); 72 | 73 | return rule; 74 | } 75 | 76 | private static void ReadActions(XmlNode node, IList actions, IRewriterConfiguration config) 77 | { 78 | XmlNode childNode = node.FirstChild; 79 | while (childNode != null) 80 | { 81 | if (childNode.NodeType == XmlNodeType.Element) 82 | { 83 | IList parsers = config.ActionParserFactory.GetParsers(childNode.LocalName); 84 | if (parsers != null) 85 | { 86 | bool parsed = false; 87 | foreach (IRewriteActionParser parser in parsers) 88 | { 89 | IRewriteAction action = parser.Parse(childNode, config); 90 | if (action != null) 91 | { 92 | parsed = true; 93 | actions.Add(action); 94 | } 95 | } 96 | 97 | if (!parsed) 98 | { 99 | throw new ConfigurationErrorsException(MessageProvider.FormatString(Message.ElementNotAllowed, node.FirstChild.Name), node); 100 | } 101 | } 102 | } 103 | 104 | childNode = childNode.NextSibling; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /test/Mocks/MockRewriteContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.Net; 4 | using System.Text.RegularExpressions; 5 | using System.Web; 6 | using Intelligencia.UrlRewriter.Configuration; 7 | using Intelligencia.UrlRewriter.Utilities; 8 | 9 | namespace Intelligencia.UrlRewriter.Mocks 10 | { 11 | /// 12 | /// A mock IRewriteContext. 13 | /// 14 | public class MockRewriteContext : IRewriteContext 15 | { 16 | /// 17 | /// Default constructor. 18 | /// 19 | public MockRewriteContext() 20 | : this(null, null, null, null, null) 21 | { 22 | } 23 | 24 | /// 25 | /// Constrtuctor. 26 | /// 27 | /// The IRewriteContext.ConfigurationManager instance 28 | /// The The IRewriteContext.HttpContext instance 29 | /// The The IRewriteContext.Properties instance 30 | /// The The IRewriteContext.ResponseHeaders instance 31 | /// The The IRewriteContext.ResponseCookies instance 32 | public MockRewriteContext( 33 | IConfigurationManager configurationManager, 34 | IHttpContext httpContext, 35 | NameValueCollection properties, 36 | NameValueCollection responseHeaders, 37 | HttpCookieCollection responseCookies) 38 | { 39 | ConfigurationManager = configurationManager ?? new MockConfigurationManager(); 40 | HttpContext = httpContext ?? new MockHttpContext(); 41 | Properties = properties ?? new NameValueCollection(); 42 | StatusCode = HttpStatusCode.OK; 43 | ResponseHeaders = responseHeaders ?? new NameValueCollection(); 44 | ResponseCookies = responseCookies ?? new HttpCookieCollection(); 45 | } 46 | 47 | /// 48 | /// Mock implementation of IRewriteContext.ConfigurationManager 49 | /// 50 | public IConfigurationManager ConfigurationManager { get; private set; } 51 | 52 | /// 53 | /// Mock implementation of IRewriteContext.HttpContext 54 | /// 55 | public IHttpContext HttpContext { get; private set; } 56 | 57 | /// 58 | /// Mock implementation of IRewriteContext.Location 59 | /// 60 | public string Location { get; set; } 61 | 62 | /// 63 | /// Mock implementation of IRewriteContext.StatusCode 64 | /// 65 | public HttpStatusCode StatusCode { get; set; } 66 | 67 | /// 68 | /// Mock implementation of IRewriteContext.Properties 69 | /// 70 | public NameValueCollection Properties { get; private set; } 71 | 72 | /// 73 | /// Mock implementation of IRewriteContext.ResponseHeaders 74 | /// 75 | public NameValueCollection ResponseHeaders { get; private set; } 76 | 77 | /// 78 | /// Mock implementation of IRewriteContext.ResponseCookies 79 | /// 80 | public HttpCookieCollection ResponseCookies { get; private set; } 81 | 82 | /// 83 | /// Mock implementation of IRewriteContext.LastMatch 84 | /// 85 | public Match LastMatch { get; set; } 86 | 87 | /// 88 | /// Mock implementation of IRewriteContext.Expand 89 | /// 90 | /// 91 | /// 92 | public string Expand(string input) 93 | { 94 | return input; // I guess? 95 | } 96 | 97 | /// 98 | /// Mock implementation of IRewriteContext.ResolveLocation 99 | /// 100 | /// 101 | /// 102 | public string ResolveLocation(string location) 103 | { 104 | return location; // I guess? 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /test/Actions/ConditionalActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class ConditionalActionTest 9 | { 10 | [TestMethod] 11 | public void Execute_WithNullContext_Throws() 12 | { 13 | // Arrange 14 | ConditionalAction action = new ConditionalAction(); 15 | IRewriteContext context = null; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => action.Execute(context)); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WhenNoActionsOrConditions_ReturnsContinueProcessing() 23 | { 24 | // Arrange 25 | ConditionalAction action = new ConditionalAction(); 26 | IRewriteContext context = new MockRewriteContext(); 27 | 28 | // Act 29 | RewriteProcessing result = action.Execute(context); 30 | 31 | // Assert 32 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 33 | } 34 | 35 | [TestMethod] 36 | public void Execute_WhenConditionAndAction_ReturnsExpectedResult() 37 | { 38 | // Arrange 39 | ConditionalAction action = new ConditionalAction(); 40 | IRewriteContext context = new MockRewriteContext(); 41 | action.Conditions.Add(new MockRewriteCondition(true)); 42 | action.Actions.Add(new MockRewriteAction(RewriteProcessing.ContinueProcessing)); 43 | 44 | // Act 45 | RewriteProcessing result = action.Execute(context); 46 | 47 | // Assert 48 | Assert.AreEqual(RewriteProcessing.ContinueProcessing, result); 49 | } 50 | 51 | [TestMethod] 52 | public void IsMatch_WhenNoConditions_ReturnsTrue() 53 | { 54 | // Arrange 55 | ConditionalAction action = new ConditionalAction(); 56 | IRewriteContext context = new MockRewriteContext(); 57 | 58 | // Act 59 | bool match = action.IsMatch(context); 60 | 61 | // Assert 62 | Assert.IsTrue(match); 63 | } 64 | 65 | [TestMethod] 66 | public void IsMatch_WhenSingleConditionMatches_ReturnsTrue() 67 | { 68 | // Arrange 69 | ConditionalAction action = new ConditionalAction(); 70 | IRewriteContext context = new MockRewriteContext(); 71 | action.Conditions.Add(new MockRewriteCondition(true)); 72 | 73 | // Act 74 | bool match = action.IsMatch(context); 75 | 76 | // Assert 77 | Assert.IsTrue(match); 78 | } 79 | 80 | [TestMethod] 81 | public void IsMatch_WhenSingleConditionDoesNotMatch_ReturnsFalse() 82 | { 83 | // Arrange 84 | ConditionalAction action = new ConditionalAction(); 85 | IRewriteContext context = new MockRewriteContext(); 86 | action.Conditions.Add(new MockRewriteCondition(false)); 87 | 88 | // Act 89 | bool match = action.IsMatch(context); 90 | 91 | // Assert 92 | Assert.IsFalse(match); 93 | } 94 | 95 | [TestMethod] 96 | public void IsMatch_WhenMultipleMatchingConditions_ReturnsTrue() 97 | { 98 | // Arrange 99 | ConditionalAction action = new ConditionalAction(); 100 | IRewriteContext context = new MockRewriteContext(); 101 | action.Conditions.Add(new MockRewriteCondition(true)); 102 | action.Conditions.Add(new MockRewriteCondition(true)); 103 | 104 | // Act 105 | bool match = action.IsMatch(context); 106 | 107 | // Assert 108 | Assert.IsTrue(match); 109 | } 110 | 111 | [TestMethod] 112 | public void IsMatch_WhenMixedConditions_ReturnsFalse() 113 | { 114 | // Arrange 115 | ConditionalAction action = new ConditionalAction(); 116 | IRewriteContext context = new MockRewriteContext(); 117 | action.Conditions.Add(new MockRewriteCondition(true)); 118 | action.Conditions.Add(new MockRewriteCondition(false)); 119 | 120 | // Act 121 | bool match = action.IsMatch(context); 122 | 123 | // Assert 124 | Assert.IsFalse(match); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /test/Actions/RewriteActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Intelligencia.UrlRewriter.Mocks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Intelligencia.UrlRewriter.Actions.Tests 6 | { 7 | [TestClass] 8 | public class RewriteActionTest 9 | { 10 | [TestMethod] 11 | public void Constructor_WithNullLocation_Throws() 12 | { 13 | // Arrange 14 | string location = null; 15 | RewriteProcessing processing = RewriteProcessing.StopProcessing; 16 | 17 | // Act/Assert 18 | ExceptionAssert.Throws(() => new RewriteAction(location, processing)); 19 | } 20 | 21 | [TestMethod] 22 | public void Execute_WithNullContext_Throws() 23 | { 24 | // Arrange 25 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 26 | IRewriteContext context = null; 27 | 28 | // Act/Assert 29 | ExceptionAssert.Throws(() => action.Execute(context)); 30 | } 31 | 32 | [TestMethod] 33 | public void Execute_SetsLocation_ReturnsCorrectValue() 34 | { 35 | // Arrange 36 | string location = "/NewLocation"; 37 | RewriteProcessing processing = RewriteProcessing.RestartProcessing; 38 | RewriteAction action = new RewriteAction(location, processing); 39 | action.Conditions.Add(new MockRewriteCondition(true)); 40 | IRewriteContext context = new MockRewriteContext(); 41 | 42 | // Act 43 | RewriteProcessing result = action.Execute(context); 44 | 45 | // Assert 46 | Assert.AreEqual(processing, result); 47 | Assert.AreEqual(location, context.Location); 48 | } 49 | 50 | [TestMethod] 51 | public void IsMatch_WhenNoConditions_ReturnsTrue() 52 | { 53 | // Arrange 54 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 55 | IRewriteContext context = new MockRewriteContext(); 56 | 57 | // Act 58 | bool match = action.IsMatch(context); 59 | 60 | // Assert 61 | Assert.IsTrue(match); 62 | } 63 | 64 | [TestMethod] 65 | public void IsMatch_WhenSingleConditionMatches_ReturnsTrue() 66 | { 67 | // Arrange 68 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 69 | IRewriteContext context = new MockRewriteContext(); 70 | action.Conditions.Add(new MockRewriteCondition(true)); 71 | 72 | // Act 73 | bool match = action.IsMatch(context); 74 | 75 | // Assert 76 | Assert.IsTrue(match); 77 | } 78 | 79 | [TestMethod] 80 | public void IsMatch_WhenSingleConditionDoesNotMatch_ReturnsFalse() 81 | { 82 | // Arrange 83 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 84 | IRewriteContext context = new MockRewriteContext(); 85 | action.Conditions.Add(new MockRewriteCondition(false)); 86 | 87 | // Act 88 | bool match = action.IsMatch(context); 89 | 90 | // Assert 91 | Assert.IsFalse(match); 92 | } 93 | 94 | [TestMethod] 95 | public void IsMatch_WhenMultipleMatchingConditions_ReturnsTrue() 96 | { 97 | // Arrange 98 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 99 | IRewriteContext context = new MockRewriteContext(); 100 | action.Conditions.Add(new MockRewriteCondition(true)); 101 | action.Conditions.Add(new MockRewriteCondition(true)); 102 | 103 | // Act 104 | bool match = action.IsMatch(context); 105 | 106 | // Assert 107 | Assert.IsTrue(match); 108 | } 109 | 110 | [TestMethod] 111 | public void IsMatch_WhenMixedConditions_ReturnsFalse() 112 | { 113 | // Arrange 114 | RewriteAction action = new RewriteAction("/", RewriteProcessing.RestartProcessing); 115 | IRewriteContext context = new MockRewriteContext(); 116 | action.Conditions.Add(new MockRewriteCondition(true)); 117 | action.Conditions.Add(new MockRewriteCondition(false)); 118 | 119 | // Act 120 | bool match = action.IsMatch(context); 121 | 122 | // Assert 123 | Assert.IsFalse(match); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Utilities/IPRange.cs: -------------------------------------------------------------------------------- 1 | // UrlRewriter - A .NET URL Rewriter module 2 | // Version 2.0 3 | // 4 | // Copyright 2011 Intelligencia 5 | // Copyright 2011 Seth Yates 6 | // 7 | 8 | using System; 9 | using System.Net; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace Intelligencia.UrlRewriter.Utilities 13 | { 14 | /// 15 | /// Represents a range of IP addresses. 16 | /// 17 | public sealed class IPRange 18 | { 19 | /// 20 | /// Constructor. 21 | /// 22 | /// A range of 1 IP address. 23 | public IPRange(IPAddress address) 24 | { 25 | _minimumAddress = address; 26 | _maximumAddress = address; 27 | } 28 | 29 | /// 30 | /// Constructor. 31 | /// 32 | /// Lowest IP address. 33 | /// Highest IP address. 34 | public IPRange(IPAddress minimumAddress, IPAddress maximumAddress) 35 | { 36 | if (Compare(minimumAddress, maximumAddress) == -1) 37 | { 38 | _minimumAddress = minimumAddress; 39 | _maximumAddress = maximumAddress; 40 | } 41 | else 42 | { 43 | _minimumAddress = maximumAddress; 44 | _maximumAddress = minimumAddress; 45 | } 46 | } 47 | 48 | /// 49 | /// Parses an IP address range. 50 | /// 51 | /// 52 | /// ddd.ddd.ddd.ddd - single IP address 53 | /// ddd.ddd.ddd.* - class C range 54 | /// ddd.ddd.* - class B range 55 | /// ddd.* - class A range 56 | /// ddd.ddd.ddd.ddd - ccc.ccc.ccc.ccc - specific range 57 | /// 58 | /// The pattern 59 | /// The IPRange instance. 60 | public static IPRange Parse(string pattern) 61 | { 62 | pattern = Regex.Replace(pattern, @"([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\.\*", @"$1.0-$1.255"); 63 | pattern = Regex.Replace(pattern, @"([0-9]{1,3}\.[0-9]{1,3})\.\*", @"$1.0.0-$1.255.255"); 64 | pattern = Regex.Replace(pattern, @"([0-9]{1,3})\.\*", @"$1.0.0.0-$1.255.255.255"); 65 | 66 | string[] parts = pattern.Split('-'); 67 | return (parts.Length > 1) 68 | ? new IPRange(IPAddress.Parse(parts[0].Trim()), IPAddress.Parse(parts[1].Trim())) 69 | : new IPRange(IPAddress.Parse(pattern.Trim())); 70 | } 71 | 72 | /// 73 | /// Deteremines if the given IP address is in the range. 74 | /// 75 | /// The IP address. 76 | /// True if the address is in the range. 77 | public bool InRange(IPAddress address) 78 | { 79 | return Compare(MinimumAddress, address) <= 0 && IPRange.Compare(address, MaximumAddress) <= 0; 80 | } 81 | 82 | /// 83 | /// Minimum address (inclusive). 84 | /// 85 | public IPAddress MinimumAddress 86 | { 87 | get { return _minimumAddress; } 88 | } 89 | 90 | /// 91 | /// Maximum address (inclusive). 92 | /// 93 | public IPAddress MaximumAddress 94 | { 95 | get { return _maximumAddress; } 96 | } 97 | 98 | /// 99 | /// Compares two IPAddresses. 100 | /// Less than zero {left} is less than {right}. 101 | /// Zero {left} equals {right}. 102 | /// Greater than zero {left} is greater than {right}. 103 | /// 104 | /// 105 | /// 106 | /// 107 | public static int Compare(IPAddress left, IPAddress right) 108 | { 109 | if (left == null) 110 | { 111 | throw new ArgumentNullException("left"); 112 | } 113 | if (right == null) 114 | { 115 | throw new ArgumentNullException("right"); 116 | } 117 | 118 | byte[] leftBytes = left.GetAddressBytes(); 119 | byte[] rightBytes = right.GetAddressBytes(); 120 | if (leftBytes.Length != rightBytes.Length) 121 | { 122 | throw new ArgumentOutOfRangeException(MessageProvider.FormatString(Message.AddressesNotOfSameType)); 123 | } 124 | 125 | for (int i = 0; i < leftBytes.Length; i++) 126 | { 127 | if (leftBytes[i] == rightBytes[i]) 128 | continue; 129 | 130 | return leftBytes[i] - rightBytes[i]; 131 | } 132 | 133 | return 0; 134 | } 135 | 136 | private IPAddress _minimumAddress; 137 | private IPAddress _maximumAddress; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/Actions/RedirectActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Intelligencia.UrlRewriter.Mocks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Intelligencia.UrlRewriter.Actions.Tests 7 | { 8 | [TestClass] 9 | public class RedirectActionTest 10 | { 11 | [TestMethod] 12 | public void Constructor_WithNullLocation_Throws() 13 | { 14 | // Arrange 15 | string location = null; 16 | bool permanent = true; 17 | 18 | // Act/Assert 19 | ExceptionAssert.Throws(() => new RedirectAction(location, permanent)); 20 | } 21 | 22 | [TestMethod] 23 | public void Execute_WithNullContext_Throws() 24 | { 25 | // Arrange 26 | RedirectAction action = new RedirectAction("/", true); 27 | IRewriteContext context = null; 28 | 29 | // Act/Assert 30 | ExceptionAssert.Throws(() => action.Execute(context)); 31 | } 32 | 33 | [TestMethod] 34 | public void Execute_WhenPermanent_SetsStatusCodeAndLocation_ReturnsStopProcessing() 35 | { 36 | // Arrange 37 | string location = "/NewLocation"; 38 | bool permanent = true; 39 | RedirectAction action = new RedirectAction(location, permanent); 40 | action.Conditions.Add(new MockRewriteCondition(true)); 41 | IRewriteContext context = new MockRewriteContext(); 42 | 43 | // Act 44 | RewriteProcessing result = action.Execute(context); 45 | 46 | // Assert 47 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 48 | Assert.AreEqual(HttpStatusCode.Moved, context.StatusCode); 49 | Assert.AreEqual(location, context.Location); 50 | } 51 | 52 | [TestMethod] 53 | public void Execute_WhenTemporary_SetsStatusCodeAndLocation_ReturnsStopProcessing() 54 | { 55 | // Arrange 56 | string location = "/NewLocation"; 57 | bool permanent = false; 58 | RedirectAction action = new RedirectAction(location, permanent); 59 | action.Conditions.Add(new MockRewriteCondition(true)); 60 | IRewriteContext context = new MockRewriteContext(); 61 | 62 | // Act 63 | RewriteProcessing result = action.Execute(context); 64 | 65 | // Assert 66 | Assert.AreEqual(RewriteProcessing.StopProcessing, result); 67 | Assert.AreEqual(HttpStatusCode.Found, context.StatusCode); 68 | Assert.AreEqual(location, context.Location); 69 | } 70 | 71 | [TestMethod] 72 | public void IsMatch_WhenNoConditions_ReturnsTrue() 73 | { 74 | // Arrange 75 | RedirectAction action = new RedirectAction("/", true); 76 | IRewriteContext context = new MockRewriteContext(); 77 | 78 | // Act 79 | bool match = action.IsMatch(context); 80 | 81 | // Assert 82 | Assert.IsTrue(match); 83 | } 84 | 85 | [TestMethod] 86 | public void IsMatch_WhenSingleConditionMatches_ReturnsTrue() 87 | { 88 | // Arrange 89 | RedirectAction action = new RedirectAction("/", true); 90 | IRewriteContext context = new MockRewriteContext(); 91 | action.Conditions.Add(new MockRewriteCondition(true)); 92 | 93 | // Act 94 | bool match = action.IsMatch(context); 95 | 96 | // Assert 97 | Assert.IsTrue(match); 98 | } 99 | 100 | [TestMethod] 101 | public void IsMatch_WhenSingleConditionDoesNotMatch_ReturnsFalse() 102 | { 103 | // Arrange 104 | RedirectAction action = new RedirectAction("/", true); 105 | IRewriteContext context = new MockRewriteContext(); 106 | action.Conditions.Add(new MockRewriteCondition(false)); 107 | 108 | // Act 109 | bool match = action.IsMatch(context); 110 | 111 | // Assert 112 | Assert.IsFalse(match); 113 | } 114 | 115 | [TestMethod] 116 | public void IsMatch_WhenMultipleMatchingConditions_ReturnsTrue() 117 | { 118 | // Arrange 119 | RedirectAction action = new RedirectAction("/", true); 120 | IRewriteContext context = new MockRewriteContext(); 121 | action.Conditions.Add(new MockRewriteCondition(true)); 122 | action.Conditions.Add(new MockRewriteCondition(true)); 123 | 124 | // Act 125 | bool match = action.IsMatch(context); 126 | 127 | // Assert 128 | Assert.IsTrue(match); 129 | } 130 | 131 | [TestMethod] 132 | public void IsMatch_WhenMixedConditions_ReturnsFalse() 133 | { 134 | // Arrange 135 | RedirectAction action = new RedirectAction("/", true); 136 | IRewriteContext context = new MockRewriteContext(); 137 | action.Conditions.Add(new MockRewriteCondition(true)); 138 | action.Conditions.Add(new MockRewriteCondition(false)); 139 | 140 | // Act 141 | bool match = action.IsMatch(context); 142 | 143 | // Assert 144 | Assert.IsFalse(match); 145 | } 146 | } 147 | } 148 | --------------------------------------------------------------------------------