From 52dce2f67a3a425ea78bddc4aa0c312e0142acc2 Mon Sep 17 00:00:00 2001 From: skroonenburg Date: Tue, 15 Nov 2011 00:07:43 +1100 Subject: [PATCH] Changed timestamp for client-side caching to use an MD5 hash of content. Removed .NET 3.5 version from nuspec, no longer supported. --- Nuspec/Rejuicer.nuspec | 7 ++-- .../Engine/BaseStringMinificationProvider.cs | 1 + .../Rejuicer/Rejuicer/Engine/HashUtilities.cs | 34 +++++++++++++++++++ .../Rejuicer/Rejuicer/Engine/OutputContent.cs | 1 + .../Rejuicer/Engine/RejuicerEngine.cs | 2 +- .../HtmlHelpers/IncludesCacheModel.cs | 4 +-- .../Rejuicer/Rejuicer/HtmlHelpers/Rejuicer.cs | 9 +++-- .../Rejuicer/Model/PhysicalFileSource.cs | 5 ++- .../Model/RejuicerConfigurationSource.cs | 10 +++--- .../Rejuicer/Properties/AssemblyInfo.cs | 4 +-- Source/Rejuicer/Rejuicer/Rejuicer.csproj | 1 + 11 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 Source/Rejuicer/Rejuicer/Engine/HashUtilities.cs diff --git a/Nuspec/Rejuicer.nuspec b/Nuspec/Rejuicer.nuspec index 0f8292e..c267055 100644 --- a/Nuspec/Rejuicer.nuspec +++ b/Nuspec/Rejuicer.nuspec @@ -2,9 +2,9 @@ Rejuicer - 1.2.6 + 1.2.7 Sam Kroonenburg - Minify your CSS/JS. Simple fluent configuration. Wildcard file matching. Works on Azure. Easy script debugging. + Minify your CSS/JS. Simple fluent configuration. Wildcard file matching. Browser caching. Works on Azure. Easy script debugging. en-US http://rejuice.me http://rejuice.me/wp-content/uploads/2011/05/rejuicer_logo.png @@ -16,8 +16,7 @@ - - + diff --git a/Source/Rejuicer/Rejuicer/Engine/BaseStringMinificationProvider.cs b/Source/Rejuicer/Rejuicer/Engine/BaseStringMinificationProvider.cs index aecf48e..570056e 100644 --- a/Source/Rejuicer/Rejuicer/Engine/BaseStringMinificationProvider.cs +++ b/Source/Rejuicer/Rejuicer/Engine/BaseStringMinificationProvider.cs @@ -39,6 +39,7 @@ public byte[] Combine(IEnumerable data) foreach (var value in data) { sb.Append(value.ReadString()); + sb.AppendLine(); } return sb.ToString().AsBytes(); diff --git a/Source/Rejuicer/Rejuicer/Engine/HashUtilities.cs b/Source/Rejuicer/Rejuicer/Engine/HashUtilities.cs new file mode 100644 index 0000000..3f811e4 --- /dev/null +++ b/Source/Rejuicer/Rejuicer/Engine/HashUtilities.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace Rejuicer.Engine +{ + public static class HashUtilities + { + public static byte[] HashArray(this byte[] bytes) + { + using (MD5 md5 = MD5.Create()) + { + return md5.ComputeHash(bytes); + } + } + + public static string HashStringValue(this byte[] bytes, bool uppercase = false) + { + return bytes.HashArray().ToDecimalString(uppercase); + } + + public static string ToDecimalString(this byte[] bytes, bool upperCase) + { + StringBuilder result = new StringBuilder(bytes.Length * 2); + + for (int i = 0; i < bytes.Length; i++) + result.Append(bytes[i].ToString(upperCase ? "D2" : "d2")); + + return result.ToString(); + } + } +} diff --git a/Source/Rejuicer/Rejuicer/Engine/OutputContent.cs b/Source/Rejuicer/Rejuicer/Engine/OutputContent.cs index 18485f3..d389448 100644 --- a/Source/Rejuicer/Rejuicer/Engine/OutputContent.cs +++ b/Source/Rejuicer/Rejuicer/Engine/OutputContent.cs @@ -15,6 +15,7 @@ public OutputContent() } public byte[] Content { get; set; } + public byte[] ContentHash { get; set; } public string ContentType { get; set; } public bool AllowClientCaching { get; set; } public DateTime LastModifiedDate { get; set; } diff --git a/Source/Rejuicer/Rejuicer/Engine/RejuicerEngine.cs b/Source/Rejuicer/Rejuicer/Engine/RejuicerEngine.cs index a76b939..bcd2bca 100644 --- a/Source/Rejuicer/Rejuicer/Engine/RejuicerEngine.cs +++ b/Source/Rejuicer/Rejuicer/Engine/RejuicerEngine.cs @@ -131,7 +131,7 @@ internal static RejuicerConfigurationSource GetConfigFor(string requestedFilenam // There are placeholders in the configuration, so iterate over each and look for a match // remove any placeholders from the incoming requested filenames var matchUrl = requestedFilename.Replace(RejuicerConfigurationSource.FilenameUniquePlaceholder, - RejuicerConfigurationSource.GetTimeStampString(DateTime.Now)); + "1234"); foreach (var pair in _configurations) { diff --git a/Source/Rejuicer/Rejuicer/HtmlHelpers/IncludesCacheModel.cs b/Source/Rejuicer/Rejuicer/HtmlHelpers/IncludesCacheModel.cs index f4ab6f1..44f5b91 100644 --- a/Source/Rejuicer/Rejuicer/HtmlHelpers/IncludesCacheModel.cs +++ b/Source/Rejuicer/Rejuicer/HtmlHelpers/IncludesCacheModel.cs @@ -10,11 +10,11 @@ namespace Rejuicer.HtmlHelpers public class IncludesCacheModel { public IHtmlString IncludesHtml { get; set; } - public DateTime Timestamp { get; set; } + public string HashValue { get; set; } public IHtmlString RenderHtml() { - return new HtmlString(IncludesHtml.ToString().Replace(RejuicerConfigurationSource.FilenameUniquePlaceholder, RejuicerConfigurationSource.GetTimeStampString(Timestamp))); + return new HtmlString(IncludesHtml.ToString().Replace(RejuicerConfigurationSource.FilenameUniquePlaceholder, HashValue)); } } } diff --git a/Source/Rejuicer/Rejuicer/HtmlHelpers/Rejuicer.cs b/Source/Rejuicer/Rejuicer/HtmlHelpers/Rejuicer.cs index b72d56b..9315b3e 100644 --- a/Source/Rejuicer/Rejuicer/HtmlHelpers/Rejuicer.cs +++ b/Source/Rejuicer/Rejuicer/HtmlHelpers/Rejuicer.cs @@ -55,7 +55,7 @@ public static IHtmlString IncludeRejuicedJsFor(this HtmlHelper instance, string return script.ToString(TagRenderMode.Normal); }))); - var cachedIncludes = new IncludesCacheModel { IncludesHtml = scripts, Timestamp = config.GetLastModifiedDate(cacheProvider) }; + var cachedIncludes = new IncludesCacheModel { IncludesHtml = scripts, HashValue = config.GetHashValue(cacheProvider) }; SetCachedIncludesFor(filename, cachedIncludes, dependencies); @@ -72,6 +72,11 @@ public static IHtmlString IncludeRejuicedCssFor(this HtmlHelper instance, string var toInclude = GetIncludesFor(filename); var config = RejuicerEngine.GetConfigFor(filename); + if (config == null) + { + return new HtmlString(""); + } + var dependencies = config.GetDependencies(); var links = new HtmlString(string.Join("\n", toInclude.Select(f => @@ -85,7 +90,7 @@ public static IHtmlString IncludeRejuicedCssFor(this HtmlHelper instance, string return link.ToString(TagRenderMode.SelfClosing); }))); - var cachedIncludes = new IncludesCacheModel { IncludesHtml = links, Timestamp = config.GetLastModifiedDate(cacheProvider) }; + var cachedIncludes = new IncludesCacheModel { IncludesHtml = links, HashValue = config.GetHashValue(cacheProvider) }; SetCachedIncludesFor(filename, cachedIncludes, dependencies); diff --git a/Source/Rejuicer/Rejuicer/Model/PhysicalFileSource.cs b/Source/Rejuicer/Rejuicer/Model/PhysicalFileSource.cs index c113006..225ed32 100644 --- a/Source/Rejuicer/Rejuicer/Model/PhysicalFileSource.cs +++ b/Source/Rejuicer/Rejuicer/Model/PhysicalFileSource.cs @@ -96,6 +96,7 @@ public OutputContent GetContent(ICacheProvider cacheProvider, Mode mode) var combinedValue = new OutputContent { Content = rejuicedValue, + ContentHash = rejuicedValue.HashArray(), AllowClientCaching = false, ContentType = ResourceType == ResourceType.Css ? "text/css" : "text/javascript", @@ -111,9 +112,11 @@ public OutputContent GetContent(ICacheProvider cacheProvider, Mode mode) Log.WriteLine("Minifying Content For '{0}'", VirtualPath); // Minified value + var minifiedContent = minificationProvider.Minify(rejuicedValue); var minifiedValue = new OutputContent { - Content = minificationProvider.Minify(rejuicedValue), + Content = minifiedContent, + ContentHash = minifiedContent.HashArray(), AllowClientCaching = false, ContentType = ResourceType == ResourceType.Css diff --git a/Source/Rejuicer/Rejuicer/Model/RejuicerConfigurationSource.cs b/Source/Rejuicer/Rejuicer/Model/RejuicerConfigurationSource.cs index c3b8d58..7b2d593 100644 --- a/Source/Rejuicer/Rejuicer/Model/RejuicerConfigurationSource.cs +++ b/Source/Rejuicer/Rejuicer/Model/RejuicerConfigurationSource.cs @@ -101,14 +101,14 @@ public DateTime GetLastModifiedDate(ICacheProvider cacheProvider) return GetContent(cacheProvider).LastModifiedDate; } - public static string GetTimeStampString(DateTime dateTime) + public string GetHashValue(ICacheProvider cacheProvider) { - return (dateTime - DateTime.MinValue).Ticks.ToString(); + return GetContent(cacheProvider).ContentHash.HashStringValue(); } public string GetTimestampedUrl(ICacheProvider cacheProvider) { - return RequestFor.Replace(FilenameUniquePlaceholder, GetTimeStampString(GetLastModifiedDate(cacheProvider))); + return RequestFor.Replace(FilenameUniquePlaceholder, GetHashValue(cacheProvider)); } public string GetNonTimestampedUrl(ICacheProvider cacheProvider) @@ -138,9 +138,11 @@ public OutputContent GetContent(ICacheProvider cacheProvider) Log.WriteLine("Combining content for '{0}'", RequestFor); // Combine all of the files into one string + var minifiedContent = minificationProvider.Combine(content.Select(x => x.Content)); cachedValue = new OutputContent { - Content = minificationProvider.Combine(content.Select(x => x.Content)), + Content = minifiedContent, + ContentHash = minifiedContent.HashArray(), AllowClientCaching = ContainsPlaceHolder, ContentType = minificationProvider.GetContentType(RequestFor), LastModifiedDate = content.Max(x => x.LastModifiedDate) diff --git a/Source/Rejuicer/Rejuicer/Properties/AssemblyInfo.cs b/Source/Rejuicer/Rejuicer/Properties/AssemblyInfo.cs index 2f6bbe7..e12546c 100644 --- a/Source/Rejuicer/Rejuicer/Properties/AssemblyInfo.cs +++ b/Source/Rejuicer/Rejuicer/Properties/AssemblyInfo.cs @@ -32,7 +32,7 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.6.0")] -[assembly: AssemblyFileVersion("1.2.6.0")] +[assembly: AssemblyVersion("1.2.7.0")] +[assembly: AssemblyFileVersion("1.2.7.0")] [assembly: InternalsVisibleTo("Rejuicer-test")] [assembly: InternalsVisibleTo("RejuicerConfiguration-test")] \ No newline at end of file diff --git a/Source/Rejuicer/Rejuicer/Rejuicer.csproj b/Source/Rejuicer/Rejuicer/Rejuicer.csproj index f9c1286..f99d0fe 100644 --- a/Source/Rejuicer/Rejuicer/Rejuicer.csproj +++ b/Source/Rejuicer/Rejuicer/Rejuicer.csproj @@ -67,6 +67,7 @@ +