Skip to content

Commit 73d7664

Browse files
CopilotJusterZhu
andauthored
Implement HTTP client requests for extension queries and downloads with automatic resumable download support (#135)
* Initial plan * Add ServerUrl to ExtensionHostConfig and update service to use it for downloads Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Remove dependency on DownloadUrl property and use ServerUrl for all downloads Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Add documentation for ServerUrl architecture and update DownloadUrl comments Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Add URL encoding for extension names and improve URL construction comments Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Implement HTTP GET requests for Query and Download methods with authentication support Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Add support for resumable downloads with Range header Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> * Implement automatic resume detection for downloads based on partial file existence Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
1 parent bed6a51 commit 73d7664

File tree

8 files changed

+404
-149
lines changed

8 files changed

+404
-149
lines changed

src/c#/ExtensionTest/Services/ExtensionServiceTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private List<AvailableExtension> CreateTestExtensions()
8181
private ExtensionService CreateExtensionService(List<AvailableExtension> extensions)
8282
{
8383
var updateQueue = new GeneralUpdate.Extension.Download.UpdateQueue();
84-
return new ExtensionService(extensions, "/tmp/test-downloads", updateQueue);
84+
return new ExtensionService(extensions, "/tmp/test-downloads", updateQueue, "https://test-server.com/api/extensions");
8585
}
8686

8787
[Fact]

src/c#/GeneralUpdate.Extension/Examples/ExtensionSystemExample.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public void Initialize()
2121
var hostVersion = new Version(1, 5, 0);
2222
var installPath = @"C:\MyApp\Extensions";
2323
var downloadPath = @"C:\MyApp\Temp\Downloads";
24+
var serverUrl = "https://your-server.com/api/extensions";
2425

2526
// Detect current platform
2627
var currentPlatform = DetectCurrentPlatform();
@@ -31,6 +32,7 @@ public void Initialize()
3132
HostVersion = hostVersion,
3233
InstallBasePath = installPath,
3334
DownloadPath = downloadPath,
35+
ServerUrl = serverUrl,
3436
TargetPlatform = currentPlatform,
3537
DownloadTimeout = 300 // 5 minutes
3638
};

src/c#/GeneralUpdate.Extension/ExtensionHostConfig.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ public class ExtensionHostConfig
2626
/// </summary>
2727
public string DownloadPath { get; set; } = null!;
2828

29+
/// <summary>
30+
/// Gets or sets the server URL for extension queries and downloads.
31+
/// This is the base URL used to construct Query and Download endpoints.
32+
/// Example: "https://your-server.com/api/extensions"
33+
/// </summary>
34+
public string ServerUrl { get; set; } = null!;
35+
2936
/// <summary>
3037
/// Gets or sets the target platform (Windows/Linux/macOS).
3138
/// Defaults to Windows if not specified.
@@ -62,6 +69,8 @@ public void Validate()
6269
throw new ArgumentNullException(nameof(InstallBasePath));
6370
if (string.IsNullOrWhiteSpace(DownloadPath))
6471
throw new ArgumentNullException(nameof(DownloadPath));
72+
if (string.IsNullOrWhiteSpace(ServerUrl))
73+
throw new ArgumentNullException(nameof(ServerUrl));
6574
}
6675
}
6776
}

