#if NETCORE using System; using System.Collections.Generic; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using System.Threading.Tasks; using System.Net; using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; namespace Apewer.Web { /// ASP.NET Core 服务器。 public sealed class AspNetCore { #region servers static AspNetCore[] _servers = new AspNetCore[65536]; internal static AspNetCore Get(int port) => _servers[port]; static object _operation = new object(); static string Add(AspNetCore server) { if (server == null) return null; var port = server.Port; lock (_operation) { if (_servers[port] != null) return "端口已配置。"; _servers[port] = server; } return null; } static void Stop(int port) { var server = null as AspNetCore; lock (_operation) { server = _servers[port]; } if (server != null) { var host = server._host; Stop(host); } } static void Stop(IDisposable disposable) => RuntimeUtility.Dispose(disposable); /// 获取所有正在解析的端口。 public static int[] Ports { get { var count = 0; var arr1 = new int[65536]; for (var i = 0; i < _servers.Length; i++) { var server = _servers[i]; if (server == null) continue; arr1[count] = i; count += 1; } var arr2 = new int[count]; if (count > 0) Array.Copy(arr1, 0, arr2, 0, count); return arr2; } } #endregion #region base private IHostBuilder _builder = null; private IHost _host = null; private int _port = 0; private bool _locked = false; /// 监听的端口。默认值:0,自动选择 80 端口或 443 端口。 public int Port { get { return _port > 0 ? _port : (Certificate == null ? 80 : 443); } set { _port = value; } } /// 获取或设置将要使用的证书。 public X509Certificate2 Certificate { get; set; } /// 获取或设置请求的最大 Body 大小,单位为字节。可接受的最小值为 1048576 字节(1 MB)。默认值:1073741824。 public long MaxBody { get; set; } = 1073741824L; /// 获取或设置 Context 处理程序。 public Action Context { get; set; } /// 获取或设置 WebSocket 处理程序。 public Action WebSocket { get; set; } private string Run(Action configure, bool async = false) where TStartup : class { if (Context == null) return "未指定 Context 处理程序。"; var port = Port; if (port < ushort.MinValue || port > ushort.MaxValue) return "端口无法使用。"; if (NetworkUtility.ListActiveTcpPort().Contains(port)) return "端口无法使用。"; try { _builder = Host.CreateDefaultBuilder().ConfigureWebHostDefaults((builder) => { configure?.Invoke(builder); builder.UseStartup(); }); _host = _builder.Build(); // 添加到 Servers。 var add = Add(this); if (!string.IsNullOrEmpty(add)) { return add; Stop(_host); } // 异步运行。 if (async) { _host.RunAsync(); return null; } // 同步运行。 _host.Run(); } catch (Exception ex) { return ex.Message; } if (!async) Stop(port); return null; } #endregion /// 启动 Kestrel 服务器。 public string RunKestrel(bool async = false) => Run((builder) => builder.ConfigureKestrel((options) => { // 配置端口和证书。 options.Listen(IPAddress.Any, Port, (listen) => { if (Certificate != null) listen.UseHttps(Certificate); }); // 同步 IO。 options.AllowSynchronousIO = ApiOptions.AllowSynchronousIO; // 限制请求大小,最小为 1MB,默认为 1GB。 options.Limits.MaxRequestBodySize = (MaxBody < 1048576L) ? 1073741824L : MaxBody; }), async); /// 启动基于 HTTP.sys 的服务器。 /// /// HTTP.sys 是仅在 Windows 上运行的适用于 ASP.NET Core 的 Web 服务器。
HTTP.sys 是 Kestrel 服务器的替代选择,提供了一些 Kestrel 不提供的功能。
/// HTTP.sys 支持以下功能:
Windows 身份验证;
端口共享;
具有 SNI 的 HTTPS;
基于 TLS 的 HTTP/2(Windows 10 或更高版本);
直接文件传输;
响应缓存;
WebSocket(Windows 8 或更高版本)。
///
public string RunHttpSys(string ip, bool async = false) { // 检查 IP 地址格式。 if (!NetworkUtility.IsIP(ip)) return "IP 地址格式无效。"; return Run((builder) => builder.UseHttpSys((options) => { // 配置端口和证书。 var protocol = Certificate == null ? "http" : "https"; var url = $"{protocol}://{ip}:{Port}"; options.UrlPrefixes.Add(url); // 同步 IO。 options.AllowSynchronousIO = ApiOptions.AllowSynchronousIO; // 限制请求大小,最小为 1MB,默认为 1GB。 options.MaxRequestBodySize = (MaxBody < 1048576L) ? 1073741824L : MaxBody; // 认证。 options.Authentication.Schemes = Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.None; options.Authentication.AllowAnonymous = true; }), async); } } } #endif