-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4a26d89
commit d4ecc7c
Showing
11 changed files
with
284 additions
and
116 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
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
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,168 @@ | ||
#nullable enable | ||
|
||
using Handyman.AspNetCore.ETags.Internals; | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Handyman.AspNetCore.ETags; | ||
|
||
public static class ETagUtility | ||
{ | ||
public static string ToETag(byte[] bytes) | ||
{ | ||
return $"W/\"{ToETagValue(bytes)}\""; | ||
} | ||
|
||
public static string ToETag(string s) | ||
{ | ||
if (s == "*") | ||
{ | ||
return s; | ||
} | ||
|
||
var value = GetETagValueOrThrow(s); | ||
|
||
if (value.Length != s.Length) | ||
{ | ||
return s; | ||
} | ||
|
||
return $"W/\"{value}\""; | ||
} | ||
|
||
public static string ToETagValue(byte[] bytes) | ||
{ | ||
var strings = bytes | ||
.SkipWhile(x => x == 0) | ||
.Select(x => x.ToString("x2")); | ||
|
||
var eTagValue = string.Join("", strings); | ||
|
||
return eTagValue.Length != 0 | ||
? eTagValue | ||
: "0"; | ||
} | ||
|
||
public static string ToETagValue(string s) | ||
{ | ||
var value = GetETagValueOrThrow(s); | ||
|
||
if (value.Length == s.Length) | ||
{ | ||
return s; | ||
} | ||
|
||
return value.ToString(); | ||
} | ||
|
||
public static bool Equals(string eTag1, byte[] eTag2) | ||
{ | ||
if (eTag1 == "*") | ||
{ | ||
return true; | ||
} | ||
|
||
return Equals(eTag1, ToETagValue(eTag2)); | ||
} | ||
|
||
public static bool Equals(string eTag1, string eTag2) | ||
{ | ||
if (eTag1 == "*" || eTag2 == "*") | ||
{ | ||
return true; | ||
} | ||
|
||
var value1 = GetETagValueOrThrow(eTag1); | ||
var value2 = GetETagValueOrThrow(eTag2); | ||
|
||
return value1.SequenceEqual(value2); | ||
} | ||
|
||
public static void EnsureEquals(string eTag1, byte[] eTag2) | ||
{ | ||
if (Equals(eTag1, eTag2)) | ||
{ | ||
return; | ||
} | ||
|
||
throw new PreconditionFailedException(); | ||
} | ||
|
||
public static void EnsureEquals(string eTag1, string eTag2) | ||
{ | ||
if (Equals(eTag1, eTag2)) | ||
{ | ||
return; | ||
} | ||
|
||
throw new PreconditionFailedException(); | ||
} | ||
|
||
internal static ReadOnlySpan<char> GetETagValueOrThrow(string eTag) | ||
{ | ||
var startsWith = eTag.StartsWith("W/\""); | ||
var endsWith = eTag.EndsWith("\""); | ||
|
||
var span = eTag.AsSpan(); | ||
|
||
if (startsWith && endsWith) | ||
{ | ||
span = span.Slice(3, span.Length - 4); | ||
} | ||
else if (!startsWith && !endsWith) | ||
{ | ||
// nothing to do here | ||
} | ||
else | ||
{ | ||
ThrowInvalidETagException(); | ||
} | ||
|
||
if (span.Length == 0) | ||
{ | ||
ThrowInvalidETagException(); | ||
} | ||
|
||
return span; | ||
} | ||
|
||
internal static bool IsValidETag(string candidate) | ||
{ | ||
if (string.IsNullOrWhiteSpace(candidate)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (candidate == "*") | ||
{ | ||
return true; | ||
} | ||
|
||
if (candidate.Length <= 4) | ||
{ | ||
return false; | ||
} | ||
|
||
if (!candidate.StartsWith("W/\"")) | ||
{ | ||
return false; | ||
} | ||
|
||
if (!candidate.EndsWith("\"")) | ||
{ | ||
return false; | ||
} | ||
|
||
if (candidate.AsSpan(3, candidate.Length - 4).Contains('"')) | ||
{ | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public static void ThrowInvalidETagException() | ||
{ | ||
throw new ArgumentException("Invalid eTag format/value."); | ||
} | ||
} |
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
41 changes: 15 additions & 26 deletions
41
src/Handyman.AspNetCore/src/ETags/Internals/ETagComparer.cs
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 |
---|---|---|
@@ -1,50 +1,39 @@ | ||
namespace Handyman.AspNetCore.ETags.Internals | ||
using System; | ||
|
||
namespace Handyman.AspNetCore.ETags.Internals | ||
{ | ||
internal class ETagComparer : IETagComparer | ||
{ | ||
private readonly IETagConverter _converter; | ||
|
||
public ETagComparer(IETagConverter converter) | ||
{ | ||
_converter = converter; | ||
} | ||
|
||
public void EnsureEquals(string eTag, byte[] bytes) | ||
{ | ||
if (Equals(eTag, bytes)) | ||
return; | ||
ArgumentNullException.ThrowIfNull(eTag); | ||
ArgumentNullException.ThrowIfNull(bytes); | ||
|
||
throw new PreconditionFailedException(); | ||
ETagUtility.EnsureEquals(eTag, bytes); | ||
} | ||
|
||
public void EnsureEquals(string eTag1, string eTag2) | ||
{ | ||
if (Equals(eTag1, eTag2)) | ||
return; | ||
ArgumentNullException.ThrowIfNull(eTag1); | ||
ArgumentNullException.ThrowIfNull(eTag2); | ||
|
||
throw new PreconditionFailedException(); | ||
ETagUtility.EnsureEquals(eTag1, eTag2); | ||
} | ||
|
||
public bool Equals(string eTag, byte[] bytes) | ||
{ | ||
if (eTag == "*") | ||
return true; | ||
|
||
if (string.IsNullOrWhiteSpace(eTag)) | ||
return false; | ||
ArgumentNullException.ThrowIfNull(eTag); | ||
ArgumentNullException.ThrowIfNull(bytes); | ||
|
||
return eTag == _converter.FromByteArray(bytes); | ||
return ETagUtility.Equals(eTag, bytes); | ||
} | ||
|
||
public bool Equals(string eTag1, string eTag2) | ||
{ | ||
if (eTag1 == "*" || eTag2 == "*") | ||
return true; | ||
|
||
if (string.IsNullOrWhiteSpace(eTag1) || string.IsNullOrWhiteSpace(eTag2)) | ||
return false; | ||
ArgumentNullException.ThrowIfNull(eTag1); | ||
ArgumentNullException.ThrowIfNull(eTag2); | ||
|
||
return eTag1 == eTag2; | ||
return ETagUtility.Equals(eTag1, eTag2); | ||
} | ||
} | ||
} |
Oops, something went wrong.