diff --git a/CSharp-OpenBMCLAPI/Modules/Cluster.cs b/CSharp-OpenBMCLAPI/Modules/Cluster.cs
index d028ba1..5314f71 100644
--- a/CSharp-OpenBMCLAPI/Modules/Cluster.cs
+++ b/CSharp-OpenBMCLAPI/Modules/Cluster.cs
@@ -1,5 +1,4 @@
-using CSharpOpenBMCLAPI.Modules.Plugin;
-using CSharpOpenBMCLAPI.Modules.Storage;
+using CSharpOpenBMCLAPI.Modules.Storage;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@@ -91,12 +90,10 @@ protected SocketIOClient.SocketIO InitializeSocket()
///
public int Start()
{
- requiredData.PluginManager.TriggerEvent(this, ProgramEventType.ClusterStarted);
// 工作进程启动
Logger.Instance.LogSystem($"工作进程 {guid} 已启动");
Task task = AsyncRun();
task.Wait();
- requiredData.PluginManager.TriggerEvent(this, ProgramEventType.ClusterStopped);
return task.Result;
}
@@ -177,7 +174,7 @@ private void InitializeService()
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.WebHost.UseKestrel(options =>
{
- options.ListenAnyIP(9388, cert != null ? configure =>
+ options.ListenAnyIP(ClusterRequiredData.Config.PORT, cert != null ? configure =>
{
configure.UseHttps(cert);
}
@@ -186,15 +183,16 @@ private void InitializeService()
application = builder.Build();
// 下载路由
- application.MapGet("/download/{hash}", (HttpContext context, string hash) =>
+ application.MapGet("/download/{hash}", async (HttpContext context, string hash) =>
{
- FileAccessInfo fai = HttpServiceProvider.DownloadHash(context, this).Result;
+ FileAccessInfo fai = await HttpServiceProvider.DownloadHash(context, this, hash);
this.counter.Add(fai);
- return Task.CompletedTask;
});
// 测速路由
- application.MapGet("/measure", (context) => HttpServiceProvider.Measure(context, this));
+ application.MapGet("/measure/{size}", async (HttpContext context, int size) => await HttpServiceProvider.Measure(context, this, size));
+
+ application.MapGet("/api/{name}", async (HttpContext context, string name) => await HttpServiceProvider.Api(context, name, this));
// 因为暂时禁用面板而注释掉
diff --git a/CSharp-OpenBMCLAPI/Modules/ClusterRequiredData.cs b/CSharp-OpenBMCLAPI/Modules/ClusterRequiredData.cs
index 43e6e6b..f72f0ae 100644
--- a/CSharp-OpenBMCLAPI/Modules/ClusterRequiredData.cs
+++ b/CSharp-OpenBMCLAPI/Modules/ClusterRequiredData.cs
@@ -1,5 +1,4 @@
-using CSharpOpenBMCLAPI.Modules.Plugin;
-using CSharpOpenBMCLAPI.Modules.Statistician;
+using CSharpOpenBMCLAPI.Modules.Statistician;
namespace CSharpOpenBMCLAPI.Modules
{
@@ -9,7 +8,6 @@ public class ClusterRequiredData
public Logger Logger { get => Logger.Instance; }
public ClusterInfo ClusterInfo { get; set; }
public TokenManager Token { get; set; }
- public PluginManager PluginManager { get => PluginManager.Instance; }
public static DataStatistician DataStatistician { get; set; } = new DataStatistician();
public SemaphoreSlim SemaphoreSlim { get; set; } = new SemaphoreSlim(0);
diff --git a/CSharp-OpenBMCLAPI/Modules/HttpRequest.cs b/CSharp-OpenBMCLAPI/Modules/HttpRequest.cs
index c9a63e3..a66dc5d 100644
--- a/CSharp-OpenBMCLAPI/Modules/HttpRequest.cs
+++ b/CSharp-OpenBMCLAPI/Modules/HttpRequest.cs
@@ -10,6 +10,7 @@ static HttpRequest()
{
client = new HttpClient()
{
+ // BaseAddress = new Uri("http://saltwood.top:9393")
// 设置基础地址,根据配置文件中的StagingMode属性判断使用哪个地址
BaseAddress = ClusterRequiredData.Config.StagingMode ?
new Uri("https://openbmclapi.staging.bangbang93.com/") :
diff --git a/CSharp-OpenBMCLAPI/Modules/HttpServiceProvider.cs b/CSharp-OpenBMCLAPI/Modules/HttpServiceProvider.cs
index dab00a7..43223ac 100644
--- a/CSharp-OpenBMCLAPI/Modules/HttpServiceProvider.cs
+++ b/CSharp-OpenBMCLAPI/Modules/HttpServiceProvider.cs
@@ -1,8 +1,8 @@
-using CSharpOpenBMCLAPI.Modules.Plugin;
-using CSharpOpenBMCLAPI.Modules.Storage;
+using CSharpOpenBMCLAPI.Modules.Storage;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
+using System;
namespace CSharpOpenBMCLAPI.Modules
{
@@ -28,20 +28,20 @@ public static void LogAccess(HttpContext context)
///
///
///
- public static async Task Measure(HttpContext context, Cluster cluster)
+ public static async Task Measure(HttpContext context, Cluster cluster, int size)
{
- PluginManager.Instance.TriggerHttpEvent(context, HttpEventType.ClientMeasure);
- var pairs = Utils.GetQueryStrings(context.Request.Path.Value?.Split('?').Last());
+ context.Request.Query.TryGetValue("s", out StringValues s);
+ context.Request.Query.TryGetValue("e", out StringValues e);
bool valid = Utils.CheckSign(context.Request.Path.Value?.Split('?').First()
, cluster.requiredData.ClusterInfo.ClusterSecret
- , pairs.GetValueOrDefault("s")
- , pairs.GetValueOrDefault("e")
+ , s.FirstOrDefault()
+ , e.FirstOrDefault()
);
if (valid)
{
context.Response.StatusCode = 200;
byte[] buffer = new byte[1024];
- for (int i = 0; i < Convert.ToInt32(context.Request.Path.Value?.Split('/').Last().Split('?').First()); i++)
+ for (int i = 0; i < size; i++)
{
for (int j = 0; j < 1024; j++)
{
@@ -63,108 +63,96 @@ public static async Task Measure(HttpContext context, Cluster cluster)
///
///
///
- public static async Task DownloadHash(HttpContext context, Cluster cluster)
+ public static async Task DownloadHash(HttpContext context, Cluster cluster, string hash)
{
- PluginManager.Instance.TriggerHttpEvent(context, HttpEventType.ClientDownload);
- // 处理用户下载
- FileAccessInfo fai = default;
- var pairs = Utils.GetQueryStrings(context.Request.Path.Value?.Split('?').Last());
- string? hash = context.Request.Path.Value?.Split('/').LastOrDefault()?.Split('?').First();
- string? s = pairs.GetValueOrDefault("s");
- string? e = pairs.GetValueOrDefault("e");
+ context.Request.Query.TryGetValue("s", out StringValues s);
+ context.Request.Query.TryGetValue("e", out StringValues e);
- bool valid = Utils.CheckSign(hash, cluster.clusterInfo.ClusterSecret, s, e);
+ bool isValid = Utils.CheckSign(hash, cluster.clusterInfo.ClusterSecret, s.FirstOrDefault(), e.FirstOrDefault());
- if (valid && hash != null && s != null && e != null)
+ if (!cluster.storage.Exists(Utils.HashToFileName(hash)))
{
- if (!cluster.storage.Exists(Utils.HashToFileName(hash)))
- {
- await cluster.FetchFileFromCenter(hash, force: true);
- }
+ LogAccess(context);
+ context.Response.StatusCode = 404;
+ }
- long from, to;
- try
- {
- if (context.Request.Headers.ContainsKey("range"))
- {
- // 206 处理部分
- context.Response.StatusCode = 206;
- (from, to) = ToRangeByte(context.Request.Headers["range"].FirstOrDefault()?.Split("=").Last().Split("-"));
- if (to < from && to != -1) (from, to) = (to, from);
- long length = 0;
+ // 获取文件信息
+ using var stream = cluster.storage.ReadFileStream(Utils.HashToFileName(hash));
+ long fileSize = cluster.storage.GetFileSize(Utils.HashToFileName(hash));
- using (Stream file = cluster.storage.ReadFileStream(Utils.HashToFileName(hash)))
- {
- if (to == -1) to = file.Length;
+ // 检查是否支持断点续传
+ var isRangeRequest = context.Request.Headers.ContainsKey("Range");
+ if (isRangeRequest)
+ {
+ // 解析 Range 头部,获取断点续传的起始位置和结束位置
+ var rangeHeader = context.Request.Headers["Range"].ToString();
+ var (startByte, endByte) = GetRange(rangeHeader, fileSize);
- length = (to - from + 1);
- context.Response.Headers["Content-Length"] = length.ToString();
+ // 设置响应头部
+ context.Response.StatusCode = 206; // Partial Content
+ context.Response.Headers.Append("Accept-Ranges", "bytes");
+ context.Response.Headers.Append("Content-Range", $"bytes {startByte}-{endByte}/{fileSize}");
+ // context.Response.Headers.Append("Content-Type", MimeTypesMap.GetMimeType(fullPath));
+ context.Response.Headers.Append("Content-Disposition", $"attachment; filename=\"{Path.GetFileName(hash)}\"");
- file.Seek(from, SeekOrigin.Begin);
- byte[] buffer = new byte[4096];
- for (; file.Position < to;)
- {
- int count = file.Read(buffer, 0, buffer.Length);
- if (file.Position > to && file.Position - count < to) await context.Response.Body.WriteAsync(buffer[..(int)(count - file.Position + to + 1)]);
- else if (count != buffer.Length) await context.Response.Body.WriteAsync(buffer[..(count)]);
- else await context.Response.Body.WriteAsync(buffer);
- }
- context.Response.Headers["Content-Range"] = $"{from}-{to}/{file.Length}";
- }
+ // 计算要读取的字节数
+ var totalBytesToRead = endByte - startByte + 1;
+ using (Stream file = cluster.storage.ReadFileStream(Utils.HashToFileName(hash)))
+ {
+ context.Response.Headers["Content-Length"] = totalBytesToRead.ToString();
- context.Response.Headers["x-bmclapi-hash"] = hash;
- context.Response.Headers["Accept-Ranges"] = "bytes";
- context.Response.Headers["Content-Type"] = "application/octet-stream";
- context.Response.Headers["Connection"] = "closed";
- fai = new FileAccessInfo
- {
- hits = 1,
- bytes = length
- };
- ClusterRequiredData.DataStatistician.DownloadCount(fai);
- }
- else
+ file.Seek(startByte, SeekOrigin.Begin);
+ byte[] buffer = new byte[4096];
+ for (; file.Position < endByte;)
{
- fai = await cluster.storage.HandleRequest(Utils.HashToFileName(hash), context);
- ClusterRequiredData.DataStatistician.DownloadCount(fai);
+ int count = file.Read(buffer, 0, buffer.Length);
+ if (file.Position > endByte && file.Position - count < endByte) await context.Response.Body.WriteAsync(buffer[..(int)(count - file.Position + endByte + 1)]);
+ else if (count != buffer.Length) await context.Response.Body.WriteAsync(buffer[..(count)]);
+ else await context.Response.Body.WriteAsync(buffer);
}
}
- catch (Exception ex)
+ LogAccess(context);
+ return new FileAccessInfo
{
- Logger.Instance.LogError(ex.ExceptionToDetail());
- Logger.Instance.LogError(context.Request.Path);
- //Logger.Instance.LogError(ex.StackTrace);
- context.Response.StatusCode = 404;
- }
+ hits = 1,
+ bytes = totalBytesToRead
+ };
}
else
{
- context.Response.StatusCode = 403;
- context.Response.Headers.Remove("Content-Length");
- await context.Response.WriteAsync($"Access to \"{context.Request.Path}\" has been blocked due to your request timeout or invalidity.");
+ // 设置响应头部
+ context.Response.Headers.Append("Accept-Ranges", "bytes");
+ context.Response.Headers.Append("Content-Disposition", $"attachment; filename=\"{Path.GetFileName(hash)}\"");
+ //context.Response.Headers.Append("Content-Type", MimeTypesMap.GetMimeType(fullPath));
+ context.Response.Headers.Append("Content-Range", $"bytes {0}-{fileSize - 1}/{fileSize}");
+ LogAccess(context);
+ return await cluster.storage.HandleRequest(Utils.HashToFileName(hash), context);
}
- LogAccess(context);
- return fai;
}
- private static (long from, long to) ToRangeByte(string[]? rangeHeader)
+
+ private static (long startByte, long endByte) GetRange(string rangeHeader, long fileSize)
{
- int from, to;
- if (string.IsNullOrWhiteSpace(rangeHeader?.FirstOrDefault()))
- from = 0;
- else
- from = Convert.ToInt32(rangeHeader?.FirstOrDefault());
- if (string.IsNullOrWhiteSpace(rangeHeader?.LastOrDefault()))
- to = -1;
- else
- to = Convert.ToInt32(rangeHeader?.LastOrDefault());
- return (from, to);
+ if (rangeHeader.Length <= 6) return (0, fileSize);
+ var ranges = rangeHeader[6..].Split("-");
+ try
+ {
+ if (ranges[1].Length > 0)
+ {
+ return (long.Parse(ranges[0]), long.Parse(ranges[1]));
+ }
+ }
+ catch (Exception)
+ {
+ return (long.Parse(ranges[0]), fileSize - 1);
+ }
+
+ return (long.Parse(ranges[0]), fileSize - 1);
}
public static async Task Api(HttpContext context, string query, Cluster cluster)
{
- PluginManager.Instance.TriggerHttpEvent(context, HttpEventType.ClientOtherRequest);
context.Response.Headers["content-type"] = "application/json";
context.Response.Headers["access-control-allow-origin"] = "*";
context.Response.StatusCode = 200;
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/HttpEventType.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/HttpEventType.cs
deleted file mode 100644
index d2ffc0d..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/HttpEventType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- public enum HttpEventType
- {
- ClientDownload,
- ClientMeasure,
- ClientOtherRequest
- }
-}
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginAttribute.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/PluginAttribute.cs
deleted file mode 100644
index c8d9448..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginAttribute.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class PluginAttribute : Attribute
- {
- public bool Hidden { get; set; } = false;
- }
-}
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginBase.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/PluginBase.cs
deleted file mode 100644
index 1aa4e83..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginBase.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- public abstract class PluginBase
- {
- public virtual string Name { get; private set; } = "Default";
- public virtual string Description { get; private set; } = "Default plugin implementation";
-
- public virtual void OnProgramStarted()
- {
-
- }
-
- public virtual void OnClusterStarted(Cluster? cluster)
- {
-
- }
-
- public virtual void OnClusterStopped(Cluster? cluster)
- {
-
- }
-
- public virtual void OnProgramStopped()
- {
-
- }
-
- public virtual void RegisterClientEvents()
- {
-
- }
-
- public virtual void RegisterStorageType()
- {
-
- }
-
- public override string ToString()
- {
- return $"<{this.GetType().FullName} Name={this.Name} at 0x{this.GetHashCode().ToString("X").PadLeft(16, '0')}>";
- }
- }
-}
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginHttpEvent.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/PluginHttpEvent.cs
deleted file mode 100644
index d232211..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginHttpEvent.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Microsoft.AspNetCore.Http;
-
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- public class PluginHttpEvent
- {
- public HttpEventType EventType { get; protected set; }
- public Action Action { get; protected set; }
-
- public PluginHttpEvent(Action action, HttpEventType type)
- {
- this.EventType = type;
- this.Action = action;
- }
-
- public void Trigger(HttpEventType eventType, HttpContext context)
- {
- if (this.EventType == eventType) this.Action.Invoke(context);
- }
- }
-}
\ No newline at end of file
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginManager.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/PluginManager.cs
deleted file mode 100644
index 43fbdc8..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/PluginManager.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using Microsoft.AspNetCore.Http;
-using System.Diagnostics;
-
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- public class PluginManager
- {
- private List events = new List();
- private List plugins = new List();
- private static PluginManager _instance = new PluginManager();
- public static PluginManager Instance { get => _instance; }
-
- ///
- /// 私有构造器,保证只有一个实例
- ///
- private PluginManager()
- {
-
- }
-
- public void RegisterPlugin(PluginBase plugin)
- {
- string? caller = null;
- try
- {
- StackTrace st = new StackTrace();
- StackFrame sf = st.GetFrame(1).ThrowIfNull();
- caller = sf.GetMethod()?.Name;
-
- plugins.Add(plugin);
-
- Logger.Instance.LogInfo($"插件 {plugin} 已成功加载!");
- }
- catch (InvalidOperationException)
- {
- Logger.Instance.LogError($"无法执行的操作:在不允许的位置调用了 {nameof(RegisterPlugin)}:{caller}!");
- }
- catch (Exception ex)
- {
- Logger.Instance.LogError($"注册插件 {plugin} 时出现错误(阶段 1,添加插件):", ex.ExceptionToDetail());
- }
- }
-
- public void RegisterHttpEvent(PluginHttpEvent e)
- {
- string? caller = null;
- try
- {
- StackTrace st = new StackTrace();
- StackFrame sf = st.GetFrame(1).ThrowIfNull();
- caller = sf.GetMethod()?.Name;
-
- events.Add(e);
- }
- catch (InvalidOperationException)
- {
- Logger.Instance.LogError($"无法执行的操作:在不允许的位置调用了 {nameof(RegisterHttpEvent)}:{caller}!");
- }
- catch (Exception ex)
- {
- Logger.Instance.LogError($"注册事件时出现错误:", ex.ExceptionToDetail());
- }
- }
-
- public void RegisterPlugin(Type type)
- {
- try
- {
- PluginBase plugin;
-
- plugin = (Activator.CreateInstance(type) as PluginBase).ThrowIfNull();
-
- RegisterPlugin(plugin);
- }
- catch (Exception ex)
- {
- Logger.Instance.LogError("注册插件时出现错误(阶段 0,实例化):", ex.ExceptionToDetail());
- }
- }
-
- public void TriggerEvent(object sender, ProgramEventType eventType)
- {
- Cluster? senderCluster = sender as Cluster;
- switch (eventType)
- {
- case ProgramEventType.ProgramStarted:
- plugins.ForEach(p => p.OnProgramStarted());
- break;
- case ProgramEventType.ProgramStopped:
- plugins.ForEach(p => p.OnProgramStopped());
- break;
- case ProgramEventType.ClusterStarted:
- plugins.ForEach(p => p.OnClusterStarted(senderCluster));
- break;
- case ProgramEventType.ClusterStopped:
- plugins.ForEach(p => p.OnClusterStopped(senderCluster));
- break;
- }
- }
-
- public void TriggerHttpEvent(HttpContext context, HttpEventType eventType)
- {
- events.ForEach(e => e.Trigger(eventType, context));
- }
- }
-}
diff --git a/CSharp-OpenBMCLAPI/Modules/Plugin/ProgramEventType.cs b/CSharp-OpenBMCLAPI/Modules/Plugin/ProgramEventType.cs
deleted file mode 100644
index 0c24529..0000000
--- a/CSharp-OpenBMCLAPI/Modules/Plugin/ProgramEventType.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace CSharpOpenBMCLAPI.Modules.Plugin
-{
- public enum ProgramEventType
- {
- ProgramStarted,
- ClusterStarted,
- ClusterStopped,
- ProgramStopped
- }
-}
diff --git a/CSharp-OpenBMCLAPI/Program.cs b/CSharp-OpenBMCLAPI/Program.cs
index 3cd9552..8fd681e 100644
--- a/CSharp-OpenBMCLAPI/Program.cs
+++ b/CSharp-OpenBMCLAPI/Program.cs
@@ -1,5 +1,4 @@
using CSharpOpenBMCLAPI.Modules;
-using CSharpOpenBMCLAPI.Modules.Plugin;
using CSharpOpenBMCLAPI.Modules.Statistician;
using Newtonsoft.Json;
using System.Reflection;
@@ -22,43 +21,6 @@ static void Main(string[] args)
program.WaitForStop();
}
- protected void LoadPlugins()
- {
- string path = Path.Combine(ClusterRequiredData.Config.clusterWorkingDirectory, "plugins");
-
- Directory.CreateDirectory(path);
-
- foreach (var file in Directory.GetFiles(path))
- {
- if (!file.EndsWith(".dll")) continue;
- try
- {
- Assembly assembly = Assembly.LoadFrom(file);
- foreach (var type in assembly.GetTypes())
- {
- Type? parent = type;
- while (parent != null)
- {
- if (parent == typeof(PluginBase))
- {
- PluginAttribute? attr = type.GetCustomAttribute();
- if (attr == null || !attr.Hidden)
- {
- PluginManager.Instance.RegisterPlugin(type);
- }
- break;
- }
- parent = parent.BaseType;
- }
- }
- }
- catch (Exception ex)
- {
- Logger.Instance.LogError($"跳过加载插件 {Path.Combine(file)}。加载插件时出现未知错误。\n", Utils.ExceptionToDetail(ex));
- }
- }
- }
-
protected Config GetConfig()
{
const string configFileName = "config.yml";
@@ -99,8 +61,6 @@ protected override int Run(string[] args)
const string bsonFile = "totals.bson";
string bsonFilePath = Path.Combine(ClusterRequiredData.Config.clusterWorkingDirectory, bsonFile);
ClusterRequiredData.Config = GetConfig();
- LoadPlugins();
- PluginManager.Instance.TriggerEvent(this, ProgramEventType.ProgramStarted);
int returns = 0;
@@ -136,8 +96,6 @@ protected override int Run(string[] args)
Console.CancelKeyPress += (sender, e) => Utils.ExitCluster(cluster).Wait();
cluster.Start();
-
- requiredData.PluginManager.TriggerEvent(this, ProgramEventType.ProgramStopped);
return returns;
}
catch (Exception ex)