mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Refactor downloader methods to use a dictionary for headers, has effect on Sources
This commit is contained in:
@@ -35,12 +35,14 @@ namespace Velopack.Sources
|
|||||||
protected virtual string? AccessToken { get; }
|
protected virtual string? AccessToken { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Bearer token used in the request.
|
/// The Bearer or other type of Authorization header used to authenticate against the Api.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual string? Authorization => string.IsNullOrWhiteSpace(AccessToken) ? null : "Bearer " + AccessToken;
|
protected abstract (string Name, string Value) Authorization { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public GitBase(string repoUrl, string? accessToken, bool prerelease, IFileDownloader? downloader = null)
|
/// Base constructor.
|
||||||
|
/// </summary>
|
||||||
|
protected GitBase(string repoUrl, string? accessToken, bool prerelease, IFileDownloader? downloader = null)
|
||||||
{
|
{
|
||||||
RepoUri = new Uri(repoUrl.TrimEnd('/'));
|
RepoUri = new Uri(repoUrl.TrimEnd('/'));
|
||||||
AccessToken = accessToken;
|
AccessToken = accessToken;
|
||||||
@@ -55,7 +57,12 @@ namespace Velopack.Sources
|
|||||||
// this might be a browser url or an api url (depending on whether we have a AccessToken or not)
|
// this might be a browser url or an api url (depending on whether we have a AccessToken or not)
|
||||||
// https://docs.github.com/en/rest/reference/releases#get-a-release-asset
|
// https://docs.github.com/en/rest/reference/releases#get-a-release-asset
|
||||||
var assetUrl = GetAssetUrlFromName(githubEntry.Release, releaseEntry.FileName);
|
var assetUrl = GetAssetUrlFromName(githubEntry.Release, releaseEntry.FileName);
|
||||||
return Downloader.DownloadFile(assetUrl, localFile, progress, Authorization, "application/octet-stream", cancelToken: cancelToken);
|
return Downloader.DownloadFile(assetUrl, localFile, progress,
|
||||||
|
new Dictionary<string, string> {
|
||||||
|
[Authorization.Name] = Authorization.Value,
|
||||||
|
["Accept"] = "application/octet-stream"
|
||||||
|
},
|
||||||
|
cancelToken: cancelToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"Expected releaseEntry to be {nameof(GitBaseAsset)} but got {releaseEntry.GetType().Name}.");
|
throw new ArgumentException($"Expected releaseEntry to be {nameof(GitBaseAsset)} but got {releaseEntry.GetType().Name}.");
|
||||||
@@ -84,7 +91,12 @@ namespace Velopack.Sources
|
|||||||
logger.Trace(ex.ToString());
|
logger.Trace(ex.ToString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var releaseBytes = await Downloader.DownloadBytes(assetUrl, Authorization, "application/octet-stream").ConfigureAwait(false);
|
var releaseBytes = await Downloader.DownloadBytes(assetUrl,
|
||||||
|
new Dictionary<string, string> {
|
||||||
|
[Authorization.Name] = Authorization.Value,
|
||||||
|
["Accept"] = "application/octet-stream"
|
||||||
|
}
|
||||||
|
).ConfigureAwait(false);
|
||||||
var txt = CoreUtil.RemoveByteOrderMarkerIfPresent(releaseBytes);
|
var txt = CoreUtil.RemoveByteOrderMarkerIfPresent(releaseBytes);
|
||||||
var feed = VelopackAssetFeed.FromJson(txt);
|
var feed = VelopackAssetFeed.FromJson(txt);
|
||||||
foreach (var f in feed.Assets) {
|
foreach (var f in feed.Assets) {
|
||||||
|
|||||||
@@ -81,11 +81,10 @@ namespace Velopack.Sources
|
|||||||
: base(repoUrl, accessToken, prerelease, downloader)
|
: base(repoUrl, accessToken, prerelease, downloader)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// The authorization token used in the request.
|
/// <inheritdoc cref="Authorization"/>
|
||||||
/// Overwrite it to token
|
protected override (string Name, string Value) Authorization => ("Authorization", $"token {AccessToken}");
|
||||||
/// </summary>
|
|
||||||
protected override string? Authorization => string.IsNullOrWhiteSpace(AccessToken) ? null : "token " + AccessToken;
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task<GiteaRelease[]> GetReleases(bool includePrereleases)
|
protected override async Task<GiteaRelease[]> GetReleases(bool includePrereleases)
|
||||||
{
|
{
|
||||||
@@ -97,7 +96,12 @@ namespace Velopack.Sources
|
|||||||
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?limit={perPage}&page={page}&draft=false";
|
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?limit={perPage}&page={page}&draft=false";
|
||||||
var baseUri = GetApiBaseUrl(RepoUri);
|
var baseUri = GetApiBaseUrl(RepoUri);
|
||||||
var getReleasesUri = new Uri(baseUri, releasesPath);
|
var getReleasesUri = new Uri(baseUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(), Authorization, "application/json").ConfigureAwait(false);
|
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
||||||
|
new Dictionary<string, string> {
|
||||||
|
[Authorization.Name] = Authorization.Value,
|
||||||
|
["Accept"] = "application/json"
|
||||||
|
}
|
||||||
|
).ConfigureAwait(false);
|
||||||
var releases = CompiledJson.DeserializeGiteaReleaseList(response);
|
var releases = CompiledJson.DeserializeGiteaReleaseList(response);
|
||||||
if (releases == null) return new GiteaRelease[0];
|
if (releases == null) return new GiteaRelease[0];
|
||||||
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ namespace Velopack.Sources
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Authorization"/>
|
||||||
|
protected override (string Name, string Value) Authorization => ("Authorization", $"Bearer {AccessToken}");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task<GithubRelease[]> GetReleases(bool includePrereleases)
|
protected override async Task<GithubRelease[]> GetReleases(bool includePrereleases)
|
||||||
{
|
{
|
||||||
@@ -96,7 +99,12 @@ namespace Velopack.Sources
|
|||||||
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
||||||
var baseUri = GetApiBaseUrl(RepoUri);
|
var baseUri = GetApiBaseUrl(RepoUri);
|
||||||
var getReleasesUri = new Uri(baseUri, releasesPath);
|
var getReleasesUri = new Uri(baseUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(), Authorization, "application/vnd.github.v3+json").ConfigureAwait(false);
|
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
||||||
|
new Dictionary<string, string> {
|
||||||
|
[Authorization.Name] = Authorization.Value,
|
||||||
|
["Accept"] = "application/vnd.github.v3+json"
|
||||||
|
}
|
||||||
|
).ConfigureAwait(false);
|
||||||
var releases = CompiledJson.DeserializeGithubReleaseList(response);
|
var releases = CompiledJson.DeserializeGithubReleaseList(response);
|
||||||
if (releases == null) return Array.Empty<GithubRelease>();
|
if (releases == null) return Array.Empty<GithubRelease>();
|
||||||
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Velopack.Util;
|
using Velopack.Util;
|
||||||
@@ -98,6 +99,9 @@ namespace Velopack.Sources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class GitlabSource : GitBase<GitlabRelease>
|
public class GitlabSource : GitBase<GitlabRelease>
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="Authorization"/>
|
||||||
|
protected override (string Name, string Value) Authorization => ("PRIVATE-TOKEN", AccessToken ?? string.Empty);
|
||||||
|
|
||||||
/// <inheritdoc cref="GitlabSource" />
|
/// <inheritdoc cref="GitlabSource" />
|
||||||
/// <param name="repoUrl">
|
/// <param name="repoUrl">
|
||||||
/// The URL of the GitLab repository to download releases from
|
/// The URL of the GitLab repository to download releases from
|
||||||
@@ -156,7 +160,11 @@ namespace Velopack.Sources
|
|||||||
var releasesPath = $"{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
var releasesPath = $"{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
||||||
var baseUri = new Uri("https://gitlab.com");
|
var baseUri = new Uri("https://gitlab.com");
|
||||||
var getReleasesUri = new Uri(baseUri, releasesPath);
|
var getReleasesUri = new Uri(baseUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(), Authorization).ConfigureAwait(false);
|
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
||||||
|
new Dictionary<string, string> {
|
||||||
|
[Authorization.Name] = Authorization.Value,
|
||||||
|
["Accept"] = "application/json"
|
||||||
|
}).ConfigureAwait(false);
|
||||||
var releases = CompiledJson.DeserializeGitlabReleaseList(response);
|
var releases = CompiledJson.DeserializeGitlabReleaseList(response);
|
||||||
if (releases == null) return new GitlabRelease[0];
|
if (releases == null) return new GitlabRelease[0];
|
||||||
return releases.OrderByDescending(d => d.ReleasedAt).Where(x => includePrereleases || !x.UpcomingRelease).ToArray();
|
return releases.OrderByDescending(d => d.ReleasedAt).Where(x => includePrereleases || !x.UpcomingRelease).ToArray();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -18,9 +19,9 @@ namespace Velopack.Sources
|
|||||||
public static ProductInfoHeaderValue UserAgent => new("Velopack", VelopackRuntimeInfo.VelopackNugetVersion.ToFullString());
|
public static ProductInfoHeaderValue UserAgent => new("Velopack", VelopackRuntimeInfo.VelopackNugetVersion.ToFullString());
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual async Task DownloadFile(string url, string targetFile, Action<int> progress, string? authorization, string? accept, double timeout, CancellationToken cancelToken = default)
|
public virtual async Task DownloadFile(string url, string targetFile, Action<int> progress, IDictionary<string, string>? headers, double timeout, CancellationToken cancelToken = default)
|
||||||
{
|
{
|
||||||
using var client = CreateHttpClient(authorization, accept, timeout);
|
using var client = CreateHttpClient(headers, timeout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using (var fs = File.Open(targetFile, FileMode.Create)) {
|
using (var fs = File.Open(targetFile, FileMode.Create)) {
|
||||||
@@ -36,9 +37,9 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual async Task<byte[]> DownloadBytes(string url, string? authorization, string? accept, double timeout)
|
public virtual async Task<byte[]> DownloadBytes(string url, IDictionary<string, string>? headers, double timeout)
|
||||||
{
|
{
|
||||||
using var client = CreateHttpClient(authorization, accept, timeout);
|
using var client = CreateHttpClient(headers, timeout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await client.GetByteArrayAsync(url).ConfigureAwait(false);
|
return await client.GetByteArrayAsync(url).ConfigureAwait(false);
|
||||||
@@ -50,9 +51,9 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual async Task<string> DownloadString(string url, string? authorization, string? accept, double timeout)
|
public virtual async Task<string> DownloadString(string url, IDictionary<string, string>? headers, double timeout)
|
||||||
{
|
{
|
||||||
using var client = CreateHttpClient(authorization, accept, timeout);
|
using var client = CreateHttpClient(headers, timeout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await client.GetStringAsync(url).ConfigureAwait(false);
|
return await client.GetStringAsync(url).ConfigureAwait(false);
|
||||||
@@ -123,16 +124,15 @@ namespace Velopack.Sources
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="HttpClient"/> for every request.
|
/// Creates a new <see cref="HttpClient"/> for every request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual HttpClient CreateHttpClient(string? authorization, string? accept, double timeout = 30)
|
protected virtual HttpClient CreateHttpClient(IDictionary<string, string>? headers, double timeout)
|
||||||
{
|
{
|
||||||
var client = new HttpClient(CreateHttpClientHandler());
|
var client = new HttpClient(CreateHttpClientHandler());
|
||||||
client.DefaultRequestHeaders.UserAgent.Add(UserAgent);
|
client.DefaultRequestHeaders.UserAgent.Add(UserAgent);
|
||||||
|
|
||||||
if (authorization != null)
|
foreach (var header in headers ?? new Dictionary<string, string>())
|
||||||
client.DefaultRequestHeaders.Add("Authorization", authorization);
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
if (accept != null)
|
}
|
||||||
client.DefaultRequestHeaders.Add("Accept", accept);
|
|
||||||
|
|
||||||
client.Timeout = TimeSpan.FromMinutes(timeout);
|
client.Timeout = TimeSpan.FromMinutes(timeout);
|
||||||
return client;
|
return client;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -14,31 +15,26 @@ namespace Velopack.Sources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="url">The url which will be downloaded.</param>
|
/// <param name="url">The url which will be downloaded.</param>
|
||||||
/// <param name="targetFile">
|
/// <param name="targetFile">
|
||||||
/// The local path where the file will be stored
|
/// The local path where the file will be stored
|
||||||
/// If a file exists at this path, it will be overwritten.</param>
|
/// If a file exists at this path, it will be overwritten.</param>
|
||||||
/// <param name="progress">
|
/// <param name="progress">
|
||||||
/// A delegate for reporting download progress, with expected values from 0-100.
|
/// A delegate for reporting download progress, with expected values from 0-100.
|
||||||
/// </param>
|
|
||||||
/// <param name="authorization">
|
|
||||||
/// Text to be sent in the 'Authorization' header of the request.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="accept">
|
|
||||||
/// Text to be sent in the 'Accept' header of the request.
|
|
||||||
/// </param>
|
/// </param>
|
||||||
|
/// <param name="headers">Headers that can be passed to Http Downloader, e.g. Accept or Authorization.</param>
|
||||||
/// <param name="timeout">
|
/// <param name="timeout">
|
||||||
/// The maximum time in minutes to wait for the download to complete.
|
/// The maximum time in minutes to wait for the download to complete.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="cancelToken">Optional token to cancel the request.</param>
|
/// <param name="cancelToken">Optional token to cancel the request.</param>
|
||||||
Task DownloadFile(string url, string targetFile, Action<int> progress, string? authorization = null, string? accept = null, double timeout = 30, CancellationToken cancelToken = default);
|
Task DownloadFile(string url, string targetFile, Action<int> progress, IDictionary<string, string>? headers = null, double timeout = 30, CancellationToken cancelToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a byte array containing the contents of the file at the specified url
|
/// Returns a byte array containing the contents of the file at the specified url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<byte[]> DownloadBytes(string url, string? authorization = null, string? accept = null, double timeout = 30);
|
Task<byte[]> DownloadBytes(string url, IDictionary<string, string>? headers = null, double timeout = 30);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a string containing the contents of the specified url
|
/// Returns a string containing the contents of the specified url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<string> DownloadString(string url, string? authorization = null, string? accept = null, double timeout = 30);
|
Task<string> DownloadString(string url, IDictionary<string, string>? headers = null, double timeout = 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,23 +7,21 @@ public class FakeDownloader : IFileDownloader
|
|||||||
{
|
{
|
||||||
public string LastUrl { get; private set; }
|
public string LastUrl { get; private set; }
|
||||||
public string LastLocalFile { get; private set; }
|
public string LastLocalFile { get; private set; }
|
||||||
public string LastAuthHeader { get; private set; }
|
public IDictionary<string, string>? LastHeaders { get; private set; }
|
||||||
public string LastAcceptHeader { get; private set; }
|
|
||||||
public byte[] MockedResponseBytes { get; set; } = [];
|
public byte[] MockedResponseBytes { get; set; } = [];
|
||||||
public bool WriteMockLocalFile { get; set; } = false;
|
public bool WriteMockLocalFile { get; set; } = false;
|
||||||
|
|
||||||
public Task<byte[]> DownloadBytes(string url, string auth, string acc, double timeout = 30)
|
public Task<byte[]> DownloadBytes(string url, IDictionary<string, string> headers, double timeout = 30)
|
||||||
{
|
{
|
||||||
LastUrl = url;
|
LastUrl = url;
|
||||||
LastAuthHeader = auth;
|
LastHeaders = headers;
|
||||||
LastAcceptHeader = acc;
|
|
||||||
return Task.FromResult(MockedResponseBytes);
|
return Task.FromResult(MockedResponseBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadFile(string url, string targetFile, Action<int> progress, string auth, string acc, double timeout, CancellationToken token)
|
public async Task DownloadFile(string url, string targetFile, Action<int> progress, IDictionary<string, string> headers, double timeout = 30, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
LastLocalFile = targetFile;
|
LastLocalFile = targetFile;
|
||||||
var resp = await DownloadBytes(url, auth, acc);
|
var resp = await DownloadBytes(url, headers);
|
||||||
progress?.Invoke(25);
|
progress?.Invoke(25);
|
||||||
progress?.Invoke(50);
|
progress?.Invoke(50);
|
||||||
progress?.Invoke(75);
|
progress?.Invoke(75);
|
||||||
@@ -32,8 +30,8 @@ public class FakeDownloader : IFileDownloader
|
|||||||
File.WriteAllBytes(targetFile, resp);
|
File.WriteAllBytes(targetFile, resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> DownloadString(string url, string auth, string acc, double timeout = 30)
|
public async Task<string> DownloadString(string url, IDictionary<string, string> headers, double timeout = 30)
|
||||||
{
|
{
|
||||||
return Encoding.UTF8.GetString(await DownloadBytes(url, auth, acc));
|
return Encoding.UTF8.GetString(await DownloadBytes(url, headers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ internal class FakeFixtureRepository : IFileDownloader
|
|||||||
_releases = releases;
|
_releases = releases;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<byte[]> DownloadBytes(string url, string authorization = null, string accept = null, double timeout = 30)
|
public Task<byte[]> DownloadBytes(string url, IDictionary<string, string> headers = null, double timeout = 30)
|
||||||
{
|
{
|
||||||
if (url.Contains($"/{_releasesName}?")) {
|
if (url.Contains($"/{_releasesName}?")) {
|
||||||
MemoryStream ms = new MemoryStream();
|
MemoryStream ms = new MemoryStream();
|
||||||
@@ -82,7 +82,7 @@ internal class FakeFixtureRepository : IFileDownloader
|
|||||||
return Task.FromResult(File.ReadAllBytes(filePath));
|
return Task.FromResult(File.ReadAllBytes(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DownloadFile(string url, string targetFile, Action<int> progress, string authorization = null, string accept = null, double timeout = 30,
|
public Task DownloadFile(string url, string targetFile, Action<int> progress, IDictionary<string, string> headers = null, double timeout = 30,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var rel = _releases.FirstOrDefault(r => url.EndsWith(r.OriginalFilename));
|
var rel = _releases.FirstOrDefault(r => url.EndsWith(r.OriginalFilename));
|
||||||
@@ -99,7 +99,7 @@ internal class FakeFixtureRepository : IFileDownloader
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> DownloadString(string url, string authorization = null, string accept = null, double timeout = 30)
|
public Task<string> DownloadString(string url, IDictionary<string, string> headers = null, double timeout = 30)
|
||||||
{
|
{
|
||||||
if (url.Contains($"/{_releasesName}?")) {
|
if (url.Contains($"/{_releasesName}?")) {
|
||||||
MemoryStream ms = new MemoryStream();
|
MemoryStream ms = new MemoryStream();
|
||||||
|
|||||||
Reference in New Issue
Block a user