Skip to content

Commit 2414dfa

Browse files
committed
重构升级库基础功能。 🔯
1 parent f5f95de commit 2414dfa

File tree

7 files changed

+154
-108
lines changed

7 files changed

+154
-108
lines changed

upgrading/.shared/HttpUtility.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace Zongsoft.Upgrading;
3535

3636
internal static class HttpUtility
3737
{
38-
public static HttpClient CreateHttpClient(string baseAddress)
38+
public static HttpClient CreateHttpClient(string baseAddress, TimeSpan timeout)
3939
{
4040
if(string.IsNullOrEmpty(baseAddress))
4141
throw new ArgumentNullException(nameof(baseAddress));
@@ -49,7 +49,11 @@ public static HttpClient CreateHttpClient(string baseAddress)
4949
AutomaticDecompression = DecompressionMethods.All,
5050
};
5151

52-
return new HttpClient(handler) { BaseAddress = new Uri(baseAddress) };
52+
return new HttpClient(handler)
53+
{
54+
BaseAddress = new Uri(baseAddress),
55+
Timeout = timeout.Ticks > TimeSpan.TicksPerSecond ? timeout : TimeSpan.FromSeconds(30),
56+
};
5357
}
5458

5559
public static System.Text.Encoding GetEncoding(HttpContent content)

upgrading/client/Downloader.Web.cs

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
using System;
3131
using System.IO;
32+
using System.Net.Http;
3233
using System.Threading;
3334
using System.Threading.Tasks;
3435

@@ -38,35 +39,82 @@ partial class Downloader
3839
{
3940
internal sealed class WebDownloader(Fetcher.WebFetcher fetcher) : Downloader
4041
{
42+
#region 私有字段
43+
private Func<HttpClient, Package, CancellationToken, ValueTask<Stream>> _downloader;
44+
private readonly Func<HttpClient, Package, CancellationToken, ValueTask<Stream>>[] _downloaders =
45+
[
46+
Download1Async,
47+
Download2Async,
48+
Download3Async,
49+
Download4Async,
50+
Download5Async,
51+
Download6Async,
52+
];
53+
4154
private readonly Fetcher.WebFetcher _fetcher = fetcher ?? throw new ArgumentNullException(nameof(fetcher));
55+
#endregion
4256

57+
#region 重写方法
4358
protected override async ValueTask<Stream> DownloadAsync(Package package, CancellationToken cancellation)
4459
{
4560
var client = _fetcher.Client;
4661
if(client == null)
4762
return null;
4863

49-
var response = await client.GetAsync($"Download/{package.Name}/{package.GetRuntimeIdentifier()}", cancellation);
50-
if(response != null && response.IsSuccessStatusCode)
51-
return await response.Content.ReadAsStreamAsync(cancellation);
64+
if(package.Properties.TryGetValue("url", out var url) && url != null)
65+
{
66+
var stream = await DownloadAsync(client, url, cancellation);
67+
68+
if(stream != null)
69+
return stream;
70+
}
71+
72+
if(package.Properties.TryGetValue("download", out url) && url != null)
73+
{
74+
var stream = await DownloadAsync(client, url, cancellation);
5275

53-
response = await client.GetAsync($"packages/{package.Name}/{package.Version}/{package.GetRuntimeIdentifier()}/{Path.GetFileName(package.Path)}", cancellation);
54-
if(response != null && response.IsSuccessStatusCode)
55-
return await response.Content.ReadAsStreamAsync(cancellation);
76+
if(stream != null)
77+
return stream;
78+
}
5679

57-
response = await client.GetAsync($"packages/{package.Name}/{package.Version}/{Path.GetFileName(package.Path)}", cancellation);
58-
if(response != null && response.IsSuccessStatusCode)
59-
return await response.Content.ReadAsStreamAsync(cancellation);
80+
if(_downloader != null)
81+
return await _downloader(client, package, cancellation);
6082

61-
response = await client.GetAsync($"packages/{Path.GetFileName(package.Path)}", cancellation);
62-
if(response != null && response.IsSuccessStatusCode)
63-
return await response.Content.ReadAsStreamAsync(cancellation);
83+
for(int i = 0; i < _downloaders.Length; i++)
84+
{
85+
var stream = await _downloaders[i](client, package, cancellation);
6486

65-
response = await client.GetAsync($"packages/{package.Name}@{package.Version}_{package.GetRuntimeIdentifier()}{Path.GetExtension(package.Path)}", cancellation);
66-
if(response != null && response.IsSuccessStatusCode)
67-
return await response.Content.ReadAsStreamAsync(cancellation);
87+
if(stream != null)
88+
{
89+
_downloader = _downloaders[i];
90+
return stream;
91+
}
92+
}
6893

6994
return null;
7095
}
96+
#endregion
97+
98+
#region 私有方法
99+
static ValueTask<Stream> Download1Async(HttpClient client, Package package, CancellationToken cancellation) => DownloadAsync(client, $"Download/{package.Name}/{package.GetRuntimeIdentifier()}", cancellation);
100+
static ValueTask<Stream> Download2Async(HttpClient client, Package package, CancellationToken cancellation) => DownloadAsync(client, $"packages/{package.Name}/{package.Version}/{package.GetRuntimeIdentifier()}/{Path.GetFileName(package.Path)}", cancellation);
101+
static ValueTask<Stream> Download3Async(HttpClient client, Package package, CancellationToken cancellation) => DownloadAsync(client, $"packages/{package.Name}/{package.Version}/{Path.GetFileName(package.Path)}", cancellation);
102+
static ValueTask<Stream> Download4Async(HttpClient client, Package package, CancellationToken cancellation) => DownloadAsync(client, $"packages/{Path.GetFileName(package.Path)}", cancellation);
103+
static ValueTask<Stream> Download5Async(HttpClient client, Package package, CancellationToken cancellation) => DownloadAsync(client, $"packages/{package.Name}@{package.Version}_{package.GetRuntimeIdentifier()}{Path.GetExtension(package.Path)}", cancellation);
104+
static ValueTask<Stream> Download6Async(HttpClient client, Package package, CancellationToken cancellation)
105+
{
106+
var path = Path.IsPathFullyQualified(package.Path) ?
107+
Path.GetRelativePath(Path.GetPathRoot(package.Path), package.Path): package.Path;
108+
return DownloadAsync(client, path, cancellation);
109+
}
110+
111+
static async ValueTask<Stream> DownloadAsync(HttpClient client, string url, CancellationToken cancellation)
112+
{
113+
var response = await client.GetAsync(url, cancellation);
114+
115+
return response != null && response.IsSuccessStatusCode ?
116+
await response.Content.ReadAsStreamAsync(cancellation) : null;
117+
}
118+
#endregion
71119
}
72120
}

