Skip to content
This repository has been archived by the owner on Jun 16, 2022. It is now read-only.

Commit

Permalink
[Upgrade] All code has been rewritten to use the new services and mod…
Browse files Browse the repository at this point in the history
…els of umbraco v6.1

[BugFix] URL changes are tracked again (#9)
[BugFix] 410 Gone response now gets lower priority than other response codes
[Improvement] Improved performance, removed hitting the DB for every single request, added caching instead
[Dropped] Dropped support for pre-6.1 umbraco versions
  • Loading branch information
kipusoep committed Jun 27, 2014
1 parent 4129028 commit 5e90761
Show file tree
Hide file tree
Showing 29 changed files with 54,951 additions and 294 deletions.
52 changes: 7 additions & 45 deletions Helpers/LoggingHelper.cs
@@ -1,28 +1,22 @@
using System;
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting;
using System.Web;
using umbraco.BusinessLogic;
using Umbraco.Core.Logging;
using UmbracoLog = umbraco.BusinessLogic.Log;

namespace InfoCaster.Umbraco.UrlTracker.Helpers
{
public static class LoggingHelper
{
static Assembly _log4netAssembly;
static bool _log4netAssemblyInitialized = false;

public static void LogException(this Exception ex)
{
LogException(ex, -1);
}

public static void LogException(this Exception ex, int nodeId)
{
LogToLog4net(exception: ex);
UmbracoLog.Add(LogTypes.Error, nodeId, string.Concat("Exception occurred in UrlTracker: ", ex.Message));
LogManager.GetLogger(typeof(LoggingHelper)).Error(ex.Message, ex);
LogHelper.Error(typeof(LoggingHelper), ex.Message, ex);
UrlTrackerLogging.Log(ex);
}

Expand All @@ -35,42 +29,10 @@ public static void LogInformation(string message)
{
if (UrlTrackerSettings.EnableLogging)
{
LogToLog4net(message: message);
UmbracoLog.Add(LogTypes.Debug, -1, message);
LogManager.GetLogger(typeof(LoggingHelper)).Debug(message);
LogHelper.Debug(typeof(LoggingHelper), () => { return message; });
UrlTrackerLogging.Log(message);
}
}

static void LogToLog4net(string message = "", Exception exception = null)
{
if (message == null && exception == null)
throw new ArgumentNullException("message and exception");

if (!_log4netAssemblyInitialized)
{
IEnumerable<Assembly> log4netAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.StartsWith("log4net"));
if (log4netAssemblies != null && log4netAssemblies.Any())
_log4netAssembly = log4netAssemblies.OrderByDescending(x => x.ImageRuntimeVersion).First();
}
if (_log4netAssembly != null)
{
Type logManagerType = _log4netAssembly.GetType("log4net.LogManager");
MethodInfo getLoggerMethod = logManagerType.GetMethod("GetLogger", new Type[] { typeof(System.Type) });
object iLog = getLoggerMethod.Invoke(null, new[] { MethodBase.GetCurrentMethod().DeclaringType });
Type iLogType = _log4netAssembly.GetType("log4net.ILog");
if (exception != null)
{
MethodInfo errorMethod = iLogType.GetMethod("Error", new Type[] { typeof(object), typeof(Exception) });
errorMethod.Invoke(iLog, new[] { null, exception });
}
else
{
MethodInfo debugMethod = iLogType.GetMethod("Debug", new Type[] { typeof(object) });
debugMethod.Invoke(iLog, new[] { message });
}
}

_log4netAssemblyInitialized = true;
}
}
}
6 changes: 5 additions & 1 deletion Helpers/UmbracoHelper.cs
Expand Up @@ -8,8 +8,8 @@
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.DataLayer;
using umbraco.IO;
using umbraco.NodeFactory;
using Umbraco.Core.IO;

