-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathHttpDownloadSource.cs
More file actions
192 lines (181 loc) · 8.49 KB
/
Copy pathHttpDownloadSource.cs
File metadata and controls
192 lines (181 loc) · 8.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GeneralUpdate.Core.Configuration;
using GeneralUpdate.Core.Download.Models;
using GeneralUpdate.Core.Network;
namespace GeneralUpdate.Core.Download.Sources;
/// <summary>
/// HTTP download source that retrieves update asset lists by calling a version-validation API.
/// Performs version validation for both Client and Upgrade app types,
/// then maps the server responses into <see cref="DownloadAsset"/> lists.
/// </summary>
/// <remarks>
/// <para>
/// This class implements <see cref="IDownloadSource"/> and serves as the standard HTTP-based
/// download source for the update workflow.
/// </para>
/// <para>
/// Workflow:
/// <list type="number">
/// <item>Calls <c>VersionService.Validate</c> for <c>AppType.Client</c> to validate the main application version.</item>
/// <item>Calls <c>VersionService.Validate</c> for <c>AppType.Upgrade</c> to validate the upgrade client version.
/// The upgrade client version uses <c>upgradeClientVersion</c> if provided; otherwise falls back to <c>clientVersion</c>.</item>
/// <item>Maps the version information returned by both validations into <see cref="DownloadAsset"/> objects.</item>
/// <item>Deduplicates assets by URL (both validation calls may return the same package).</item>
/// <item>Returns a <see cref="DownloadSourceResult"/> containing the asset list and update flags.</item>
/// </list>
/// </para>
/// <para>
/// The two-step validation design supports scenarios where the client's own update package
/// and the main application's update package come from the same version server but belong
/// to different application types.
/// </para>
/// </remarks>
public class HttpDownloadSource : Abstractions.IDownloadSource
{
private readonly string _updateUrl;
private readonly string _clientVersion;
private readonly string? _upgradeClientVersion;
private readonly string _appSecretKey;
private readonly PlatformType _platform;
private readonly string? _productId;
private readonly string? _scheme;
private readonly string? _token;
private readonly Security.AuthScheme _authScheme;
private readonly string? _basicUsername;
private readonly string? _basicPassword;
/// <summary>
/// Initializes a new instance of the <see cref="HttpDownloadSource"/> class
/// with the specified update configuration parameters.
/// </summary>
/// <param name="updateUrl">The URL of the version-validation API.</param>
/// <param name="clientVersion">The current client version string.</param>
/// <param name="upgradeClientVersion">
/// The current version string of the Upgrade application.
/// If null or empty, <paramref name="clientVersion"/> is used instead.
/// </param>
/// <param name="appSecretKey">The application secret key used for API authentication.</param>
/// <param name="platform">The target platform type (Windows, Linux, macOS, etc.).</param>
/// <param name="productId">An optional product identifier.</param>
/// <param name="scheme">An optional authentication scheme.</param>
/// <param name="token">An optional authentication token.</param>
/// <param name="authScheme">Explicitly selects the HTTP authentication method.</param>
/// <param name="basicUsername">An optional username for HTTP Basic Authentication.</param>
/// <param name="basicPassword">An optional password for HTTP Basic Authentication.</param>
public HttpDownloadSource(
string updateUrl,
string clientVersion,
string? upgradeClientVersion,
string appSecretKey,
PlatformType platform,
string? productId,
string? scheme,
string? token,
Security.AuthScheme authScheme,
string? basicUsername,
string? basicPassword)
{
_updateUrl = updateUrl;
_clientVersion = clientVersion;
_upgradeClientVersion = upgradeClientVersion;
_appSecretKey = appSecretKey;
_platform = platform;
_productId = productId;
_scheme = scheme;
_token = token;
_authScheme = authScheme;
_basicUsername = basicUsername;
_basicPassword = basicPassword;
}
/// <summary>
/// Asynchronously retrieves the list of downloadable assets by performing version validation
/// for both Client and Upgrade app types, merging the results, and deduplicating by URL.
/// </summary>
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
/// <returns>
/// A <see cref="DownloadSourceResult"/> containing the merged and deduplicated list of
/// <see cref="DownloadAsset"/> objects, along with flags indicating whether main and/or
/// upgrade updates are available.
/// </returns>
/// <remarks>
/// <para>
/// This method calls <c>VersionService.Validate</c> twice:
/// </para>
/// <list type="number">
/// <item>First with <c>AppType.Client</c> to validate the main application update.</item>
/// <item>Second with <c>AppType.Upgrade</c> to validate the upgrade program's own update.</item>
/// </list>
/// <para>
/// Both calls use the same <c>_updateUrl</c>, <c>_appSecretKey</c>, and <c>_platform</c>,
/// but with different <c>AppType</c> values. This allows the server to return different
/// update packages depending on the application type.
/// The resulting asset list is deduplicated by URL, as both validation calls may return the same packages.
/// </para>
/// </remarks>
public async Task<DownloadSourceResult> ListAsync(CancellationToken token = default)
{
var mainResp = await VersionService.Validate(
_updateUrl, _clientVersion, AppType.Client,
_appSecretKey, _platform, _productId,
_scheme, _token, _authScheme, _basicUsername, _basicPassword, token);
var upgradeResp = await VersionService.Validate(
_updateUrl, _upgradeClientVersion ?? _clientVersion, AppType.Upgrade,
_appSecretKey, _platform, _productId,
_scheme, _token, _authScheme, _basicUsername, _basicPassword, token);
// Check that returned VersionEntry items' AppType matches the requested type,
// rather than just checking Count > 0. The server may return items whose
// AppType doesn't match the request, which would mis-identify the scenario.
var hasMainUpdate = mainResp?.Body?.Any(v => v.AppType == (int)AppType.Client) == true;
var hasUpgradeUpdate = upgradeResp?.Body?.Any(v => v.AppType == (int)AppType.Upgrade) == true;
var assets = new List<DownloadAsset>();
if (mainResp?.Body != null)
{
foreach (var v in mainResp.Body)
assets.Add(MapVersionEntry(v));
}
if (upgradeResp?.Body != null)
{
foreach (var v in upgradeResp.Body)
assets.Add(MapVersionEntry(v));
}
// Deduplicate by URL — both Validate calls may return the same packages
return new DownloadSourceResult
{
Assets = assets.GroupBy(a => a.Url).Select(g => g.First()).ToList(),
HasMainUpdate = hasMainUpdate,
HasUpgradeUpdate = hasUpgradeUpdate
};
}
/// <summary>
/// Maps a server-returned <see cref="VersionEntry"/> object to a <see cref="DownloadAsset"/> object.
/// </summary>
/// <param name="v">The version information returned by the server.</param>
/// <returns>
/// A <see cref="DownloadAsset"/> instance populated with the name, URL, size, hash value,
/// version number, forced-update flag, freeze flag, authentication information, and other metadata.
/// </returns>
private static DownloadAsset MapVersionEntry(VersionEntry v)
{
return new DownloadAsset(
Name: v.Name ?? v.Version ?? "unknown",
Url: v.Url ?? string.Empty,
Size: v.Size ?? 0,
SHA256: v.Hash,
Version: v.Version ?? "0.0.0",
IsForcibly: v.IsForcibly == true,
IsFreeze: v.IsFreeze == true,
RecordId: v.RecordId,
UpgradeMode: v.UpgradeMode,
AppType: v.AppType,
IsCrossVersion: v.IsCrossVersion == true,
FromVersion: v.FromVersion,
MinClientVersion: v.MinClientVersion,
SourceArchiveHash: v.SourceArchiveHash,
TargetArchiveHash: v.TargetArchiveHash,
AuthScheme: v.AuthScheme,
AuthToken: v.AuthToken
);
}
}