using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Net; using System.Security.Authentication; using System.Text; using System.Threading; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Protocol; using SuperWebSocket.Protocol; using SuperWebSocket.SubProtocol; namespace SuperWebSocket { /// /// WebSocketSession basic interface /// public interface IWebSocketSession : IAppSession { /// /// Gets or sets the method. /// /// /// The method. /// string Method { get; set; } /// /// Gets the host. /// string Host { get; } /// /// Gets or sets the path. /// /// /// The path. /// string Path { get; set; } /// /// Gets or sets the HTTP version. /// /// /// The HTTP version. /// string HttpVersion { get; set; } /// /// Gets the sec web socket version. /// string SecWebSocketVersion { get; } /// /// Gets the origin. /// string Origin { get; } /// /// Gets the URI scheme. /// string UriScheme { get; } /// /// Gets a value indicating whether this is handshaked. /// /// /// true if handshaked; otherwise, false. /// bool Handshaked { get; } /// /// Sends the raw binary data to client. /// /// The data. /// The offset. /// The length. void SendRawData(byte[] data, int offset, int length); /// /// Try to send the raw binary data to client. /// /// The data. /// The offset. /// The length. /// if the data to be sent is queued, return true, else the queue is full, then return false bool TrySendRawData(byte[] data, int offset, int length); /// /// Gets the app server. /// new IWebSocketServer AppServer { get; } /// /// Gets or sets the protocol processor. /// /// /// The protocol processor. /// IProtocolProcessor ProtocolProcessor { get; set; } /// /// Gets the available sub protocol. /// /// The protocol. /// string GetAvailableSubProtocol(string protocol); } /// /// WebSocket AppSession /// public class WebSocketSession : WebSocketSession { /// /// Gets the app server. /// public new WebSocketServer AppServer { get { return (WebSocketServer)base.AppServer; } } } /// /// WebSocket AppSession class /// /// The type of the web socket session. public class WebSocketSession : AppSession, IWebSocketSession, IAppSession where TWebSocketSession : WebSocketSession, new() { /// /// Gets or sets the method. /// /// /// The method. /// public string Method { get; set; } /// /// Gets or sets the path. /// /// /// The path. /// public string Path { get; set; } /// /// Gets or sets the HTTP version. /// /// /// The HTTP version. /// public string HttpVersion { get; set; } /// /// Gets the host. /// public string Host { get { return this.Items.GetValue(WebSocketConstant.Host, string.Empty); } } /// /// Gets the origin. /// public string Origin { get; internal set; } /// /// Gets the upgrade. /// public string Upgrade { get { return this.Items.GetValue(WebSocketConstant.Upgrade, string.Empty); } } /// /// Gets the connection. /// public string Connection { get { return this.Items.GetValue(WebSocketConstant.Connection, string.Empty); } } /// /// Gets the sec web socket version. /// public string SecWebSocketVersion { get { return this.Items.GetValue(WebSocketConstant.SecWebSocketVersion, string.Empty); } } /// /// Gets the sec web socket protocol. /// public string SecWebSocketProtocol { get { return this.Items.GetValue(WebSocketConstant.SecWebSocketProtocol, string.Empty); } } internal List Frames { get; private set; } internal DateTime StartClosingHandshakeTime { get; private set; } private const string m_CurrentTokenSlotName = "CurrentRequestToken"; internal LocalDataStoreSlot SetCurrentToken(string token) { var slot = Thread.GetNamedDataSlot(m_CurrentTokenSlotName); Thread.SetData(slot, token); return slot; } /// /// Gets the current token. /// public string CurrentToken { get { return Thread.GetData(Thread.GetNamedDataSlot(m_CurrentTokenSlotName)) as string; } } /// /// Gets the app server. /// public new WebSocketServer AppServer { get { return (WebSocketServer)base.AppServer; } } IWebSocketServer IWebSocketSession.AppServer { get { return (IWebSocketServer)base.AppServer; } } string IWebSocketSession.GetAvailableSubProtocol(string protocol) { if (string.IsNullOrEmpty(protocol)) { SubProtocol = AppServer.DefaultSubProtocol; return string.Empty; } var arrNames = protocol.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (protocol.ToLower().Contains("ocpp2.0")) { arrNames = new string[] { "ocpp2.0" }; } foreach (var name in arrNames) { var subProtocol = AppServer.GetSubProtocol(name); if (subProtocol != null) { SubProtocol = subProtocol; return name; } } return string.Empty; } /// /// Gets the URI scheme, ws or wss /// public string UriScheme { get { if (SocketSession.SecureProtocol == SslProtocols.None) return WebSocketConstant.WsSchema; else return WebSocketConstant.WssSchema; } } /// /// Gets the sub protocol. /// public ISubProtocol SubProtocol { get; private set; } private bool m_Handshaked = false; /// /// Gets a value indicating whether this is handshaked. /// /// /// true if handshaked; otherwise, false. /// public bool Handshaked { get { return m_Handshaked; } } internal void OnHandshakeSuccess() { m_Handshaked = true; SetCookie(); OnSessionStarted(); AppServer.FireOnNewSessionConnected(this); } /// /// Gets a value indicating whether the session [in closing]. /// /// /// true if [in closing]; otherwise, false. /// public bool InClosing { get; private set; } /// /// Called when [init]. /// protected override void OnInit() { Frames = new List(); base.OnInit(); } void IAppSession.StartSession() { //Do nothing. Avoid firing thhe OnSessionStarted() method of base class } /// /// Sets the cookie. /// private void SetCookie() { string cookieValue = this.Items.GetValue(WebSocketConstant.Cookie, string.Empty); if (string.IsNullOrEmpty(cookieValue)) return; var cookies = new StringDictionary(); this.Cookies = cookies; string[] pairs = cookieValue.Split(';'); int pos; string key, value; foreach (var p in pairs) { pos = p.IndexOf('='); if (pos <= 0) continue; key = p.Substring(0, pos).Trim(); pos += 1; if (pos < p.Length) value = p.Substring(pos).Trim(); else value = string.Empty; if (string.IsNullOrEmpty(value)) { cookies[key] = string.Empty; continue; } try { cookies[key] = Uri.UnescapeDataString(value); } catch (Exception e) { Logger.Error(this, string.Format("Failed to read cookie, key: {0}, value: {1}.", key, value), e); } } } /// /// Gets the cookies. /// public StringDictionary Cookies { get; private set; } /// /// Sends the message to client. /// /// The message. public override void Send(string message) { ProtocolProcessor.SendMessage(this, message); } /// /// Tries to send. /// /// The message to be sent. /// public override bool TrySend(string message) { return ProtocolProcessor.TrySendMessage(this, message); } /// /// Sends the data to client. /// /// The data. /// The offset. /// The length. public override void Send(byte[] data, int offset, int length) { if (!ProtocolProcessor.CanSendBinaryData) { if (Logger.IsErrorEnabled) Logger.Error("The websocket of this version cannot used for sending binary data!"); return; } ProtocolProcessor.SendData(this, data, offset, length); } /// /// Tries to send the data over the websocket connection. /// /// The segment to be sent. /// public override bool TrySend(ArraySegment segment) { if (!ProtocolProcessor.CanSendBinaryData) { if (Logger.IsErrorEnabled) Logger.Error("The websocket of this version cannot used for sending binary data!"); return false; } return ProtocolProcessor.TrySendData(this, segment.Array, segment.Offset, segment.Count); } /// /// Tries to send the data over the websocket connection. /// /// The data. /// The offset. /// The length. /// public override bool TrySend(byte[] data, int offset, int length) { if (!ProtocolProcessor.CanSendBinaryData) { if (Logger.IsErrorEnabled) Logger.Error("The websocket of this version cannot used for sending binary data!"); return false; } return ProtocolProcessor.TrySendData(this, data, offset, length); } /// /// Sends the segment to client. /// /// The segment. public override void Send(ArraySegment segment) { this.Send(segment.Array, segment.Offset, segment.Count); } /// /// Sends the raw binary data. /// /// The data. /// The offset. /// The length. void IWebSocketSession.SendRawData(byte[] data, int offset, int length) { base.Send(data, offset, length); } /// /// Try to send the raw binary data to client. /// /// The data. /// The offset. /// The length. /// /// if the data to be sent is queued, return true, else the queue is full, then return false /// bool IWebSocketSession.TrySendRawData(byte[] data, int offset, int length) { return base.TrySend(new ArraySegment(data, offset, length)); } /// /// Tries the send raw data segments. /// /// The segments. /// internal bool TrySendRawData(IList> segments) { return base.TrySend(segments); } /// /// Closes the with handshake. /// /// The reason text. public void CloseWithHandshake(string reasonText) { this.CloseWithHandshake(ProtocolProcessor.CloseStatusClode.NormalClosure, reasonText); } /// /// Closes the with handshake. /// /// The status code. /// The reason text. public void CloseWithHandshake(int statusCode, string reasonText) { if (!InClosing) InClosing = true; ProtocolProcessor.SendCloseHandshake(this, statusCode, reasonText); StartClosingHandshakeTime = DateTime.Now; AppServer.PushToCloseHandshakeQueue(this); } /// /// Sends the close handshake response. /// /// The status code. public void SendCloseHandshakeResponse(int statusCode) { if (!InClosing) InClosing = true; ProtocolProcessor.SendCloseHandshake(this, statusCode, string.Empty); } /// /// Closes the specified reason. /// /// The reason. public override void Close(CloseReason reason) { if (reason == CloseReason.TimeOut && ProtocolProcessor != null) { CloseWithHandshake(ProtocolProcessor.CloseStatusClode.NormalClosure, "Session timeOut"); return; } base.Close(reason); } /// /// Gets or sets the protocol processor. /// /// /// The protocol processor. /// public IProtocolProcessor ProtocolProcessor { get; set; } /// /// Handles the unknown command. /// /// The request info. internal protected virtual void HandleUnknownCommand(SubRequestInfo requestInfo) { } /// /// Handles the unknown request. /// /// The request info. protected override void HandleUnknownRequest(IWebSocketFragment requestInfo) { base.Close(); } } }