Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Use CRC32 instead of MD5 for hashing the file content. #1918

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private CompilerCacheEntry GetOrAdd(RelativeFileInfo relativeFileInfo,

// Timestamp doesn't match but it might be because of deployment, compare the hash.
if (cacheEntry.IsPreCompiled &&
string.Equals(cacheEntry.Hash, RazorFileHash.GetHash(fileInfo), StringComparison.Ordinal))
cacheEntry.Hash == RazorFileHash.GetHash(fileInfo, cacheEntry.HashAlgorithmVersion))
{
// Cache hit, but we need to update the entry.
// Assigning to LastModified is an atomic operation and will result in a safe race if it is
Expand Down
26 changes: 15 additions & 11 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type compiledT
Length = info.Length;
LastModified = info.LastModified;
Hash = info.Hash;
HashAlgorithmVersion = info.HashAlgorithmVersion;
IsPreCompiled = true;
}

/// <summary>
Expand All @@ -40,17 +42,17 @@ public CompilerCacheEntry([NotNull] RelativeFileInfo info, [NotNull] Type compil
/// <summary>
/// Gets the <see cref="Type"/> produced as a result of compilation.
/// </summary>
public Type CompiledType { get; private set; }
public Type CompiledType { get; }

/// <summary>
/// Gets the path of the compiled file relative to the root of the application.
/// </summary>
public string RelativePath { get; private set; }
public string RelativePath { get; }

/// <summary>
/// Gets the size of file (in bytes) on disk.
/// </summary>
public long Length { get; private set; }
public long Length { get; }

/// <summary>
/// Gets or sets the last modified <see cref="DateTimeOffset"/> for the file at the time of compilation.
Expand All @@ -60,18 +62,20 @@ public CompilerCacheEntry([NotNull] RelativeFileInfo info, [NotNull] Type compil
/// <summary>
/// Gets the file hash, should only be available for pre compiled files.
/// </summary>
public string Hash { get; private set; }
public long Hash { get; }

/// <summary>
/// Gets the version of the hash algorithm used to generate <see cref="Hash"/>.
/// </summary>
/// <remarks>
/// This value is only initialized for precompiled views.
/// </remarks>
public int HashAlgorithmVersion { get; }

/// <summary>
/// Gets a flag that indicates if the file is precompiled.
/// </summary>
public bool IsPreCompiled
{
get
{
return Hash != null;
}
}
public bool IsPreCompiled { get; }

/// <summary>
/// Gets or sets the <see cref="CompilerCacheEntry"/> for the nearest ViewStart that the compiled type
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 91 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Razor/Crc32.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;

namespace Microsoft.AspNet.Mvc.Razor
{
internal static class Crc32
{
private static readonly int BufferSize = 4 * 1024;

private static readonly uint[] _crcTable = new uint[256]
{
0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u,
0x706af48fu, 0xe963a535u, 0x9e6495a3u, 0x0edb8832u, 0x79dcb8a4u,
0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u,
0x90bf1d91u, 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu,
0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u, 0x136c9856u,
0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u,
0xfa0f3d63u, 0x8d080df5u, 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u,
0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,
0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u,
0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u, 0x26d930acu, 0x51de003au,
0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u,
0xb8bda50fu, 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u,
0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du, 0x76dc4190u,
0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu,
0x9fbfe4a5u, 0xe8b8d433u, 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu,
0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,
0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu,
0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u, 0x65b0d9c6u, 0x12b7e950u,
0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u,
0xfbd44c65u, 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u,
0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu, 0x4369e96au,
0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u,
0xaa0a4c5fu, 0xdd0d7cc9u, 0x5005713cu, 0x270241aau, 0xbe0b1010u,
0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,
0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u,
0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu, 0xedb88320u, 0x9abfb3b6u,
0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u,
0x73dc1683u, 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u,
0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u, 0xf00f9344u,
0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu,
0x196c3671u, 0x6e6b06e7u, 0xfed41b76u, 0x89d32be0u, 0x10da7a5au,
0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,
0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u,
0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu, 0xd80d2bdau, 0xaf0a1b4cu,
0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu,
0x4669be79u, 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u,
0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu, 0xc5ba3bbeu,
0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u,
0x2cd99e8bu, 0x5bdeae1du, 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu,
0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,
0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu,
0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u, 0x86d3d2d4u, 0xf1d4e242u,
0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u,
0x18b74777u, 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu,
0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u, 0xa00ae278u,
0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u,
0x4969474du, 0x3e6e77dbu, 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u,
0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,
0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u,
0xcdd70693u, 0x54de5729u, 0x23d967bfu, 0xb3667a2eu, 0xc4614ab8u,
0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu,
0x2d02ef8du
};

public static uint Calculate(Stream stream)
{
var buffer = new byte[BufferSize];
var crc32 = 0xffffffffU;
while (true)
{
var read = stream.Read(buffer, 0, BufferSize);
if (read == 0)
{
break;
}

var offset = 0;
while (offset < read)
{
crc32 = _crcTable[(crc32 ^ buffer[offset++]) & 0xFF] ^ (crc32 >> 8);
}
}

return crc32 ^ 0xffffffffU;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class RazorFileInfo
/// <summary>
/// A hash of the file content.
/// </summary>
public string Hash { get; set; }
public long Hash { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably keep as string


/// <summary>
/// The version of hash algorithm used to generate <see cref="Hash"/>.
/// </summary>
public int HashAlgorithmVersion { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ protected virtual string GenerateFile([NotNull] RazorFileInfo fileInfo)
fileInfo.Length,
fileInfo.RelativePath,
fileInfo.FullTypeName,
fileInfo.Hash);
fileInfo.Hash,
fileInfo.HashAlgorithmVersion);
}

protected virtual string Top
Expand Down Expand Up @@ -103,7 +104,8 @@ protected virtual string FileFormat
" + nameof(RazorFileInfo.Length) + @" = {1:D},
" + nameof(RazorFileInfo.RelativePath) + @" = @""{2}"",
" + nameof(RazorFileInfo.FullTypeName) + @" = @""{3}"",
" + nameof(RazorFileInfo.Hash) + @" = @""{4}"",
" + nameof(RazorFileInfo.Hash) + @" = {4},
" + nameof(RazorFileInfo.HashAlgorithmVersion) + @" = {5},
}};
fileInfos.Add(info);
";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ protected virtual PrecompilationCacheEntry GetCacheEntry([NotNull] RelativeFileI

if (fullTypeName != null)
{
var hash = RazorFileHash.GetHash(fileInfo.FileInfo);
var hash = RazorFileHash.GetHash(fileInfo.FileInfo, RazorFileHash.HashAlgorthmVersion1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: HashAlgorithmVersion1

var razorFileInfo = new RazorFileInfo
{
RelativePath = fileInfo.RelativePath,
Expand Down
27 changes: 14 additions & 13 deletions src/Microsoft.AspNet.Mvc.Razor/Razor/RazorFileHash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,36 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Security.Cryptography;
using Microsoft.AspNet.FileProviders;

namespace Microsoft.AspNet.Mvc.Razor
{
public static class RazorFileHash
{
public static string GetHash([NotNull] IFileInfo file)
/// <summary>
/// Version 1 of the hash algorithm used for generating hashes of Razor files.
/// </summary>
public static readonly int HashAlgorthmVersion1 = 1;

public static long GetHash([NotNull] IFileInfo file, int hashAlgorithmVersion)
{
if (hashAlgorithmVersion != HashAlgorthmVersion1)
{
throw new ArgumentException(Resources.RazorHash_UnsupportedHashAlgorithm,
nameof(hashAlgorithmVersion));
}

try
{
using (var stream = file.CreateReadStream())
{
return GetHash(stream);
return Crc32.Calculate(stream);
}
}
catch (Exception)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a catch (Exception) ? catch specific type here

{
// Don't throw if reading the file fails.
return string.Empty;
}
}

internal static string GetHash(Stream stream)
{
using (var md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(stream));
return 0;
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,7 @@
<data name="ViewMustBeContextualized" xml:space="preserve">
<value>The '{0}' method must be called before '{1}' can be invoked.</value>
</data>
<data name="RazorHash_UnsupportedHashAlgorithm" xml:space="preserve">
<value>Unsupported hash algorithm.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ public ViewCollection()
Add(new RazorFileInfo()
{
FullTypeName = typeof(PreCompile).FullName,
Hash = RazorFileHash.GetHash(GetMemoryStream(content)),
Hash = Crc32.Calculate(GetMemoryStream(content)),
HashAlgorithmVersion = 1,
LastModified = DateTime.FromFileTimeUtc(10000),
Length = length,
RelativePath = "ab",
Expand Down Expand Up @@ -127,7 +128,8 @@ public void GetOrAdd_UsesFilesFromCache_IfTimestampDiffers_ButContentAndLengthAr
var razorFileInfo = new RazorFileInfo
{
FullTypeName = typeof(PreCompile).FullName,
Hash = RazorFileHash.GetHash(GetMemoryStream(precompiledContent)),
Hash = Crc32.Calculate(GetMemoryStream(precompiledContent)),
HashAlgorithmVersion = 1,
LastModified = DateTime.FromFileTimeUtc(10000),
Length = Encoding.UTF8.GetByteCount(precompiledContent),
RelativePath = "ab",
Expand Down Expand Up @@ -169,7 +171,8 @@ public void GetOrAdd_RecompilesFile_IfContentAndLengthAreChanged(
var razorFileInfo = new RazorFileInfo
{
FullTypeName = typeof(PreCompile).FullName,
Hash = RazorFileHash.GetHash(GetMemoryStream(precompiledContent)),
Hash = Crc32.Calculate(GetMemoryStream(precompiledContent)),
HashAlgorithmVersion = 1,
LastModified = DateTime.FromFileTimeUtc(10000),
Length = Encoding.UTF8.GetByteCount(precompiledContent),
RelativePath = "ab",
Expand Down Expand Up @@ -210,7 +213,8 @@ public void GetOrAdd_UsesValueFromCache_IfViewStartHasNotChanged()
fileProvider.AddFile("_ViewStart.cshtml", viewStartFileInfo);
var viewStartRazorFileInfo = new RazorFileInfo
{
Hash = RazorFileHash.GetHash(GetMemoryStream(viewStartContent)),
Hash = Crc32.Calculate(GetMemoryStream(viewStartContent)),
HashAlgorithmVersion = 1,
LastModified = viewStartFileInfo.LastModified,
Length = viewStartFileInfo.Length,
RelativePath = "_ViewStart.cshtml",
Expand Down Expand Up @@ -295,7 +299,8 @@ public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewStartWasDeletedS
FullTypeName = typeof(RuntimeCompileIdentical).FullName,
RelativePath = viewStartFileInfo.PhysicalPath,
LastModified = viewStartFileInfo.LastModified,
Hash = RazorFileHash.GetHash(viewStartFileInfo),
Hash = RazorFileHash.GetHash(viewStartFileInfo, hashAlgorithmVersion: 1),
HashAlgorithmVersion = 1,
Length = viewStartFileInfo.Length
};
fileProvider.AddFile(viewStartFileInfo.PhysicalPath, viewStartFileInfo);
Expand Down Expand Up @@ -332,7 +337,8 @@ public static IEnumerable<object[]> GetOrAdd_IgnoresCachedValue_IfViewStartWasCh

var razorFileInfo = new RazorFileInfo
{
Hash = RazorFileHash.GetHash(contentStream),
Hash = Crc32.Calculate(contentStream),
HashAlgorithmVersion = 1,
LastModified = lastModified,
Length = length,
RelativePath = path
Expand Down Expand Up @@ -381,7 +387,8 @@ public void GetOrAdd_IgnoresCachedValue_IfViewStartWasChangedSinceCacheWasCreate
var razorFileInfo = new RazorFileInfo
{
FullTypeName = typeof(PreCompile).FullName,
Hash = RazorFileHash.GetHash(fileInfo),
Hash = RazorFileHash.GetHash(fileInfo, hashAlgorithmVersion: 1),
HashAlgorithmVersion = 1,
LastModified = lastModified,
Length = Encoding.UTF8.GetByteCount(content),
RelativePath = fileInfo.PhysicalPath,
Expand Down
Loading