Supports RFC 6455

This commit is contained in:
sta
2012-07-31 10:36:52 +09:00
parent fc6ee2db12
commit 118e0015d5
63 changed files with 2857 additions and 646 deletions

View File

@@ -17,7 +17,7 @@ using System.Runtime.CompilerServices;
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.1.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.

View File

@@ -0,0 +1,38 @@
#region MIT License
/**
* ByteOrder.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp
{
public enum ByteOrder : byte
{
LITTLE = 0x0,
BIG = 0x1
}
}

View File

@@ -0,0 +1,87 @@
#region MIT License
/**
* CloseEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012 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.Text;
using WebSocketSharp.Frame;
namespace WebSocketSharp
{
public class CloseEventArgs : MessageEventArgs
{
private ushort _code;
private string _reason;
private bool _wasClean;
public CloseStatusCode Code
{
get
{
return (CloseStatusCode)_code;
}
}
public string Reason
{
get
{
return _reason;
}
}
public bool WasClean
{
get
{
return _wasClean;
}
set
{
_wasClean = value;
}
}
public CloseEventArgs(PayloadData data)
: base(Opcode.CLOSE, data)
{
_code = data.ToBytes().SubArray(0, 2).To<ushort>(ByteOrder.BIG);
if (data.Length > 2)
{
var buffer = data.ToBytes().SubArray(2, (int)(data.Length - 2));
_reason = Encoding.UTF8.GetString(buffer);
}
else
{
_reason = String.Empty;
}
_wasClean = false;
}
}
}

View File

@@ -4,7 +4,7 @@
*
* The MIT License
*
* Copyright (c) 2010 sta.blockhead
* Copyright (c) 2010-2012 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
@@ -28,19 +28,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace WebSocketSharp
{
public static class Ext
{
public static bool AreNotEqualDo(
public static bool EqualsAndSaveTo(this int value, char c, List<byte> dest)
{
byte b = (byte)value;
dest.Add(b);
return b == Convert.ToByte(c);
}
public static string GetHeaderValue(this string src, string separater)
{
int i = src.IndexOf(separater);
return src.Substring(i + 1).Trim();
}
public static bool IsHostOrder(this ByteOrder order)
{
if (BitConverter.IsLittleEndian ^ (order == ByteOrder.LITTLE))
{// true ^ false or false ^ true
return false;
}
else
{// true ^ true or false ^ false
return true;
}
}
public static bool IsNullDo<T>(this T value, Action act)
where T : class
{
if (value == null)
{
act();
return true;
}
return false;
}
public static bool NotEqualsDo(
this string expected,
string actual,
Func<string, string, string> func,
out string ret)
out string ret,
bool ignoreCase)
{
if (expected != actual)
if (String.Compare(expected, actual, ignoreCase) != 0)
{
ret = func(expected, actual);
return true;
@@ -50,106 +90,226 @@ namespace WebSocketSharp
return false;
}
public static bool EqualsWithSaveTo(this int asByte, char c, List<byte> dist)
public static byte[] ReadBytes<TStream>(this TStream stream, ulong length, int bufferLength)
where TStream : System.IO.Stream
{
byte b = (byte)asByte;
dist.Add(b);
return b == Convert.ToByte(c);
}
List<byte> readData = new List<byte>();
public static uint GenerateKey(this Random rand, int space)
{
uint max = (uint)(0xffffffff / space);
ulong count = length / (ulong)bufferLength;
int remainder = (int)(length % (ulong)bufferLength);
int upper16 = (int)((max & 0xffff0000) >> 16);
int lower16 = (int)(max & 0x0000ffff);
byte[] buffer1 = new byte[bufferLength];
return ((uint)rand.Next(upper16 + 1) << 16) + (uint)rand.Next(lower16 + 1);
}
public static char GeneratePrintableASCIIwithoutSPandNum(this Random rand)
{
int ascii = rand.Next(2) == 0 ? rand.Next(33, 48) : rand.Next(58, 127);
return Convert.ToChar(ascii);
}
public static string GenerateSecKey(this Random rand, out uint key)
{
int space = rand.Next(1, 13);
int ascii = rand.Next(1, 13);
key = rand.GenerateKey(space);
long mKey = key * space;
List<char> secKey = new List<char>(mKey.ToString().ToCharArray());
int i;
ascii.Times( () =>
count.Times(() =>
{
i = rand.Next(secKey.Count + 1);
secKey.Insert(i, rand.GeneratePrintableASCIIwithoutSPandNum());
} );
stream.Read(buffer1, 0, bufferLength);
readData.AddRange(buffer1);
});
space.Times( () =>
if (remainder > 0)
{
i = rand.Next(1, secKey.Count);
secKey.Insert(i, ' ');
} );
return new String(secKey.ToArray());
}
public static byte[] InitializeWithPrintableASCII(this byte[] bytes, Random rand)
{
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = (byte)rand.Next(32, 127);
}
return bytes;
}
public static bool IsValid(this string[] response, byte[] expectedCR, byte[] actualCR, out string message)
{
string expectedCRtoHexStr = BitConverter.ToString(expectedCR);
string actualCRtoHexStr = BitConverter.ToString(actualCR);
Func<string, Func<string, string, string>> func = s =>
{
return (e, a) =>
{
#if DEBUG
Console.WriteLine("WS: Error @IsValid: Invalid {0} response.", s);
Console.WriteLine(" expected: {0}", e);
Console.WriteLine(" actual : {0}", a);
#endif
return String.Format("Invalid {0} response: {1}", s, a);
};
};
Func<string, string, string> func1 = func("handshake");
Func<string, string, string> func2 = func("challenge");
string msg;
if ("HTTP/1.1 101 WebSocket Protocol Handshake".AreNotEqualDo(response[0], func1, out msg) ||
"Upgrade: WebSocket".AreNotEqualDo(response[1], func1, out msg) ||
"Connection: Upgrade".AreNotEqualDo(response[2], func1, out msg) ||
expectedCRtoHexStr.AreNotEqualDo(actualCRtoHexStr, func2, out msg))
{
message = msg;
return false;
byte[] buffer2 = new byte[remainder];
stream.Read(buffer2, 0, remainder);
readData.AddRange(buffer2);
}
message = String.Empty;
return true;
return readData.ToArray();
}
public static void Times(this int n, Action act)
public static T[] SubArray<T>(this T[] array, int startIndex, int length)
{
for (int i = 0; i < n; i++)
if (startIndex == 0 && array.Length == length)
{
return array;
}
T[] subArray = new T[length];
Array.Copy(array, startIndex, subArray, 0, length);
return subArray;
}
public static void Times<T>(this T n, Action act)
where T : struct
{
if (typeof(T) != typeof(byte) &&
typeof(T) != typeof(Int16) &&
typeof(T) != typeof(Int32) &&
typeof(T) != typeof(Int64) &&
typeof(T) != typeof(UInt16) &&
typeof(T) != typeof(UInt32) &&
typeof(T) != typeof(UInt64))
{
throw new NotSupportedException("Not supported Struct type: " + typeof(T).ToString());
}
ulong m = (ulong)(object)n;
for (ulong i = 0; i < m; i++)
{
act();
}
}
public static void Times<T>(this T n, Action<ulong> act)
where T : struct
{
if (typeof(T) != typeof(byte) &&
typeof(T) != typeof(Int16) &&
typeof(T) != typeof(Int32) &&
typeof(T) != typeof(Int64) &&
typeof(T) != typeof(UInt16) &&
typeof(T) != typeof(UInt32) &&
typeof(T) != typeof(UInt64))
{
throw new NotSupportedException("Not supported Struct type: " + typeof(T).ToString());
}
ulong m = (ulong)(object)n;
for (ulong i = 0; i < m; i++)
{
act(i);
}
}
public static T To<T>(this byte[] src, ByteOrder srcOrder)
where T : struct
{
T dest;
byte[] buffer = src.ToHostOrder(srcOrder);
if (typeof(T) == typeof(Boolean))
{
dest = (T)(object)BitConverter.ToBoolean(buffer, 0);
}
else if (typeof(T) == typeof(Char))
{
dest = (T)(object)BitConverter.ToChar(buffer, 0);
}
else if (typeof(T) == typeof(Double))
{
dest = (T)(object)BitConverter.ToDouble(buffer, 0);
}
else if (typeof(T) == typeof(Int16))
{
dest = (T)(object)BitConverter.ToInt16(buffer, 0);
}
else if (typeof(T) == typeof(Int32))
{
dest = (T)(object)BitConverter.ToInt32(buffer, 0);
}
else if (typeof(T) == typeof(Int64))
{
dest = (T)(object)BitConverter.ToInt64(buffer, 0);
}
else if (typeof(T) == typeof(Single))
{
dest = (T)(object)BitConverter.ToSingle(buffer, 0);
}
else if (typeof(T) == typeof(UInt16))
{
dest = (T)(object)BitConverter.ToUInt16(buffer, 0);
}
else if (typeof(T) == typeof(UInt32))
{
dest = (T)(object)BitConverter.ToUInt32(buffer, 0);
}
else if (typeof(T) == typeof(UInt64))
{
dest = (T)(object)BitConverter.ToUInt64(buffer, 0);
}
else
{
dest = default(T);
}
return dest;
}
public static byte[] ToBytes<T>(this T value, ByteOrder order)
where T : struct
{
byte[] buffer;
if (typeof(T) == typeof(Boolean))
{
buffer = BitConverter.GetBytes((Boolean)(object)value);
}
else if (typeof(T) == typeof(Char))
{
buffer = BitConverter.GetBytes((Char)(object)value);
}
else if (typeof(T) == typeof(Double))
{
buffer = BitConverter.GetBytes((Double)(object)value);
}
else if (typeof(T) == typeof(Int16))
{
buffer = BitConverter.GetBytes((Int16)(object)value);
}
else if (typeof(T) == typeof(Int32))
{
buffer = BitConverter.GetBytes((Int32)(object)value);
}
else if (typeof(T) == typeof(Int64))
{
buffer = BitConverter.GetBytes((Int64)(object)value);
}
else if (typeof(T) == typeof(Single))
{
buffer = BitConverter.GetBytes((Single)(object)value);
}
else if (typeof(T) == typeof(UInt16))
{
buffer = BitConverter.GetBytes((UInt16)(object)value);
}
else if (typeof(T) == typeof(UInt32))
{
buffer = BitConverter.GetBytes((UInt32)(object)value);
}
else if (typeof(T) == typeof(UInt64))
{
buffer = BitConverter.GetBytes((UInt64)(object)value);
}
else
{
buffer = new byte[]{};
}
return order.IsHostOrder()
? buffer
: buffer.Reverse().ToArray();
}
public static byte[] ToHostOrder(this byte[] src, ByteOrder srcOrder)
{
byte[] buffer = new byte[src.Length];
src.CopyTo(buffer, 0);
return srcOrder.IsHostOrder()
? buffer
: buffer.Reverse().ToArray();
}
public static string ToString<T>(this T[] array, string separater)
{
int len;
StringBuilder sb;
len = array.Length;
if (len == 0)
{
return String.Empty;
}
sb = new StringBuilder();
for (int i = 0; i < len - 1; i++)
{
sb.AppendFormat("{0}{1}", array[i].ToString(), separater);
}
sb.Append(array[len - 1].ToString());
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,59 @@
#region MIT License
/**
* CloseStatusCode.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp.Frame
{
public enum CloseStatusCode : ushort
{
/*
* Close Status Code
*
* Defined Status Codes: http://tools.ietf.org/html/rfc6455#section-7.4.1
*
* "Reserved value" MUST NOT be set as a status code in a Close control frame by an endpoint.
* It is designated for use in applications expecting a status code to indicate that connection
* was closed due to a system grounds.
*
*/
NORMAL = 1000, // Normal closure.
AWAY = 1001, // A Server going down or a browser having navigated away from a page.
PROTOCOL_ERROR = 1002, // Terminating the connection due to a protocol error.
INCORRECT_DATA = 1003, // Received a type of data it cannot accept.
UNDEFINED = 1004, // Reserved value. Still undefined.
NO_STATUS_CODE = 1005, // Reserved value.
ABNORMAL = 1006, // Reserved value. Connection was closed abnormally.
INCONSISTENT_DATA = 1007, // Received data within a message that was not consistent with the type of the message.
POLICY_VIOLATION = 1008, // Received a message that violates its policy.
TOO_BIG = 1009, // Received a message that is too big.
IGNORE_EXTENSION = 1010, // Server ignored negotiated extensions.
SERVER_ERROR = 1011, // Server encountered an unexpected condition.
HANDSHAKE_FAILURE = 1015 // Reserved value. Failure to establish a connection.
}
}