namespace InfoCaster.Umbraco.UrlTracker.Helpers
{
Expand All @@ -18,7 +18,9 @@ public static class UmbracoHelper
static readonly object _locker = new object();
static volatile string _reservedUrlsCache;
static string _reservedPathsCache;
#pragma warning disable 0618
static StartsWithContainer _reservedList = new StartsWithContainer();
#pragma warning restore

/// <summary>
/// Determines whether the specified URL is reserved or is inside a reserved path.
Expand All @@ -40,7 +42,9 @@ internal static bool IsReservedPathOrUrl(string url)
_reservedUrlsCache = GlobalSettings.ReservedUrls;

// add URLs and paths to a new list
#pragma warning disable 0618
StartsWithContainer _newReservedList = new StartsWithContainer();
#pragma warning restore
foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
{
//resolves the url to support tilde chars
Expand Down
4 changes: 2 additions & 2 deletions Models/UrlTrackerModel.cs
Expand Up @@ -53,7 +53,7 @@ public string CalculatedOldUrl
return CalculatedOldUrlWithDomain;
Uri calculatedOldUrlWithDomain = new Uri(CalculatedOldUrlWithDomain);
string pathAndQuery = Uri.UnescapeDataString(calculatedOldUrlWithDomain.PathAndQuery);
return pathAndQuery.StartsWith("/") ? pathAndQuery.Substring(1) : pathAndQuery;
return !pathAndQuery.StartsWith("/") ? string.Concat("/", pathAndQuery.Substring(1)) : pathAndQuery;
}
}
public string CalculatedOldUrlWithDomain
Expand Down Expand Up @@ -91,7 +91,7 @@ public string CalculatedRedirectUrl
)
).AbsolutePath :
string.Empty;
return calculatedRedirectUrl.StartsWith("/") && calculatedRedirectUrl != "/" ? calculatedRedirectUrl.Substring(1) : calculatedRedirectUrl;
return !calculatedRedirectUrl.StartsWith("/") ? string.Concat("/", calculatedRedirectUrl) : calculatedRedirectUrl;
}
}
public Node RedirectRootNode
Expand Down
190 changes: 133 additions & 57 deletions Modules/UrlTrackerModule.cs
Expand Up @@ -58,7 +58,7 @@ void context_EndRequest(object sender, EventArgs e)
UrlTrackerDo("EndRequest");
}

