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();
}
}
}