Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Resharper added and reformatted code

  • Loading branch information...
commit 338a26e748e8b0973f5c1a5505f1c200367f01ab 1 parent 335f6f0
@DorianGray DorianGray authored
View
40 APServer.cs
@@ -21,9 +21,9 @@
*/
using System;
-using System.Text;
using System.Net;
using System.Net.Sockets;
+using System.Text;
namespace Alchemy.Server
{
@@ -32,31 +32,33 @@ namespace Alchemy.Server
/// It manages sending the XML cross domain policy to flash socket clients over port 843.
/// See http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html for details.
/// </summary>
- public class APServer : TCPServer, IDisposable
+ public class AccessPolicyServer : TcpServer, IDisposable
{
- private string _allowedHost = "localhost";
- private int _allowedPort = 80;
-
/// <summary>
/// The pre-formatted XML response.
/// </summary>
- private const string _response =
+ private const string Response =
"<cross-domain-policy>\r\n" +
- "\t<allow-access-from domain=\"{0}\" to-ports=\"{1}\" />\r\n" +
+ "\t<allow-access-from domain=\"{0}\" to-ports=\"{1}\" />\r\n" +
"</cross-domain-policy>\r\n\0";
+ private readonly string _allowedHost = "localhost";
+ private readonly int _allowedPort = 80;
+
/// <summary>
- /// Initializes a new instance of the <see cref="APServer"/> class.
+ /// Initializes a new instance of the <see cref="AccessPolicyServer"/> class.
/// </summary>
- /// <param name="ListenAddress">The listen address.</param>
- /// <param name="OriginDomain">The origin domain.</param>
- /// <param name="AllowedPort">The allowed port.</param>
- public APServer(IPAddress listenAddress, string originDomain, int allowedPort)
+ /// <param name="listenAddress">The listen address.</param>
+ /// <param name="originDomain">The origin domain.</param>
+ /// <param name="allowedPort">The allowed port.</param>
+ public AccessPolicyServer(IPAddress listenAddress, string originDomain, int allowedPort)
: base(843, listenAddress)
{
_allowedHost = "*";
if (originDomain != String.Empty)
+ {
_allowedHost = originDomain;
+ }
_allowedPort = allowedPort;
}
@@ -64,7 +66,7 @@ public APServer(IPAddress listenAddress, string originDomain, int allowedPort)
/// <summary>
/// Fires when a client connects.
/// </summary>
- /// <param name="AConnection">The TCP Connection.</param>
+ /// <param name="connection">The TCP Connection.</param>
protected override void OnRunClient(TcpClient connection)
{
try
@@ -73,16 +75,22 @@ protected override void OnRunClient(TcpClient connection)
SendResponse(connection);
connection.Client.Close();
}
- catch { /* Ignore */ }
+ // ReSharper disable EmptyGeneralCatchClause
+ catch
+ // ReSharper restore EmptyGeneralCatchClause
+ {
+ /* Ignore */
+ }
}
/// <summary>
/// Sends the response.
/// </summary>
- /// <param name="AConnection">The TCP Connection.</param>
+ /// <param name="connection">The TCP Connection.</param>
public void SendResponse(TcpClient connection)
{
- connection.Client.Send(UTF8Encoding.UTF8.GetBytes(String.Format(_response, _allowedHost, _allowedPort.ToString())));
+ connection.Client.Send(
+ Encoding.UTF8.GetBytes(String.Format(Response, _allowedHost, _allowedPort.ToString())));
}
}
}
View
16 Alchemy.config
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2011 Olivine Labs, LLC.
@@ -24,19 +24,17 @@ along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
<configuration>
<configSections>
- <section name="log4net"
- type="log4net.Config.Log4NetConfigurationSectionHandler,
- log4net-net-1.0"
- />
+ <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,
+ log4net-net-1.0" />
</configSections>
<log4net>
<logger name="Alchemy.Log">
- <level value="INFO"/>
+ <level value="INFO" />
</logger>
- <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
+ <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="Alchemy.Log" />
<param name="AppendToFile" value="true" />
<rollingStyle value="Size" />
@@ -47,7 +45,7 @@ along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
<param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n" />
</layout>
</appender>
-
+
<appender name="ConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="INFO" />
@@ -79,6 +77,6 @@ along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
-
+
</log4net>
</configuration>
View
133 Classes/Header.cs
@@ -46,126 +46,135 @@ public class Header
/// <summary>
/// Regular expression to parse http header
/// </summary>
- public static string Pattern =
- @"^(?<connect>[^\s]+)\s(?<path>[^\s]+)\sHTTP\/1\.1\r\n" + // HTTP Request
- @"((?<field_name>[^:\r\n]+):(?<field_value>[^\r\n]+)\r\n)+"; // HTTP Header Fields (<Field_Name>: <Field_Value> CR LF)
+ public static string Pattern =
+ @"^(?<connect>[^\s]+)\s(?<path>[^\s]+)\sHTTP\/1\.1\r\n" + // HTTP Request
+ @"((?<field_name>[^:\r\n]+):(?<field_value>[^\r\n]+)\r\n)+";
- /// <summary>
- /// The HTTP Method (GET/POST/PUT, etc.)
- /// </summary>
- public String Method = String.Empty;
+ // HTTP Header Fields (<Field_Name>: <Field_Value> CR LF)
/// <summary>
- /// What protocol this header represents, if any.
+ /// A collection of fields attached to the header.
/// </summary>
- public Protocol Protocol = Protocol.None;
-
+ private readonly NameValueCollection _fields = new NameValueCollection();
/// <summary>
- /// The path requested by the header.
+ /// Any cookies sent with the header.
/// </summary>
- public string RequestPath = string.Empty;
+ public HttpCookieCollection Cookies = new HttpCookieCollection();
/// <summary>
- /// Any cookies sent with the header.
+ /// The HTTP Method (GET/POST/PUT, etc.)
/// </summary>
- public HttpCookieCollection Cookies = new HttpCookieCollection();
+ public String Method = String.Empty;
/// <summary>
- /// A collection of fields attached to the header.
+ /// What protocol this header represents, if any.
/// </summary>
- private NameValueCollection Fields = new NameValueCollection();
+ public Protocol Protocol = Protocol.None;
+
/// <summary>
- /// Gets or sets the Fields object with the specified key.
+ /// The path requested by the header.
/// </summary>
- public string this[string Key]
- {
- get
- {
- return Fields[Key];
- }
- set
- {
- Fields[Key] = value;
- }
- }
+ public string RequestPath = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="Header"/> class.
/// Accepts a string that represents an HTTP header.
/// </summary>
/// <param name="data">The data.</param>
- public Header(string Data)
+ public Header(string data)
{
try
{
// Parse HTTP Header
- Regex regex = new Regex(Pattern, RegexOptions.IgnoreCase);
- Match match = regex.Match(Data);
- GroupCollection SomeFields = match.Groups;
+ var regex = new Regex(Pattern, RegexOptions.IgnoreCase);
+ Match match = regex.Match(data);
+ GroupCollection someFields = match.Groups;
// run through every match and save them in the handshake object
- for (int i = 0; i < SomeFields["field_name"].Captures.Count; i++)
+ for (int i = 0; i < someFields["field_name"].Captures.Count; i++)
{
- string Name = SomeFields["field_name"].Captures[i].ToString().ToLower();
- string Value = SomeFields["field_value"].Captures[i].ToString().Trim();
- switch (Name)
+ string name = someFields["field_name"].Captures[i].ToString().ToLower();
+ string value = someFields["field_value"].Captures[i].ToString().Trim();
+ switch (name)
{
case "cookie":
- string[] CookieArray = Value.Split(';');
- foreach (string ACookie in CookieArray)
+ string[] cookieArray = value.Split(';');
+ foreach (string cookie in cookieArray)
{
try
{
- string CookieName = ACookie.Remove(ACookie.IndexOf('='));
- string CookieValue = ACookie.Substring(ACookie.IndexOf('=') + 1);
- Cookies.Add(new HttpCookie(CookieName.TrimStart(), CookieValue));
+ string cookieName = cookie.Remove(cookie.IndexOf('='));
+ string cookieValue = cookie.Substring(cookie.IndexOf('=') + 1);
+ Cookies.Add(new HttpCookie(cookieName.TrimStart(), cookieValue));
+ }
+ // ReSharper disable EmptyGeneralCatchClause
+ catch
+ // ReSharper restore EmptyGeneralCatchClause
+ {
+ /* Ignore bad cookie */
}
- catch { /* Ignore bad cookie */ }
}
break;
default:
- Fields.Add(Name, Value);
+ _fields.Add(name, value);
break;
}
}
- RequestPath = SomeFields["path"].Captures[0].Value.Trim();
- Method = SomeFields["connect"].Captures[0].Value.Trim();
+ RequestPath = someFields["path"].Captures[0].Value.Trim();
+ Method = someFields["connect"].Captures[0].Value.Trim();
- string Version = string.Empty;
+ string version = string.Empty;
try
{
- Version = Fields["sec-websocket-version"];
+ version = _fields["sec-websocket-version"];
}
- catch (Exception){}
-
- if(Int32.Parse(Version) >= 8)
+ // ReSharper disable EmptyGeneralCatchClause
+ catch {}
+ // ReSharper restore EmptyGeneralCatchClause
+
+ if (Int32.Parse(version) >= 8)
{
- this.Protocol = Protocol.WebSocketHybi10;
+ Protocol = Protocol.WebSocketHybi10;
}
else
{
- string[] PathExplode = RequestPath.Split('/');
- string ProtocolString = string.Empty;
- if (PathExplode.Length > 0)
- ProtocolString = PathExplode[PathExplode.Length - 1].ToLower().Trim();
- switch (ProtocolString)
+ string[] pathExplode = RequestPath.Split('/');
+ string protocolString = string.Empty;
+ if (pathExplode.Length > 0)
+ {
+ protocolString = pathExplode[pathExplode.Length - 1].ToLower().Trim();
+ }
+ switch (protocolString)
{
case "websocket":
- this.Protocol = Protocol.WebSocketHybi00;
+ Protocol = Protocol.WebSocketHybi00;
break;
case "flashsocket":
- this.Protocol = Protocol.FlashSocket;
+ Protocol = Protocol.FlashSocket;
break;
default:
- this.Protocol = Protocol.None;
+ Protocol = Protocol.None;
break;
}
}
-
}
- catch{ /* Ignore bad header */ }
+ // ReSharper disable EmptyGeneralCatchClause
+ catch
+ // ReSharper restore EmptyGeneralCatchClause
+ {
+ /* Ignore bad header */
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the Fields object with the specified key.
+ /// </summary>
+ public string this[string key]
+ {
+ get { return _fields[key]; }
+ set { _fields[key] = value; }
}
}
-}
+}
View
2  Classes/Response.cs
@@ -30,4 +30,4 @@ public abstract class Response
{
public const string NotImplemented = "HTTP/1.1 501 Not Implemented";
}
-}
+}
View
229 Classes/UserContext.cs
@@ -21,11 +21,12 @@
*/
using System;
-using System.Text;
+using System.Net;
using System.Net.Sockets;
-using Alchemy.Server.Handlers;
+using System.Text;
using System.Threading;
-using System.Net;
+using Alchemy.Server.Handlers;
+using Alchemy.Server.Handlers.WebSocket;
namespace Alchemy.Server.Classes
{
@@ -34,53 +35,61 @@ namespace Alchemy.Server.Classes
/// </summary>
public class UserContext
{
+ public readonly Header Header;
+
/// <summary>
/// AQ Link to the parent User Context
/// </summary>
- private Context Context = null;
+ private readonly Context _context;
+
/// <summary>
- /// The data Frame that this client is currently processing.
+ /// The remote endpoint address.
/// </summary>
- public Alchemy.Server.Handlers.WebSocket.DataFrame DataFrame = null;
+ public EndPoint ClientAddress;
+
/// <summary>
- /// What character encoding to use.
+ /// User defined data. Can be anything.
/// </summary>
- public UTF8Encoding Encoding = new UTF8Encoding();
+ public Object Data;
+
/// <summary>
- /// User defined data. Can be anything.
+ /// The data Frame that this client is currently processing.
/// </summary>
- public Object Data = null;
+ public DataFrame DataFrame;
+
/// <summary>
- /// The path of this request.
+ /// What character encoding to use.
/// </summary>
- public string RequestPath = "/";
+ public UTF8Encoding Encoding = new UTF8Encoding();
+
/// <summary>
- /// The remote endpoint address.
+ /// OnEvent Delegates specific to this connection.
/// </summary>
- public EndPoint ClientAddress = null;
+ protected OnEventDelegate OnConnectDelegate = x => { };
+
+ protected OnEventDelegate OnConnectedDelegate = x => { };
+ protected OnEventDelegate OnDisconnectDelegate = x => { };
+ protected OnEventDelegate OnReceiveDelegate = x => { };
+ protected OnEventDelegate OnSendDelegate = x => { };
+
/// <summary>
/// The type of connection this is
/// </summary>
public Protocol Protocol = Protocol.None;
+
/// <summary>
- /// OnEvent Delegates specific to this connection.
+ /// The path of this request.
/// </summary>
- protected OnEventDelegate _OnConnect = (x) => { };
- protected OnEventDelegate _OnConnected = (x) => { };
- protected OnEventDelegate _OnDisconnect = (x) => { };
- protected OnEventDelegate _OnReceive = (x) => { };
- protected OnEventDelegate _OnSend = (x) => { };
-
- public readonly Header Header;
+ public string RequestPath = "/";
/// <summary>
/// Initializes a new instance of the <see cref="UserContext"/> class.
/// </summary>
/// <param name="context">The user context.</param>
- public UserContext(Context AContext)
+ public UserContext(Context context)
{
- this.Context = AContext;
- this.Header = this.Context.Header;
+ _context = context;
+ Header = context.Header;
}
/// <summary>
@@ -90,9 +99,12 @@ public void OnConnect()
{
try
{
- _OnConnect(this);
+ OnConnectDelegate(this);
+ }
+ catch (Exception e)
+ {
+ _context.Server.Log.Error("Fatal Error in user specified OnConnect", e);
}
- catch (Exception e) { Context.Server.Log.Error("Fatal Error in user specified OnConnect", e); }
}
/// <summary>
@@ -102,9 +114,12 @@ public void OnConnected()
{
try
{
- _OnConnected(this);
+ OnConnectedDelegate(this);
+ }
+ catch (Exception e)
+ {
+ _context.Server.Log.Error("Fatal Error in user specified OnConnected", e);
}
- catch (Exception e) { Context.Server.Log.Error("Fatal Error in user specified OnConnected", e); }
}
/// <summary>
@@ -114,10 +129,13 @@ public void OnDisconnect()
{
try
{
- Context.Connected = false;
- _OnDisconnect(this);
+ _context.Connected = false;
+ OnDisconnectDelegate(this);
+ }
+ catch (Exception e)
+ {
+ _context.Server.Log.Error("Fatal Error in user specified OnDisconnect", e);
}
- catch (Exception e) { Context.Server.Log.Error("Fatal Error in user specified OnDisconnect", e); }
}
/// <summary>
@@ -127,9 +145,12 @@ public void OnSend()
{
try
{
- _OnSend(this);
+ OnSendDelegate(this);
+ }
+ catch (Exception e)
+ {
+ _context.Server.Log.Error("Fatal Error in user specified OnSend", e);
}
- catch (Exception e) { Context.Server.Log.Error("Fatal Error in user specified OnSend", e); }
}
/// <summary>
@@ -139,54 +160,57 @@ public void OnReceive()
{
try
{
- _OnReceive(this);
+ OnReceiveDelegate(this);
+ }
+ catch (Exception e)
+ {
+ _context.Server.Log.Error("Fatal Error in user specified OnReceive", e);
}
- catch (Exception e) { Context.Server.Log.Error("Fatal Error in user specified OnReceive", e); }
}
/// <summary>
/// Sets the on connect event.
/// </summary>
- /// <param name="ADelegate">The Event Delegate.</param>
- public void SetOnConnect(OnEventDelegate ADelegate)
+ /// <param name="aDelegate">The Event Delegate.</param>
+ public void SetOnConnect(OnEventDelegate aDelegate)
{
- _OnConnect = ADelegate;
+ OnConnectDelegate = aDelegate;
}
/// <summary>
/// Sets the on connected event.
/// </summary>
- /// <param name="ADelegate">The Event Delegate.</param>
- public void SetOnConnected(OnEventDelegate ADelegate)
+ /// <param name="aDelegate">The Event Delegate.</param>
+ public void SetOnConnected(OnEventDelegate aDelegate)
{
- _OnConnected = ADelegate;
+ OnConnectedDelegate = aDelegate;
}
/// <summary>
/// Sets the on disconnect event.
/// </summary>
- /// <param name="ADelegate">The Event Delegate.</param>
- public void SetOnDisconnect(OnEventDelegate ADelegate)
+ /// <param name="aDelegate">The Event Delegate.</param>
+ public void SetOnDisconnect(OnEventDelegate aDelegate)
{
- _OnDisconnect = ADelegate;
+ OnDisconnectDelegate = aDelegate;
}
/// <summary>
/// Sets the on send event.
/// </summary>
- /// <param name="ADelegate">The Event Delegate.</param>
- public void SetOnSend(OnEventDelegate ADelegate)
+ /// <param name="aDelegate">The Event Delegate.</param>
+ public void SetOnSend(OnEventDelegate aDelegate)
{
- _OnSend = ADelegate;
+ OnSendDelegate = aDelegate;
}
/// <summary>
/// Sets the on receive event.
/// </summary>
- /// <param name="ADelegate">The Event Delegate.</param>
- public void SetOnReceive(OnEventDelegate ADelegate)
+ /// <param name="aDelegate">The Event Delegate.</param>
+ public void SetOnReceive(OnEventDelegate aDelegate)
{
- _OnReceive = ADelegate;
+ OnReceiveDelegate = aDelegate;
}
/// <summary>
@@ -194,9 +218,9 @@ public void SetOnReceive(OnEventDelegate ADelegate)
/// </summary>
/// <param name="data">The data.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
- public void Send(string Data, bool Close = false)
+ public void Send(string data, bool close = false)
{
- Send(Encoding.GetBytes(Data), Close);
+ Send(Encoding.GetBytes(data), close);
}
/// <summary>
@@ -204,18 +228,18 @@ public void Send(string Data, bool Close = false)
/// </summary>
/// <param name="data">The data.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
- public void Send(byte[] Data, bool Close = false)
+ public void Send(byte[] data, bool close = false)
{
- Context.Handler.Send(Data, Context, Close);
+ _context.Handler.Send(data, _context, close);
}
/// <summary>
/// Sends raw data.
/// </summary>
/// <param name="data">The data.</param>
- public void SendRaw(byte[] Data)
+ public void SendRaw(byte[] data)
{
- _defaultHandler.Instance.Send(Data, Context);
+ DefaultHandler.Instance.Send(data, _context);
}
}
@@ -227,50 +251,70 @@ public class Context : IDisposable
/// <summary>
/// The exported version of this context.
/// </summary>
- public readonly UserContext UserContext = null;
+ public readonly UserContext UserContext;
+
/// <summary>
- /// The raw client connection.
+ /// The buffer used for accepting raw data from the socket.
/// </summary>
- public TcpClient Connection = null;
+ public byte[] Buffer;
+
/// <summary>
/// Whether or not the TCPClient is still connected.
/// </summary>
public bool Connected = true;
+
/// <summary>
- /// The buffer used for accepting raw data from the socket.
+ /// The raw client connection.
/// </summary>
- public byte[] Buffer = null;
+ public TcpClient Connection;
+
/// <summary>
- /// How many pings in a row we've had from this client, indicates inactivity.
+ /// The current connection handler.
/// </summary>
- public int Pings = 0;
+ public Handler Handler = DefaultHandler.Instance;
+
/// <summary>
- /// How many bytes we received this tick.
+ /// The Header
/// </summary>
- public int ReceivedByteCount = 0;
+ public Header Header;
+
/// <summary>
/// Whether or not this client has passed all the setup routines for the current handler(authentication, etc)
/// </summary>
- public Boolean IsSetup = false;
+ public Boolean IsSetup;
+
/// <summary>
- /// The current connection handler.
+ /// How many pings in a row we've had from this client, indicates inactivity.
/// </summary>
- public Handler Handler = _defaultHandler.Instance;
+ public int Pings;
+
/// <summary>
/// Semaphores that limit sends and receives to 1 and a time.
/// </summary>
public SemaphoreSlim ReceiveReady = new SemaphoreSlim(1);
+
+ /// <summary>
+ /// How many bytes we received this tick.
+ /// </summary>
+ public int ReceivedByteCount;
+
public SemaphoreSlim SendReady = new SemaphoreSlim(1);
+
/// <summary>
/// A link to the server listener instance this client is currently hosted on.
/// </summary>
- public WSServer Server = null;
+ public WebSocketServer Server;
+
+ private int _bufferSize = 512;
+
/// <summary>
- /// The Header
+ /// Initializes a new instance of the <see cref="Context"/> class.
/// </summary>
- public Header Header = null;
-
- private int _BufferSize = 512;
+ public Context()
+ {
+ Buffer = new byte[_bufferSize];
+ UserContext = new UserContext(this);
+ }
/// <summary>
/// Gets or sets the size of the buffer.
@@ -280,25 +324,15 @@ public class Context : IDisposable
/// </value>
public int BufferSize
{
- get
- {
- return _BufferSize;
- }
+ get { return _bufferSize; }
set
{
- _BufferSize = value;
- Buffer = new byte[_BufferSize];
+ _bufferSize = value;
+ Buffer = new byte[_bufferSize];
}
}
- /// <summary>
- /// Initializes a new instance of the <see cref="Context"/> class.
- /// </summary>
- public Context()
- {
- Buffer = new byte[_BufferSize];
- this.UserContext = new UserContext(this);
- }
+ #region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
@@ -310,7 +344,10 @@ public void Dispose()
Connection.Client.Close();
Connection = null;
}
- catch (Exception e) { Server.Log.Debug("Client Already Disconnected", e); }
+ catch (Exception e)
+ {
+ Server.Log.Debug("Client Already Disconnected", e);
+ }
finally
{
if (Connected)
@@ -321,16 +358,22 @@ public void Dispose()
}
}
+ #endregion
+
/// <summary>
/// Resets this instance.
/// Clears the dataframe if necessary. Resets Received byte count.
/// </summary>
public void Reset()
{
- if(UserContext.DataFrame != null)
- if (UserContext.DataFrame.State == Alchemy.Server.Handlers.WebSocket.DataFrame.DataState.Complete)
- UserContext.DataFrame.Clear();
+ if (UserContext.DataFrame != null)
+ {
+ if (UserContext.DataFrame.State == DataFrame.DataState.Complete)
+ {
+ UserContext.DataFrame.Clear();
+ }
+ }
ReceivedByteCount = 0;
}
}
-}
+}
View
43 Handlers/DefaultHandler.cs
@@ -21,8 +21,9 @@
*/
using System;
-using Alchemy.Server.Classes;
using System.Net.Sockets;
+using Alchemy.Server.Classes;
+using Alchemy.Server.Handlers.WebSocket.hybi00;
namespace Alchemy.Server.Handlers
{
@@ -30,22 +31,22 @@ namespace Alchemy.Server.Handlers
/// When the protocol has not yet been determined the system defaults to this request handler.
/// Singleton, just like the other handlers.
/// </summary>
- class _defaultHandler : Handler
+ internal class DefaultHandler : Handler
{
- private static _defaultHandler _instance;
+ private static DefaultHandler _instance;
- private _defaultHandler() { }
+ private DefaultHandler() {}
- public static _defaultHandler Instance
+ public static DefaultHandler Instance
{
- get
+ get
{
- _createLock.Wait();
+ CreateLock.Wait();
if (_instance == null)
{
- _instance = new _defaultHandler();
+ _instance = new DefaultHandler();
}
- _createLock.Release();
+ CreateLock.Release();
return _instance;
}
}
@@ -83,25 +84,27 @@ public void ProcessHeader(Context context)
//if it is, we access the Access Policy Server instance to send the appropriate response.
context.Server.AccessPolicyServer.SendResponse(context.Connection);
}
- catch { }
+ // ReSharper disable EmptyGeneralCatchClause
+ catch {}
+ // ReSharper restore EmptyGeneralCatchClause
context.Dispose();
}
- else//If it isn't, process http/websocket header as normal.
+ else //If it isn't, process http/websocket header as normal.
{
context.Header = new Header(data);
switch (context.Header.Protocol)
{
case Protocol.WebSocketHybi00:
- context.Handler = Alchemy.Server.Handlers.WebSocket.hybi00.WebSocketHandler.Instance;
- context.UserContext.DataFrame = new Alchemy.Server.Handlers.WebSocket.hybi00.DataFrame();
+ context.Handler = WebSocketHandler.Instance;
+ context.UserContext.DataFrame = new DataFrame();
break;
case Protocol.WebSocketHybi10:
- context.Handler = Alchemy.Server.Handlers.WebSocket.hybi10.WebSocketHandler.Instance;
- context.UserContext.DataFrame = new Alchemy.Server.Handlers.WebSocket.hybi10.DataFrame();
+ context.Handler = WebSocket.hybi10.WebSocketHandler.Instance;
+ context.UserContext.DataFrame = new WebSocket.hybi10.DataFrame();
break;
case Protocol.FlashSocket:
- context.Handler = Alchemy.Server.Handlers.WebSocket.hybi00.WebSocketHandler.Instance;
- context.UserContext.DataFrame = new Alchemy.Server.Handlers.WebSocket.hybi00.DataFrame();
+ context.Handler = WebSocketHandler.Instance;
+ context.UserContext.DataFrame = new DataFrame();
break;
default:
context.Header.Protocol = Protocol.None;
@@ -128,7 +131,9 @@ public override void Send(byte[] data, Context context, bool close = false)
{
AsyncCallback callback = EndSend;
if (close)
+ {
callback = EndSendAndClose;
+ }
context.SendReady.Wait();
try
{
@@ -146,7 +151,7 @@ public override void Send(byte[] data, Context context, bool close = false)
/// <param name="result">The Async result.</param>
public override void EndSend(IAsyncResult result)
{
- Context context = (Context)result.AsyncState;
+ var context = (Context) result.AsyncState;
try
{
context.Connection.Client.EndSend(result);
@@ -165,7 +170,7 @@ public override void EndSend(IAsyncResult result)
/// <param name="result">The Async result.</param>
public override void EndSendAndClose(IAsyncResult result)
{
- Context context = (Context)result.AsyncState;
+ var context = (Context) result.AsyncState;
try
{
context.Connection.Client.EndSend(result);
View
16 Handlers/Handler.cs
@@ -21,8 +21,8 @@
*/
using System;
-using Alchemy.Server.Classes;
using System.Threading;
+using Alchemy.Server.Classes;
using Alchemy.Server.Handlers.WebSocket;
namespace Alchemy.Server.Handlers
@@ -33,11 +33,11 @@ namespace Alchemy.Server.Handlers
/// </summary>
public abstract class Handler
{
- public WebSocketAuthentication Authentication = null;
- protected static SemaphoreSlim _createLock = new SemaphoreSlim(1);
- public abstract void HandleRequest(Context Request);
- public abstract void Send(byte[] Data, Context AContext, bool Close = false);
- public abstract void EndSend(IAsyncResult AResult);
- public abstract void EndSendAndClose(IAsyncResult AResult);
+ protected static SemaphoreSlim CreateLock = new SemaphoreSlim(1);
+ public IWebSocketAuthentication Authentication;
+ public abstract void HandleRequest(Context request);
+ public abstract void Send(byte[] data, Context context, bool close = false);
+ public abstract void EndSend(IAsyncResult result);
+ public abstract void EndSendAndClose(IAsyncResult result);
}
-}
+}
View
45 Handlers/WebSocket/DataFrame.cs
@@ -20,10 +20,9 @@
along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
*/
-using System;
+using System;
using System.Text;
-
namespace Alchemy.Server.Handlers.WebSocket
{
/// <summary>
@@ -33,6 +32,8 @@ namespace Alchemy.Server.Handlers.WebSocket
/// </summary>
public abstract class DataFrame
{
+ #region DataState enum
+
/// <summary>
/// The Dataframe's state
/// </summary>
@@ -46,21 +47,21 @@ public enum DataState
Pong = 4
}
+ #endregion
+
+ protected DataState InternalState = DataState.Empty;
+
/// <summary>
/// The internal byte buffer used to store received data until the entire frame comes through.
/// </summary>
- protected byte[] _rawFrame = null;
- protected DataState _state = DataState.Empty;
+ protected byte[] RawFrame;
/// <summary>
/// Gets the current length of the received frame.
/// </summary>
public int Length
{
- get
- {
- return _rawFrame.Length;
- }
+ get { return RawFrame.Length; }
}
/// <summary>
@@ -68,10 +69,7 @@ public int Length
/// </summary>
public DataState State
{
- get
- {
- return _state;
- }
+ get { return InternalState; }
}
/// <summary>
@@ -108,10 +106,11 @@ public byte[] Wrap(string data)
/// </returns>
public override string ToString()
{
- if (_rawFrame != null)
- return UTF8Encoding.UTF8.GetString(_rawFrame);
- else
- return String.Empty;
+ if (RawFrame != null)
+ {
+ return Encoding.UTF8.GetString(RawFrame);
+ }
+ return String.Empty;
}
/// <summary>
@@ -122,10 +121,11 @@ public override string ToString()
/// </returns>
public byte[] ToBytes()
{
- if (_rawFrame != null)
- return _rawFrame;
- else
- return new byte[0];
+ if (RawFrame != null)
+ {
+ return RawFrame;
+ }
+ return new byte[0];
}
/// <summary>
@@ -133,9 +133,8 @@ public byte[] ToBytes()
/// </summary>
public void Clear()
{
- _rawFrame = null;
- _state = DataState.Empty;
+ RawFrame = null;
+ InternalState = DataState.Empty;
}
-
}
}
View
74 Handlers/WebSocket/WebSocketAuthentication.cs → Handlers/WebSocket/IWebSocketAuthentication.cs
@@ -1,37 +1,37 @@
-/*
-Copyright 2011 Olivine Labs, LLC.
-http://www.olivinelabs.com
-*/
-
-/*
-This file is part of Alchemy Websockets.
-
-Alchemy Websockets is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Alchemy Websockets is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-using Alchemy.Server.Classes;
-
-namespace Alchemy.Server.Handlers.WebSocket
-{
- /// <summary>
- /// Handles the handshaking between the client and the host, when a new connection is created
- /// </summary>
- public interface WebSocketAuthentication
- {
- void SetOrigin(string origin);
- void SetLocation(string location);
-
- bool CheckHandshake(Context AContext);
- }
-}
+/*
+Copyright 2011 Olivine Labs, LLC.
+http://www.olivinelabs.com
+*/
+
+/*
+This file is part of Alchemy Websockets.
+
+Alchemy Websockets is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Alchemy Websockets is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using Alchemy.Server.Classes;
+
+namespace Alchemy.Server.Handlers.WebSocket
+{
+ /// <summary>
+ /// Handles the handshaking between the client and the host, when a new connection is created
+ /// </summary>
+ public interface IWebSocketAuthentication
+ {
+ void SetOrigin(string origin);
+ void SetLocation(string location);
+
+ bool CheckHandshake(Context context);
+ }
+}
View
42 Handlers/WebSocket/hybi00/DataFrame.cs
@@ -20,8 +20,7 @@
along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
*/
-using System;
-
+using System;
namespace Alchemy.Server.Handlers.WebSocket.hybi00
{
@@ -30,7 +29,7 @@ namespace Alchemy.Server.Handlers.WebSocket.hybi00
/// Automatically manages adding received data to an existing frame and checking whether or not we've received the entire frame yet.
/// See http://www.whatwg.org/specs/web-socket-protocol/ for more details on the WebSocket Protocol.
/// </summary>
- public class DataFrame : Alchemy.Server.Handlers.WebSocket.DataFrame
+ public class DataFrame : WebSocket.DataFrame
{
public const byte StartByte = 0;
public const byte EndByte = 255;
@@ -44,7 +43,7 @@ public class DataFrame : Alchemy.Server.Handlers.WebSocket.DataFrame
public override byte[] Wrap(byte[] data)
{
// wrap the array with the wrapper bytes
- byte[] wrappedBytes = new byte[data.Length + 2];
+ var wrappedBytes = new byte[data.Length + 2];
wrappedBytes[0] = StartByte;
wrappedBytes[wrappedBytes.Length - 1] = EndByte;
Array.Copy(data, 0, wrappedBytes, 1, data.Length);
@@ -62,16 +61,17 @@ public override void Append(byte[] data)
int end = Array.IndexOf(data, EndByte);
if (end != -1)
{
- _state = DataState.Complete;
+ InternalState = DataState.Complete;
}
else //If no match found, default.
{
end = data.Length;
- _state = DataState.Receiving;
+ InternalState = DataState.Receiving;
}
int start = Array.IndexOf(data, StartByte);
- if ((start != -1) && (start < end)) // Make sure the start is before the end and that we actually found a match.
+ if ((start != -1) && (start < end))
+ // Make sure the start is before the end and that we actually found a match.
{
start++; // Do not include the Start Byte
}
@@ -87,19 +87,29 @@ public override void Append(byte[] data)
/// <summary>
/// Appends the data to frame. Manages recreating the byte array and such.
/// </summary>
- /// <param name="SomeBytes">Some bytes.</param>
- /// <param name="Start">The start index.</param>
- /// <param name="End">The end index.</param>
+ /// <param name="someBytes">Some bytes.</param>
+ /// <param name="start">The start index.</param>
+ /// <param name="end">The end index.</param>
private void AppendDataToFrame(byte[] someBytes, int start, int end)
{
int currentFrameLength = 0;
- if (_rawFrame != null)
- currentFrameLength = _rawFrame.Length;
- byte[] newFrame = new byte[currentFrameLength + (end - start)];
- if (currentFrameLength > 0)
- Array.Copy(_rawFrame, 0, newFrame, 0, currentFrameLength);
+ byte[] newFrame;
+ if (RawFrame != null)
+ {
+ currentFrameLength = RawFrame.Length;
+
+ newFrame = new byte[currentFrameLength + (end - start)];
+ if (currentFrameLength > 0)
+ {
+ Array.Copy(RawFrame, 0, newFrame, 0, currentFrameLength);
+ }
+ }
+ else
+ {
+ newFrame = new byte[end - start];
+ }
Array.Copy(someBytes, start, newFrame, currentFrameLength, end - start);
- _rawFrame = newFrame;
+ RawFrame = newFrame;
}
}
}
View
64 Handlers/WebSocket/hybi00/Handshakes.cs
@@ -22,6 +22,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Web;
using Alchemy.Server.Classes;
@@ -36,7 +37,7 @@ public class ClientHandshake
/// <summary>
/// The preformatted handshake as a string.
/// </summary>
- private const String _handshake =
+ private const String Handshake =
"GET {0} HTTP/1.1\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
@@ -46,20 +47,16 @@ public class ClientHandshake
"Sec-Websocket-Key2: {4}\r\n" +
"{5}";
- public string Origin = String.Empty;
public string Host = String.Empty;
- public string ResourcePath = String.Empty;
public string Key1 = String.Empty;
public string Key2 = String.Empty;
- public ArraySegment<byte> ChallengeBytes { get; set; }
- public HttpCookieCollection Cookies { get; set; }
- public string SubProtocol { get; set; }
- public Dictionary<string, string> AdditionalFields { get; set; }
+ public string Origin = String.Empty;
+ public string ResourcePath = String.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="ClientHandshake"/> class.
/// </summary>
- /// <param name="ChallengeBytes">The challenge bytes.</param>
+ /// <param name="challengeBytes">The challenge bytes.</param>
/// <param name="header">The header.</param>
public ClientHandshake(ArraySegment<byte> challengeBytes, Header header)
{
@@ -73,6 +70,11 @@ public ClientHandshake(ArraySegment<byte> challengeBytes, Header header)
Cookies = header.Cookies;
}
+ public ArraySegment<byte> ChallengeBytes { get; set; }
+ public HttpCookieCollection Cookies { get; set; }
+ public string SubProtocol { get; set; }
+ public Dictionary<string, string> AdditionalFields { get; set; }
+
/// <summary>
/// Determines whether this instance is valid.
/// </summary>
@@ -82,13 +84,12 @@ public ClientHandshake(ArraySegment<byte> challengeBytes, Header header)
public bool IsValid()
{
return (
- (ChallengeBytes != null) &&
- (Host != null) &&
- (Key1 != null) &&
- (Key2 != null) &&
- (Origin != null) &&
- (ResourcePath != null)
- );
+ (Host != null) &&
+ (Key1 != null) &&
+ (Key2 != null) &&
+ (Origin != null) &&
+ (ResourcePath != null)
+ );
}
/// <summary>
@@ -103,21 +104,22 @@ public override string ToString()
if (Cookies != null)
{
- additionalFields += "Cookie: " + Cookies.ToString() + "\r\n";
+ additionalFields += "Cookie: " + Cookies + "\r\n";
}
if (SubProtocol != null)
+ {
additionalFields += "Sec-Websocket-Protocol: " + SubProtocol + "\r\n";
+ }
- if (additionalFields != null)
+ if (additionalFields != String.Empty)
{
- foreach (KeyValuePair<string, string> field in this.AdditionalFields)
- {
- additionalFields += field.Key + ": " + field.Value + "\r\n";
- }
+ additionalFields = AdditionalFields.Aggregate(additionalFields,
+ (current, field) =>
+ current + (field.Key + ": " + field.Value + "\r\n"));
}
additionalFields += "\r\n";
- return String.Format(_handshake, ResourcePath, Origin, Host, Key1, Key2, AdditionalFields);
+ return String.Format(Handshake, ResourcePath, Origin, Host, Key1, Key2, additionalFields);
}
}
@@ -130,17 +132,17 @@ public class ServerHandshake
/// <summary>
/// The preformatted handshake string.
/// </summary>
- private const string _handshake =
+ private const string Handshake =
"HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
- "Upgrade: WebSocket\r\n" +
- "Connection: Upgrade\r\n" +
- "Sec-WebSocket-Origin: {0}\r\n" +
- "Sec-WebSocket-Location: {1}\r\n" +
- "{2}" +
- " ";//Empty space for challenge answer
+ "Upgrade: WebSocket\r\n" +
+ "Connection: Upgrade\r\n" +
+ "Sec-WebSocket-Origin: {0}\r\n" +
+ "Sec-WebSocket-Location: {1}\r\n" +
+ "{2}" +
+ " "; //Empty space for challenge answer
- public string Origin = String.Empty;
public string Location = String.Empty;
+ public string Origin = String.Empty;
public byte[] AnswerBytes { get; set; }
public string SubProtocol { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; }
@@ -160,7 +162,7 @@ public override string ToString()
}
additionalFields += "\r\n";
- return String.Format(_handshake, Origin, Location, AdditionalFields);
+ return String.Format(Handshake, Origin, Location, additionalFields);
}
}
}
View
57 Handlers/WebSocket/hybi00/WebSocketAuthentication.cs
@@ -22,8 +22,8 @@
using System;
using System.Linq;
-using System.Threading;
using System.Security.Cryptography;
+using System.Threading;
using Alchemy.Server.Classes;
namespace Alchemy.Server.Handlers.WebSocket.hybi00
@@ -31,33 +31,32 @@ namespace Alchemy.Server.Handlers.WebSocket.hybi00
/// <summary>
/// Handles the handshaking between the client and the host, when a new connection is created
/// </summary>
- public class WebSocketAuthentication : Alchemy.Server.Handlers.WebSocket.WebSocketAuthentication
+ public class WebSocketAuthentication : IWebSocketAuthentication
{
public static string Origin = string.Empty;
public static string Location = string.Empty;
- private static SemaphoreSlim _createLock = new SemaphoreSlim(1);
- private static WebSocketAuthentication _instance = null;
-
- private WebSocketAuthentication()
- {
+ private static readonly SemaphoreSlim CreateLock = new SemaphoreSlim(1);
+ private static WebSocketAuthentication _instance;
- }
+ private WebSocketAuthentication() {}
public static WebSocketAuthentication Instance
{
get
{
- _createLock.Wait();
+ CreateLock.Wait();
if (_instance == null)
{
_instance = new WebSocketAuthentication();
}
- _createLock.Release();
+ CreateLock.Release();
return _instance;
}
}
+ #region IWebSocketAuthentication Members
+
public void SetOrigin(string origin)
{
Origin = origin;
@@ -72,17 +71,27 @@ public bool CheckHandshake(Context context)
{
if (context.ReceivedByteCount > 8)
{
- ClientHandshake handshake = new ClientHandshake(new ArraySegment<byte>(context.Buffer, context.ReceivedByteCount - 8, 8), context.Header);
+ var handshake =
+ new ClientHandshake(new ArraySegment<byte>(context.Buffer, context.ReceivedByteCount - 8, 8),
+ context.Header);
// See if our header had the required information
if (handshake.IsValid())
{
// Optionally check Origin and Location if they're set.
if (Origin != string.Empty)
+ {
if (handshake.Origin != "http://" + Origin)
+ {
return false;
+ }
+ }
if (Location != string.Empty)
+ {
if (handshake.Host != Location + ":" + context.Server.Port.ToString())
+ {
return false;
+ }
+ }
// Generate response handshake for the client
ServerHandshake serverShake = GenerateResponseHandshake(handshake);
// Send the response handshake
@@ -93,13 +102,17 @@ public bool CheckHandshake(Context context)
return false;
}
+ #endregion
+
private static ServerHandshake GenerateResponseHandshake(ClientHandshake handshake)
{
- ServerHandshake responseHandshake = new ServerHandshake();
- responseHandshake.Location = "ws://" + handshake.Host + handshake.ResourcePath;
- responseHandshake.Origin = handshake.Origin;
- responseHandshake.SubProtocol = handshake.SubProtocol;
- responseHandshake.AnswerBytes = GenerateAnswerBytes(handshake.Key1, handshake.Key2, handshake.ChallengeBytes);
+ var responseHandshake = new ServerHandshake
+ {
+ Location = "ws://" + handshake.Host + handshake.ResourcePath,
+ Origin = handshake.Origin,
+ SubProtocol = handshake.SubProtocol,
+ AnswerBytes = GenerateAnswerBytes(handshake.Key1, handshake.Key2, handshake.ChallengeBytes)
+ };
return responseHandshake;
}
@@ -119,24 +132,24 @@ private static byte[] TranslateKey(string key)
int keySpaceCount = key.Count(x => x == ' ');
// Get a number which is a concatenation of all digits in the keys.
- string keyNumberString = new String(key.Where(x => Char.IsDigit(x)).ToArray());
+ var keyNumberString = new String(key.Where(Char.IsDigit).ToArray());
// Divide the number with the number of spaces
- Int32 keyResult = (Int32)(Int64.Parse(keyNumberString) / keySpaceCount);
+ var keyResult = (Int32) (Int64.Parse(keyNumberString)/keySpaceCount);
// convert the results to 32 bit big endian byte arrays
- byte[] KeyResultBytes = BitConverter.GetBytes(keyResult);
+ byte[] keyResultBytes = BitConverter.GetBytes(keyResult);
if (BitConverter.IsLittleEndian)
{
- Array.Reverse(KeyResultBytes);
+ Array.Reverse(keyResultBytes);
}
- return KeyResultBytes;
+ return keyResultBytes;
}
private static byte[] GenerateAnswerBytes(string key1, string key2, ArraySegment<byte> challenge)
{
// Translate the two keys, concatenate them and the 8 challenge bytes from the client
- byte[] rawAnswer = new byte[16];
+ var rawAnswer = new byte[16];
Array.Copy(TranslateKey(key1), 0, rawAnswer, 0, 4);
Array.Copy(TranslateKey(key2), 0, rawAnswer, 4, 4);
Array.Copy(challenge.Array, challenge.Offset, rawAnswer, 8, 8);
View
56 Handlers/WebSocket/hybi00/WebSocketHandler.cs
@@ -29,25 +29,25 @@ namespace Alchemy.Server.Handlers.WebSocket.hybi00
/// <summary>
/// A threadsafe singleton that contains functions which are used to handle incoming connections for the WebSocket Protocol
/// </summary>
- sealed class WebSocketHandler : Handler
+ internal sealed class WebSocketHandler : Handler
{
private static WebSocketHandler _instance;
private WebSocketHandler()
{
- Authentication = Alchemy.Server.Handlers.WebSocket.hybi00.WebSocketAuthentication.Instance;
+ Authentication = WebSocketAuthentication.Instance;
}
public static WebSocketHandler Instance
{
- get
+ get
{
- _createLock.Wait();
+ CreateLock.Wait();
if (_instance == null)
{
_instance = new WebSocketHandler();
}
- _createLock.Release();
+ CreateLock.Release();
return _instance;
}
}
@@ -61,7 +61,7 @@ public override void HandleRequest(Context context)
if (context.IsSetup)
{
context.UserContext.DataFrame.Append(context.Buffer);
- if (context.UserContext.DataFrame.State == DataFrame.DataState.Complete)
+ if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Complete)
{
switch (context.UserContext.DataFrame.Length)
{
@@ -78,7 +78,8 @@ public override void HandleRequest(Context context)
context.Pings = 0;
context.UserContext.OnReceive();
}
- if ((context.Pings >= context.Server.MaxPingsInSequence) && (context.Server.MaxPingsInSequence != 0))
+ if ((context.Pings >= context.Server.MaxPingsInSequence) &&
+ (context.Server.MaxPingsInSequence != 0))
{
context.Dispose();
}
@@ -89,7 +90,7 @@ public override void HandleRequest(Context context)
break;
}
}
- else if (context.UserContext.DataFrame.State == DataFrame.DataState.Closed)
+ else if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Closed)
{
context.UserContext.Send(new byte[0], true);
}
@@ -129,19 +130,22 @@ private static void Authenticate(Context context)
/// <param name="close">if set to <c>true</c> [close].</param>
public override void Send(byte[] data, Context context, bool close = false)
{
- byte[] wrappedData = context.UserContext.DataFrame.Wrap(data);
- AsyncCallback callback = EndSend;
- if (close)
- callback = EndSendAndClose;
- context.SendReady.Wait();
- try
- {
- context.Connection.Client.BeginSend(wrappedData, 0, wrappedData.Length, SocketFlags.None, callback, context);
- }
- catch
- {
- context.SendReady.Release();
- }
+ byte[] wrappedData = context.UserContext.DataFrame.Wrap(data);
+ AsyncCallback callback = EndSend;
+ if (close)
+ {
+ callback = EndSendAndClose;
+ }
+ context.SendReady.Wait();
+ try
+ {
+ context.Connection.Client.BeginSend(wrappedData, 0, wrappedData.Length, SocketFlags.None, callback,
+ context);
+ }
+ catch
+ {
+ context.SendReady.Release();
+ }
}
/// <summary>
@@ -150,7 +154,7 @@ public override void Send(byte[] data, Context context, bool close = false)
/// <param name="result">The Async result.</param>
public override void EndSend(IAsyncResult result)
{
- Context context = (Context)result.AsyncState;
+ var context = (Context) result.AsyncState;
try
{
context.Connection.Client.EndSend(result);
@@ -158,7 +162,7 @@ public override void EndSend(IAsyncResult result)
}
catch
{
- context.SendReady.Release();
+ context.SendReady.Release();
}
context.UserContext.OnSend();
}
@@ -169,7 +173,7 @@ public override void EndSend(IAsyncResult result)
/// <param name="result">The Async result.</param>
public override void EndSendAndClose(IAsyncResult result)
{
- Context context = (Context)result.AsyncState;
+ var context = (Context) result.AsyncState;
try
{
context.Connection.Client.EndSend(result);
@@ -177,7 +181,7 @@ public override void EndSendAndClose(IAsyncResult result)
}
catch
{
- context.SendReady.Release();
+ context.SendReady.Release();
}
context.UserContext.OnSend();
context.Dispose();
@@ -192,4 +196,4 @@ private static void SendPingResponse(Context context)
context.UserContext.Send(context.Server.PongCommand);
}
}
-}
+}
View
104 Handlers/WebSocket/hybi10/DataFrame.cs
@@ -20,9 +20,7 @@
along with Alchemy Websockets. If not, see <http://www.gnu.org/licenses/>.
*/
-using System;
-using System.Text;
-
+using System;
namespace Alchemy.Server.Handlers.WebSocket.hybi10
{
@@ -31,20 +29,23 @@ namespace Alchemy.Server.Handlers.WebSocket.hybi10
/// Automatically manages adding received data to an existing frame and checking whether or not we've received the entire frame yet.
/// See http://www.whatwg.org/specs/web-socket-protocol/ for more details on the WebSocket Protocol.
/// </summary>
- public class DataFrame : Alchemy.Server.Handlers.WebSocket.DataFrame
+ public class DataFrame : WebSocket.DataFrame
{
+ #region OpCode enum
+
public enum OpCode
{
- Continue = 0x0,
- Text = 0x1,
- Binary = 0x2,
- Close = 0x8,
- Ping = 0x9,
- Pong = 0xA
+ Continue = 0x0,
+ Text = 0x1,
+ Binary = 0x2,
+ Close = 0x8,
+ Ping = 0x9,
+ Pong = 0xA
}
- private const byte _continueBit = 0x0;
- private const byte _endBit = 0x80;
+ #endregion
+
+ private const byte EndBit = 0x80;
/// <summary>
/// Wraps the specified data in WebSocket Start/End Bytes.
@@ -54,24 +55,24 @@ public enum OpCode
/// <returns>The data array wrapped in WebSocket DataFrame Start/End qualifiers.</returns>
public override byte[] Wrap(byte[] data)
{
- byte[] wrappedBytes = null;
+ byte[] wrappedBytes;
if (data.Length > 0)
{
// wrap the array with the wrapper bytes
int startIndex = 2;
- byte[] headerBytes = new byte[14];
+ var headerBytes = new byte[14];
headerBytes[0] = 0x81;
if (data.Length <= 125)
{
- headerBytes[1] = (byte)data.Length;
+ headerBytes[1] = (byte) data.Length;
}
else
{
if (data.Length <= ushort.MaxValue)
{
headerBytes[1] = 126;
- byte[] extendedLength = BitConverter.GetBytes((UInt16)data.Length);
+ byte[] extendedLength = BitConverter.GetBytes((UInt16) data.Length);
headerBytes[2] = extendedLength[1];
headerBytes[3] = extendedLength[0];
startIndex = 4;
@@ -79,7 +80,7 @@ public override byte[] Wrap(byte[] data)
else
{
headerBytes[1] = 127;
- byte[] extendedLength = BitConverter.GetBytes((UInt64)data.Length);
+ byte[] extendedLength = BitConverter.GetBytes((UInt64) data.Length);
headerBytes[2] = extendedLength[7];
headerBytes[3] = extendedLength[6];
headerBytes[4] = extendedLength[5];
@@ -91,14 +92,14 @@ public override byte[] Wrap(byte[] data)
startIndex = 10;
}
}
- headerBytes[1] = (byte)(headerBytes[1] | 0x80);
+ headerBytes[1] = (byte) (headerBytes[1] | 0x80);
- Random random = new Random();
+ var random = new Random();
int key = random.Next(Int32.MaxValue);
Array.Copy(BitConverter.GetBytes(key), 0, headerBytes, startIndex, 4);
startIndex += 4;
- byte[] maskedData=Mask(data, key);
+ byte[] maskedData = Mask(data, key);
wrappedBytes = new byte[data.Length + startIndex];
Array.Copy(headerBytes, 0, wrappedBytes, 0, startIndex);
@@ -120,16 +121,18 @@ public override void Append(byte[] data)
{
if (data.Length > 0)
{
- byte nibble2 = (byte)(data[0] & 0x0F);
- byte nibble1 = (byte)(data[0] & 0xF0);
+ var nibble2 = (byte) (data[0] & 0x0F);
+ var nibble1 = (byte) (data[0] & 0xF0);
- if ((nibble1 & _endBit) == _endBit)
- _state = DataState.Complete;
+ if ((nibble1 & EndBit) == EndBit)
+ {
+ InternalState = DataState.Complete;
+ }
//Combine bytes to form one large number
int startIndex = 2;
- Int64 dataLength = (byte)(data[1] & 0x7F);
+ Int64 dataLength = (byte) (data[1] & 0x7F);
if (dataLength == 126)
{
BitConverter.ToInt16(data, startIndex);
@@ -149,12 +152,14 @@ public override void Append(byte[] data)
startIndex = startIndex + 4;
}
- byte[] payload = new byte[dataLength];
- Array.Copy(data, (int)startIndex, payload, 0, (int)dataLength);
- if(masked)
+ var payload = new byte[dataLength];
+ Array.Copy(data, startIndex, payload, 0, (int) dataLength);
+ if (masked)
+ {
payload = Mask(payload, maskingKey);
+ }
- OpCode currentFrameOpcode = (OpCode)nibble2;
+ var currentFrameOpcode = (OpCode) nibble2;
switch (currentFrameOpcode)
{
case OpCode.Continue:
@@ -163,13 +168,13 @@ public override void Append(byte[] data)
AppendDataToFrame(payload);
break;
case OpCode.Close:
- _state = DataState.Closed;
+ InternalState = DataState.Closed;
break;
case OpCode.Ping:
- _state = DataState.Ping;
+ InternalState = DataState.Ping;
break;
case OpCode.Pong:
- _state = DataState.Pong;
+ InternalState = DataState.Pong;
break;
}
}
@@ -178,29 +183,38 @@ public override void Append(byte[] data)
/// <summary>
/// Appends the data to frame. Manages recreating the byte array and such.
/// </summary>
- /// <param name="SomeBytes">Some bytes.</param>
- /// <param name="Start">The start index.</param>
- /// <param name="End">The end index.</param>
+ /// <param name="someBytes">Some bytes.</param>
private void AppendDataToFrame(byte[] someBytes)
{
int currentFrameLength = 0;
- if (_rawFrame != null)
- currentFrameLength = _rawFrame.Length;
- byte[] newFrame = new byte[currentFrameLength + someBytes.Length];
- if(currentFrameLength > 0)
- Array.Copy(_rawFrame, 0, newFrame, 0, currentFrameLength);
+ byte[] newFrame;
+ if (RawFrame != null)
+ {
+ currentFrameLength = RawFrame.Length;
+
+ newFrame = new byte[currentFrameLength + someBytes.Length];
+ if (currentFrameLength > 0)
+ {
+ Array.Copy(RawFrame, 0, newFrame, 0, currentFrameLength);
+ }
+ Array.Copy(someBytes, 0, newFrame, currentFrameLength, someBytes.Length);
+ }
+ else
+ {
+ newFrame = someBytes;
+ }
Array.Copy(someBytes, 0, newFrame, currentFrameLength, someBytes.Length);
- _rawFrame = newFrame;
+ RawFrame = newFrame;
}
private static byte[] Mask(byte[] someBytes, Int32 key)
{
- byte[] newBytes = new byte[someBytes.Length];
+ var newBytes = new byte[someBytes.Length];
byte[] byteKeys = BitConverter.GetBytes(key);
- for(int index = 0; index < someBytes.Length; index++)
+ for (int index = 0; index < someBytes.Length; index++)
{
- int KeyIndex = index % 4;
- newBytes[index] = (byte)(someBytes[index]^byteKeys[KeyIndex]);
+ int keyIndex = index%4;
+ newBytes[index] = (byte) (someBytes[index] ^ byteKeys[keyIndex]);
}
return newBytes;
}
View
60 Handlers/WebSocket/hybi10/Handshakes.cs
@@ -22,6 +22,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Web;
using Alchemy.Server.Classes;
@@ -36,7 +37,7 @@ public class ClientHandshake
/// <summary>
/// The preformatted handshake as a string.
/// </summary>
- private const String _handshake =
+ private const String Handshake =
"GET {0} HTTP/1.1\r\n" +
"Host: {2}\r\n" +
"Upgrade: websocket\r\n" +
@@ -44,36 +45,38 @@ public class ClientHandshake
"Sec-WebSocket-Key: {4}\r\n" +
"Sec-WebSocket-Origin: {1}\r\n" +
"Sec-WebSocket-Protocol: {3}\r\n" +
- "Sec-WebSocket-Version: 8\r\n" +
+ "Sec-WebSocket-Version: 8\r\n" +
"{5}";
- public string Origin = String.Empty;
public string Host = String.Empty;
- public string ResourcePath = String.Empty;
public string Key = String.Empty;
- public HttpCookieCollection Cookies { get; set; }
- public string SubProtocol { get; set; }
- public string Version { get; set; }
- public Dictionary<string,string> AdditionalFields { get; set; }
+ public string Origin = String.Empty;
+ public string ResourcePath = String.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="ClientHandshake"/> class.
/// </summary>
- /// <param name="ChallengeBytes">The challenge bytes.</param>
/// <param name="header">The header.</param>
public ClientHandshake(Header header)
{
- ResourcePath= header.RequestPath;
- Key = header["sec-websocket-key"];
+ ResourcePath = header.RequestPath;
+ Key = header["sec-websocket-key"];
SubProtocol = header["sec-websocket-protocol"];
- Origin = header["origin"];
+ Origin = header["origin"];
if (String.IsNullOrEmpty(Origin))
+ {
Origin = header["sec-websocket-origin"];
- Host = header["host"];
- Version = header["sec-websocket-version"];
- Cookies = header.Cookies;
+ }
+ Host = header["host"];
+ Version = header["sec-websocket-version"];
+ Cookies = header.Cookies;
}
+ public HttpCookieCollection Cookies { get; set; }
+ public string SubProtocol { get; set; }
+ public string Version { get; set; }
+ public Dictionary<string, string> AdditionalFields { get; set; }
+
/// <summary>
/// Determines whether this instance is valid.
/// </summary>
@@ -83,10 +86,10 @@ public ClientHandshake(Header header)
public bool IsValid()
{
return (
- (Host != null) &&
- (Key != null) &&
- (Int32.Parse(Version) >= 8)
- );
+ (Host != null) &&
+ (Key != null) &&
+ (Int32.Parse(Version) >= 8)
+ );
}
/// <summary>
@@ -101,19 +104,18 @@ public override string ToString()
if (Cookies != null)
{
- additionalFields += "Cookie: " + Cookies.ToString() + "\r\n";
+ additionalFields += "Cookie: " + Cookies + "\r\n";
}
- if (additionalFields != null)
+ if (additionalFields != String.Empty)
{
- foreach (KeyValuePair<string, string> field in this.AdditionalFields)
- {
- additionalFields += field.Key + ": " + field.Value + "\r\n";
- }
+ additionalFields = AdditionalFields.Aggregate(additionalFields,
+ (current, field) =>
+ current + (field.Key + ": " + field.Value + "\r\n"));
}
additionalFields += "\r\n";
- return String.Format(_handshake, ResourcePath, Origin, Host, SubProtocol, Key, additionalFields);
+ return String.Format(Handshake, ResourcePath, Origin, Host, SubProtocol, Key, additionalFields);
}
}
@@ -126,7 +128,7 @@ public class ServerHandshake
/// <summary>
/// The preformatted handshake string.
/// </summary>
- private const string _handshake =
+ private const string Handshake =
"HTTP/1.1 101 Switching Protocols\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
@@ -152,7 +154,7 @@ public override string ToString()
additionalFields += "Sec-WebSocket-Protocol: " + SubProtocol + "\r\n";
}
additionalFields += "\r\n";
- return String.Format(_handshake, Accept, additionalFields);
+ return String.Format(Handshake, Accept, additionalFields);
}
}
-}
+}
View
40 Handlers/WebSocket/hybi10/WebSocketAuthentication.cs
@@ -21,41 +21,40 @@
*/
using System;
+using System.Security.Cryptography;
using System.Threading;
using Alchemy.Server.Classes;
-using System.Security.Cryptography;
namespace Alchemy.Server.Handlers.WebSocket.hybi10
{
/// <summary>
/// Handles the handshaking between the client and the host, when a new connection is created
/// </summary>
- public class WebSocketAuthentication : Alchemy.Server.Handlers.WebSocket.WebSocketAuthentication
+ public class WebSocketAuthentication : IWebSocketAuthentication
{
public static string Origin = string.Empty;
public static string Location = string.Empty;
- private static SemaphoreSlim _createLock = new SemaphoreSlim(1);
- private static WebSocketAuthentication _instance = null;
-
- private WebSocketAuthentication()
- {
+ private static readonly SemaphoreSlim CreateLock = new SemaphoreSlim(1);
+ private static WebSocketAuthentication _instance;
- }
+ private WebSocketAuthentication() {}
public static WebSocketAuthentication Instance
{
get
{
- _createLock.Wait();
+ CreateLock.Wait();
if (_instance == null)
{
_instance = new WebSocketAuthentication();
}
- _createLock.Release();
+ CreateLock.Release();
return _instance;
}
}
+ #region IWebSocketAuthentication Members
+
public void SetOrigin(string origin)
{
Origin = origin;
@@ -68,19 +67,27 @@ public void SetLocation(string location)
public bool CheckHandshake(Context context)
{
- if(context.ReceivedByteCount > 8)
+ if (context.ReceivedByteCount > 8)
{
- ClientHandshake handshake = new ClientHandshake(context.Header);
+ var handshake = new ClientHandshake(context.Header);
// See if our header had the required information
if (handshake.IsValid())
{
// Optionally check Origin and Location if they're set.
if (!String.IsNullOrEmpty(Origin))
+ {
if (handshake.Origin != "http://" + Origin)
+ {
return false;
+ }
+ }
if (!String.IsNullOrEmpty(Location))
+ {
if (handshake.Host != Location + ":" + context.Server.Port.ToString())
+ {
return false;
+ }
+ }
// Generate response handshake for the client
ServerHandshake serverShake = GenerateResponseHandshake(handshake, context);
serverShake.SubProtocol = handshake.SubProtocol;
@@ -92,13 +99,14 @@ public bool CheckHandshake(Context context)
return false;
}
+ #endregion
+
private static ServerHandshake GenerateResponseHandshake(ClientHandshake handshake, Context context)
{
- ServerHandshake responseHandshake = new ServerHandshake();
- responseHandshake.Accept = GenerateAccept(handshake.Key, context);
+ var responseHandshake = new ServerHandshake {Accept = GenerateAccept(handshake.Key, context)};
return responseHandshake;
}
-
+
private static void SendServerHandshake(ServerHandshake handshake, Context context)
{
// generate a byte array representation of the handshake including the answer to the challenge
@@ -116,4 +124,4 @@ private static string GenerateAccept(string key, Context context)
return Convert.ToBase64String(hasher.ComputeHash(context.UserContext.Encoding.GetBytes(rawAnswer)));
}
}
-}
+}
View
56 Handlers/WebSocket/hybi10/WebSocketHandler.cs
@@ -29,25 +29,25 @@ namespace Alchemy.Server.Handlers.WebSocket.hybi10
/// <summary>
/// A threadsafe singleton that contains functions which are used to handle incoming connections for the WebSocket Protocol
/// </summary>
- sealed class WebSocketHandler : Handler
+ internal sealed class WebSocketHandler : Handler
{
private static WebSocketHandler _instance;
private WebSocketHandler()
{
- Authentication = Alchemy.Server.Handlers.WebSocket.hybi10.WebSocketAuthentication.Instance;
+ Authentication = WebSocketAuthentication.Instance;
}
public static WebSocketHandler Instance
{
- get
+ get
{
- _createLock.Wait();
+ CreateLock.Wait();
if (_instance == null)
{
_instance = new WebSocketHandler();
}
- _createLock.Release();
+ CreateLock.Release();
return _instance;
}
}
@@ -61,7 +61,7 @@ public override void HandleRequest(Context context)
if (context.IsSetup)
{
context.UserContext.DataFrame.Append(context.Buffer);
- if (context.UserContext.DataFrame.State == DataFrame.DataState.Complete)
+ if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Complete)
{
switch (context.UserContext.DataFrame.Length)
{
@@ -78,7 +78,8 @@ public override void HandleRequest(Context context)
context.Pings = 0;
context.UserContext.OnReceive();
}
- if ((context.Pings >= context.Server.MaxPingsInSequence) && (context.Server.MaxPingsInSequence != 0))
+ if ((context.Pings >= context.Server.MaxPingsInSequence) &&
+ (context.Server.MaxPingsInSequence != 0))
{
context.Dispose();
}
@@ -89,7 +90,7 @@ public override void HandleRequest(Context context)
break;
}
}
- else if (context.UserContext.DataFrame.State == DataFrame.DataState.Closed)
+ else if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Closed)
{
context.UserContext.Send(new byte[0], true);