mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-01 10:58:29 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
253 lines
6.3 KiB
C#
253 lines
6.3 KiB
C#
using Sentry;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
|
|
namespace Sandbox.Utility;
|
|
|
|
internal static class Web
|
|
{
|
|
static HttpClient client = new HttpClient()
|
|
{
|
|
Timeout = TimeSpan.FromMinutes( 120 )
|
|
};
|
|
|
|
/// <summary>
|
|
/// Download a file to a target filename (todo - progress)
|
|
/// </summary>
|
|
public static async Task<bool> DownloadFile( string url, string targetFileName, CancellationToken cancelToken, Sandbox.Utility.DataProgress.Callback progress = null )
|
|
{
|
|
Assert.NotNull( targetFileName );
|
|
|
|
var tempName = targetFileName + $".{Random.Shared.Int( 10000, 99999 )}.tmp";
|
|
|
|
int tries = 0;
|
|
retry:
|
|
|
|
try
|
|
{
|
|
tries++;
|
|
var response = await client.GetAsync( url, HttpCompletionOption.ResponseContentRead );
|
|
|
|
if ( !response.IsSuccessStatusCode )
|
|
{
|
|
if ( tries < 3 )
|
|
{
|
|
Log.Warning( $"Error downloading {url} (status was {response.StatusCode}) - retrying" );
|
|
await Task.Delay( 500 * tries );
|
|
goto retry;
|
|
}
|
|
|
|
Log.Warning( $"Error downloading {url} (status was {response.StatusCode})" );
|
|
SentrySdk.AddBreadcrumb( $"Error downloading {url} (status was {response.StatusCode})", "download.failed" );
|
|
return false;
|
|
}
|
|
|
|
DataProgress p = new DataProgress();
|
|
p.TotalBytes = response.Content.Headers.ContentLength ?? 0;
|
|
progress?.Invoke( p );
|
|
|
|
// Make sure the directory exists
|
|
var path = new DirectoryInfo( System.IO.Path.GetDirectoryName( tempName ) );
|
|
if ( !path.Exists ) path.Create();
|
|
|
|
using var fileStream = new FileStream( tempName, FileMode.Create );
|
|
using var bodyStream = await response.Content.ReadAsStreamAsync( cancelToken );
|
|
var content = new DataProgress.HttpContentStream( bodyStream );
|
|
|
|
if ( progress is not null )
|
|
{
|
|
content.Progress = p => MainThread.Queue( () => progress.Invoke( p ) );
|
|
}
|
|
|
|
await content.CopyToAsync( fileStream, cancelToken );
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
throw;
|
|
}
|
|
catch ( System.Net.Http.HttpRequestException e )
|
|
{
|
|
if ( tries <= 3 )
|
|
{
|
|
Log.Warning( $"Error downloading {url} ({e.Message}) - retrying" );
|
|
await Task.Delay( 500 * tries );
|
|
goto retry;
|
|
}
|
|
|
|
Log.Warning( e, $"Http Error downloading {url}" );
|
|
SentrySdk.CaptureException( e, scope => scope.SetExtra( "url", url ) );
|
|
return false;
|
|
}
|
|
catch ( System.IO.IOException ioe )
|
|
{
|
|
if ( tries <= 3 )
|
|
{
|
|
Log.Warning( $"Error downloading {url} ({ioe.Message}) - retrying" );
|
|
await Task.Delay( 500 * tries );
|
|
goto retry;
|
|
}
|
|
|
|
Log.Warning( ioe, $"IO Error downloading {url}" );
|
|
SentrySdk.CaptureException( ioe, scope => scope.SetExtra( "url", url ) );
|
|
return false;
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Warning( ex, $"Error downloading {url}" );
|
|
SentrySdk.CaptureException( ex, scope => scope.SetExtra( "url", url ) );
|
|
return false;
|
|
}
|
|
|
|
cancelToken.ThrowIfCancellationRequested();
|
|
|
|
int imoveTries = 0;
|
|
while ( true )
|
|
{
|
|
imoveTries++;
|
|
|
|
try
|
|
{
|
|
System.IO.File.Move( tempName, targetFileName, true );
|
|
return true;
|
|
}
|
|
catch ( System.Exception )
|
|
{
|
|
if ( imoveTries > 10 )
|
|
return false;
|
|
|
|
await Task.Delay( 100 * tries );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a file to a byte array
|
|
/// </summary>
|
|
public static async Task<byte[]> GrabFile( string url, CancellationToken cancelToken, Action<int> loader = null )
|
|
{
|
|
try
|
|
{
|
|
using ( var stream = await client.GetStreamAsync( url, cancelToken ) )
|
|
{
|
|
using ( var fileStream = new MemoryStream() )
|
|
{
|
|
var t = stream.CopyToAsync( fileStream, cancelToken );
|
|
|
|
while ( !t.IsCompleted )
|
|
{
|
|
loader?.Invoke( (int)fileStream.Position );
|
|
await Task.Delay( 16, cancelToken );
|
|
cancelToken.ThrowIfCancellationRequested();
|
|
}
|
|
|
|
//await Task.Delay( 100 );
|
|
|
|
loader?.Invoke( (int)fileStream.Position );
|
|
return fileStream.ToArray();
|
|
}
|
|
}
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
throw;
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Warning( ex, $"Error downloading {url}" );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a url to a string
|
|
/// </summary>
|
|
public static async Task<string> DownloadString( string url, CancellationToken cancelToken )
|
|
{
|
|
for ( int i = 0; i <= 3; i++ )
|
|
{
|
|
try
|
|
{
|
|
return await client.GetStringAsync( url, cancelToken );
|
|
}
|
|
catch ( HttpRequestException e )
|
|
{
|
|
if ( e.StatusCode == System.Net.HttpStatusCode.NotFound )
|
|
{
|
|
Log.Warning( e, $"Error downloading string '{url}' (not found)" );
|
|
return null;
|
|
}
|
|
|
|
Log.Warning( e, $"Error downloading string '{url}' ({e.StatusCode}) ({i}/3)" );
|
|
await Task.Delay( 500 );
|
|
continue;
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
throw;
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Warning( ex, $"Error downloading string '{url}'" );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a url to a string
|
|
/// </summary>
|
|
public static async Task<T> DownloadJson<T>( string url, CancellationToken cancelToken = default )
|
|
{
|
|
var json = await DownloadString( url, cancelToken );
|
|
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<T>( json, new JsonSerializerOptions( JsonSerializerOptions.Default ) { PropertyNameCaseInsensitive = true } );
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
throw;
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Warning( ex, $"Error downloading json {url}" );
|
|
return default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a file to a target filename (todo - progress)
|
|
/// </summary>
|
|
public static async Task<bool> PutAsync( Stream fileStream, string endpoint, CancellationToken cancelationToken, Sandbox.Utility.DataProgress.Callback progress )
|
|
{
|
|
DataProgress p = new DataProgress { TotalBytes = fileStream.Length };
|
|
progress?.Invoke( p );
|
|
|
|
using var content = new DataProgress.HttpContentStream( fileStream );
|
|
content.Progress = p => MainThread.Queue( () => progress?.Invoke( p ) );
|
|
|
|
// Assume azure
|
|
content.Headers.Add( "x-ms-blob-type", "BlockBlob" );
|
|
|
|
HttpResponseMessage r = default;
|
|
|
|
await Task.Run( async () => r = await client.PutAsync( endpoint, content, cancelationToken ), cancelationToken );
|
|
|
|
if ( r.StatusCode != System.Net.HttpStatusCode.Created )
|
|
{
|
|
Log.Warning( $"PutAsync failed status code: {r}" );
|
|
return false;
|
|
}
|
|
|
|
p.ProgressBytes = p.TotalBytes;
|
|
progress?.Invoke( p );
|
|
|
|
cancelationToken.ThrowIfCancellationRequested();
|
|
return true;
|
|
}
|
|
}
|