#region License /* * WebSocketBehavior.cs * * The MIT License * * Copyright (c) 2012-2016 sta.blockhead * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion using System; using System.IO; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; namespace WebSocketSharp.Server { /// /// Exposes the methods and properties used to define the behavior of a WebSocket service /// provided by the or . /// /// /// The WebSocketBehavior class is an abstract class. /// public abstract class WebSocketBehavior : IWebSocketSession { #region Private Fields private WebSocketContext _context; private Func _cookiesValidator; private bool _emitOnPing; private string _id; private bool _ignoreExtensions; private Func _originValidator; private string _protocol; private WebSocketSessionManager _sessions; private DateTime _startTime; private WebSocket _websocket; #endregion #region Protected Constructors /// /// Initializes a new instance of the class. /// protected WebSocketBehavior () { _startTime = DateTime.MaxValue; } #endregion #region Protected Properties /// /// Gets the logging functions. /// /// /// A that provides the logging functions, /// or if the WebSocket connection isn't established. /// protected Logger Log { get { return _websocket != null ? _websocket.Log : null; } } /// /// Gets the access to the sessions in the WebSocket service. /// /// /// A that provides the access to the sessions, /// or if the WebSocket connection isn't established. /// protected WebSocketSessionManager Sessions { get { return _sessions; } } #endregion #region Public Properties /// /// Gets the information in a handshake request to the WebSocket service. /// /// /// A instance that provides the access to the handshake request, /// or if the WebSocket connection isn't established. /// public WebSocketContext Context { get { return _context; } } /// /// Gets or sets the delegate called to validate the HTTP cookies included in /// a handshake request to the WebSocket service. /// /// /// This delegate is called when the used in a session validates /// the handshake request. /// /// /// /// A Func<CookieCollection, CookieCollection, bool> delegate that references /// the method(s) used to validate the cookies. /// /// /// 1st parameter passed to this delegate contains /// the cookies to validate if any. /// /// /// 2nd parameter passed to this delegate receives /// the cookies to send to the client. /// /// /// This delegate should return true if the cookies are valid. /// /// /// The default value is , and it does nothing to validate. /// /// public Func CookiesValidator { get { return _cookiesValidator; } set { _cookiesValidator = value; } } /// /// Gets or sets a value indicating whether the used in a session emits /// a event when receives a Ping. /// /// /// true if the emits a event /// when receives a Ping; otherwise, false. The default value is false. /// public bool EmitOnPing { get { return _websocket != null ? _websocket.EmitOnPing : _emitOnPing; } set { if (_websocket != null) { _websocket.EmitOnPing = value; return; } _emitOnPing = value; } } /// /// Gets the unique ID of a session. /// /// /// A that represents the unique ID of the session, /// or if the WebSocket connection isn't established. /// public string ID { get { return _id; } } /// /// Gets or sets a value indicating whether the WebSocket service ignores /// the Sec-WebSocket-Extensions header included in a handshake request. /// /// /// true if the WebSocket service ignores the extensions requested from /// a client; otherwise, false. The default value is false. /// public bool IgnoreExtensions { get { return _ignoreExtensions; } set { _ignoreExtensions = value; } } /// /// Gets or sets the delegate called to validate the Origin header included in /// a handshake request to the WebSocket service. /// /// /// This delegate is called when the used in a session validates /// the handshake request. /// /// /// /// A Func<string, bool> delegate that references the method(s) used to /// validate the origin header. /// /// /// parameter passed to this delegate represents the value of /// the origin header to validate if any. /// /// /// This delegate should return true if the origin header is valid. /// /// /// The default value is , and it does nothing to validate. /// /// public Func OriginValidator { get { return _originValidator; } set { _originValidator = value; } } /// /// Gets or sets the WebSocket subprotocol used in the WebSocket service. /// /// /// Set operation of this property is available before the WebSocket connection has /// been established. /// /// /// /// A that represents the subprotocol if any. /// The default value is . /// /// /// The value to set must be a token defined in /// RFC 2616. /// /// public string Protocol { get { return _websocket != null ? _websocket.Protocol : (_protocol ?? String.Empty); } set { if (State != WebSocketState.Connecting) return; if (value != null && (value.Length == 0 || !value.IsToken ())) return; _protocol = value; } } /// /// Gets the time that a session has started. /// /// /// A that represents the time that the session has started, /// or if the WebSocket connection isn't established. /// public DateTime StartTime { get { return _startTime; } } /// /// Gets the state of the used in a session. /// /// /// One of the enum values, indicates the state of /// the . /// public WebSocketState State { get { return _websocket != null ? _websocket.ReadyState : WebSocketState.Connecting; } } #endregion #region Private Methods private string checkHandshakeRequest (WebSocketContext 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 (); } #endregion #region Internal Methods internal void Start (WebSocketContext context, WebSocketSessionManager sessions) { if (_websocket != null) { _websocket.Log.Error ("A session instance cannot be reused."); context.WebSocket.Close (HttpStatusCode.ServiceUnavailable); return; } _context = context; _sessions = sessions; _websocket = context.WebSocket; _websocket.CustomHandshakeRequestChecker = checkHandshakeRequest; _websocket.EmitOnPing = _emitOnPing; _websocket.IgnoreExtensions = _ignoreExtensions; _websocket.Protocol = _protocol; var waitTime = sessions.WaitTime; if (waitTime != _websocket.WaitTime) _websocket.WaitTime = waitTime; _websocket.OnOpen += onOpen; _websocket.OnMessage += onMessage; _websocket.OnError += onError; _websocket.OnClose += onClose; _websocket.InternalAccept (); } #endregion #region Protected Methods /// /// Calls the method with the specified and /// . /// /// /// This method doesn't call the method if is /// or empty. /// /// /// A that represents the error message. /// /// /// An instance that represents the cause of the error if any. /// protected void Error (string message, Exception exception) { if (message != null && message.Length > 0) OnError (new ErrorEventArgs (message, exception)); } /// /// Called when the WebSocket connection used in a session has been closed. /// /// /// A that represents the event data passed to /// a event. /// protected virtual void OnClose (CloseEventArgs e) { } /// /// Called when the used in a session gets an error. /// /// /// A that represents the event data passed to /// a event. /// protected virtual void OnError (ErrorEventArgs e) { } /// /// Called when the used in a session receives a message. /// /// /// A that represents the event data passed to /// a event. /// protected virtual void OnMessage (MessageEventArgs e) { } /// /// Called when the WebSocket connection used in a session has been established. /// protected virtual void OnOpen () { } /// /// Sends binary to the client on a session. /// /// /// This method is available after the WebSocket connection has been established. /// /// /// An array of that represents the binary data to send. /// protected void Send (byte[] data) { if (_websocket != null) _websocket.Send (data); } /// /// Sends the specified as binary data to the client on a session. /// /// /// This method is available after the WebSocket connection has been established. /// /// /// A that represents the file to send. /// protected void Send (FileInfo file) { if (_websocket != null) _websocket.Send (file); } /// /// Sends text to the client on a session. /// /// /// This method is available after the WebSocket connection has been established. /// /// /// A that represents the text data to send. /// protected void Send (string data) { if (_websocket != null) _websocket.Send (data); } /// /// Sends binary asynchronously to the client on a session. /// /// /// /// This method is available after the WebSocket connection has been established. /// /// /// This method doesn't wait for the send to be complete. /// /// /// /// An array of that represents the binary data to send. /// /// /// An Action<bool> delegate that references the method(s) called when /// the send is complete. A passed to this delegate is true /// if the send is complete successfully. /// protected void SendAsync (byte[] data, Action completed) { if (_websocket != null) _websocket.SendAsync (data, completed); } /// /// Sends the specified as binary data asynchronously to /// the client on a session. /// /// /// /// This method is available after the WebSocket connection has been established. /// /// /// This method doesn't wait for the send to be complete. /// /// /// /// A that represents the file to send. /// /// /// An Action<bool> delegate that references the method(s) called when /// the send is complete. A passed to this delegate is true /// if the send is complete successfully. /// protected void SendAsync (FileInfo file, Action completed) { if (_websocket != null) _websocket.SendAsync (file, completed); } /// /// Sends text asynchronously to the client on a session. /// /// /// /// This method is available after the WebSocket connection has been established. /// /// /// This method doesn't wait for the send to be complete. /// /// /// /// A that represents the text data to send. /// /// /// An Action<bool> delegate that references the method(s) called when /// the send is complete. A passed to this delegate is true /// if the send is complete successfully. /// protected void SendAsync (string data, Action completed) { if (_websocket != null) _websocket.SendAsync (data, completed); } /// /// Sends binary data from the specified asynchronously to /// the client on a session. /// /// /// /// This method is available after the WebSocket connection has been established. /// /// /// This method doesn't wait for the send to be complete. /// /// /// /// A from which contains the binary data to send. /// /// /// An that represents the number of bytes to send. /// /// /// An Action<bool> delegate that references the method(s) called when /// the send is complete. A passed to this delegate is true /// if the send is complete successfully. /// protected void SendAsync (Stream stream, int length, Action completed) { if (_websocket != null) _websocket.SendAsync (stream, length, completed); } #endregion } }