#if !NET20 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Apewer.WebSocket { /// <summary></summary> public class Connection { #region internal internal Connection(SocketWrapper socket, Action<Connection> initialize, Func<byte[], HttpRequest> parseRequest, Func<HttpRequest, ComposableHandler> handlerFactory, Func<IEnumerable<string>, string> negotiateSubProtocol) { Socket = socket; OnOpen = () => { }; OnClose = () => { }; OnMessage = x => { }; OnBytes = x => { }; OnPing = x => Pong(x); OnPong = x => { }; OnError = x => { }; _initialize = initialize; _handlerFactory = handlerFactory; _parseRequest = parseRequest; _negotiateSubProtocol = negotiateSubProtocol; } internal SocketWrapper Socket { get; set; } private readonly Action<Connection> _initialize; private readonly Func<HttpRequest, ComposableHandler> _handlerFactory; private readonly Func<IEnumerable<string>, string> _negotiateSubProtocol; readonly Func<byte[], HttpRequest> _parseRequest; internal ComposableHandler Handler { get; set; } private bool _closing; private bool _closed; private const int ReadSize = 1024 * 4; internal Action OnOpen { get; set; } internal Action OnClose { get; set; } internal Action<string> OnMessage { get; set; } internal Action<byte[]> OnBytes { get; set; } internal Action<byte[]> OnPing { get; set; } internal Action<byte[]> OnPong { get; set; } internal Action<Exception> OnError { get; set; } internal ConnectionInfo ConnectionInfo { get; private set; } /// <exception cref="InvalidOperationException"></exception> private Task Send<T>(T message, Func<T, byte[]> createFrame) { if (Handler == null) throw new InvalidOperationException("Cannot send before handshake."); if (!Available) { const string errorMessage = "Data sent while closing or after close. Ignoring."; WebSocketLog.Warn(errorMessage); var taskForException = new TaskCompletionSource<object>(); taskForException.SetException(new ConnectionNotAvailableException(errorMessage)); return taskForException.Task; } var bytes = createFrame(message); return SendBytes(bytes); } /// <summary></summary> internal void StartReceiving() { var data = new List<byte>(ReadSize); var buffer = new byte[ReadSize]; Read(data, buffer); } internal void Close(int code) { if (!Available) return; _closing = true; if (Handler == null) { CloseSocket(); return; } var bytes = Handler.FrameClose(code); if (bytes.Length == 0) CloseSocket(); else SendBytes(bytes, CloseSocket); } internal void CreateHandler(IEnumerable<byte> data) { var request = _parseRequest(data.ToArray()); if (request == null) return; Handler = _handlerFactory(request); if (Handler == null) return; var subProtocol = _negotiateSubProtocol(request.SubProtocols); ConnectionInfo = ConnectionInfo.Create(request, Socket.RemoteIpAddress, Socket.RemotePort, subProtocol); _initialize(this); var handshake = Handler.CreateHandshake(subProtocol); SendBytes(handshake, OnOpen); } private void Read(List<byte> data, byte[] buffer) { if (!Available) return; Socket.Receive(buffer, r => { if (r <= 0) { WebSocketLog.Debug("0 bytes read. Closing."); CloseSocket(); return; } WebSocketLog.Debug(r + " bytes read"); var readBytes = buffer.Take(r); if (Handler != null) { Handler.Receive(readBytes); } else { data.AddRange(readBytes); CreateHandler(data); } Read(data, buffer); }, HandleReadError); } private void HandleReadError(Exception e) { if (e is AggregateException) { var agg = e as AggregateException; HandleReadError(agg.InnerException); return; } if (e is ObjectDisposedException) { WebSocketLog.Debug("Swallowing ObjectDisposedException", e); return; } OnError(e); if (e is WebSocketException) { WebSocketLog.Debug("Error while reading", e); Close(((WebSocketException)e).StatusCode); } else if (e is SubProtocolNegotiationFailureException) { WebSocketLog.Debug(e.Message); Close(StatusCodes.ProtocolError); } else if (e is IOException) { WebSocketLog.Debug("Error while reading", e); Close(StatusCodes.AbnormalClosure); } else { WebSocketLog.Error("Application Error", e); Close(StatusCodes.InternalServerError); } } private Task SendBytes(byte[] bytes, Action callback = null) { return Socket.Send(bytes, () => { WebSocketLog.Debug("Sent " + bytes.Length + " bytes"); if (callback != null) callback(); }, e => { if (e is IOException) WebSocketLog.Debug("Failed to send. Disconnecting.", e); else WebSocketLog.Info("Failed to send. Disconnecting.", e); CloseSocket(); }); } private void CloseSocket() { _closing = true; OnClose(); _closed = true; Socket.Close(); Socket.Dispose(); _closing = false; } #endregion #region properties /// <summary></summary> public bool Available { get { return !_closing && !_closed && Socket.Connected; } } /// <summary>客户端 IP 地址。</summary> public string Address { get { return (ConnectionInfo == null) ? "" : ConnectionInfo.ClientIpAddress; } } /// <summary>客户端端口。</summary> public int Port { get { return (ConnectionInfo == null) ? 0 : ConnectionInfo.ClientPort; } } /// <summary>获取主机信息。例: /// <para>从请求 ws://127.0.0.1:8000/page?field=value</para> /// <para>中获取 127.0.0.1:8000</para></summary> public string Host { get { return (ConnectionInfo == null) ? "" : ConnectionInfo.Host; } } /// <summary>获取源站信息。例: /// <para>从请求 ws://127.0.0.1:8000/page?field=value</para> /// <para>中获取 http://localhost</para></summary> public string Origin { get { return (ConnectionInfo == null) ? "" : ConnectionInfo.Origin; } } /// <summary>获取 URL 查询路径。例: /// <para>从请求 ws://127.0.0.1:8000/page?field=value</para> /// <para>中获取 /page?field=value</para></summary> public string QueryPath { get { return (ConnectionInfo == null) ? "" : ConnectionInfo.Path; } } #endregion #region methods /// <summary>发送文本。</summary> /// <exception cref="InvalidOperationException"></exception> public Task Send(params char[] message) { if (message == null || message.Length < 1) return null; if (message.Length == 1) { return Send(message[0].ToString(), Handler.FrameText); } else { var sb = new System.Text.StringBuilder(); foreach (var i in message) { if ((object)i != null) sb.Append(i); } var text = sb.ToString(); if (text.Length < 1) return null; return Send(text, Handler.FrameText); } } /// <summary>发送文本。</summary> /// <exception cref="InvalidOperationException"></exception> public Task Send(params string[] message) { if (message == null || message.Length < 1) return null; if (message.Length == 1) { if (string.IsNullOrEmpty(message[0])) return null; return Send(message[0], Handler.FrameText); } else { var sb = new System.Text.StringBuilder(); foreach (var i in message) { if (i != null) sb.Append(i); } var text = sb.ToString(); if (text.Length < 1) return null; return Send(text, Handler.FrameText); } } /// <summary>发送字节数组。</summary> /// <exception cref="InvalidOperationException"></exception> public Task Send(params byte[] message) { if (message == null || message.Length < 1) return null; return Send(message, Handler.FrameBytes); } /// <summary>发送 PING。</summary> /// <exception cref="InvalidOperationException"></exception> public Task Ping(params byte[] message) { return Send(message, Handler.FramePing); } /// <summary>发送 PONG。</summary> /// <exception cref="InvalidOperationException"></exception> public Task Pong(params byte[] message) { return Send(message, Handler.FramePong); } /// <summary>关闭 Socket 连接。</summary> public void Close() { Close(StatusCodes.NormalClosure); } #endregion } } #endif