diff --git a/src/lib-csharp/Sources/HttpClientFileDownloader.cs b/src/lib-csharp/Sources/HttpClientFileDownloader.cs index d0a2e7d0..85e1488f 100644 --- a/src/lib-csharp/Sources/HttpClientFileDownloader.cs +++ b/src/lib-csharp/Sources/HttpClientFileDownloader.cs @@ -18,48 +18,54 @@ namespace Velopack.Sources public static ProductInfoHeaderValue UserAgent => new("Velopack", VelopackRuntimeInfo.VelopackNugetVersion.ToFullString()); /// - public virtual async Task DownloadFile(string url, string targetFile, Action progress, IDictionary? headers, double timeout, CancellationToken cancelToken = default) + public virtual async Task DownloadFile(string url, string targetFile, Action progress, IDictionary? headers, double timeout, + CancellationToken cancelToken = default) { using var client = CreateHttpClient(headers, timeout); + await TryDownloadThenLowercase( + async (reqUrl) => { + using (var fs = File.Open(targetFile, FileMode.Create)) { + await DownloadToStreamInternal(client, reqUrl, fs, progress, cancelToken).ConfigureAwait(false); + } - try { - using (var fs = File.Open(targetFile, FileMode.Create)) { - await DownloadToStreamInternal(client, url, fs, progress, cancelToken).ConfigureAwait(false); - } - } catch { - // NB: Some super brain-dead services are case-sensitive yet - // corrupt case on upload. I can't even. - using (var fs = File.Open(targetFile, FileMode.Create)) { - await DownloadToStreamInternal(client, url.ToLower(), fs, progress, cancelToken).ConfigureAwait(false); - } - } + return true; + }, + url).ConfigureAwait(false); } /// public virtual async Task DownloadBytes(string url, IDictionary? headers, double timeout) { using var client = CreateHttpClient(headers, timeout); - - try { - return await client.GetByteArrayAsync(url).ConfigureAwait(false); - } catch { - // NB: Some super brain-dead services are case-sensitive yet - // corrupt case on upload. I can't even. - return await client.GetByteArrayAsync(url.ToLower()).ConfigureAwait(false); - } + return await TryDownloadThenLowercase(client.GetByteArrayAsync, url).ConfigureAwait(false); } /// public virtual async Task DownloadString(string url, IDictionary? headers, double timeout) { using var client = CreateHttpClient(headers, timeout); + return await TryDownloadThenLowercase(client.GetStringAsync, url).ConfigureAwait(false); + } + /// + /// Tries to download a string from the specified url. If it fails, it will attempt to + /// download the string again with the url lowercased. This is useful for services that + /// are case-sensitive yet corrupt the case on upload. + /// + protected virtual async Task TryDownloadThenLowercase(Func> downloadFunc, string url) + { try { - return await client.GetStringAsync(url).ConfigureAwait(false); + return await downloadFunc(url).ConfigureAwait(false); } catch { - // NB: Some super brain-dead services are case-sensitive yet - // corrupt case on upload. I can't even. - return await client.GetStringAsync(url.ToLower()).ConfigureAwait(false); + try { + // NB: Some super brain-dead services are case-sensitive yet + // corrupt case on upload. I can't even. + return await downloadFunc(url.ToLower()).ConfigureAwait(false); + } catch { + // we don't want to throw the "fallback" exception + } + + throw; // rethrow the original exception } } @@ -67,7 +73,8 @@ namespace Velopack.Sources /// Asynchronously downloads a remote url to the specified destination stream while /// providing progress updates. /// - protected virtual async Task DownloadToStreamInternal(HttpClient client, string requestUri, Stream destination, Action? progress = null, CancellationToken cancelToken = default) + protected virtual async Task DownloadToStreamInternal(HttpClient client, string requestUri, Stream destination, Action? progress = null, + CancellationToken cancelToken = default) { // https://stackoverflow.com/a/46497896/184746 // Get the http headers first to examine the content length @@ -128,8 +135,7 @@ namespace Velopack.Sources var client = new HttpClient(CreateHttpClientHandler()); client.DefaultRequestHeaders.UserAgent.Add(UserAgent); - foreach (var header in headers ?? new Dictionary()) - { + foreach (var header in headers ?? new Dictionary()) { client.DefaultRequestHeaders.Add(header.Key, header.Value); } @@ -137,4 +143,4 @@ namespace Velopack.Sources return client; } } -} +} \ No newline at end of file