Code/Proxy/NetworkStorageProxyClient.cs

The NetworkStorage class provides internal proxy methods to route network storage requests through the game host. It handles building security requests, acquiring client authentication tokens, communicating with the host proxy, and processing the response envelope.

NetworkingExternal DownloadCredential Access
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace Sandbox;

public static partial class NetworkStorage
{
	// ── Proxy Methods (non-host clients route through host) ──

	/// <summary>
	/// Route an endpoint call through the game host via the registered RequestProxy delegate.
	/// </summary>
	private static async Task<JsonElement?> CallEndpointViaProxy( string slug, object input, bool allowSecurityRetry = true, bool allowClockRetry = true )
	{
		var steamId = Game.SteamId.ToString();
		EndpointSecurityRequest securityRequest;
		string inputJson;
		try
		{
			securityRequest = await BuildEndpointSecurityRequest( slug, input ?? new { }, allowAuthSession: false );
			if ( securityRequest is null )
				return null;
			inputJson = JsonSerializer.Serialize( securityRequest.Body );
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogErrors )
				Log.Warning( $"[NetworkStorage] {slug} PROXY request build failed — {ex.Message}" );
			RecordEndpointError( slug, ex is TimeoutException ? "PROXY_TIMEOUT" : "PROXY_FAILED", ex.Message );
			return null;
		}
		string proxySlug = slug;

		// Get the client's auth token as consent proof (included in HMAC signature)
		string clientToken = null;
		try
		{
			clientToken = await GetAuthTokenWithRetry( $"{slug} proxy consent ({steamId})" );
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogErrors )
				Log.Warning( $"[NetworkStorage] {slug} PROXY: failed to get client token — {ex.Message}" );
		}

		if ( NetworkStorageLogConfig.LogProxy )
		{
			NetLog.Request( slug, $"PROXY → host (steamId={steamId})" );
			Log.Info( $"[NetworkStorage] {slug} PROXY request via host for steamId={steamId} route={(string.IsNullOrEmpty( proxySlug ) ? "obfuscated" : proxySlug)} input={inputJson ?? "null"}" );
		}

		try
		{
			var result = await RequestProxy( steamId, clientToken ?? "", proxySlug, inputJson );
			if ( result == null )
			{
				if ( NetworkStorageLogConfig.LogErrors )
				{
					Log.Warning( $"[NetworkStorage] {slug} PROXY returned null — host may have rejected the request" );
					NetLog.Error( slug, "Proxy returned null" );
				}
				RecordEndpointError( slug, "PROXY_FAILED", "Proxy returned null" );
				return null;
			}

			var reply = NetworkStorageProxyEnvelope.Read( result, NetworkStorageProxyEnvelope.EndpointKind );
			if ( !reply.Ok )
			{
				RecordEndpointError( slug, reply.ErrorCode, reply.ErrorMessage );
				return null;
			}

			result = UnsealEndpointResponse( securityRequest.ResponseContext, slug, reply.Body );
			if ( result is null )
				return null;
			if ( NetworkStorageLogConfig.LogProxy )
				Log.Info( $"[NetworkStorage] {slug} PROXY → {TruncateJson( result, 200 )}" );
			var parsed = ParseResponse( slug, result );
			if ( parsed.HasValue && NetworkStorageLogConfig.LogProxy )
				NetLog.Response( slug, $"PROXY OK — {TruncateJson( parsed.Value )}" );
			if ( !parsed.HasValue && allowSecurityRetry && IsRetrySafeSecurityRefusal( result ) )
			{
				await EnsureRuntimeSecurityConfigAsync( "proxy-security-retry", forceRefresh: true );
				return await CallEndpointViaProxy( slug, input, allowSecurityRetry: false, allowClockRetry: allowClockRetry );
			}
			if ( !parsed.HasValue && allowClockRetry && LastEndpointErrorWasStaleRequest( slug ) )
			{
				Log.Info( $"[NetworkStorage] {slug} request id was outside the server replay window, retrying with server time." );
				return await CallEndpointViaProxy( slug, input, allowSecurityRetry, allowClockRetry: false );
			}
			return parsed;
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogErrors )
			{
				Log.Warning( $"[NetworkStorage] {slug} PROXY FAILED — {ex.Message}" );
				NetLog.Error( slug, $"Proxy error: {ex.Message}" );
			}
			RecordEndpointError( slug, ex is TimeoutException ? "PROXY_TIMEOUT" : "PROXY_FAILED", ex.Message );
			return null;
		}
	}

	/// <summary>
	/// Route a document read through the game host via the registered DocumentProxy delegate.
	/// </summary>
	private static async Task<JsonElement?> GetDocumentViaProxy( string collectionId, string documentId )
	{
		var steamId = Game.SteamId.ToString();
		var docId = documentId ?? steamId;

		// Get the client's auth token as consent proof (included in HMAC signature)
		string clientToken = null;
		try
		{
			clientToken = await GetAuthTokenWithRetry( $"storage proxy consent ({collectionId}/{docId})" );
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogErrors )
				Log.Warning( $"[NetworkStorage] storage PROXY: failed to get client token — {ex.Message}" );
		}

		if ( NetworkStorageLogConfig.LogProxy )
		{
			NetLog.Request( "storage", $"PROXY → host GET {collectionId}/{docId}" );
			Log.Info( $"[NetworkStorage] storage PROXY request via host for {collectionId}/{docId}" );
		}

		try
		{
			var result = await DocumentProxy( steamId, clientToken ?? "", collectionId, docId );
			if ( result == null )
			{
				if ( NetworkStorageLogConfig.LogErrors )
				{
					Log.Warning( $"[NetworkStorage] storage PROXY returned null for {collectionId}/{docId}" );
					NetLog.Error( "storage", "Proxy returned null" );
				}
				RecordEndpointError( "storage", "PROXY_FAILED", "Proxy returned null" );
				return null;
			}

			var reply = NetworkStorageProxyEnvelope.Read( result, NetworkStorageProxyEnvelope.DocumentKind );
			if ( !reply.Ok )
			{
				RecordEndpointError( "storage", reply.ErrorCode, reply.ErrorMessage );
				return null;
			}

			if ( NetworkStorageLogConfig.LogProxy )
				Log.Info( $"[NetworkStorage] storage PROXY → {TruncateJson( reply.Body, 200 )}" );
			var document = JsonSerializer.Deserialize<JsonElement>( reply.Body );
			if ( NetworkStorageLogConfig.LogProxy )
				NetLog.Response( "storage", $"PROXY OK ({reply.Body.Length} bytes)" );
			return document;
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogErrors )
			{
				Log.Warning( $"[NetworkStorage] storage PROXY FAILED — {ex.Message}" );
				NetLog.Error( "storage", $"Proxy error: {ex.Message}" );
			}
			RecordEndpointError( "storage", ex is TimeoutException ? "PROXY_TIMEOUT" : "PROXY_FAILED", ex.Message );
			return null;
		}
	}

}