upgrading/client/Downloader.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,36 @@ namespace Zongsoft.Upgrading;
3636

3737
public abstract partial class Downloader : IDownloader
3838
{
39+
#region 构造函数
40+
protected Downloader() { }
41+
#endregion
42+
43+
#region 公共方法
3944
public async ValueTask DownloadAsync(string directory, Package package, CancellationToken cancellation = default)
4045
{
4146
ArgumentNullException.ThrowIfNull(package);
4247
ArgumentNullException.ThrowIfNullOrEmpty(directory);
4348

49+
//如果指定的目标目录不存在则直接返回
4450
if(!Directory.Exists(directory))
4551
return;
4652

53+
//下载升级包文件
4754
using var source = await this.DownloadAsync(package, cancellation);
4855
if(source == null || !source.CanRead)
4956
return;
5057

58+
//将下载的升级包文件保存到指定目录
5159
using var stream = File.OpenWrite(this.GetFilePath(directory, package));
5260
await source.CopyToAsync(stream, cancellation);
5361
}
62+
#endregion
5463

64+
#region 抽象方法
5565
protected abstract ValueTask<Stream> DownloadAsync(Package package, CancellationToken cancellation);
56-
protected virtual string GetFilePath(string directory, Package package) => Path.Combine(directory, Path.GetFileName(package.Path));
66+
#endregion
67+
68+
#region 虚拟方法
69+
protected virtual string GetFilePath(string directory, Package package) => Path.Combine(directory, "packages", Path.GetFileName(package.Path));
70+
#endregion
5771
}

upgrading/client/Fetcher.Web.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace Zongsoft.Upgrading;
3939

