-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for Cache Digest in HTTP/2 Server Push
- Loading branch information
Showing
7 changed files
with
751 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Text; | ||
using System.Security.Cryptography; | ||
|
||
namespace Lib.Web.Mvc.Http | ||
{ | ||
internal static class CacheDigestHashAlgorithm | ||
{ | ||
#region Constants | ||
private const int _hashValueLengthUpperBound = 32; | ||
#endregion | ||
|
||
#region Methods | ||
internal static uint ComputeHash(string url, int count, uint probability, bool validators = false, string entityTag = null) | ||
{ | ||
int hashValueLength = (int)Math.Log(count * probability, 2); | ||
if (hashValueLength >= _hashValueLengthUpperBound) | ||
{ | ||
throw new NotSupportedException("Only hash-values up to 31 bits are supported."); | ||
} | ||
|
||
// This assumes that URL is already converted to an ASCII string by percent-encoding as appropriate (RFC3986). | ||
string key = url; | ||
|
||
// If validators is true and ETag is not null. | ||
if (validators && !String.IsNullOrWhiteSpace(entityTag)) | ||
{ | ||
// Append ETag to key as an ASCII string. | ||
key += entityTag; | ||
} | ||
|
||
// Let hash-value be the SHA-256 message digest (RFC6234) of key, expressed as an integer. | ||
uint hashValue = BitConverter.ToUInt32(new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes(key)), 0); | ||
if (BitConverter.IsLittleEndian) | ||
{ | ||
hashValue = (hashValue & 0x000000FFU) << 24 | (hashValue & 0x0000FF00U) << 8 | (hashValue & 0x00FF0000U) >> 8 | (hashValue & 0xFF000000U) >> 24; | ||
} | ||
|
||
// Truncate hash-value to log2(N*P) bits. | ||
return (hashValue >> (_hashValueLengthUpperBound - hashValueLength)) & (uint)((1 << hashValueLength) - 1); | ||
} | ||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
|
||
namespace Lib.Web.Mvc.Http | ||
{ | ||
/// <summary> | ||
/// Represents the value of the Cache-Digest header. | ||
/// </summary> | ||
public class CacheDigestHeaderValue | ||
{ | ||
#region Constants | ||
private const string ResetFlag = "RESET"; | ||
private const string CompleteFlag = "COMPLETE"; | ||
private const string ValidatorsFlag = "VALIDATORS"; | ||
private const string StaleFlag = "STALE"; | ||
private const string DigestValueSeparator = ";"; | ||
#endregion | ||
|
||
#region Fields | ||
private Lazy<CacheDigestValue> _lazyDigestValue; | ||
#endregion | ||
|
||
#region Properties | ||
/// <summary> | ||
/// Indicates that any and all cache digests for the applicable origin held by the recipient MUST be considered invalid. | ||
/// </summary> | ||
public bool Reset { get; private set; } | ||
|
||
/// <summary> | ||
/// Indicates that the currently valid set of cache digests held by the server constitutes a complete representation of the cache’s state regarding that origin, for the type of cached response indicated by the Stale property. | ||
/// </summary> | ||
public bool Complete { get; private set; } | ||
|
||
/// <summary> | ||
/// Indicates that the validators are included in the digest. | ||
/// </summary> | ||
public bool Validators { get; private set; } | ||
|
||
/// <summary> | ||
/// Indicates that all cached responses represented in the digest are stale. | ||
/// </summary> | ||
public bool Stale { get; private set; } | ||
|
||
/// <summary> | ||
/// The digest value. | ||
/// </summary> | ||
public CacheDigestValue DigestValue { get { return _lazyDigestValue.Value; } } | ||
#endregion | ||
|
||
#region Constructor | ||
private CacheDigestHeaderValue() | ||
{ | ||
Reset = false; | ||
Complete = false; | ||
Validators = false; | ||
Stale = false; | ||
|
||
_lazyDigestValue = new Lazy<CacheDigestValue>(() => CacheDigestValue.FromUrls(new Dictionary<string, string>())); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes new instance of CacheDigestHeaderValue class. | ||
/// </summary> | ||
/// <param name="value">The value of the header.</param> | ||
public CacheDigestHeaderValue(string value) | ||
: this() | ||
{ | ||
if (!String.IsNullOrWhiteSpace(value)) | ||
{ | ||
Reset = CheckFlagSet(value, ResetFlag); | ||
Complete = CheckFlagSet(value, CompleteFlag); | ||
Validators = CheckFlagSet(value, ValidatorsFlag); | ||
Stale = CheckFlagSet(value, StaleFlag); | ||
|
||
string digestBase64String = value.Substring(0, value.IndexOf(DigestValueSeparator)); | ||
_lazyDigestValue = new Lazy<CacheDigestValue>(() => CacheDigestValue.FromBase64String(digestBase64String)); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Initializes new instance of CacheDigestHeaderValue class. | ||
/// </summary> | ||
/// <param name="digestValue">The digest value.</param> | ||
/// <param name="reset">Flag idicating that any and all cache digests for the applicable origin held by the recipient MUST be considered invalid.</param> | ||
/// <param name="validators">Flag indicating the validators are included in the digest.</param> | ||
/// <param name="stale">Flag indicating that all cached responses represented in the digest are stale.</param> | ||
public CacheDigestHeaderValue(CacheDigestValue digestValue, bool reset = false, bool validators = false, bool stale = false) | ||
: this() | ||
{ | ||
Reset = reset; | ||
Complete = true; | ||
Validators = validators; | ||
Stale = stale; | ||
|
||
_lazyDigestValue = new Lazy<CacheDigestValue>(() => digestValue); | ||
} | ||
#endregion | ||
|
||
#region Methods | ||
/// <summary> | ||
/// Queries the digest in order to determine whether there is a match in the digest. | ||
/// </summary> | ||
/// <param name="url">The URL of the resource, converted to an ASCII string by percent-encoding as appropriate per RFC3986.</param> | ||
/// <param name="entityTag">The ETag of the resource, including both the weak indicator (if present) and double quotes, as per RFC7232 Section 2.3.</param> | ||
/// <returns>True if there is a match in the digest, otherwise false.</returns> | ||
public bool QueryDigest(string url, string entityTag = null) | ||
{ | ||
return DigestValue.Contains(url, Validators, entityTag); | ||
} | ||
|
||
private static bool CheckFlagSet(string value, string flag) | ||
{ | ||
return (CultureInfo.InvariantCulture.CompareInfo.IndexOf(value, flag, CompareOptions.IgnoreCase) >= 0); | ||
} | ||
#endregion | ||
} | ||
} |
Oops, something went wrong.