• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / HashGate / 25220934046

01 May 2026 03:43PM UTC coverage: 64.311% (-8.4%) from 72.756%
25220934046

push

github

pwelter34
Add OpenTelemetry diagnostics and metrics

189 of 398 branches covered (47.49%)

Branch coverage included in aggregate %.

101 of 165 new or added lines in 4 files covered. (61.21%)

539 of 734 relevant lines covered (73.43%)

27.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.27
/src/HashGate.AspNetCore/DefaultHmacReplayProtection.cs
1
using System.Diagnostics;
2

3
using Microsoft.Extensions.Caching.Hybrid;
4

5
namespace HashGate.AspNetCore;
6

7
/// <summary>
8
/// Default <see cref="IHmacReplayProtection"/> implementation backed by
9
/// <see cref="HybridCache"/> with automatic TTL-based expiry.
10
/// </summary>
11
/// <remarks>
12
/// Uses the <see cref="HybridCache"/> L1 (in-process) cache by default.
13
/// When a distributed cache (e.g. Redis via <c>StackExchange.Redis</c>) is registered
14
/// in the container, <see cref="HybridCache"/> automatically promotes it to the L2
15
/// backing store, extending replay protection across multiple server nodes with no
16
/// code changes. Each entry's lifetime mirrors the per-request tolerance window,
17
/// so entries are evicted exactly when the timestamp check would already reject the request.
18
/// </remarks>
19
internal sealed class DefaultHmacReplayProtection : IHmacReplayProtection
20
{
21
    private readonly HybridCache _cache;
22

23
    /// <summary>
24
    /// Initializes a new instance of the <see cref="DefaultHmacReplayProtection"/> class.
25
    /// </summary>
26
    /// <param name="cache">The <see cref="HybridCache"/> instance used to track seen signatures.</param>
27
    public DefaultHmacReplayProtection(HybridCache cache)
10✔
28
    {
29
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
10!
30
    }
10✔
31

32
    /// <inheritdoc />
33
    public async ValueTask<bool> TryStoreAsync(string signature, DateTimeOffset expiry, CancellationToken cancellationToken = default)
34
    {
35
        var ttl = expiry - DateTimeOffset.UtcNow;
21✔
36
        if (ttl <= TimeSpan.Zero)
21!
37
        {
NEW
38
            HashGateDiagnostics.RecordReplayProtectionCheck("expired", 0);
×
39
            return false;
×
40
        }
41

42
        using var activity = HashGateDiagnostics.ActivitySource.StartActivity("HashGate.ReplayProtection.Store", ActivityKind.Internal);
21✔
43
        activity?.SetTag("hashgate.replay_protection.ttl_ms", ttl.TotalMilliseconds);
21!
44

45
        bool isNew = false;
21✔
46

47
        // GetOrCreateAsync only invokes the factory when the key is absent.
48
        // Factory runs  → signature is new  → allow.
49
        // Factory skipped (cache hit) → signature was seen before → replay.
50
        await _cache.GetOrCreateAsync(
21✔
51
            key: $"HashGate:Signature:{signature}",
21✔
52
            factory: _ => { isNew = true; return ValueTask.FromResult(true); },
42✔
53
            options: new HybridCacheEntryOptions { Expiration = ttl },
21✔
54
            cancellationToken: cancellationToken
21✔
55
        );
21✔
56

57
        var result = isNew ? "new" : "replay";
21!
58
        activity?.SetTag("hashgate.replay_protection.result", result);
21!
59

60
        if (!isNew)
21!
NEW
61
            activity?.SetStatus(ActivityStatusCode.Error, result);
×
62

63
        HashGateDiagnostics.RecordReplayProtectionCheck(result, ttl.TotalMilliseconds);
21✔
64

65
        return isNew;
21✔
66
    }
21✔
67
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc