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
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
|
|
|