EonaCat.Network/EonaCat.Network/System/Sockets/Web/Server/WSEndpoint.cs

209 lines
5.7 KiB
C#

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
using System;
using System.IO;
namespace EonaCat.Network
{
public abstract class WSEndpoint : IWSSession
{
private bool _emitOnPing;
private string _protocol;
private WSClient _websocket;
protected WSEndpoint()
{
StartTime = DateTime.MaxValue;
}
public WSContext Context { get; private set; }
public Func<CookieCollection, CookieCollection, bool> CookiesValidator { get; set; }
public bool EmitOnPing
{
get
{
return _websocket != null ? _websocket.CallMessageOnPing : _emitOnPing;
}
set
{
if (_websocket != null)
{
_websocket.CallMessageOnPing = value;
return;
}
_emitOnPing = value;
}
}
public string ID { get; private set; }
public bool IgnoreExtensions { get; set; }
public Func<string, bool> OriginValidator { get; set; }
public string Protocol
{
get
{
return _websocket != null ? _websocket.Protocol : (_protocol ?? string.Empty);
}
set
{
if (State != WSState.Connecting)
{
return;
}
if (value != null && (value.Length == 0 || !value.IsToken()))
{
return;
}
_protocol = value;
}
}
public DateTime StartTime { get; private set; }
public WSState State => _websocket != null ? _websocket.ReadyState : WSState.Connecting;
protected WSSessionManager Sessions { get; private set; }
internal void Start(WSContext context, WSSessionManager sessions)
{
if (_websocket != null)
{
Logger.Error("A session instance cannot be reused.");
context.WebSocket.Close(HttpStatusCode.ServiceUnavailable);
return;
}
Context = context;
Sessions = sessions;
_websocket = context.WebSocket;
_websocket.CustomHandshakeRequestChecker = checkHandshakeRequest;
_websocket.CallMessageOnPing = _emitOnPing;
_websocket.IgnoreExtensions = IgnoreExtensions;
_websocket.Protocol = _protocol;
var responseWaitingTime = sessions.ResponseWaitingTime;
if (responseWaitingTime != _websocket.ResponseWaitingTime)
{
_websocket.ResponseWaitingTime = responseWaitingTime;
}
_websocket.OnConnect += onOpen;
_websocket.OnMessageReceived += onMessage;
_websocket.OnError += onError;
_websocket.OnDisconnect += onClose;
_websocket.InternalAccept();
}
protected void Error(string message, Exception exception)
{
if (message != null && message.Length > 0)
{
OnError(new ErrorEventArgs(message, exception));
}
}
protected virtual void OnClose(CloseEventArgs e)
{
}
protected virtual void OnError(ErrorEventArgs e)
{
}
protected virtual void OnMessage(MessageEventArgs e)
{
}
protected virtual void OnOpen()
{
}
protected void Send(byte[] data)
{
_websocket?.Send(data);
}
protected void Send(FileInfo file)
{
_websocket?.Send(file);
}
protected void Send(string data)
{
_websocket?.Send(data);
}
protected void SendAsync(byte[] data, Action<bool> completed)
{
_websocket?.SendAsync(data, completed);
}
protected void SendAsync(FileInfo file, Action<bool> completed)
{
_websocket?.SendAsync(file, completed);
}
protected void SendAsync(string data, Action<bool> completed)
{
_websocket?.SendAsync(data, completed);
}
protected void SendAsync(Stream stream, int length, Action<bool> completed)
{
_websocket?.SendAsync(stream, length, completed);
}
private string checkHandshakeRequest(WSContext context)
{
return OriginValidator != null && !OriginValidator(context.Origin)
? "Includes no Origin header, or it has an invalid value."
: CookiesValidator != null
&& !CookiesValidator(context.CookieCollection, context.WebSocket.CookieCollection)
? "Includes no cookie, or an invalid cookie exists."
: null;
}
private void onClose(object sender, CloseEventArgs e)
{
if (ID == null)
{
return;
}
Sessions.Remove(ID);
OnClose(e);
}
private void onError(object sender, ErrorEventArgs e)
{
OnError(e);
}
private void onMessage(object sender, MessageEventArgs e)
{
OnMessage(e);
}
private void onOpen(object sender, EventArgs e)
{
ID = Sessions.Add(this);
if (ID == null)
{
_websocket.Close(CloseStatusCode.Away);
return;
}
StartTime = DateTime.Now;
OnOpen();
}
}
}