feat: freeze v1 transport contracts (#4)
quality-gate / quality (push) Successful in 51s

Closes #4
This commit is contained in:
KyuubiYoru
2026-07-16 04:52:38 +02:00
parent e626b89909
commit 69c8b2d2bc
43 changed files with 3737 additions and 12 deletions
@@ -0,0 +1,134 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
namespace FinalFactory.Rendezvous.Contracts;
public static class ContractValidation
{
private const string CapabilityAlphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
public static RendezvousErrorCode ValidateContractVersion(int contractVersion) =>
contractVersion == ContractLimits.ContractVersion
? RendezvousErrorCode.None
: RendezvousErrorCode.UnsupportedContractVersion;
public static bool AreProtocolsCompatible(uint requested, uint offered) => requested == offered;
public static bool IsHttpRequestSizeValid(int byteCount) =>
byteCount is >= 0 and <= ContractLimits.HttpRequestMaxBytes;
public static bool IsBrowserResponseSizeValid(int byteCount) =>
byteCount is >= 0 and <= ContractLimits.BrowserResponseMaxBytes;
public static bool IsUtf8LengthWithin(string? value, int maximumBytes)
{
if (maximumBytes < 0)
{
throw new ArgumentOutOfRangeException(nameof(maximumBytes));
}
return value is not null && Encoding.UTF8.GetByteCount(value) <= maximumBytes;
}
public static bool IsPageSizeValid(int pageSize) =>
pageSize is >= 1 and <= ContractLimits.BrowserPageMaxItems;
public static bool IsIdempotencyKeyValid(string? value) =>
IsVisibleAsciiWithin(value, ContractLimits.IdempotencyKeyMaxCharacters);
public static bool IsCursorValid(string? value) =>
value is null || IsVisibleAsciiWithin(value, ContractLimits.CursorMaxCharacters);
public static bool IsDiagnosticCodeValid(string? value) =>
value is null || IsVisibleAsciiWithin(value, ContractLimits.DiagnosticCodeMaxCharacters);
public static bool IsBuildVersionValid(string? value) =>
IsUtf8LengthWithin(value, ContractLimits.BuildVersionMaxBytes);
public static bool IsDisplayNameValid(string? value) =>
IsUtf8LengthWithin(value, ContractLimits.DisplayNameMaxBytes);
public static bool IsOpaqueHttpCredentialValid(string? value) =>
value is not null
&& value.Length is > 0 and <= ContractLimits.OpaqueHttpCredentialMaxCharacters;
public static bool IsCapacityValid(SessionCapacity? capacity) =>
capacity is not null
&& capacity.MaximumPlayers is >= 1 and <= ContractLimits.SessionCapacityMaxPlayers
&& capacity.CurrentPlayers >= 0
&& capacity.CurrentPlayers <= capacity.MaximumPlayers;
public static bool IsCapabilityValid(string? capability) =>
IsBase64UrlValueValid(capability, ContractLimits.UdpCapabilityMaxCharacters);
public static bool IsConnectionTicketValid(string? ticket) =>
IsBase64UrlValueValid(ticket, ContractLimits.ConnectionTicketMaxCharacters);
public static bool IsNetworkEndpointValid(NetworkEndpoint? endpoint)
{
if (endpoint is null
|| endpoint.Port is < 1 or > ushort.MaxValue
|| !IPAddress.TryParse(endpoint.Address, out IPAddress? address))
{
return false;
}
return endpoint.AddressFamily switch
{
AddressFamilyKind.Ipv4 => address.AddressFamily == AddressFamily.InterNetwork,
AddressFamilyKind.Ipv6 => address.AddressFamily == AddressFamily.InterNetworkV6,
_ => false,
};
}
public static bool IsMetadataValid(IReadOnlyDictionary<string, string>? metadata)
{
if (metadata is null || metadata.Count > ContractLimits.MetadataMaxKeys)
{
return false;
}
foreach (KeyValuePair<string, string> item in metadata)
{
if (string.IsNullOrWhiteSpace(item.Key)
|| !IsUtf8LengthWithin(item.Key, ContractLimits.MetadataKeyMaxBytes)
|| !IsUtf8LengthWithin(item.Value, ContractLimits.MetadataValueMaxBytes))
{
return false;
}
}
byte[] encoded = JsonSerializer.SerializeToUtf8Bytes(metadata, ContractJson.Options);
return encoded.Length <= ContractLimits.MetadataMaxBytes;
}
internal static bool IsSlug(string? value, int maximumCharacters)
{
if (string.IsNullOrEmpty(value)
|| value.Length > maximumCharacters
|| value[0] is < 'a' or > 'z')
{
return false;
}
return value.All(static character =>
character is >= 'a' and <= 'z'
or >= '0' and <= '9'
or '-');
}
private static bool IsBase64UrlValueValid(string? value, int maximumCharacters) =>
value is not null
&& value.Length is > 0
&& value.Length <= maximumCharacters
&& value.All(static character => CapabilityAlphabet.Contains(character));
private static bool IsVisibleAsciiWithin(string? value, int maximumCharacters) =>
value is not null
&& value.Length is > 0
&& value.Length <= maximumCharacters
&& value.All(static character => character is >= '!' and <= '~');
}