feat: implement authenticated NAT mediator (#11)
quality-gate / quality (push) Successful in 59s
quality-gate / quality (push) Successful in 59s
Closes #11
This commit is contained in:
@@ -4,6 +4,7 @@ namespace FinalFactory.Rendezvous.Server.State;
|
||||
|
||||
internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousStore
|
||||
{
|
||||
private static readonly TimeSpan UdpMaintenanceInterval = TimeSpan.FromSeconds(1);
|
||||
private readonly object _gate = new();
|
||||
private readonly EphemeralStoreOptions _options;
|
||||
private readonly IMonotonicClock _monotonicClock;
|
||||
@@ -19,6 +20,8 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
private readonly Dictionary<string, TimeSpan> _replay = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, TimeSpan> _revocations = new(StringComparer.Ordinal);
|
||||
private TimeSpan? _drainDeadline;
|
||||
private TimeSpan _nextUdpMaintenance;
|
||||
private long _maintenanceSweepCount;
|
||||
private bool _available = true;
|
||||
|
||||
public InMemoryEphemeralRendezvousStore(
|
||||
@@ -38,6 +41,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
}
|
||||
|
||||
public Guid InstanceId { get; }
|
||||
internal long MaintenanceSweepCount => Interlocked.Read(ref _maintenanceSweepCount);
|
||||
|
||||
public bool IsAvailable
|
||||
{
|
||||
@@ -270,6 +274,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
|
||||
if (!_presenceHandles.TryGetValue(command.Handle, out SessionListingId listingId)
|
||||
|| !_listings.TryGetValue(listingId, out ListingEntry? entry)
|
||||
|| entry.LeaseDeadline <= now
|
||||
|| entry.Definition.HostPresenceFingerprint != command.CapabilityFingerprint)
|
||||
{
|
||||
return new(StoreResultCode.NotFound);
|
||||
@@ -285,7 +290,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
command.LocalEndpoint,
|
||||
now + _options.PresenceLifetime);
|
||||
return new(StoreResultCode.Success, Snapshot(entry));
|
||||
}, cancellationToken);
|
||||
}, cancellationToken, eagerCleanup: false);
|
||||
|
||||
public StoreResult<IReadOnlyList<StoredListing>> BrowseVisibleListings(
|
||||
VisibleListingQuery query,
|
||||
@@ -446,13 +451,18 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
return new(StoreResultCode.NotFound);
|
||||
}
|
||||
|
||||
if (attempt.IntroductionConsumed)
|
||||
{
|
||||
return new(StoreResultCode.Conflict);
|
||||
}
|
||||
|
||||
RemoveAttempt(command.AttemptId);
|
||||
return new(StoreResultCode.Success, true);
|
||||
}, cancellationToken);
|
||||
|
||||
public StoreResult<StoredJoinAttempt> BindAttemptEndpoint(
|
||||
BindAttemptEndpointCommand command,
|
||||
CancellationToken cancellationToken = default) => Atomic<StoredJoinAttempt>(_ =>
|
||||
CancellationToken cancellationToken = default) => Atomic<StoredJoinAttempt>(now =>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
if (command.Handle.Value == Guid.Empty
|
||||
@@ -469,7 +479,13 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
}
|
||||
|
||||
if (!_attemptHandles.TryGetValue(command.Handle, out JoinAttemptId attemptId)
|
||||
|| !_attempts.TryGetValue(attemptId, out AttemptEntry? attempt))
|
||||
|| !_attempts.TryGetValue(attemptId, out AttemptEntry? attempt)
|
||||
|| attempt.Deadline <= now)
|
||||
{
|
||||
return new(StoreResultCode.NotFound);
|
||||
}
|
||||
|
||||
if (!HasFreshHostPresence(attempt, now))
|
||||
{
|
||||
return new(StoreResultCode.NotFound);
|
||||
}
|
||||
@@ -503,7 +519,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
}
|
||||
|
||||
return new(StoreResultCode.Success, Snapshot(attempt));
|
||||
}, cancellationToken);
|
||||
}, cancellationToken, eagerCleanup: false);
|
||||
|
||||
public StoreResult<IntroductionEndpoints> ConsumeIntroduction(
|
||||
MediationHandle handle,
|
||||
@@ -515,7 +531,13 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
}
|
||||
|
||||
if (!_attemptHandles.TryGetValue(handle, out JoinAttemptId attemptId)
|
||||
|| !_attempts.TryGetValue(attemptId, out AttemptEntry? attempt))
|
||||
|| !_attempts.TryGetValue(attemptId, out AttemptEntry? attempt)
|
||||
|| attempt.Deadline <= now)
|
||||
{
|
||||
return new(StoreResultCode.NotFound);
|
||||
}
|
||||
|
||||
if (!HasFreshHostPresence(attempt, now))
|
||||
{
|
||||
return new(StoreResultCode.NotFound);
|
||||
}
|
||||
@@ -540,7 +562,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
Snapshot(attempt),
|
||||
attempt.HostEndpoint,
|
||||
attempt.ClientEndpoint));
|
||||
}, cancellationToken);
|
||||
}, cancellationToken, eagerCleanup: false);
|
||||
|
||||
public StoreResult<bool> ConsumeConnectionTicket(
|
||||
ConsumeConnectionTicketCommand command,
|
||||
@@ -687,18 +709,38 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
}
|
||||
}
|
||||
|
||||
private StoreResult<T> Atomic<T>(Func<TimeSpan, StoreResult<T>> operation, CancellationToken cancellationToken)
|
||||
private StoreResult<T> Atomic<T>(
|
||||
Func<TimeSpan, StoreResult<T>> operation,
|
||||
CancellationToken cancellationToken,
|
||||
bool eagerCleanup = true)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
lock (_gate)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
TimeSpan now = _monotonicClock.Elapsed;
|
||||
Cleanup(now);
|
||||
// Authenticated UDP duplicates need O(1) store work. Their operations
|
||||
// check exact resource deadlines and amortize physical expiry removal.
|
||||
bool drainExpired = _drainDeadline is TimeSpan drainDeadline
|
||||
&& now >= drainDeadline;
|
||||
if (drainExpired || eagerCleanup || now >= _nextUdpMaintenance)
|
||||
{
|
||||
Cleanup(now);
|
||||
_nextUdpMaintenance = now + UdpMaintenanceInterval;
|
||||
}
|
||||
|
||||
return operation(now);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasFreshHostPresence(AttemptEntry attempt, TimeSpan now) =>
|
||||
_listings.TryGetValue(attempt.Command.ListingId, out ListingEntry? listing)
|
||||
&& listing.LeaseDeadline > now
|
||||
&& _presence.TryGetValue(
|
||||
listing.Definition.HostPresenceHandle,
|
||||
out PresenceEntry? presence)
|
||||
&& presence.Deadline > now;
|
||||
|
||||
private StoreResult<T>? CheckNewWorkAdmission<T>(string subject)
|
||||
{
|
||||
if (!_available)
|
||||
@@ -718,6 +760,7 @@ internal sealed class InMemoryEphemeralRendezvousStore : IEphemeralRendezvousSto
|
||||
|
||||
private void Cleanup(TimeSpan now)
|
||||
{
|
||||
_maintenanceSweepCount++;
|
||||
if (_drainDeadline is TimeSpan drainDeadline && now >= drainDeadline)
|
||||
{
|
||||
ClearActiveState();
|
||||
|
||||
Reference in New Issue
Block a user