public void VerifyExecuteWithOneConditionalValue()
		{
			var text = "http://%1/$1";
			var pattern = new Pattern("/([a-z]+)/index.aspx", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			var target = new DefaultRuleAction(pattern, text);

			var condTest = new DefaultConditionTestValue("%{HTTP_HOST}");
			var condPattern = new Pattern(".*", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			var cond = MockCond(condPattern, condTest, null);

			Uri url = new Uri("http://www.managedfusion.com/success/index.aspx");
			var httpContext = HttpHelpers.MockHttpContext(url);
			httpContext.Request.SetServerVariables(new Dictionary<string, string> { 
				{ "HTTP_HOST", "www.managedfusion.com" } 
			});
			var rule = MockRule(new List<ICondition> { cond }, target, null);

			RuleContext context = CreateRuleContext(url, httpContext, rule);
			target.Execute(context);

			Uri result = context.SubstitutedUrl;
			Uri expected = new Uri("http://www.managedfusion.com/success");

			Assert.AreEqual(expected, result);
		}
		public void VerifyExecute()
		{
			var text = "/$1";
			var pattern = new Pattern("/([a-z]+)/index.aspx", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			var target = new DefaultRuleAction(pattern, text);

			Uri url = new Uri("http://www.somesite.com/success/index.aspx");
			var httpContext = HttpHelpers.MockHttpContext(url);
			var rule = MockRule(null, target, null);

			RuleContext context = CreateRuleContext(url, httpContext, rule);
			target.Execute(context);

			Uri result = context.SubstitutedUrl;
			Uri expected = new Uri("http://www.somesite.com/success");

			Assert.AreEqual(expected, result);
		}
		/// <summary>
		/// Refreshes the rules.
		/// </summary>
		/// <param name="reader">The reader.</param>
		public void RefreshRules(TextReader reader)
		{
			// put a lock on the refresh process so that only one refresh can happen at a time
			lock (_refreshLock)
			{
				Manager.LogEnabled = false;
				Manager.LogPath = null;

				string tempBase = PhysicalBase;
				string tempLogPath = null;
				int tempLogLevel = 0;
				int tempMaxInternalTransfers = 10;
				bool tempEngineEnabled = false;

				string line;
				IList<ICondition> conditions = new List<ICondition>(0);
				IList<IRule> rules = new List<IRule>();
				IList<IRule> outputRules = new List<IRule>();
				IList<string> unknownLines = new List<string>();
				ModuleFactory modules = new ModuleFactory();

				while (reader.Peek() >= 0)
				{
					line = reader.ReadLine().Trim();

					if (String.IsNullOrEmpty(line))
					{
						// just plain old ignore empty lines no logging or anything
						continue;
					}
					else if (line[0] == '#')
					{
						Manager.LogIf(tempLogLevel >= 4, "Comment: " + line, "Rule Processing");
					}
					else if (RewriteEngineLine.IsMatch(line))
					{
						#region RewriteEngine

						Match match = RewriteEngineLine.Match(line);
						string engineState = match.Groups["state"].Value;

						// by default the engine is turned off
						if (String.IsNullOrEmpty(engineState) || String.Equals(engineState, "off", StringComparison.OrdinalIgnoreCase))
						{
							rules.Clear();
							tempEngineEnabled = false;

							// don't bother processing any other rules if the engine is disabled
							break;
						}
						else
						{
							tempEngineEnabled = true;
						}

						Manager.LogIf(tempLogLevel >= 3, "RewriteEngine: " + (tempEngineEnabled ? "Enabled" : "Disabled"), "Rule Processing");

						#endregion
					}
					else if (RewriteOptionsLine.IsMatch(line))
					{
						#region RewriteOptions

						Match match = RewriteOptionsLine.Match(line);
						Group variables = match.Groups["var"];

						if (variables.Success)
						{
							foreach (Capture var in variables.Captures)
							{
								string[] parts = var.Value.Split(new[] { '=' }, 2);
								bool variableUnderstood = false;

								if (parts.Length == 2)
								{
									switch (parts[0])
									{
										case "inherit":
											break;

										// obsolete in 2.1 mod_rewrite
										case "MaxRedirects":
											Manager.LogIf(tempLogLevel >= 1, "MaxRedirects is obsolete", "Obsolete");

											int maxInternalTransfers;
											if (Int32.TryParse(parts[1], out maxInternalTransfers))
											{
												tempMaxInternalTransfers = maxInternalTransfers;
												variableUnderstood = true;
											}
											break;
									}
								}

								if (!variableUnderstood)
									Manager.LogIf(tempLogLevel >= 4, "Not Understood: " + var.Value, "Unknown");
							}
						}

						#endregion
					}
					else if (RewriteBaseLine.IsMatch(line))
					{
						#region RewriteBase

						Match match = RewriteBaseLine.Match(line);
						tempBase = match.Groups["base"].Value;

						Manager.LogIf(tempLogLevel >= 3, "RewriteBase: " + VirtualBase, "Rule Processing");

						#endregion
					}
					else if (RewriteModuleLine.IsMatch(line))
					{
						#region RewriteModule

						Match match = RewriteModuleLine.Match(line);
						string moduleName = match.Groups["name"].Value;
						string moduleType = match.Groups["type"].Value;
						Type module = Type.GetType(moduleType, false, true);

						if (module == null)
							module = BuildManager.GetType(moduleType, false, true);

						if (module == null)
						{
							Manager.LogIf(tempLogLevel >= 3, "RewriteModule: Error finding " + moduleType, "Rule Processing");
						}
						else
						{
							// add the module to the list
							modules.AddModule(moduleName, module);

							Manager.LogIf(tempLogLevel >= 3, "RewriteModule: " + moduleType, "Rule Processing");
						}

						#endregion
					}
					else if (RewriteLogLine.IsMatch(line))
					{
						#region RewriteLog

						Match match = RewriteLogLine.Match(line);
						tempLogPath = match.Groups["location"].Value;
						tempLogPath = NormalizeLogLocation(tempLogPath);

						Manager.LogIf(tempLogLevel >= 3, "RewriteLog: " + tempLogPath, "Rule Processing");

						#endregion
					}
					else if (RewriteLogLevelLine.IsMatch(line))
					{
						#region RewriteLogLevel

						Match match = RewriteLogLevelLine.Match(line);
						int logLevel = 1;

						if (!Int32.TryParse(match.Groups["level"].Value, out logLevel))
						{
							tempLogLevel = 0;
							Manager.LogIf(tempLogLevel >= 3, "RewriteLogLevel: " + match.Groups["level"].Value + " not understood.", "Rule Processing");
						}
						else
						{
							tempLogLevel = logLevel;
						}

						Manager.LogIf(tempLogLevel >= 3, "RewriteLogLevel: " + logLevel, "Rule Processing");

						#endregion
					}
					else if (RewriteCondLine.IsMatch(line))
					{
						#region RewriteCond

						Match match = RewriteCondLine.Match(line);

						string module1 = match.Groups["module1"].Value;
						string module2 = match.Groups["module2"].Value;

						Type moduleType1 = null;
						Type moduleType2 = null;

						// set the types of the first module
						if (modules.ContainsName(module1))
							moduleType1 = modules.GetModule(module1);

						// make sure the module is of the right type
						if (moduleType1 != null && moduleType1.GetInterface("ICondition", false) == null)
							moduleType1 = null;

						// set the types of the second module
						if (modules.ContainsName(module2))
							moduleType2 = modules.GetModule(module2);

						// make sure the module is of the right type
						if (moduleType2 != null && moduleType2.GetInterface("IConditionTestValue", false) == null)
							moduleType2 = null;

						try
						{
							RegexOptions patternOptions = Manager.RuleOptions;
							IConditionFlagProcessor flags;

							if (match.Groups["flags"] != null)
								flags = SplitConditionFlags(match.Groups["flags"].Value);
							else
								flags = new ConditionFlagProcessor();

							// check to see if the pattern should ignore the case when testing
							if (ConditionFlagsProcessor.HasNoCase(flags))
								patternOptions |= RegexOptions.IgnoreCase;

							string test = match.Groups["test"].Value;
							string pattern = match.Groups["pattern"].Value;
							IConditionTestValue testValue;
							ICondition condition;

							// create the second module
							if (moduleType2 == null)
								testValue = GetConditionTestValue(ref test);
							else
								testValue = Activator.CreateInstance(moduleType2) as IConditionTestValue;

							// create the first module
							if (moduleType1 == null)
								condition = GetCondition(pattern);
							else
								condition = Activator.CreateInstance(moduleType1) as ICondition;

							// initialize the modules
							testValue.Init(test);
							condition.Init(new Pattern(pattern, patternOptions), testValue, flags);

							// add condition to next rule that shows up
							conditions.Add(condition);
						}
						catch (Exception exc)
						{
							if (tempLogLevel >= 3)
								Manager.Log("RewriteCond: " + exc.Message, "Error");
							else
								Manager.Log("RewriteCond: " + exc, "Error");
						}
						finally
						{
							Manager.LogIf(tempLogLevel >= 3, "RewriteCond: " + match.Groups["test"].Value + " " + match.Groups["pattern"].Value + " [" + match.Groups["flags"].Value + "]", "Rule Processing");
						}

						#endregion
					}
					else if (RewriteRuleLine.IsMatch(line))
					{
						#region RewriteRule

						Match match = RewriteRuleLine.Match(line);

						string module1 = match.Groups["module1"].Value;
						string module2 = match.Groups["module2"].Value;

						Type moduleType1 = null;
						Type moduleType2 = null;

						// set the types of the first module
						if (modules.ContainsName(module1))
							moduleType1 = modules.GetModule(module1);

						// make sure the module is of the right type
						if (moduleType1 != null && moduleType1.GetInterface("IRule", false) == null)
							moduleType1 = null;

						// set the types of the second module
						if (modules.ContainsName(module2))
							moduleType2 = modules.GetModule(module2);

						// make sure the module is of the right type
						if (moduleType2 != null && moduleType2.GetInterface("IRuleAction", false) == null)
							moduleType2 = null;

						try
						{
							RegexOptions patternOptions = Manager.RuleOptions;
							IRuleFlagProcessor flags;

							if (match.Groups["flags"] != null)
								flags = SplitRuleFlags(match.Groups["flags"].Value);
							else
								flags = new RuleFlagProcessor();

							// check to see if the pattern should ignore the case when testing
							if (RuleFlagsProcessor.HasNoCase(flags))
								patternOptions |= RegexOptions.IgnoreCase;

							IRule rule = null;
							IRuleAction substitution = null;
							Pattern pattern = new Pattern(match.Groups["pattern"].Value, patternOptions);

							// create the first module
							if (moduleType1 == null)
								rule = new DefaultRule();
							else
								rule = Activator.CreateInstance(moduleType1) as IRule;

							// create the second module
							if (moduleType2 == null)
								substitution = new DefaultRuleAction();
							else
								substitution = Activator.CreateInstance(moduleType2) as IRuleAction;

							// initialize the modules
							substitution.Init(pattern, match.Groups["substitution"].Value);
							rule.Init(conditions, substitution, flags);

							// add condition to next rule that shows up
							rules.Add(rule);

							// clear conditions for next rule
							conditions.Clear();
						}
						catch (Exception exc)
						{
							if (tempLogLevel >= 3)
								Manager.Log("RewriteRule: " + exc.Message, "Error");
							else
								Manager.Log("RewriteRule: " + exc, "Error");
						}
						finally
						{
							Manager.LogIf(tempLogLevel >= 3, "RewriteRule: " + match.Groups["pattern"].Value + " " + match.Groups["substitution"].Value + " [" + match.Groups["flags"].Value + "]", "Rule Processing");
						}

						#endregion
					}
					else if (OutRewriteCondLine.IsMatch(line))
					{
						#region OutRewriteCond

						Match match = OutRewriteCondLine.Match(line);

						string module1 = match.Groups["module1"].Value;
						string module2 = match.Groups["module2"].Value;

						Type moduleType1 = null;
						Type moduleType2 = null;

						// set the types of the first module
						if (modules.ContainsName(module1))
							moduleType1 = modules.GetModule(module1);

						// make sure the module is of the right type
						if (moduleType1 != null && moduleType1.GetInterface("ICondition", false) == null)
							moduleType1 = null;

						// set the types of the second module
						if (modules.ContainsName(module2))
							moduleType2 = modules.GetModule(module2);

						// make sure the module is of the right type
						if (moduleType2 != null && moduleType2.GetInterface("IConditionTestValue", false) == null)
							moduleType2 = null;

						try
						{
							RegexOptions patternOptions = Manager.RuleOptions;
							IConditionFlagProcessor flags;

							if (match.Groups["flags"] != null)
								flags = SplitConditionFlags(match.Groups["flags"].Value);
							else
								flags = new ConditionFlagProcessor();

							// check to see if the pattern should ignore the case when testing
							if (ConditionFlagsProcessor.HasNoCase(flags))
								patternOptions |= RegexOptions.IgnoreCase;

							string test = match.Groups["test"].Value;
							string pattern = match.Groups["pattern"].Value;
							IConditionTestValue testValue;
							ICondition condition;

							// create the second module
							if (moduleType2 == null)
								testValue = GetConditionTestValue(ref test);
							else
								testValue = Activator.CreateInstance(moduleType2) as IConditionTestValue;

							// create the first module
							if (moduleType1 == null)
								condition = GetCondition(pattern);
							else
								condition = Activator.CreateInstance(moduleType1) as ICondition;

							// initialize the modules
							testValue.Init(test);
							condition.Init(new Pattern(pattern, patternOptions), testValue, flags);

							// add condition to next rule that shows up
							conditions.Add(condition);
						}
						catch (Exception exc)
						{
							if (tempLogLevel >= 3)
								Manager.Log("OutRewriteCond: " + exc.Message, "Error");
							else
								Manager.Log("OutRewriteCond: " + exc, "Error");
						}
						finally
						{
							Manager.LogIf(tempLogLevel >= 3, "OutRewriteCond: " + match.Groups["test"].Value + " " + match.Groups["pattern"].Value + " [" + match.Groups["flags"].Value + "]", "Rule Processing");
						}

						#endregion
					}
					else if (OutRewriteRuleLine.IsMatch(line))
					{
						#region OutRewriteRule

						Match match = OutRewriteRuleLine.Match(line);

						string module1 = match.Groups["module1"].Value;
						string module2 = match.Groups["module2"].Value;

						Type moduleType1 = null;
						Type moduleType2 = null;

						// set the types of the first module
						if (modules.ContainsName(module1))
							moduleType1 = modules.GetModule(module1);

						// make sure the module is of the right type
						if (moduleType1 != null && moduleType1.GetInterface("IRule", false) == null)
							moduleType1 = null;

						// set the types of the second module
						if (modules.ContainsName(module2))
							moduleType2 = modules.GetModule(module2);

						// make sure the module is of the right type
						if (moduleType2 != null && moduleType2.GetInterface("IRuleAction", false) == null)
							moduleType2 = null;

						try
						{
							RegexOptions patternOptions = Manager.RuleOptions;
							IRuleFlagProcessor flags;

							if (match.Groups["flags"] != null)
								flags = SplitRuleFlags(match.Groups["flags"].Value);
							else
								flags = new RuleFlagProcessor();

							// check to see if the pattern should ignore the case when testing
							if (RuleFlagsProcessor.HasNoCase(flags))
								patternOptions |= RegexOptions.IgnoreCase;

							IRule rule = null;
							IRuleAction substitution = null;
							Pattern pattern = new Pattern(match.Groups["pattern"].Value, patternOptions);

							// create the first module
							if (moduleType1 == null)
								rule = new DefaultRule();
							else
								rule = Activator.CreateInstance(moduleType1) as IRule;

							// create the second module
							if (moduleType2 == null)
								substitution = new DefaultOutputRuleAction();
							else
								substitution = Activator.CreateInstance(moduleType2) as IRuleAction;

							// initialize the modules
							substitution.Init(pattern, match.Groups["substitution"].Value);
							rule.Init(conditions, substitution, flags);

							// add condition to next rule that shows up
							outputRules.Add(rule);

							// clear conditions for next rule
							conditions.Clear();
						}
						catch (Exception exc)
						{
							if (tempLogLevel >= 3)
								Manager.Log("OutRewriteRule: " + exc.Message, "Error");
							else
								Manager.Log("OutRewriteRule: " + exc, "Error");
						}
						finally
						{
							Manager.LogIf(tempLogLevel >= 3, "OutRewriteRule: " + match.Groups["pattern"].Value + " " + match.Groups["substitution"].Value + " [" + match.Groups["flags"].Value + "]", "Rule Processing");
						}

						#endregion
					}
					else
					{
						unknownLines.Add(line);
					}
				}

				Manager.LogIf(tempLogLevel > 0, "Managed Fusion Rewriter Version: " + Manager.RewriterVersion, "Rule Processing");

				// clear and add new rules
				ClearRules();
				AddRules(rules);
				AddOutputRules(outputRules);

				// try to process any unknown lines
				if (unknownLines.Count > 0)
				{
					RefreshUnknownLines(ref unknownLines);

					foreach (var unknownLine in unknownLines)
						Manager.LogIf(tempLogLevel >= 4, "Not Understood: " + unknownLine, "Unknown");
				}
							

				// set the ruleset defining properties
				VirtualBase = tempBase;
				LogLocation = tempLogPath;
				LogLevel = tempLogLevel;
				EngineEnabled = tempEngineEnabled;
				Manager.LogPath = tempLogPath;
				Manager.LogEnabled = tempLogLevel > 0;
			}
		}
		/// <summary>
		/// Gets the action.
		/// </summary>
		/// <param name="actionElement">The action element.</param>
		/// <param name="ruleFlags">The rule flags.</param>
		/// <returns></returns>
		private IRuleAction GetAction(XmlNode actionElement, Pattern pattern, ref IRuleFlagProcessor ruleFlags)
		{
			ActionType type = ActionType.None;
			string url = null;
			bool appendQueryString = true; // from schema definition
			RedirectType redirectType = RedirectType.Permanent;
			uint statusCode = 0U;
			uint subStatusCode = 0U; // from schema definition
			string statusReason = null;
			string statusDescription = null;

			if (actionElement.Attributes["type"] != null)
			{
				try { type = (ActionType)Enum.Parse(typeof(ActionType), actionElement.Attributes["type"].Value, true); }
				catch (Exception exc) { Manager.Log("Action: " + exc.Message, "Error"); }
			}

			if (actionElement.Attributes["url"] != null)
				url = actionElement.Attributes["url"].Value;

			if (actionElement.Attributes["appendQueryString"] != null)
				appendQueryString = XmlConvert.ToBoolean(actionElement.Attributes["appendQueryString"].Value);

			if (actionElement.Attributes["redirectType"] != null)
			{
				try { redirectType = (RedirectType)Enum.Parse(typeof(RedirectType), actionElement.Attributes["redirectType"].Value, true); }
				catch (Exception exc) { Manager.Log("Action: " + exc.Message, "Error"); }
			}

			if (actionElement.Attributes["statusCode"] != null)
				statusCode = XmlConvert.ToUInt32(actionElement.Attributes["statusCode"].Value);

			if (actionElement.Attributes["subStatusCode"] != null)
				subStatusCode = XmlConvert.ToUInt32(actionElement.Attributes["subStatusCode"].Value);

			if (actionElement.Attributes["statusReason"] != null)
				statusReason = actionElement.Attributes["statusReason"].Value;

			if (actionElement.Attributes["statusDescription"] != null)
				statusDescription = actionElement.Attributes["statusDescription"].Value;

			if (String.IsNullOrEmpty(url))
				throw new RuleSetException("Action URL must be a non-empty value.");

			// validationType="requireTrimmedString"
			url = url.Trim();

			if (type == ActionType.Redirect)
			{
				ruleFlags.Add(new RF.RedirectFlag((int)redirectType));
			}
			else if (statusCode > 0U)
			{
				// validationType="integerRange" validationParameter="300,307,exclude"
				if (statusCode >= 300U && statusCode <= 307U)
					throw new RuleSetException("Action Status Code should not be an int between 300 - 307, use the redirectType for this range.");

				if (statusCode < 1U || statusCode > 999U)
					throw new RuleSetException("Action Status Code should be between 1 - 999.");

				if (subStatusCode < 0U || subStatusCode > 999U)
					throw new RuleSetException("Action Sub Status Code should be between 0 - 999.");

				ruleFlags.Add(new RF.ResponseStatusFlag(statusCode, subStatusCode, statusReason, statusDescription));
			}

			IRuleAction substitution = new DefaultRuleAction();
			substitution.Init(pattern, url);

			return substitution;
		}