/// <summary> /// Runs the rules. /// </summary> /// <param name="httpContext">The HTTP context.</param> /// <param name="url">The URL.</param> /// <returns> /// Returns a rewritten <see cref="System.Uri"/>, or a value of <see langword="null"/> if no rewriting was done to <paramref name="url"/>. /// </returns> public Uri RunRules(HttpContextBase httpContext, Uri url) { var context = new RuleSetContext(this, url, httpContext); var currentUrl = url; if (!EngineEnabled) { Manager.LogIf(!EngineEnabled && LogLevel >= 9, "Rewrite Engine Is DISABLED", "Rewrite"); } if (_rules.Count > 0 && EngineEnabled) { Manager.LogIf(LogLevel >= 1, "**********************************************************************************"); Manager.LogIf(LogLevel >= 1, "Input: " + currentUrl, "Rewrite"); // check if max number of internal transfers have been exceeded if (InternalTransferCount(httpContext) > MaxInternalTransfers) { string message = "Exceeded the max number of internal transfers."; Manager.LogIf(LogLevel >= 1, message, "Error"); throw new HttpException(500, message); } var temporyFlags = (IRuleFlagProcessor)null; var skipNextChain = false; var initialUrl = currentUrl; if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/") { currentUrl = RemoveBase(VirtualBase, currentUrl); } // process rules according to their settings for (int i = 0; i < _rules.Count; i++) { var ruleContext = new RuleContext(i, context, currentUrl, _rules[i]); temporyFlags = _rules[i].Flags; // continue if this rule shouldn't be processed because it doesn't allow internal transfer requests if (RuleFlagsProcessor.HasNotForInternalSubRequests(temporyFlags) && IsInternalTransfer(httpContext)) { continue; } bool containsChain = RuleFlagsProcessor.HasChain(_rules[i].Flags); bool previousContainsChain = RuleFlagsProcessor.HasChain(_rules[Math.Max(0, i - 1)].Flags); // if the previous rule doesn't contain a chain flag then set the initial URL // this will be used to reset a chain if one of the chain rules fail if (!previousContainsChain) { initialUrl = currentUrl; } // skip if the current rule or the last rule has a chain flag // and if the skip next chain is set if (skipNextChain && (previousContainsChain || containsChain)) { continue; } else { skipNextChain = false; } if (_rules[i].TryExecute(ruleContext)) { var flagResponse = temporyFlags.Apply(ruleContext); currentUrl = ruleContext.SubstitutedUrl; i = ruleContext.RuleIndex ?? -1; bool breakLoop = false; // apply the flags to the rules, and only do special processing // for the flag responses listed in the switch statement below switch (flagResponse) { case RuleFlagProcessorResponse.ExitRuleSet: return(null); case RuleFlagProcessorResponse.LastRule: breakLoop = true; break; } // break the loop because we have reached the last rule as indicated by a flag if (breakLoop) { break; } } else if (containsChain) { skipNextChain = true; // reset the current URL back to the initial URL from the start of the chain currentUrl = initialUrl; } else if (previousContainsChain) { // reset the current URL back to the initial URL from the start of the chain currentUrl = initialUrl; } } // if the scheme, host, and ports do not match on the request vs the rewrite a redirect needs to be performed instead of a rewrite if (Uri.Compare(currentUrl, context.RequestedUrl, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) != 0) { Manager.LogIf(LogLevel >= 1, "Output: 302 Redirect to " + currentUrl, "Rewrite"); Manager.Redirect(httpContext, "found", currentUrl); } if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/") { currentUrl = AddBase(VirtualBase, currentUrl); } Manager.LogIf(LogLevel >= 1, "Output: " + currentUrl, "Rewrite"); Manager.LogIf(LogLevel >= 1, "**********************************************************************************"); Manager.TryToAddXRewriteUrlHeader(httpContext); Manager.TryToAddVanityHeader(httpContext); } // if the http request url matches for both the request and the rewrite no work was done so the url should be null if (Uri.Compare(currentUrl, url, UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0) { currentUrl = null; } return(currentUrl); }
/// <summary> /// /// </summary> /// <param name="httpContext"></param> /// <param name="content"></param> /// <returns></returns> public byte[] RunOutputRules(HttpContextBase httpContext, byte[] content) { var context = new RuleSetContext(this, content, httpContext); byte[] currentContent = content; if (!EngineEnabled) { Manager.LogIf(!EngineEnabled && LogLevel >= 9, "Rewrite Engine Is DISABLED", "OutRewrite"); } if (_outputRules.Count > 0 && EngineEnabled) { Manager.LogIf(LogLevel >= 1, "**********************************************************************************"); //Manager.LogIf(LogLevel >= 9, "Input: " + currentContent, "OutRewrite"); var temporyFlags = (IRuleFlagProcessor)null; bool skipNextChain = false; byte[] initialContent = currentContent; // process rules according to their settings for (int i = 0; i < _outputRules.Count; i++) { var ruleContext = new RuleContext(i, context, currentContent, _outputRules[i]); temporyFlags = _outputRules[i].Flags; bool containsChain = RuleFlagsProcessor.HasChain(_outputRules[i].Flags); bool previousContainsChain = RuleFlagsProcessor.HasChain(_outputRules[Math.Max(0, i - 1)].Flags); // if the previous rule doesn't contain a chain flag then set the initial URL // this will be used to reset a chain if one of the chain rules fail if (!previousContainsChain) { initialContent = currentContent; } // skip if the current rule or the last rule has a chain flag // and if the skip next chain is set if (skipNextChain && (previousContainsChain || containsChain)) { continue; } else { skipNextChain = false; } if (_outputRules[i].TryExecute(ruleContext)) { var flagResponse = temporyFlags.Apply(ruleContext); currentContent = ruleContext.SubstitutedContent; i = ruleContext.RuleIndex ?? -1; bool breakLoop = false; // apply the flags to the rules, and only do special processing // for the flag responses listed in the switch statement below switch (flagResponse) { case RuleFlagProcessorResponse.ExitRuleSet: return(null); case RuleFlagProcessorResponse.LastRule: breakLoop = true; break; } // break the loop because we have reached the last rule as indicated by a flag if (breakLoop) { break; } } else if (containsChain) { skipNextChain = true; // reset the current URL back to the initial URL from the start of the chain currentContent = initialContent; } else if (previousContainsChain) { // reset the current URL back to the initial URL from the start of the chain currentContent = initialContent; } } //Manager.LogIf(LogLevel >= 9, "Output: " + currentContent, "OutRewrite"); Manager.LogIf(LogLevel >= 1, "**********************************************************************************"); } return(currentContent); }
/// <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; } }