private static void UrlTrackerDo(string callingEventName, bool ignoreHttpStatusCode = false)
static void UrlTrackerDo(string callingEventName, bool ignoreHttpStatusCode = false)
{
HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
Expand Down Expand Up @@ -149,65 +149,18 @@ private static void UrlTrackerDo(string callingEventName, bool ignoreHttpStatusC
int? redirectHttpCode = null;
bool redirectPassThroughQueryString = true;

// Normal matching
string query = "SELECT * FROM icUrlTracker WHERE Is404 = 0 AND ForceRedirect = @forceRedirect AND (RedirectRootNodeId = @redirectRootNodeId OR RedirectRootNodeId IS NULL) AND (OldUrl = @url OR OldUrl = @shortestUrl) ORDER BY OldUrlQueryString DESC";
using (IRecordsReader reader = _sqlHelper.ExecuteReader(query, _sqlHelper.CreateParameter("forceRedirect", ignoreHttpStatusCode ? "1" : "0"), _sqlHelper.CreateParameter("redirectRootNodeId", rootNodeId), _sqlHelper.CreateParameter("url", urlWithoutQueryString), _sqlHelper.CreateParameter("shortestUrl", shortestUrl)))
if (!ignoreHttpStatusCode)
{
while (reader.Read())
{
LoggingHelper.LogInformation("UrlTracker HttpModule | URL match found");
if (!reader.IsNull("RedirectNodeId"))
{
int redirectNodeId = reader.GetInt("RedirectNodeId");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node id: {0}", redirectNodeId);
Node n = new Node(redirectNodeId);
if (n != null && n.Name != null && n.Id > 0)
{
string tempUrl = UmbracoHelper.GetUrl(redirectNodeId);
redirectUrl = tempUrl.StartsWith("http") ? tempUrl : string.Format("{0}://{1}{2}{3}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Host, HttpContext.Current.Request.Url.Port != 80 ? string.Concat(":", HttpContext.Current.Request.Url.Port) : string.Empty, tempUrl);
if (redirectUrl.StartsWith("http"))
{
Uri redirectUri = new Uri(redirectUrl);
string pathAndQuery = Uri.UnescapeDataString(redirectUri.PathAndQuery);
redirectUrl = pathAndQuery.StartsWith("/") && pathAndQuery != "/" ? pathAndQuery.Substring(1) : pathAndQuery;
}
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}
else
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node is invalid; node is null, name is null or id <= 0");
continue;
}
}
else if (!reader.IsNull("RedirectUrl"))
{
redirectUrl = reader.GetString("RedirectUrl");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}

redirectPassThroughQueryString = reader.GetBoolean("RedirectPassThroughQueryString");
LoggingHelper.LogInformation("UrlTracker HttpModule | PassThroughQueryString is {0}", redirectPassThroughQueryString ? "enabled" : "disabled");

NameValueCollection oldUrlQueryString = null;
if (!reader.IsNull("OldUrlQueryString"))
{
oldUrlQueryString = HttpUtility.ParseQueryString(reader.GetString("OldUrlQueryString"));
LoggingHelper.LogInformation("UrlTracker HttpModule | Old URL query string set to: {0}", oldUrlQueryString.ToQueryString());
}

if ((urlHasQueryString || oldUrlQueryString != null) && (oldUrlQueryString != null && !request.QueryString.CollectionEquals(oldUrlQueryString)))
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Aborting; query strings don't match");
continue;
}

redirectHttpCode = reader.GetInt("RedirectHttpCode");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect http code set to: {0}", redirectHttpCode);

break;
}
// Normal matching (database)
LoadUrlTrackerMatchesFromDatabase(request, urlWithoutQueryString, urlHasQueryString, shortestUrl, rootNodeId, ref redirectUrl, ref redirectHttpCode, ref redirectPassThroughQueryString);
}
else
{
// Forced matching (cache)
LoadUrlTrackerMatchesFromCache(request, urlWithoutQueryString, urlHasQueryString, shortestUrl, rootNodeId, ref redirectUrl, ref redirectHttpCode, ref redirectPassThroughQueryString);
}

string query;
if (!redirectHttpCode.HasValue)
{
// Regex matching
Expand Down Expand Up @@ -341,5 +294,128 @@ private static void UrlTrackerDo(string callingEventName, bool ignoreHttpStatusC

LoggingHelper.LogInformation("UrlTracker HttpModule | {0} end", callingEventName);
}

static void LoadUrlTrackerMatchesFromDatabase(HttpRequest request, string urlWithoutQueryString, bool urlHasQueryString, string shortestUrl, int rootNodeId, ref string redirectUrl, ref int? redirectHttpCode, ref bool redirectPassThroughQueryString)
{
string query = "SELECT * FROM icUrlTracker WHERE Is404 = 0 AND ForceRedirect = 0 AND (RedirectRootNodeId = @redirectRootNodeId OR RedirectRootNodeId IS NULL) AND (OldUrl = @url OR OldUrl = @shortestUrl) ORDER BY CASE WHEN RedirectHttpCode = 410 THEN 2 ELSE 1 END, OldUrlQueryString DESC";
using (IRecordsReader reader = _sqlHelper.ExecuteReader(query, _sqlHelper.CreateParameter("redirectRootNodeId", rootNodeId), _sqlHelper.CreateParameter("url", urlWithoutQueryString), _sqlHelper.CreateParameter("shortestUrl", shortestUrl)))
{
while (reader.Read())
{
LoggingHelper.LogInformation("UrlTracker HttpModule | URL match found");
if (!reader.IsNull("RedirectNodeId"))
{
int redirectNodeId = reader.GetInt("RedirectNodeId");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node id: {0}", redirectNodeId);
Node n = new Node(redirectNodeId);
if (n != null && n.Name != null && n.Id > 0)
{
string tempUrl = UmbracoHelper.GetUrl(redirectNodeId);
redirectUrl = tempUrl.StartsWith("http") ? tempUrl : string.Format("{0}://{1}{2}{3}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Host, HttpContext.Current.Request.Url.Port != 80 ? string.Concat(":", HttpContext.Current.Request.Url.Port) : string.Empty, tempUrl);
if (redirectUrl.StartsWith("http"))
{
Uri redirectUri = new Uri(redirectUrl);
string pathAndQuery = Uri.UnescapeDataString(redirectUri.PathAndQuery);
redirectUrl = pathAndQuery.StartsWith("/") && pathAndQuery != "/" ? pathAndQuery.Substring(1) : pathAndQuery;
}
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}
else
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node is invalid; node is null, name is null or id <= 0");
continue;
}
}
else if (!reader.IsNull("RedirectUrl"))
{
redirectUrl = reader.GetString("RedirectUrl");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}

redirectPassThroughQueryString = reader.GetBoolean("RedirectPassThroughQueryString");
LoggingHelper.LogInformation("UrlTracker HttpModule | PassThroughQueryString is {0}", redirectPassThroughQueryString ? "enabled" : "disabled");

NameValueCollection oldUrlQueryString = null;
if (!reader.IsNull("OldUrlQueryString"))
{
oldUrlQueryString = HttpUtility.ParseQueryString(reader.GetString("OldUrlQueryString"));
LoggingHelper.LogInformation("UrlTracker HttpModule | Old URL query string set to: {0}", oldUrlQueryString.ToQueryString());
}

if ((urlHasQueryString || oldUrlQueryString != null) && (oldUrlQueryString != null && !request.QueryString.CollectionEquals(oldUrlQueryString)))
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Aborting; query strings don't match");
continue;
}

redirectHttpCode = reader.GetInt("RedirectHttpCode");
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect http code set to: {0}", redirectHttpCode);

break;
}
}
}


static void LoadUrlTrackerMatchesFromCache(HttpRequest request, string urlWithoutQueryString, bool urlHasQueryString, string shortestUrl, int rootNodeId, ref string redirectUrl, ref int? redirectHttpCode, ref bool redirectPassThroughQueryString)
{
List<UrlTrackerModel> forcedRedirects = UrlTrackerRepository.GetForcedRedirects();
if (forcedRedirects == null || !forcedRedirects.Any())
return;

foreach (UrlTrackerModel forcedRedirect in forcedRedirects.Where(x => !x.Is404 && x.RedirectRootNodeId == rootNodeId && (x.OldUrl == urlWithoutQueryString || x.OldUrl == shortestUrl)).OrderBy(x => x.RedirectHttpCode == 410 ? 2 : 1).ThenByDescending(x => x.OldUrlQueryString))
{
LoggingHelper.LogInformation("UrlTracker HttpModule | URL match found");
if (forcedRedirect.RedirectNodeId.HasValue)
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node id: {0}", forcedRedirect.RedirectNodeId.Value);

Node n = new Node(forcedRedirect.RedirectNodeId.Value);
if (n != null && n.Name != null && n.Id > 0)
{
string tempUrl = UmbracoHelper.GetUrl(forcedRedirect.RedirectNodeId.Value);
redirectUrl = tempUrl.StartsWith("http") ? tempUrl : string.Format("{0}://{1}{2}{3}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Host, HttpContext.Current.Request.Url.Port != 80 ? string.Concat(":", HttpContext.Current.Request.Url.Port) : string.Empty, tempUrl);
if (redirectUrl.StartsWith("http"))
{
Uri redirectUri = new Uri(redirectUrl);
string pathAndQuery = Uri.UnescapeDataString(redirectUri.PathAndQuery);
redirectUrl = pathAndQuery.StartsWith("/") && pathAndQuery != "/" ? pathAndQuery.Substring(1) : pathAndQuery;
}
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}
else
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect node is invalid; node is null, name is null or id <= 0");
continue;
}
}
else if (!string.IsNullOrEmpty(forcedRedirect.RedirectUrl))
{
redirectUrl = forcedRedirect.RedirectUrl;
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect url set to: {0}", redirectUrl);
}

redirectPassThroughQueryString = forcedRedirect.RedirectPassThroughQueryString;
LoggingHelper.LogInformation("UrlTracker HttpModule | PassThroughQueryString is {0}", redirectPassThroughQueryString ? "enabled" : "disabled");

NameValueCollection oldUrlQueryString = null;
if (!string.IsNullOrEmpty(forcedRedirect.OldUrlQueryString))
{
oldUrlQueryString = HttpUtility.ParseQueryString(forcedRedirect.OldUrlQueryString);
LoggingHelper.LogInformation("UrlTracker HttpModule | Old URL query string set to: {0}", oldUrlQueryString.ToQueryString());
}

if ((urlHasQueryString || oldUrlQueryString != null) && (oldUrlQueryString != null && !request.QueryString.CollectionEquals(oldUrlQueryString)))
{
LoggingHelper.LogInformation("UrlTracker HttpModule | Aborting; query strings don't match");
continue;
}

redirectHttpCode = forcedRedirect.RedirectHttpCode;
LoggingHelper.LogInformation("UrlTracker HttpModule | Redirect http code set to: {0}", redirectHttpCode);

break;
}
}
}
}

0 comments on commit 5e90761

Please sign in to comment.