src/c#/GeneralUpdate.Extension/GeneralExtensionHost.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ public GeneralExtensionHost(ExtensionHostConfig config)
109109
new List<Metadata.AvailableExtension>(),
110110
config.DownloadPath,
111111
_updateQueue,
112+
config.ServerUrl,
112113
config.HostVersion,
113114
_validator,
114115
config.DownloadTimeout,
@@ -130,6 +131,7 @@ public GeneralExtensionHost(ExtensionHostConfig config)
130131
/// <param name="hostVersion">The current host application version.</param>
131132
/// <param name="installBasePath">Base directory for extension installations.</param>
132133
/// <param name="downloadPath">Directory for downloading extension packages.</param>
134+
/// <param name="serverUrl">Server base URL for extension queries and downloads.</param>
133135
/// <param name="targetPlatform">The current platform (Windows/Linux/macOS).</param>
134136
/// <param name="downloadTimeout">Download timeout in seconds (default: 300).</param>
135137
/// <param name="authScheme">Optional HTTP authentication scheme (e.g., "Bearer", "Basic").</param>
@@ -140,6 +142,7 @@ public GeneralExtensionHost(
140142
Version hostVersion,
141143
string installBasePath,
142144
string downloadPath,
145+
string serverUrl,
143146
Metadata.TargetPlatform targetPlatform = Metadata.TargetPlatform.Windows,
144147
int downloadTimeout = 300,
145148
string? authScheme = null,
@@ -149,6 +152,7 @@ public GeneralExtensionHost(
149152
HostVersion = hostVersion,
150153
InstallBasePath = installBasePath,
151154
DownloadPath = downloadPath,
155+
ServerUrl = serverUrl,
152156
TargetPlatform = targetPlatform,
153157
DownloadTimeout = downloadTimeout,
154158
AuthScheme = authScheme,

src/c#/GeneralUpdate.Extension/Metadata/ExtensionDescriptor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ public class ExtensionDescriptor
7575

7676
/// <summary>
7777
/// Gets or sets the download URL for the extension package.
78+
/// NOTE: This field is optional and primarily for backward compatibility.
79+
/// The system constructs download URLs dynamically from the ServerUrl configured in ExtensionHostConfig.
80+
/// Format: {ServerUrl}/Download/{ExtensionName}
7881
/// </summary>
7982
[JsonPropertyName("downloadUrl")]
8083
public string? DownloadUrl { get; set; }

src/c#/GeneralUpdate.Extension/README.md

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ Note: This library is currently distributed as source. A NuGet package may be av
3434
using GeneralUpdate.Extension;
3535
using GeneralUpdate.Extension.Metadata;
3636

37-
// Create extension host
38-
var host = new ExtensionHost(
39-
hostVersion: new Version(1, 0, 0),
40-
installPath: @"C:\MyApp\Extensions",
41-
downloadPath: @"C:\MyApp\Downloads",
42-
targetPlatform: TargetPlatform.Windows);
37+
// Create extension host with configuration
38+
var config = new ExtensionHostConfig
39+
{
40+
HostVersion = new Version(1, 0, 0),
41+
InstallBasePath = @"C:\MyApp\Extensions",
42+
DownloadPath = @"C:\MyApp\Downloads",
43+
ServerUrl = "https://your-server.com/api/extensions",
44+
TargetPlatform = TargetPlatform.Windows
45+
};
46+
47+
var host = new GeneralExtensionHost(config);
4348

4449
// Load installed extensions
4550
host.LoadInstalledExtensions();
@@ -54,6 +59,32 @@ host.UpdateStateChanged += (sender, args) =>
5459
var installed = host.GetInstalledExtensions();
5560
```
5661

62+
## Server URL Architecture
63+
64+
The extension system uses a server-based architecture for querying and downloading extensions. The `ServerUrl` configured in `ExtensionHostConfig` serves as the base URL for all extension operations.
65+
66+
### URL Construction
67+
68+
The system automatically constructs the following endpoints:
69+
70+
- **Query Endpoint**: `{ServerUrl}/Query` - Used for searching and filtering extensions
71+
- **Download Endpoint**: `{ServerUrl}/Download/{ExtensionName}` - Used for downloading extension packages
72+
73+
### Example
74+
75+
If your `ServerUrl` is `https://your-server.com/api/extensions`:
76+
- Query endpoint: `https://your-server.com/api/extensions/Query`
77+
- Download for extension "my-extension": `https://your-server.com/api/extensions/Download/my-extension`
78+
79+
### Server Requirements
80+
81+
Your server should implement these endpoints:
82+
83+
1. **Query Endpoint** - Returns available extensions based on filter criteria
84+
2. **Download Endpoint** - Returns the extension package file (typically .zip format)
85+
86+
The `DownloadUrl` field in extension descriptors is now optional and primarily for backward compatibility. The system constructs download URLs dynamically from the configured ServerUrl.
87+
5788
## Complete Usage Guide
5889

5990
### 1. Dependency Injection Setup
@@ -70,25 +101,29 @@ public class YourModule : IModule
70101
{
71102
public void RegisterTypes(IContainerRegistry containerRegistry)
72103
{
73-
var hostVersion = new Version(1, 0, 0);
74-
var installPath = @"C:\MyApp\Extensions";
75-
var downloadPath = @"C:\MyApp\Downloads";
76-
var platform = Metadata.TargetPlatform.Windows;
104+
var config = new ExtensionHostConfig
105+
{
106+
HostVersion = new Version(1, 0, 0),
107+
InstallBasePath = @"C:\MyApp\Extensions",
108+
DownloadPath = @"C:\MyApp\Downloads",
109+
ServerUrl = "https://your-server.com/api/extensions",
110+
TargetPlatform = Metadata.TargetPlatform.Windows
111+
};
77112

78113
// Register as singletons
79114
containerRegistry.RegisterSingleton<Core.IExtensionCatalog>(() =>
80-
new Core.ExtensionCatalog(installPath));
115+
new Core.ExtensionCatalog(config.InstallBasePath));
81116

82117
containerRegistry.RegisterSingleton<Compatibility.ICompatibilityValidator>(() =>
83-
new Compatibility.CompatibilityValidator(hostVersion));
118+
new Compatibility.CompatibilityValidator(config.HostVersion));
84119

85120
containerRegistry.RegisterSingleton<Download.IUpdateQueue, Download.UpdateQueue>();
86121

87122
containerRegistry.RegisterSingleton<PackageGeneration.IExtensionPackageGenerator,
88123
PackageGeneration.ExtensionPackageGenerator>();
89124

90125
containerRegistry.RegisterSingleton<IExtensionHost>(() =>
91-
new ExtensionHost(hostVersion, installPath, downloadPath, platform));
126+
new GeneralExtensionHost(config));
92127
}
93128
}
94129

