From fea7f2fdd98fb67ac2a8e4bcb92ea176734d9a39 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 26 Aug 2024 13:07:43 +0300 Subject: [PATCH 1/2] Fallback to chunk CompressedLength if Content-Length is not set --- SteamKit2/SteamKit2/Steam/CDN/Client.cs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/SteamKit2/SteamKit2/Steam/CDN/Client.cs b/SteamKit2/SteamKit2/Steam/CDN/Client.cs index 90bdc7c67..51ba0c33c 100644 --- a/SteamKit2/SteamKit2/Steam/CDN/Client.cs +++ b/SteamKit2/SteamKit2/Steam/CDN/Client.cs @@ -224,17 +224,25 @@ public async Task DownloadDepotChunkAsync( uint depotId, DepotManifest.Chun throw new SteamKitWebRequestException( $"Response status code does not indicate success: {response.StatusCode:D} ({response.ReasonPhrase}).", response ); } - if ( !response.Content.Headers.ContentLength.HasValue ) - { - throw new SteamKitWebRequestException( "Response does not have Content-Length", response ); - } + var contentLength = ( int )chunk.CompressedLength; - var contentLength = ( int )response.Content.Headers.ContentLength; + if ( response.Content.Headers.ContentLength.HasValue ) + { + contentLength = ( int )response.Content.Headers.ContentLength; - // assert that lengths match only if the chunk has a length assigned. - if ( chunk.CompressedLength > 0 && contentLength != chunk.CompressedLength ) + // assert that lengths match only if the chunk has a length assigned. + if ( chunk.CompressedLength > 0 && contentLength != chunk.CompressedLength ) + { + throw new InvalidDataException( $"Content-Length mismatch for depot chunk! (was {contentLength}, but should be {chunk.CompressedLength})" ); + } + } + else if ( contentLength > 0 ) + { + DebugLog.WriteLine( nameof( CDN ), $"Response does not have Content-Length, falling back to chunk.CompressedLength." ); + } + else { - throw new InvalidDataException( $"Content-Length mismatch for depot chunk! (was {contentLength}, but should be {chunk.CompressedLength})" ); + throw new SteamKitWebRequestException( "Response does not have Content-Length and chunk.CompressedLength is not set.", response ); } cts.CancelAfter( ResponseBodyTimeout ); From 1cdf7a748e2096080788b2da65b8ca1675113fe6 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 26 Aug 2024 13:16:00 +0300 Subject: [PATCH 2/2] Support downloading manifests without Content-Length --- SteamKit2/SteamKit2/Steam/CDN/Client.cs | 50 +++++++++++++++++-------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/SteamKit2/SteamKit2/Steam/CDN/Client.cs b/SteamKit2/SteamKit2/Steam/CDN/Client.cs index 51ba0c33c..45aeec466 100644 --- a/SteamKit2/SteamKit2/Steam/CDN/Client.cs +++ b/SteamKit2/SteamKit2/Steam/CDN/Client.cs @@ -100,30 +100,45 @@ public async Task DownloadManifestAsync( uint depotId, ulong mani throw new SteamKitWebRequestException( $"Response status code does not indicate success: {response.StatusCode:D} ({response.ReasonPhrase}).", response ); } - if ( !response.Content.Headers.ContentLength.HasValue ) - { - throw new SteamKitWebRequestException( "Response does not have Content-Length", response ); - } - cts.CancelAfter( ResponseBodyTimeout ); - var contentLength = ( int )response.Content.Headers.ContentLength; - var buffer = ArrayPool.Shared.Rent( contentLength ); + var contentLength = -1; + byte[]? buffer = null; + + if ( response.Content.Headers.ContentLength.HasValue ) + { + contentLength = ( int )response.Content.Headers.ContentLength; + buffer = ArrayPool.Shared.Rent( contentLength ); + } + else + { + DebugLog.WriteLine( nameof( CDN ), $"Manifest response does not have Content-Length, falling back to unbuffered read." ); + } try { - using var ms = new MemoryStream( buffer, 0, contentLength ); + MemoryStream ms; - // Stream the http response into the rented buffer - await response.Content.CopyToAsync( ms, cts.Token ); + if ( buffer != null ) + { + ms = new MemoryStream( buffer, 0, contentLength ); - if ( ms.Position != contentLength ) + // Stream the http response into the rented buffer + await response.Content.CopyToAsync( ms, cts.Token ); + + if ( ms.Position != contentLength ) + { + throw new InvalidDataException( $"Length mismatch after downloading depot manifest! (was {ms.Position}, but should be {contentLength})" ); + } + + ms.Position = 0; + } + else { - throw new InvalidDataException( $"Length mismatch after downloading depot manifest! (was {ms.Position}, but should be {contentLength})" ); + var data = await response.Content.ReadAsByteArrayAsync(); + ms = new MemoryStream( data ); } - ms.Position = 0; - // Decompress the zipped manifest data using var zip = new ZipArchive( ms ); var entries = zip.Entries; @@ -132,10 +147,15 @@ public async Task DownloadManifestAsync( uint depotId, ulong mani using var zipEntryStream = entries[ 0 ].Open(); depotManifest = DepotManifest.Deserialize( zipEntryStream ); + + ms.Dispose(); } finally { - ArrayPool.Shared.Return( buffer ); + if ( buffer != null ) + { + ArrayPool.Shared.Return( buffer ); + } } } catch ( Exception ex )