feat(client): add rendezvous traversal coordinators (#12)
quality-gate / quality (push) Successful in 56s

This commit is contained in:
KyuubiYoru
2026-07-16 08:39:05 +02:00
parent 6d076c281a
commit b4b6072fe1
28 changed files with 2949 additions and 57 deletions
@@ -0,0 +1,83 @@
using System.Text;
using FinalFactory.Rendezvous.Contracts;
namespace FinalFactory.Rendezvous.Client;
public sealed class DirectConnectionRequest
{
public JoinAttemptId AttemptId { get; set; }
public string ConnectionTicket { get; set; } = string.Empty;
public override string ToString() =>
$"[DirectConnectionRequest {AttemptId}; ticket redacted]";
}
public static class DirectConnectionRequestCodec
{
public const int EncodedLength = 63;
private const int MagicLength = 4;
private const int AttemptIdLength = 16;
private const int TicketLength = ContractLimits.DerivedCredentialCharacters;
private static readonly byte[] Magic = [(byte)'R', (byte)'V', (byte)'D', (byte)'1'];
public static bool IsRendezvousRequest(ReadOnlySpan<byte> encoded) =>
encoded.Length >= MagicLength && encoded[..MagicLength].SequenceEqual(Magic);
public static byte[] Encode(JoinAttemptId attemptId, string connectionTicket)
{
if (attemptId.Value == Guid.Empty
|| connectionTicket is null
|| connectionTicket.Length != TicketLength
|| !ContractValidation.IsConnectionTicketValid(connectionTicket))
{
throw new ArgumentException("The direct connection request fields are invalid.");
}
byte[] encoded = new byte[EncodedLength];
Magic.CopyTo(encoded, 0);
if (!attemptId.Value.TryWriteBytes(encoded.AsSpan(MagicLength, AttemptIdLength)))
{
throw new InvalidOperationException("The join attempt identifier could not be encoded.");
}
Encoding.ASCII.GetBytes(
connectionTicket,
0,
connectionTicket.Length,
encoded,
MagicLength + AttemptIdLength);
return encoded;
}
public static bool TryDecode(
ReadOnlySpan<byte> encoded,
out DirectConnectionRequest? request)
{
request = null;
if (encoded.Length != EncodedLength
|| !encoded[..MagicLength].SequenceEqual(Magic))
{
return false;
}
Guid attemptId = new(encoded.Slice(MagicLength, AttemptIdLength));
if (attemptId == Guid.Empty)
{
return false;
}
string ticket = Encoding.ASCII.GetString(encoded[(MagicLength + AttemptIdLength)..]);
if (!ContractValidation.IsConnectionTicketValid(ticket))
{
return false;
}
request = new DirectConnectionRequest
{
AttemptId = new JoinAttemptId(attemptId),
ConnectionTicket = ticket,
};
return true;
}
}