View File

@@ -0,0 +1,38 @@
#region MIT License
/**
* Fin.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp.Frame
{
public enum Fin : byte
{
MORE = 0x0,
FINAL = 0x1
}
}

View File

@@ -0,0 +1,38 @@
#region MIT License
/**
* Mask.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp.Frame
{
public enum Mask : byte
{
UNMASK = 0x0,
MASK = 0x1
}
}

View File

@@ -0,0 +1,43 @@
#region MIT License
/**
* Opcode.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp.Frame
{
[Flags]
public enum Opcode : byte
{
CONT = 0x0,
TEXT = 0x1,
BINARY = 0x2,
CLOSE = 0x8,
PING = 0x9,
PONG = 0xa
}
}

View File

@@ -0,0 +1,184 @@
#region MIT License
/**
* PayloadData.cs
*
* The MIT License
*
* Copyright (c) 2012 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.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebSocketSharp.Frame
{
public class PayloadData : IEnumerable<byte>
{
#region Public Static Fields
public static readonly ulong MaxLength;
#endregion
#region Properties
public byte[] ExtensionData { get; private set; }
public byte[] ApplicationData { get; private set; }
public bool IsMasked { get; private set; }
public ulong Length
{
get
{
return (ulong)(ExtensionData.LongLength + ApplicationData.LongLength);
}
}
#endregion
#region Static Constructor
static PayloadData()
{
MaxLength = long.MaxValue;
}
#endregion
#region Public Constructors
public PayloadData(string appData)
: this(Encoding.UTF8.GetBytes(appData))
{
}
public PayloadData(byte[] appData)
: this(new byte[]{}, appData)
{
}
public PayloadData(byte[] appData, bool masked)
: this(new byte[]{}, appData, masked)
{
}
public PayloadData(byte[] extData, byte[] appData)
: this(extData, appData, false)
{
}
public PayloadData(byte[] extData, byte[] appData, bool masked)
{
Func<string, Action> func = s => () =>
{
string message = String.Format("{0} must not be null.", s);
throw new ArgumentNullException(message);
};
extData.IsNullDo(func("extData"));
appData.IsNullDo(func("appData"));
if ((ulong)extData.LongLength + (ulong)appData.LongLength > MaxLength)
{
throw new ArgumentOutOfRangeException("Plus extData length and appData lenght must be less than MaxLength.");
}
ExtensionData = extData;
ApplicationData = appData;
IsMasked = masked;
}
#endregion
#region Private Methods
private void mask(byte[] src, byte[] key)
{
if (key.Length != 4)
{
throw new ArgumentOutOfRangeException("key length must be 4.");
}
for (long i = 0; i < src.LongLength; i++)
{
src[i] = (byte)(src[i] ^ key[i % 4]);
}
}
#endregion
#region Public Methods
public IEnumerator<byte> GetEnumerator()
{
foreach (byte b in ExtensionData)
{
yield return b;
}
foreach (byte b in ApplicationData)
{
yield return b;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Mask(byte[] maskingKey)
{
if (ExtensionData.LongLength > 0)
{
mask(ExtensionData, maskingKey);
}
if (ApplicationData.LongLength > 0)
{
mask(ApplicationData, maskingKey);
}
IsMasked = !IsMasked;
}
public byte[] ToBytes()
{
if (ExtensionData.LongLength > 0)
{
return ExtensionData.Concat(ApplicationData).ToArray();
}
else
{
return ApplicationData;
}
}
public override string ToString()
{
return BitConverter.ToString(ToBytes());
}
#endregion
}
}

View File

@@ -0,0 +1,38 @@
#region MIT License
/**
* Rsv.cs
*
* The MIT License
*
* Copyright (c) 2012 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;
namespace WebSocketSharp.Frame
{
public enum Rsv : byte
{
OFF = 0x0,
ON = 0x1
}
}

View File

@@ -0,0 +1,461 @@
#region MIT License
/**
* WsFrame.cs
*
* The MIT License
*
* Copyright (c) 2012 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 System.Collections.Generic;
using System.Text;
namespace WebSocketSharp.Frame
{
public class WsFrame : IEnumerable<byte>
{
#region Private Static Fields
private static readonly int _readBufferLen;
#endregion
#region Properties
public Fin Fin { get; private set; }
public Rsv Rsv1 { get; private set; }
public Rsv Rsv2 { get; private set; }
public Rsv Rsv3 { get; private set; }
public Opcode Opcode { get; private set; }
public Mask Masked { get; private set; }
public byte PayloadLen { get; private set; }
public byte[] ExtPayloadLen { get; private set; }
public byte[] MaskingKey { get; private set; }
public PayloadData PayloadData { get; private set; }
public ulong Length
{
get
{
return 2 + (ulong)(ExtPayloadLen.Length + MaskingKey.Length) + PayloadLength;
}
}
public ulong PayloadLength
{
get
{
return PayloadData.Length;
}
}
#endregion
#region Static Constructor
static WsFrame()
{
_readBufferLen = 1024;
}
#endregion
#region Private Constructors
private WsFrame()
{
Rsv1 = Rsv.OFF;
Rsv2 = Rsv.OFF;
Rsv3 = Rsv.OFF;
ExtPayloadLen = new byte[]{};
MaskingKey = new byte[]{};
}
#endregion
#region Public Constructors
public WsFrame(Opcode opcode, PayloadData payloadData)
: this(Fin.FINAL, opcode, payloadData)
{
}
public WsFrame(Fin fin, Opcode opcode, PayloadData payloadData)
: this(fin, opcode, Mask.MASK, payloadData)
{
}
public WsFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payloadData)
: this()
{
Fin = fin;
Opcode = opcode;
Masked = mask;
ulong dataLength = payloadData.Length;
if (dataLength < 126)
{
PayloadLen = (byte)dataLength;
}
else if (dataLength < 0x010000)
{
PayloadLen = (byte)126;
ExtPayloadLen = ((ushort)dataLength).ToBytes(ByteOrder.BIG);
}
else
{
PayloadLen = (byte)127;
ExtPayloadLen = dataLength.ToBytes(ByteOrder.BIG);
}
PayloadData = payloadData;
if (Masked == Mask.MASK)
{
MaskingKey = new byte[4];
var rand = new Random();
rand.NextBytes(MaskingKey);
PayloadData.Mask(MaskingKey);
}
}
#endregion
#region Public Static Methods
public static WsFrame Parse(byte[] src)
{
return Parse(src, true);
}
public static WsFrame Parse(byte[] src, bool unmask)
{
using (MemoryStream ms = new MemoryStream(src))
{
return Parse(ms, unmask);
}
}
public static WsFrame Parse<TStream>(TStream stream)
where TStream : System.IO.Stream
{
return Parse(stream, true);
}
public static WsFrame Parse<TStream>(TStream stream, bool unmask)
where TStream : System.IO.Stream
{
Fin fin;
Rsv rsv1, rsv2, rsv3;
Opcode opcode;
Mask masked;
byte payloadLen;
byte[] extPayloadLen = new byte[]{};
byte[] maskingKey = new byte[]{};
PayloadData payloadData;
byte[] buffer1, buffer2, buffer3;
int buffer1Len = 2;
int buffer2Len = 0;
ulong buffer3Len = 0;
int maskingKeyLen = 4;
buffer1 = new byte[buffer1Len];
stream.Read(buffer1, 0, buffer1Len);
// FIN
fin = (buffer1[0] & 0x80) == 0x80
? Fin.FINAL
: Fin.MORE;
// RSV1
rsv1 = (buffer1[0] & 0x40) == 0x40
? Rsv.ON
: Rsv.OFF;
// RSV2
rsv2 = (buffer1[0] & 0x20) == 0x20
? Rsv.ON
: Rsv.OFF;
// RSV3
rsv3 = (buffer1[0] & 0x10) == 0x10
? Rsv.ON
: Rsv.OFF;
// opcode
opcode = (Opcode)(buffer1[0] & 0x0f);
// MASK
masked = (buffer1[1] & 0x80) == 0x80
? Mask.MASK
: Mask.UNMASK;
// Payload len
payloadLen = (byte)(buffer1[1] & 0x7f);
// Extended payload length
if (payloadLen <= 125)
{
buffer3Len = payloadLen;
}
else if (payloadLen == 126)
{
buffer2Len = 2;
}
else
{
buffer2Len = 8;
}
if (buffer2Len > 0)
{
buffer2 = new byte[buffer2Len];
stream.Read(buffer2, 0, buffer2Len);
extPayloadLen = buffer2;
switch (buffer2Len)
{
case 2:
buffer3Len = extPayloadLen.To<ushort>(ByteOrder.BIG);
break;
case 8:
buffer3Len = extPayloadLen.To<ulong>(ByteOrder.BIG);
break;
}
}
if (buffer3Len > PayloadData.MaxLength)
{
throw new WsReceivedTooBigMessageException();
}
// Masking-key
if (masked == Mask.MASK)
{
maskingKey = new byte[maskingKeyLen];
stream.Read(maskingKey, 0, maskingKeyLen);
}
// Payload Data
if (buffer3Len <= (ulong)_readBufferLen)
{
buffer3 = new byte[buffer3Len];
stream.Read(buffer3, 0, (int)buffer3Len);
}
else
{
buffer3 = stream.ReadBytes(buffer3Len, _readBufferLen);
}
if (masked == Mask.MASK)
{
payloadData = new PayloadData(buffer3, true);
if (unmask == true)
{
payloadData.Mask(maskingKey);
masked = Mask.UNMASK;
maskingKey = new byte[]{};
}
}
else
{
payloadData = new PayloadData(buffer3);
}
return new WsFrame
{
Fin = fin,
Rsv1 = rsv1,
Rsv2 = rsv2,
Rsv3 = rsv3,
Opcode = opcode,
Masked = masked,
PayloadLen = payloadLen,
ExtPayloadLen = extPayloadLen,
MaskingKey = maskingKey,
PayloadData = payloadData
};
}
#endregion
#region Public Methods
public IEnumerator<byte> GetEnumerator()
{
foreach (byte b in ToBytes())
{
yield return b;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Print()
{
byte[] buffer;
long count, i, j;
int countDigit, remainder;
string countFmt, extPayloadLen, headerFmt, topLineFmt, bottomLineFmt, payloadData, spFmt;
switch (ExtPayloadLen.Length)
{
case 2:
extPayloadLen = ExtPayloadLen.To<ushort>(ByteOrder.BIG).ToString();
break;
case 8:
extPayloadLen = ExtPayloadLen.To<ulong>(ByteOrder.BIG).ToString();
break;
default:
extPayloadLen = String.Empty;
break;
}
if (((Opcode.TEXT | Opcode.PING | Opcode.PONG) & Opcode) == Opcode &&
Masked == Mask.UNMASK &&
PayloadLength > 0)
{
payloadData = Encoding.UTF8.GetString(PayloadData.ToBytes());
}
else
{
payloadData = BitConverter.ToString(PayloadData.ToBytes());
}
headerFmt = @"
WsFrame:
FIN={0}, RSV1={1}, RSV2={2}, RSV3={3}, Opcode={4},
MASK={5}, Payload Len={6}, Extended Payload Len={7},
Masking Key ={8},
Payload Data={9}";
buffer = ToBytes();
count = (long)(Length / 4);
remainder = (int)(Length % 4);
if (count < 10000)
{
countDigit = 4;
countFmt = "{0,4}";
}
else if (count < 0x010000)
{
countDigit = 4;
countFmt = "{0,4:X}";
}
else if (count < 0x0100000000)
{
countDigit = 8;
countFmt = "{0,8:X}";
}
else
{
countDigit = 16;
countFmt = "{0,16:X}";
}
spFmt = String.Format("{{0,{0}}}", countDigit);
topLineFmt = String.Format(@"
{0} 01234567 89ABCDEF 01234567 89ABCDEF
{0}+--------+--------+--------+--------+", spFmt);
Func<string, Action<string, string, string, string>> func = s =>
{
long lineCount = 0;
string lineFmt = String.Format(" {0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|", s);
return (arg1, arg2, arg3, arg4) =>
{
Console.WriteLine(lineFmt, ++lineCount, arg1, arg2, arg3, arg4);
};
};
var printLine = func(countFmt);
bottomLineFmt = String.Format(" {0}+--------+--------+--------+--------+", spFmt);
Console.WriteLine(headerFmt,
Fin, Rsv1, Rsv2, Rsv3, Opcode,
Masked, PayloadLen, extPayloadLen,
BitConverter.ToString(MaskingKey),
payloadData);
Console.WriteLine(topLineFmt, String.Empty);
for (i = 0; i <= count; i++)
{
j = i * 4;
if (i < count)
{
printLine(
Convert.ToString(buffer[j], 2).PadLeft(8, '0'),
Convert.ToString(buffer[j + 1], 2).PadLeft(8, '0'),
Convert.ToString(buffer[j + 2], 2).PadLeft(8, '0'),
Convert.ToString(buffer[j + 3], 2).PadLeft(8, '0'));
}
else if (i == count && remainder > 0)
{
printLine(
Convert.ToString(buffer[j], 2).PadLeft(8, '0'),
remainder >= 2 ? Convert.ToString(buffer[j + 1], 2).PadLeft(8, '0') : String.Empty,
remainder == 3 ? Convert.ToString(buffer[j + 2], 2).PadLeft(8, '0') : String.Empty,
String.Empty);
}
}
Console.WriteLine(bottomLineFmt, String.Empty);
}
public byte[] ToBytes()
{
var bytes = new List<byte>();
int first16 = (int)Fin;
first16 = (first16 << 1) + (int)Rsv1;
first16 = (first16 << 1) + (int)Rsv2;
first16 = (first16 << 1) + (int)Rsv3;
first16 = (first16 << 4) + (int)Opcode;
first16 = (first16 << 1) + (int)Masked;
first16 = (first16 << 7) + (int)PayloadLen;
bytes.AddRange(((ushort)first16).ToBytes(ByteOrder.BIG));
if (PayloadLen >= 126)
{
bytes.AddRange(ExtPayloadLen);
}
if (Masked == Mask.MASK)
{
bytes.AddRange(MaskingKey);
}
if (PayloadLen > 0)
{
bytes.AddRange(PayloadData.ToBytes());
}
return bytes.ToArray();
}
public override string ToString()
{
return BitConverter.ToString(ToBytes());
}
#endregion
}
}

View File

@@ -0,0 +1,87 @@
#region MIT License
/**
* MessageEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012 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.Text;
using WebSocketSharp.Frame;
namespace WebSocketSharp
{
public class MessageEventArgs : EventArgs
{
private Opcode _type;
private PayloadData _data;
public Opcode Type
{
get
{
return _type;
}
}
public string Data
{
get
{
if (((Opcode.TEXT | Opcode.PING | Opcode.PONG) & _type) == _type)
{
if (_data.Length > 0)
{
return Encoding.UTF8.GetString(_data.ToBytes());
}
else
{
return String.Empty;
}
}
return _type.ToString();
}
}
public byte[] RawData
{
get
{
return _data.ToBytes();
}
}
public MessageEventArgs(string data)
: this(Opcode.TEXT, new PayloadData(data))
{
}
public MessageEventArgs(Opcode type, PayloadData data)
{
_type = type;
_data = data;
}
}
}

View File

@@ -4,7 +4,7 @@
*
* The MIT License
*
* Copyright (c) 2010 sta.blockhead
* Copyright (c) 2010-2012 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
@@ -27,15 +27,18 @@
#endregion
using System;
using WebSocketSharp.Frame;
namespace WebSocketSharp
namespace WebSocketSharp.Stream
{
public interface IWsStream : IDisposable
{
void Close();
int Read(byte[] buffer, int offset, int size);
int ReadByte();
void Write(byte[] buffer, int offset, int count);
void WriteByte(byte value);
void Close();
int Read(byte[] buffer, int offset, int size);
int ReadByte();
WsFrame ReadFrame();
void Write(byte[] buffer, int offset, int count);
void WriteByte(byte value);
void WriteFrame(WsFrame frame);
}
}

View File

@@ -4,7 +4,7 @@
*
* The MIT License
*
* Copyright (c) 2010 sta.blockhead
* Copyright (c) 2010-2012 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
@@ -32,21 +32,24 @@ using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using WebSocketSharp.Frame;
namespace WebSocketSharp
namespace WebSocketSharp.Stream
{
public class WsStream<T> : IWsStream
where T : Stream
public class WsStream<TStream> : IWsStream
where TStream : System.IO.Stream
{
private T innerStream;
private TStream _innerStream;
private Object _forRead;
private Object _forWrite;
public WsStream(T innerStream)
public WsStream(TStream innerStream)
{
Type streamType = typeof(T);
Type streamType = typeof(TStream);
if (streamType != typeof(NetworkStream) &&
streamType != typeof(SslStream))
{
throw new NotSupportedException("Unsupported Stream type: " + streamType.ToString());
throw new NotSupportedException("Not supported Stream type: " + streamType.ToString());
}
if (innerStream == null)
@@ -54,37 +57,56 @@ namespace WebSocketSharp
throw new ArgumentNullException("innerStream");
}
this.innerStream = innerStream;
_innerStream = innerStream;
_forRead = new object();
_forWrite = new object();
}
public void Close()
{
innerStream.Close();
_innerStream.Close();
}
public void Dispose()
{
innerStream.Dispose();
_innerStream.Dispose();
}
public int Read(byte[] buffer, int offset, int size)
{
return innerStream.Read(buffer, offset, size);
return _innerStream.Read(buffer, offset, size);
}
public int ReadByte()
{
return innerStream.ReadByte();
return _innerStream.ReadByte();
}
public WsFrame ReadFrame()
{
lock (_forRead)
{
return WsFrame.Parse(_innerStream);
}
}
public void Write(byte[] buffer, int offset, int count)
{
innerStream.Write(buffer, offset, count);
_innerStream.Write(buffer, offset, count);
}
public void WriteByte(byte value)
{
innerStream.WriteByte(value);
_innerStream.WriteByte(value);
}
public void WriteFrame(WsFrame frame)
{
lock (_forWrite)
{
var buffer = frame.ToBytes();
_innerStream.Write(buffer, 0, buffer.Length);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
#region MIT License
/**
* WsReceivedTooBigMessageException.cs
*
* The MIT License
*
* Copyright (c) 2012 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 WebSocketSharp.Frame;
namespace WebSocketSharp
{
public class WsReceivedTooBigMessageException : Exception
{
private static readonly string _defaultMessage;
static WsReceivedTooBigMessageException()
{
_defaultMessage = String.Format(
"Client received a payload data bigger than the allowable value({0} bytes).", PayloadData.MaxLength);
}
public WsReceivedTooBigMessageException()
: this(_defaultMessage)
{
}
public WsReceivedTooBigMessageException(string message)
: base(message)
{
}
}
}

View File

@@ -4,7 +4,7 @@
*
* The MIT License
*
* Copyright (c) 2010 sta.blockhead
* Copyright (c) 2010-2012 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

View File

@@ -52,17 +52,30 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="notify-sharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=2df29c54e245917a">
<Package>notify-sharp</Package>
</Reference>
<Reference Include="System.ServiceModel" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Ext.cs" />
<Compile Include="WebSocket.cs" />
<Compile Include="WsState.cs" />
<Compile Include="IWsStream.cs" />
<Compile Include="WsStream.cs" />
<Compile Include="MessageEventArgs.cs" />
<Compile Include="CloseEventArgs.cs" />
<Compile Include="WsReceivedTooBigMessageException.cs" />
<Compile Include="ByteOrder.cs" />
<Compile Include="Stream\IWsStream.cs" />
<Compile Include="Stream\WsStream.cs" />
<Compile Include="Frame\WsFrame.cs" />
<Compile Include="Frame\CloseStatusCode.cs" />
<Compile Include="Frame\Fin.cs" />
<Compile Include="Frame\Mask.cs" />
<Compile Include="Frame\Opcode.cs" />
<Compile Include="Frame\PayloadData.cs" />
<Compile Include="Frame\Rsv.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Folder Include="Stream\" />
<Folder Include="Frame\" />
</ItemGroup>
</Project>

Binary file not shown.