@@ -102,24 +137,28 @@ var host = container.Resolve<IExtensionHost>();
102137
using Microsoft.Extensions.DependencyInjection;
103138

104139
var services = new ServiceCollection();
105-
var hostVersion = new Version(1, 0, 0);
106-
var installPath = @"C:\Extensions";
107-
var downloadPath = @"C:\Downloads";
140+
var config = new ExtensionHostConfig
141+
{
142+
HostVersion = new Version(1, 0, 0),
143+
InstallBasePath = @"C:\Extensions",
144+
DownloadPath = @"C:\Downloads",
145+
ServerUrl = "https://your-server.com/api/extensions",
146+
TargetPlatform = Metadata.TargetPlatform.Windows
147+
};
108148

109149
services.AddSingleton<Core.IExtensionCatalog>(sp =>
110-
new Core.ExtensionCatalog(installPath));
150+
new Core.ExtensionCatalog(config.InstallBasePath));
111151

112152
services.AddSingleton<Compatibility.ICompatibilityValidator>(sp =>
113-
new Compatibility.CompatibilityValidator(hostVersion));
153+
new Compatibility.CompatibilityValidator(config.HostVersion));
114154

115155
services.AddSingleton<Download.IUpdateQueue, Download.UpdateQueue>();
116156

117157
services.AddSingleton<PackageGeneration.IExtensionPackageGenerator,
118158
PackageGeneration.ExtensionPackageGenerator>();
119159

120160
services.AddSingleton<IExtensionHost>(sp =>
121-
new ExtensionHost(hostVersion, installPath, downloadPath,
122-
Metadata.TargetPlatform.Windows));
161+
new GeneralExtensionHost(config));
123162

124163
var provider = services.BuildServiceProvider();
125164
var host = provider.GetRequiredService<IExtensionHost>();
@@ -128,11 +167,16 @@ var host = provider.GetRequiredService<IExtensionHost>();
128167
#### Without DI (Direct Instantiation)
129168

130169
```csharp
131-
var host = new ExtensionHost(
132-
new Version(1, 0, 0),
133-
@"C:\Extensions",
134-
@"C:\Downloads",
135-
Metadata.TargetPlatform.Windows);
170+
var config = new ExtensionHostConfig
171+
{
172+
HostVersion = new Version(1, 0, 0),
173+
InstallBasePath = @"C:\Extensions",
174+
DownloadPath = @"C:\Downloads",
175+
ServerUrl = "https://your-server.com/api/extensions",
176+
TargetPlatform = Metadata.TargetPlatform.Windows
177+
};
178+
179+
var host = new GeneralExtensionHost(config);
136180
```
137181

138182
### 2. Loading and Managing Extensions

0 commit comments

Comments
 (0)