4040
partial class Fetcher
4141
{
42-
internal sealed class WebFetcher : FetcherBase
42+
internal sealed class WebFetcher : Fetcher
4343
{
4444
#region 构造函数
4545
public WebFetcher() : base("Web") => this.Downloader = new Downloader.WebDownloader(this);
@@ -52,10 +52,15 @@ public HttpClient Client
5252
{
5353
if(field == null)
5454
{
55-
var settings = this.GetSettings();
55+
var settings = this.Settings;
5656

57-
if(settings != null && settings.TryGetValue("server", out var url) && !string.IsNullOrEmpty(url))
58-
return field = HttpUtility.CreateHttpClient(url);
57+
if(settings != null)
58+
{
59+
var timeout = settings.TryGetValue("timeout", out var value) && Common.TimeSpanUtility.TryParse(value, out var timespan) ? timespan : TimeSpan.Zero;
60+
61+
if(settings.TryGetValue("server", out var url) && !string.IsNullOrEmpty(url))
62+
return field = HttpUtility.CreateHttpClient(url, timeout);
63+
}
5964
}
6065

6166
return field;

upgrading/client/Fetcher.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
using System;
3131
using System.IO;
32+
using System.Linq;
3233
using System.Threading;
3334
using System.Threading.Tasks;
3435
using System.Collections.Generic;
@@ -38,7 +39,7 @@
3839

3940
namespace Zongsoft.Upgrading;
4041

41-
public sealed partial class Fetcher
42+
public abstract partial class Fetcher
4243
{
4344
#region 私有字段
4445
private static readonly Dictionary<string, IFetcher> _fetchers;
@@ -123,3 +124,61 @@ public static async ValueTask<string> FetchAsync(string name, Version version, C
123124
}
124125
#endregion
125126
}
127+
128+
partial class Fetcher : IFetcher
129+
{
130+
#region 构造函数
131+
protected Fetcher(string name, IDownloader downloader = null)
132+
{
133+
this.Name = name ?? throw new ArgumentNullException(nameof(name));
134+
this.Downloader = downloader;
135+
}
136+
#endregion
137+
138+
#region 保护属性
139+
protected string Name { get; }
140+
protected IDownloader Downloader { get; set; }
141+
protected IConnectionSettings Settings => field ??= ApplicationContext.Current?.Configuration.GetConnectionSettings(nameof(Upgrading), this.Name);
142+
#endregion
143+
144+
#region 显式实现
145+
string IFetcher.Name => this.Name;
146+
IDownloader IFetcher.Downloader => this.Downloader;
147+
async ValueTask<Upgrader.Manifest> IFetcher.FetchAsync(Version version, CancellationToken cancellation)
148+
{
149+
var baseline = default(Package);
150+
var deltas = new List<Package>();
151+
var upgradingVersion = version;
152+
var currentlyVersion = Utility.ApplicationVersion;
153+
154+
//获取升级包集合
155+
var packages = this.OnFetchAsync(version, cancellation);
156+
157+
//遍历所有可升级包
158+
await foreach(var package in packages)
159+
{
160+
//筛选出满足要求的升级包:
161+
//未废弃的,应用名、平台和架构匹配的,版本号大于当前版本且小于等于升级版本
162+
if(!package.Deprecated &&
163+
Utility.Platform == package.Platform &&
164+
Utility.Architecture == package.Architecture &&
165+
string.Equals(Utility.ApplicationName, package.Name, StringComparison.OrdinalIgnoreCase) &&
166+
package.Version > currentlyVersion && (upgradingVersion.IsZero() || package.Version <= upgradingVersion))
167+
{
168+
if(package.Kind == PackageKind.Delta)
169+
deltas.Add(package);
170+
else if(baseline == null || package.Version > baseline.Version)
171+
baseline = package;
172+
}
173+
}
174+
175+
return baseline == null ?
176+
new(deltas.OrderBy(delta => delta.Version).ToArray()) :
177+
new(baseline, deltas.Where(delta => delta.Version > baseline.Version).OrderBy(delta => delta.Version).ToArray());
178+
}
179+
#endregion
180+
181+
#region 抽象方法
182+
protected abstract IAsyncEnumerable<Package> OnFetchAsync(Version version, CancellationToken cancellation);
183+
#endregion
184+
}

upgrading/client/FetcherBase.cs

Lines changed: 0 additions & 84 deletions
This file was deleted.

upgrading/client/Zongsoft.Upgrading.Client.option

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<option path="/Upgrading">
55
<connectionSettings default="Web">
66
<connectionSetting connectionSetting.name="Web"
7-
value="server=http://127.0.0.1:7969/Upgrading/Releases" />
7+
value="server=http://127.0.0.1:7969/Upgrading/Releases;timeout=30s" />
88

99
<connectionSetting connectionSetting.name="File" driver="amazon.s3"
1010
value="server=http://127.0.0.1:9000;region=cn-north-1;accessKey=rustfsadmin;secretKey=rustfsadmin;path=/upgrading/packages" />

0 commit comments

Comments
 (0)