using System.Diagnostics; using System.Net; using System.Collections.Frozen; using XFEExtension.NetCore.AutoImplement; using XFEExtension.NetCore.CyberComm; using XFEExtension.NetCore.DelegateExtension; using XFEExtension.NetCore.ServerInteractive.Exceptions; using XFEExtension.NetCore.ServerInteractive.Implements.ServerService; using XFEExtension.NetCore.ServerInteractive.Interfaces.CoreService; using XFEExtension.NetCore.ServerInteractive.Models.ServerModels; using XFEExtension.NetCore.ServerInteractive.Utilities.Helpers; using XFEExtension.NetCore.StringExtension; using XFEExtension.NetCore.XFETransform.Json; namespace XFEExtension.NetCore.ServerInteractive.Utilities.Server; /// /// XFE服务器核心 /// [CreateImpl] public abstract class XFEServerCore : ServerCoreServiceBase { /// /// 是否接收非标准的Json字符串 /// public bool AcceptNonStandardJson { get; set; } = true; /// /// 是否接收GET请求 /// public bool AcceptGet { get; set; } /// /// 是否接收POST请求 /// public bool AcceptPost { get; set; } = true; /// /// 服务器核心错误委托 /// public XFEEventHandler? ServerCoreError { get; set; } /// /// 获取IP地址的函数,默认为从请求事件参数中获取客户端IP地址 /// public Func GetIPFunction { get; set; } = args => args.ClientIP; /// /// 主入口点路径(ServerCore主要入口点),默认为空字符串(为空时直接使用次级入口点)。 /// 多个核心服务器绑定同一端口时,使用不同的主入口点区分核心服务器。 /// public string MainEntryPoint { get; set; } = string.Empty; /// /// 核心服务列表 /// internal readonly List ServerCoreServiceList = []; /// /// 核心校验服务列表 /// internal IReadOnlyList> ServerCoreVerifyServiceList = []; /// /// 核心标准服务工厂字典(按路由路径创建实例) /// internal IReadOnlyDictionary> StandardCoreServiceDictionary = FrozenDictionary>.Empty; /// /// 通配符路由服务工厂列表(模式 → 工厂) /// internal IReadOnlyList<(string Pattern, Func Factory)> WildcardCoreServiceList = []; /// /// 禁用框架请求日志的入口点路径或通配符模式集合 /// internal IReadOnlySet NoLogEntryPointSet = FrozenSet.Empty; /// /// 网络通讯服务器 /// public CyberCommServer CyberCommServer { get; internal set; } = new(); /// 底层 CyberComm 限制。 public CyberCommLimitOptions TransportLimits { get; internal set; } = new(); /// 底层 TLS 配置。 public CyberCommTlsOptions? TlsOptions { get; internal set; } /// /// 当前核心所属的共享监听主机。 /// internal XFEServerCoreHost? ServerCoreHost { get; set; } /// /// 主入口点包含的路径段数量。 /// internal int MainEntryPointSegmentCount => GetEntryPointSegments(MainEntryPoint).Count; /// /// 处理已经由共享监听主机分发到当前核心的请求。 /// internal async Task ProcessRequest(CyberCommRequestEventArgs e, CancellationToken cancellationToken = default) { var r = new ServerCoreReturnArgs { Args = e, ServerCore = this }; try { cancellationToken.ThrowIfCancellationRequested(); var clientIP = e.ClientIP; try { clientIP = GetIPFunction(e); } catch (Exception ex) { Console.WriteLine($"[WARN]获取IP地址失败:{ex.Message}"); } r.ClientIP = clientIP; // 在校验日志产生前解析并匹配入口点,以便 [NoLog] 能关闭完整的框架请求日志。 string route; Func? serviceFactory; string? matchedPattern; try { var url = e.RequestUrl; if (url is null) { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.BadRequest, ReturnArgs = r, ServerException = new ProcessStandardRequestException("请求URL为空") }); return; } if (!TryGetRoute(url, out route)) { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.BadRequest, ReturnArgs = r, ServerException = new ProcessStandardRequestException($"请求路径不匹配主入口点: {MainEntryPoint}") }); return; } if (route.IsNullOrEmpty()) { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.BadRequest, ReturnArgs = r, ServerException = new ProcessStandardRequestException("请求路由为空") }); return; } TryResolveService(route, out serviceFactory, out matchedPattern); r.SuppressLog = matchedPattern is not null && NoLogEntryPointSet.Contains(matchedPattern); } catch (Exception ex) { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.InternalServerError, ReturnArgs = r, ServerException = new ProcessStandardRequestException("解析请求路由时发生异常", ex) }); return; } try { foreach (var serverCoreVerifyService in ServerCoreVerifyServiceList.Select(serverCoreVerifyFactory => serverCoreVerifyFactory())) { cancellationToken.ThrowIfCancellationRequested(); serverCoreVerifyService.ReturnArgs = r; serverCoreVerifyService.Args = e; if (!serverCoreVerifyService.VerifyRequest() || !await serverCoreVerifyService.VerifyRequestAsync().ConfigureAwait(false)) { if (e.Context is not null && !e.Context.Response.IsCompleted) await e.ReplyAndClose("请求未通过验证", r.StatusCode == HttpStatusCode.InternalServerError ? HttpStatusCode.Forbidden : r.StatusCode).ConfigureAwait(false); return; } } } catch (Exception ex) { ServerCoreError?.Invoke(this, new() { StatusCode = r.StatusCode, Handled = r.Handled, ReturnArgs = r, ServerException = new ProcessStandardRequestException("请求校验失败", ex) }); return; } // 从URL路径中提取路由信息 XFEJsonNode? queryableJsonNode = null; try { // 尝试解析请求体为JSON(可选) if (e.RequestBody is not null) { queryableJsonNode = e.RequestBody; } } catch (Exception ex) { if (!AcceptNonStandardJson) { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.BadRequest, ReturnArgs = r, ServerException = new ProcessStandardRequestException("无法解析 JSON 请求体", ex) }); return; } } try { Stopwatch? stopWatch = null; if (!r.SuppressLog) { Console.Write($"[{e.CorrelationId}]({ServerCoreName})【{clientIP}】请求路由-{route}:"); stopWatch = Stopwatch.StartNew(); } if (serviceFactory is not null && matchedPattern is not null) { var serviceInstance = serviceFactory(); try { serviceInstance.XFEServerCore = this; serviceInstance.Route = route; serviceInstance.Json = queryableJsonNode; serviceInstance.Args = e; serviceInstance.ReturnArgs = r; serviceInstance.Initialize(); // 根据匹配的模式调用对应的处理方法(同步与异步互斥,优先同步) if (serviceInstance.SyncEntryPoints.TryGetValue(matchedPattern, out var syncHandler)) syncHandler(); else if (serviceInstance.AsyncEntryPoints.TryGetValue(matchedPattern, out var asyncHandler)) await asyncHandler().WaitAsync(cancellationToken).ConfigureAwait(false); else { ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.NotFound, ReturnArgs = r, ServerException = new ExecutionUnregisteredException($"服务已注册但路由未找到对应处理方法-{route}") }); } } catch (Exception ex) { ServerCoreError?.Invoke(this, new() { StatusCode = r.StatusCode, Handled = r.Handled, ReturnArgs = r, ServerException = new XFEServerCoreRequestInnerException($"请求异常-{route}", ex) }); } if (stopWatch is not null) { stopWatch.Stop(); Console.WriteLine($"\t[耗时 {InteractiveHelper.GetStopWatchTime(stopWatch)}]"); } return; } r.Handled = true; ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.NotFound, ReturnArgs = r, ServerException = new ExecutionUnregisteredException($"请求的路由未注册-{route}") }); } catch (Exception ex) { r.Handled = false; ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.InternalServerError, ReturnArgs = r, ServerException = new ProcessStandardRequestException("处理标准请求时发生异常", ex) }); } } catch (Exception ex) { r.Handled = false; Console.WriteLine($"[ERROR]处理请求时发生未捕获的异常:{ex.Message}"); Console.WriteLine($"[TRACE] {ex.StackTrace}"); ServerCoreError?.Invoke(this, new() { StatusCode = HttpStatusCode.InternalServerError, ReturnArgs = r, ServerException = new ProcessStandardRequestException("处理请求时发生未捕获的异常", ex) }); } } /// /// 启动核心服务 /// /// public override async Task StartServerCore() { ServerCoreHost ??= XFEServerCoreHost.Configure([this]).Single(); await ServerCoreHost.StartServerCore().ConfigureAwait(false); } /// /// 规范化主入口点,移除首尾及重复的路径分隔符。 /// internal static string NormalizeMainEntryPoint(string? mainEntryPoint) => string.Join( "/", (mainEntryPoint ?? string.Empty).Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); /// /// 获取请求URL中的全部非空路径段。 /// internal static IReadOnlyList GetRequestSegments(Uri requestUrl) => requestUrl.Segments .Skip(1) .Select(segment => Uri.UnescapeDataString(segment.Trim('/'))) .Where(segment => segment.Length > 0) .ToArray(); /// /// 尝试匹配当前核心的主入口点,并提取次级路由。 /// internal bool TryGetRoute(Uri requestUrl, out string route) { var requestSegments = GetRequestSegments(requestUrl); var entryPointSegments = GetEntryPointSegments(MainEntryPoint); if (requestSegments.Count < entryPointSegments.Count) { route = string.Empty; return false; } for (var i = 0; i < entryPointSegments.Count; i++) { if (requestSegments[i] == entryPointSegments[i]) continue; route = string.Empty; return false; } route = string.Join("/", requestSegments.Skip(entryPointSegments.Count)); return true; } /// /// 按精确路由优先、最具体通配符次之的规则查找服务。 /// private bool TryResolveService(string route, out Func? serviceFactory, out string? matchedPattern) { if (StandardCoreServiceDictionary.TryGetValue(route, out serviceFactory)) { matchedPattern = route; return true; } matchedPattern = null; serviceFactory = null; var bestPriority = int.MinValue; foreach (var (pattern, factory) in WildcardCoreServiceList) { if (!RouteMatchHelper.MatchWildcardRoute(pattern, route)) continue; // 在所有命中的候选中选择最具体的模式(字面量段越多越优先),避免结果依赖注册顺序。 var currentPriority = RouteMatchHelper.GetWildcardPatternPriority(pattern); if (currentPriority <= bestPriority) continue; bestPriority = currentPriority; matchedPattern = pattern; serviceFactory = factory; } return serviceFactory is not null; } internal void NotifyServerStarted(object? sender, EventArgs e) { foreach (var service in ServerCoreServiceList) service.ServerStarted(sender, e); } internal void NotifyRequestReceived(object? sender, CyberCommRequestEventArgs e) { foreach (var service in ServerCoreServiceList) service.RequestReceived(sender, e); } internal void NotifyClientConnected(object? sender, CyberCommServerEventArgs e) { foreach (var service in ServerCoreServiceList) service.ClientConnected(sender, e); } internal void NotifyMessageReceived(object? sender, CyberCommServerEventArgs e) { foreach (var service in ServerCoreServiceList) service.MessageReceived(sender, e); } internal void NotifyConnectionClosed(object? sender, CyberCommServerEventArgs e) { foreach (var service in ServerCoreServiceList) service.ConnectionClosed(sender, e); } private static IReadOnlyList GetEntryPointSegments(string mainEntryPoint) => mainEntryPoint .Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); }