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.

232 lines
7.1 KiB

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
namespace Apewer.Web
{
/// <summary></summary>
public sealed class MiniServer
{
/// <summary></summary>
public MiniServer()
{
SynchronousIO = true;
MaxRequest = 1024 * 1024 * 1024;
}
#region configuration
/// <summary>上下文处理程序。</summary>
/// <remarks>默认值:未指定</remarks>
public Action<MiniContext> Handler { get; set; }
/// <summary>捕获处理请求的异常。</summary>
/// <remarks>默认值:未指定</remarks>
public Action<Exception> CatchException { get; set; }
/// <summary>获取已指定的本地终结点。</summary>
public IPEndPoint IPEndPoint { get; private set; }
/// <summary>获取已监听的本地端口。</summary>
public int Port { get => GetPort(); }
/// <summary>入站超时毫秒数。指定 0 或 -1 时为无限大。</summary>
/// <remarks>默认值:0</remarks>
public int InboundTimeout { get; set; }
/// <summary>出站超时毫秒数。指定 0 或 -1 时为无限大。</summary>
/// <remarks>默认值:0</remarks>
public int OutboundTimeout { get; set; }
/// <summary>SSL 证书。</summary>
internal X509Certificate SslCertificate { get; set; }
/// <summary>SSL 域名。</summary>
internal string SslDomain { get; set; }
/// <summary>SSL 协议。</summary>
internal SslProtocols SslProtocols { get; set; }
/// <summary>同步 IO 操作。</summary>
/// <remarks>默认值:True</remarks>
internal bool SynchronousIO { get; set; }
/// <summary>对响应体分块。</summary>
/// <remarks>默认值:False</remarks>
public bool Chunked { get; set; }
/// <summary>限制请求大小。</summary>
/// <remarks>默认值:1 GB。</remarks>
public int MaxRequest { get; set; }
/// <summary>允许压缩。</summary>
public bool Compression { get; set; }
#endregion
#region launcher
Thread _thread = null;
/// <summary>启动服务器。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="SocketException"></exception>
public void Run(int port = 0, bool await = true)
{
if (port < 0 || port > 65535) throw new ArgumentOutOfRangeException(nameof(port));
Run(new IPEndPoint(IPAddress.Any, port), await);
}
/// <summary>启动服务器。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="SocketException"></exception>
public void Run(IPEndPoint ipEndpoint, bool await = true)
{
if (_socket != null) throw new InvalidOperationException("存在已启动的 Socket。");
if (ipEndpoint == null) throw new ArgumentNullException(nameof(ipEndpoint));
IPEndPoint = ipEndpoint;
var ipep = ipEndpoint;
_socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(ipep);
_socket.Listen(500);
var inTimeout = InboundTimeout;
var outTimeout = OutboundTimeout;
if (inTimeout > 0) _socket.ReceiveTimeout = inTimeout;
if (outTimeout > 0) _socket.SendTimeout = outTimeout;
_thread = new Thread(Listen);
_thread.IsBackground = true;
_thread.Start();
if (await) while (_socket != null) Thread.Sleep(300);
}
/// <summary>关闭服务器。</summary>
public void Shutdown()
{
var socket = _socket;
if (socket != null)
{
try { socket.Shutdown(SocketShutdown.Both); } catch { }
try { socket.Close(1); } catch { }
_socket = null;
}
}
#endregion
#region listener
Socket _socket = null;
int GetPort()
{
var socket = _socket;
if (socket != null) return 0;
try { if (socket.LocalEndPoint is IPEndPoint ipep) return ipep.Port; } catch { }
return 0;
}
void Listen()
{
if (SynchronousIO)
{
while (_socket != null)
{
var socket = null as Socket;
try
{
socket = _socket.Accept();
}
catch
{
if (socket != null)
{
try { socket.Close(); } catch { }
try { socket.Disconnect(false); } catch { }
#if !NET20
try { socket.Dispose(); } catch { }
#endif
}
break;
}
if (socket != null) ThreadPool.QueueUserWorkItem(Process, socket);
}
}
else
{
var e = new SocketAsyncEventArgs();
e.UserToken = this;
e.Completed += (sender, arg) => Process(arg);
Socket dummy = null;
Accept(e, ref dummy);
}
}
void Accept(SocketAsyncEventArgs e, ref Socket socket)
{
e.AcceptSocket = null;
var async = false;
try
{
async = _socket.AcceptAsync(e);
}
catch
{
if (socket != null)
{
try { socket.Close(); } catch { }
socket = null;
}
return;
}
if (!async) Process(e);
}
void Process(SocketAsyncEventArgs e)
{
var socket = null as Socket;
if (e.SocketError == SocketError.Success) socket = e.AcceptSocket;
Accept(e, ref socket);
if (socket == null) return;
Process(socket);
}
void Process(object obj)
{
var socket = obj as Socket;
if (socket == null) return;
try
{
var conn = null as MiniConnection;
conn = new MiniConnection(this, socket);
conn.BeginRead();
}
catch (Exception ex)
{
Shutdown();
Logger.Web.Exception(this, ex);
CatchException?.Invoke(ex);
return;
}
}
#endregion
}
}