From 0cebafdae2a29bb7e202254bdea1cefeb405421d Mon Sep 17 00:00:00 2001 From: Ryan Kistner Date: Wed, 26 Jun 2019 19:11:22 -0600 Subject: [PATCH] Added ContentServerDirectoryService for the discovery of ContentServers (#699) --- SteamKit2/SteamKit2/Steam/CDNClient.cs | 9 +- .../WebAPI/ContentServerDirectoryService.cs | 120 ++++++++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 SteamKit2/SteamKit2/Steam/WebAPI/ContentServerDirectoryService.cs diff --git a/SteamKit2/SteamKit2/Steam/CDNClient.cs b/SteamKit2/SteamKit2/Steam/CDNClient.cs index 2e7020970..d521274b0 100644 --- a/SteamKit2/SteamKit2/Steam/CDNClient.cs +++ b/SteamKit2/SteamKit2/Steam/CDNClient.cs @@ -64,6 +64,11 @@ public enum ConnectionProtocol /// public string Type { get; internal set; } + /// + /// Gets the SourceID this server belongs to. + /// + public int SourceID { get; internal set; } + /// /// Gets the CellID this server belongs to. /// @@ -370,7 +375,7 @@ public async Task ConnectAsync( Server csServer ) } // Nothing needs to be done to initialize a session to a CDN server - if ( csServer.Type == "CDN" ) + if ( csServer.Type == "CDN" || csServer.Type == "SteamCache" ) { connectedServer = csServer; return; @@ -426,7 +431,7 @@ public async Task AuthenticateDepotAsync( uint depotid, byte[] depotKey = null, string data; - if ( connectedServer.Type != "CDN" || cdnAuthToken == null ) + if ( (connectedServer.Type != "CDN" && connectedServer.Type != "SteamCache") || cdnAuthToken == null ) { if ( appTicket == null ) { diff --git a/SteamKit2/SteamKit2/Steam/WebAPI/ContentServerDirectoryService.cs b/SteamKit2/SteamKit2/Steam/WebAPI/ContentServerDirectoryService.cs new file mode 100644 index 000000000..97b943e05 --- /dev/null +++ b/SteamKit2/SteamKit2/Steam/WebAPI/ContentServerDirectoryService.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using SteamKit2.Discovery; + +namespace SteamKit2 +{ + /// + /// Helper class to load servers from the Content Server Directory Service Web API. + /// + public static class ContentServerDirectoryService + { + /// + /// Load a list of servers from the Content Server Directory Service. + /// + /// Configuration Object + /// A with the Result set to an enumerable list of s. + public static Task> LoadAsync( SteamConfiguration configuration ) + => LoadCoreAsync( configuration, null, null, CancellationToken.None ); + + /// + /// Load a list of servers from the Content Server Directory Service. + /// + /// Configuration Object + /// Cancellation Token + /// A with the Result set to an enumerable list of s. + public static Task> LoadAsync( SteamConfiguration configuration, CancellationToken cancellationToken ) + => LoadCoreAsync( configuration, null, null, cancellationToken ); + + /// + /// Load a list of servers from the Content Server Directory Service. + /// + /// Configuration Object + /// Preferred steam cell id + /// Cancellation Token + /// A with the Result set to an enumerable list of s. + public static Task> LoadAsync( SteamConfiguration configuration, int cellId, CancellationToken cancellationToken ) + => LoadCoreAsync( configuration, cellId, null, cancellationToken ); + + /// + /// Load a list of servers from the Content Server Directory Service. + /// + /// Configuration Object + /// Preferred steam cell id + /// Max number of servers to return. + /// Cancellation Token + /// A with the Result set to an enumerable list of s. + public static Task> LoadAsync( SteamConfiguration configuration, int cellId, int maxNumServers, CancellationToken cancellationToken ) + => LoadCoreAsync( configuration, cellId, maxNumServers, cancellationToken ); + + static async Task> LoadCoreAsync( SteamConfiguration configuration, int? cellId, int? maxNumServers, CancellationToken cancellationToken ) + { + if ( configuration == null ) + { + throw new ArgumentNullException( nameof( configuration ) ); + } + + var directory = configuration.GetAsyncWebAPIInterface( "IContentServerDirectoryService" ); + var args = new Dictionary(); + + if ( cellId.HasValue ) + { + args[ "cell_id" ] = cellId.Value.ToString( CultureInfo.InvariantCulture ); + } + else + { + args[ "cell_id" ] = configuration.CellID.ToString( CultureInfo.InvariantCulture ); + } + + if ( maxNumServers.HasValue ) + { + args[ "max_servers" ] = maxNumServers.Value.ToString( CultureInfo.InvariantCulture ); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var response = await directory.CallAsync( HttpMethod.Get, "GetServersForSteamPipe", version: 1, args: args ).ConfigureAwait( false ); + + var result = ( EResult )response[ "result" ].AsInteger( ( int )EResult.OK ); + if ( result != EResult.OK || response["servers"] == KeyValue.Invalid ) + { + throw new InvalidOperationException( string.Format( "Steam Web API returned EResult.{0}", result ) ); + } + + var serverList = response[ "servers" ]; + + cancellationToken.ThrowIfCancellationRequested(); + + var serverRecords = new List( capacity: serverList.Children.Count ); + + foreach ( var child in serverList.Children ) + { + string httpsSupport = child[ "https_support" ].AsString(); + var protocol = ( httpsSupport == "optional" || httpsSupport == "mandatory" ) ? CDNClient.Server.ConnectionProtocol.HTTPS : CDNClient.Server.ConnectionProtocol.HTTP; + + serverRecords.Add( new CDNClient.Server + { + Protocol = protocol, + Host = child[ "host" ].AsString(), + VHost = child[ "vhost" ].AsString(), + Port = protocol == CDNClient.Server.ConnectionProtocol.HTTPS ? 443 : 80, + + Type = child[ "type" ].AsString(), + SourceID = child[ "source_id"].AsInteger(), + CellID = (uint)child[ "cell" ].AsInteger(), + + Load = child[ "load" ].AsInteger(), + WeightedLoad = child[ "weighted_load" ].AsInteger(), + NumEntries = child[ "num_entries_in_client_list" ].AsInteger( 1 ) + } + ); + } + + return serverRecords.AsReadOnly(); + } + } +}