You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.8 KiB

#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
{
/// <summary>ASP.NET Core 服务器。</summary>
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);
/// <summary>获取所有正在解析的端口。</summary>
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;
/// <summary>监听的端口。默认值:0,自动选择 80 端口或 443 端口。</summary>
public int Port
{
get { return _port > 0 ? _port : (Certificate == null ? 80 : 443); }
set { _port = value; }
}
/// <summary>获取或设置将要使用的证书。</summary>
public X509Certificate2 Certificate { get; set; }
/// <summary>获取或设置请求的最大 Body 大小,单位为字节。可接受的最小值为 1048576 字节(1 MB)。默认值:1073741824。</summary>
public long MaxBody { get; set; } = 1073741824L;
/// <summary>获取或设置 Context 处理程序。</summary>
public Action<HttpContext> Context { get; set; }
/// <summary>获取或设置 WebSocket 处理程序。</summary>
public Action<HttpContext, System.Net.WebSockets.WebSocket> WebSocket { get; set; }
private string Run<TStartup>(Action<IWebHostBuilder> 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<TStartup>();
});
_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
/// <summary>启动 Kestrel 服务器。</summary>
public string RunKestrel(bool async = false) => Run<KestrelStartup>((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);
/// <summary>启动基于 HTTP.sys 的服务器。</summary>
/// <remarks>
/// <para>HTTP.sys 是仅在 Windows 上运行的适用于 ASP.NET Core 的 Web 服务器。<br />HTTP.sys 是 Kestrel 服务器的替代选择,提供了一些 Kestrel 不提供的功能。</para>
/// <para>HTTP.sys 支持以下功能:<br />Windows 身份验证;<br />端口共享;<br />具有 SNI 的 HTTPS;<br />基于 TLS 的 HTTP/2(Windows 10 或更高版本);<br />直接文件传输;<br />响应缓存;<br />WebSocket(Windows 8 或更高版本)。</para>
/// </remarks>
public string RunHttpSys(string ip, bool async = false)
{
// 检查 IP 地址格式。
if (!NetworkUtility.IsIP(ip)) return "IP 地址格式无效。";
return Run<HttpSysStartup>((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