diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 74c76460..0b3f4e83 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -440,7 +440,7 @@ private static async Task DownloadWebFile( uint appId, string fileName, string u Directory.CreateDirectory( Path.GetDirectoryName( fileStagingPath ) ); using ( var file = File.OpenWrite( fileStagingPath ) ) - using ( var client = new HttpClient() ) + using ( var client = HttpClientFactory.CreateHttpClient() ) { Console.WriteLine( "Downloading {0}", fileName ); var responseStream = await client.GetStreamAsync( url ); diff --git a/DepotDownloader/HttpClientFactory.cs b/DepotDownloader/HttpClientFactory.cs new file mode 100644 index 00000000..fc28888c --- /dev/null +++ b/DepotDownloader/HttpClientFactory.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace DepotDownloader +{ + // This is based on the dotnet issue #44686 and its workaround at https://github.com/dotnet/runtime/issues/44686#issuecomment-733797994 + // We don't know if the IPv6 stack is functional. + class HttpClientFactory + { + public static HttpClient CreateHttpClient() + { + var client = new HttpClient(new SocketsHttpHandler + { + ConnectCallback = IPv4ConnectAsync + }); + + var assemblyVersion = typeof(HttpClientFactory).Assembly.GetName().Version.ToString(fieldCount: 3); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DepotDownloader", assemblyVersion)); + + return client; + } + + static async ValueTask IPv4ConnectAsync(SocketsHttpConnectionContext context, CancellationToken cancellationToken) + { + // By default, we create dual-mode sockets: + // Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + socket.NoDelay = true; + + try + { + await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false); + return new NetworkStream(socket, ownsSocket: true); + } + catch + { + socket.Dispose(); + throw; + } + } + } +} diff --git a/DepotDownloader/Properties/AssemblyInfo.cs b/DepotDownloader/Properties/AssemblyInfo.cs index 987e75ec..7f1d3641 100644 --- a/DepotDownloader/Properties/AssemblyInfo.cs +++ b/DepotDownloader/Properties/AssemblyInfo.cs @@ -31,4 +31,4 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.4.2.0")] +[assembly: AssemblyVersion("2.4.3.0")] diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index e6f3f59c..069780da 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -89,7 +89,12 @@ public Steam3Session( SteamUser.LogOnDetails details ) this.PackageInfo = new Dictionary(); this.AppBetaPasswords = new Dictionary(); - this.steamClient = new SteamClient(); + var clientConfiguration = SteamConfiguration.Create(config => + config + .WithHttpClientFactory(HttpClientFactory.CreateHttpClient) + ); + + this.steamClient = new SteamClient(clientConfiguration); this.steamUser = this.steamClient.GetHandler(); this.steamApps = this.steamClient.GetHandler(); @@ -531,6 +536,9 @@ private void DisconnectedCallback( SteamClient.DisconnectedCallback disconnected if ( disconnected.UserInitiated || bExpectingDisconnectRemote ) { Console.WriteLine( "Disconnected from Steam" ); + + // Any operations outstanding need to be aborted + bAborted = true; } else if ( connectionBackoff >= 10 ) {