Fix for the per message compression

This commit is contained in:
sta
2013-04-25 02:31:38 +09:00
parent b33b5e63ba
commit a1345386fd
63 changed files with 230 additions and 135 deletions

View File

@@ -42,6 +42,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Sockets;
using System.Text;
@@ -63,6 +64,83 @@ namespace WebSocketSharp {
#region Private Methods
private static byte[] compress(this byte[] value)
{
if (value.LongLength == 0)
//return new Byte[] { 0x00, 0x00, 0x00, 0xff, 0xff };
return value;
using (var input = new MemoryStream(value))
using (var output = input.compress())
{
output.Close();
return output.ToArray();
}
}
private static MemoryStream compress(this Stream stream)
{
var output = new MemoryStream();
if (stream.Length == 0)
return output;
using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
{
int readLen = 0;
var buffer = new byte[256];
while (true)
{
readLen = stream.Read(buffer, 0, buffer.Length);
if (readLen == 0)
break;
ds.Write(buffer, 0, readLen);
}
ds.Close(); // "BFINAL" set to 1.
output.Position = 0;
return output;
}
}
private static byte[] decompress(this byte[] value)
{
if (value.LongLength == 0)
return value;
using (var input = new MemoryStream(value))
using (var output = input.decompress())
{
output.Close();
return output.ToArray();
}
}
private static MemoryStream decompress(this Stream stream)
{
var output = new MemoryStream();
if (stream.Length == 0)
return output;
using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
{
int readLen = 0;
var buffer = new byte[256];
while (true)
{
readLen = ds.Read(buffer, 0, buffer.Length);
if (readLen == 0)
break;
output.Write(buffer, 0, readLen);
}
output.Position = 0;
return output;
}
}
private static void times(this ulong n, Action act)
{
for (ulong i = 0; i < n; i++)
@@ -73,6 +151,34 @@ namespace WebSocketSharp {
#region Internal Methods
internal static byte[] Compress(this byte[] value, CompressionMethod method)
{
return method == CompressionMethod.DEFLATE
? value.compress()
: value;
}
internal static Stream Compress(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.DEFLATE
? stream.compress()
: stream;
}
internal static byte[] Decompress(this byte[] value, CompressionMethod method)
{
return method == CompressionMethod.DEFLATE
? value.decompress()
: value;
}
internal static Stream Decompress(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.DEFLATE
? stream.decompress()
: stream;
}
internal static string GetNameInternal(this string nameAndValue, string separator)
{
int i = nameAndValue.IndexOf(separator);

View File

@@ -128,48 +128,6 @@ namespace WebSocketSharp {
#region Private Methods
private static byte[] compress(byte[] value)
{
if (value.LongLength == 0)
//return new Byte[] { 0x00, 0x00, 0x00, 0xff, 0xff };
return value;
using (var comp = new MemoryStream())
using (var ds = new DeflateStream(comp, CompressionMode.Compress, true))
{
ds.Write(value, 0, value.Length);
ds.Close(); // "BFINAL" set to 1.
comp.Close();
return comp.ToArray();
}
}
private static byte[] decompress(byte[] value)
{
if (value.LongLength == 0)
return value;
using (var decomp = new MemoryStream())
using (var comp = new MemoryStream(value))
using (var ds = new DeflateStream(comp, CompressionMode.Decompress, true))
{
int readLen = 0;
var buffer = new byte[256];
while (true)
{
readLen = ds.Read(buffer, 0, buffer.Length);
if (readLen == 0)
break;
decomp.Write(buffer, 0, readLen);
}
decomp.Close();
return decomp.ToArray();
}
}
private static void mask(byte[] src, byte[] key)
{
for (long i = 0; i < src.LongLength; i++)
@@ -184,14 +142,13 @@ namespace WebSocketSharp {
{
try
{
if (method == CompressionMethod.NONE)
return false;
if (ExtensionData.LongLength > 0)
return false;
if (method == CompressionMethod.DEFLATE)
ApplicationData = compress(ApplicationData);
else
return false;
ApplicationData = ApplicationData.Compress(method);
return true;
}
catch
@@ -204,14 +161,12 @@ namespace WebSocketSharp {
{
try
{
if (ApplicationData.LongLength == 0)
return true;
if (method == CompressionMethod.DEFLATE)
ApplicationData = decompress(ApplicationData);
else
if (method == CompressionMethod.NONE)
return false;
if (ApplicationData.LongLength > 0)
ApplicationData = ApplicationData.Decompress(method);
return true;
}
catch

View File

@@ -36,6 +36,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
@@ -63,12 +64,6 @@ namespace WebSocketSharp {
#endregion
#region Private Static Fields
private static NameValueCollection _extensionsReg;
#endregion
#region Private Fields
private string _base64key;
@@ -82,6 +77,7 @@ namespace WebSocketSharp {
private Object _forClose;
private Object _forSend;
private string _origin;
private bool _perFrameCompress;
private string _protocol;
private string _protocols;
private volatile WsState _readyState;
@@ -93,18 +89,6 @@ namespace WebSocketSharp {
#endregion
#region Static Constructor
static WebSocket()
{
_extensionsReg = new NameValueCollection();
_extensionsReg.Add("deflate", "deflate-frame");
_extensionsReg.Add("deflate", "perframe-compress; method=deflate");
_extensionsReg.Add("deflate", "permessage-compress; method=deflate");
}
#endregion
#region Private Constructors
private WebSocket()
@@ -115,6 +99,7 @@ namespace WebSocketSharp {
_forClose = new Object();
_forSend = new Object();
_origin = String.Empty;
_perFrameCompress = false;
_protocol = String.Empty;
_readyState = WsState.CONNECTING;
}
@@ -512,7 +497,7 @@ namespace WebSocketSharp {
private void closeHandshake(PayloadData data)
{
var args = new CloseEventArgs(data);
var frame = createControlFrame(Opcode.CLOSE, data);
var frame = createControlFrame(Opcode.CLOSE, data, _client);
if (send(frame))
args.WasClean = true;
@@ -574,7 +559,7 @@ namespace WebSocketSharp {
}
// As client
private string createBase64Key()
private static string createBase64Key()
{
var src = new byte[16];
var rand = new Random();
@@ -583,37 +568,38 @@ namespace WebSocketSharp {
return Convert.ToBase64String(src);
}
// As client
private string createCompressionExtension()
private static string createCompressExtension(CompressionMethod method)
{
return _compression != CompressionMethod.NONE
//? "deflate-frame"
//? String.Format("perframe-compress; method={0}", _compression.ToString().ToLower())
? String.Format("permessage-compress; method={0}", _compression.ToString().ToLower())
return method != CompressionMethod.NONE
? String.Format("permessage-compress; method={0}", method.ToString().ToLower())
: String.Empty;
}
private WsFrame createControlFrame(Opcode opcode, PayloadData payloadData)
private static WsFrame createControlFrame(Opcode opcode, PayloadData payloadData, bool client)
{
return _client
? new WsFrame(opcode, Mask.MASK, payloadData)
: new WsFrame(opcode, Mask.UNMASK, payloadData);
var mask = client ? Mask.MASK : Mask.UNMASK;
return new WsFrame(opcode, mask, payloadData);
}
private WsFrame createDataFrame(Fin fin, Opcode opcode, PayloadData payloadData)
private static WsFrame createDataFrame(
Fin fin, Opcode opcode, PayloadData payloadData, CompressionMethod method, bool compressed, bool client)
{
return _client
? new WsFrame(fin, opcode, Mask.MASK, payloadData, _compression)
: new WsFrame(fin, opcode, Mask.UNMASK, payloadData, _compression);
var mask = client ? Mask.MASK : Mask.UNMASK;
var compress = compressed ? CompressionMethod.NONE : method;
var frame = new WsFrame(fin, opcode, mask, payloadData, compress);
if (compressed)
frame.PerMessageCompressed = true;
return frame;
}
// As client
private string createRequestExtensions()
{
var extensions = new StringBuilder(64);
var compression = createCompressionExtension();
if (!compression.IsEmpty())
extensions.Append(compression);
var compress = createCompressExtension(_compression);
if (!compress.IsEmpty())
extensions.Append(compress);
return extensions.Length > 0
? extensions.ToString()
@@ -712,28 +698,14 @@ namespace WebSocketSharp {
_client = false;
}
// As client
private bool isCompressionExtension(string value)
private static bool isCompressExtension(string value, CompressionMethod method)
{
var expected = createCompressionExtension();
var expected = createCompressExtension(method);
return !expected.IsEmpty()
? value.Equals(expected)
: false;
}
// As server
private bool isDeflateExtension(string value)
{
if (value.StartsWith("x-webkit-"))
value = value.Substring(9);
foreach (var deflate in _extensionsReg.GetValues("deflate"))
if (value.Equals(deflate))
return true;
return false;
}
private bool isOpened(bool errorIfOpened)
{
if (_readyState != WsState.OPEN && _readyState != WsState.CLOSING)
@@ -745,6 +717,18 @@ namespace WebSocketSharp {
return true;
}
// As server
private static bool isPerFrameCompressExtension(string value)
{
if (value.Equals("deflate-frame"))
return true;
if (value.Equals("perframe-compress; method=deflate"))
return true;
return false;
}
// As server
private bool isValidHostHeader()
{
@@ -831,7 +815,7 @@ namespace WebSocketSharp {
return false;
}
var frame = createControlFrame(Opcode.PING, new PayloadData(buffer));
var frame = createControlFrame(Opcode.PING, new PayloadData(buffer), _client);
if (!send(frame))
return false;
@@ -840,7 +824,7 @@ namespace WebSocketSharp {
private void pong(PayloadData data)
{
var frame = createControlFrame(Opcode.PONG, data);
var frame = createControlFrame(Opcode.PONG, data, _client);
send(frame);
}
@@ -914,7 +898,7 @@ namespace WebSocketSharp {
private void processFragments(WsFrame first)
{
bool compressed = first.IsCompressed;
if (compressed)
if (compressed && _perFrameCompress)
first.Decompress(_compression);
var buffer = new List<byte>(first.PayloadData.ToByteArray());
@@ -923,7 +907,7 @@ namespace WebSocketSharp {
if (!contFrame.IsContinuation)
return false;
if (compressed)
if (contFrame.IsCompressed)
contFrame.Decompress(_compression);
buffer.AddRange(contFrame.PayloadData.ToByteArray());
@@ -966,7 +950,11 @@ namespace WebSocketSharp {
return;
}
onMessage(new MessageEventArgs(first.Opcode, new PayloadData(buffer.ToArray())));
var data = compressed && !_perFrameCompress
? buffer.ToArray().Decompress(_compression)
: buffer.ToArray();
onMessage(new MessageEventArgs(first.Opcode, new PayloadData(data)));
}
private void processFrame(WsFrame frame)
@@ -1022,23 +1010,40 @@ namespace WebSocketSharp {
if (extensions.IsNullOrEmpty())
return;
bool deflate = false;
var buffer = new StringBuilder(64);
var compress = false;
var buffer = new List<string>();
foreach (var extension in extensions.SplitHeaderValue(','))
{
var tmp = extension.Trim();
if (!deflate && isDeflateExtension(tmp))
var e = extension.Trim();
var tmp = e.StartsWith("x-webkit-")
? e.Substring(9)
: e;
if (!compress)
{
buffer.Append(tmp);
deflate = true;
if (isPerFrameCompressExtension(tmp))
{
_perFrameCompress = true;
_compression = CompressionMethod.DEFLATE;
compress = true;
}
if (!compress && isCompressExtension(tmp, CompressionMethod.DEFLATE))
{
_compression = CompressionMethod.DEFLATE;
compress = true;
}
if (compress)
{
buffer.Add(e);
continue;
}
}
}
if (deflate)
_compression = CompressionMethod.DEFLATE;
if (buffer.Length > 0)
_extensions = buffer.ToString();
if (buffer.Count > 0)
_extensions = buffer.ToArray().ToString(",");
}
// As server
@@ -1080,13 +1085,13 @@ namespace WebSocketSharp {
// As client
private void processResponseExtensions(string extensions)
{
bool compress = false;
var compress = false;
if (!extensions.IsNullOrEmpty())
{
foreach (var extension in extensions.SplitHeaderValue(','))
{
var tmp = extension.Trim();
if (!compress && isCompressionExtension(tmp))
var e = extension.Trim();
if (!compress && isCompressExtension(e, _compression))
compress = true;
}
@@ -1197,6 +1202,20 @@ namespace WebSocketSharp {
}
private void send(Opcode opcode, Stream stream)
{
if (_compression == CompressionMethod.NONE || _perFrameCompress)
{
send(opcode, stream, false);
return;
}
using (var compressed = stream.Compress(_compression))
{
send(opcode, compressed, true);
}
}
private void send(Opcode opcode, Stream stream, bool compressed)
{
lock (_forSend)
{
@@ -1210,9 +1229,9 @@ namespace WebSocketSharp {
var length = stream.Length;
if (length <= _fragmentLen)
send(Fin.FINAL, opcode, stream.ReadBytes((int)length));
send(Fin.FINAL, opcode, stream.ReadBytes((int)length), compressed);
else
sendFragmented(opcode, stream);
sendFragmented(opcode, stream, compressed);
}
catch (Exception ex)
{
@@ -1221,9 +1240,10 @@ namespace WebSocketSharp {
}
}
private bool send(Fin fin, Opcode opcode, byte[] data)
private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed)
{
var frame = createDataFrame(fin, opcode, new PayloadData(data));
var frame = createDataFrame(
fin, opcode, new PayloadData(data), _compression, compressed, _client);
return send(frame);
}
@@ -1257,7 +1277,7 @@ namespace WebSocketSharp {
action.BeginInvoke(opcode, stream, callback, null);
}
private long sendFragmented(Opcode opcode, Stream stream)
private long sendFragmented(Opcode opcode, Stream stream, bool compressed)
{
var length = stream.Length;
var quo = length / _fragmentLen;
@@ -1270,7 +1290,7 @@ namespace WebSocketSharp {
// First
tmpLen = stream.Read(buffer, 0, _fragmentLen);
if (send(Fin.MORE, opcode, buffer))
if (send(Fin.MORE, opcode, buffer, compressed))
readLen += tmpLen;
else
return 0;
@@ -1279,7 +1299,7 @@ namespace WebSocketSharp {
for (long i = 0; i < count; i++)
{
tmpLen = stream.Read(buffer, 0, _fragmentLen);
if (send(Fin.MORE, Opcode.CONT, buffer))
if (send(Fin.MORE, Opcode.CONT, buffer, compressed))
readLen += tmpLen;
else
return readLen;
@@ -1289,7 +1309,7 @@ namespace WebSocketSharp {
if (rem != 0)
buffer = new byte[rem];
tmpLen = stream.Read(buffer, 0, buffer.Length);
if (send(Fin.FINAL, Opcode.CONT, buffer))
if (send(Fin.FINAL, Opcode.CONT, buffer, compressed))
readLen += tmpLen;
return readLen;

View File

@@ -178,6 +178,19 @@ namespace WebSocketSharp {
}
}
internal bool PerMessageCompressed {
get {
return IsData && IsCompressed;
}
set {
if (value && !IsData)
return;
Rsv1 = value ? Rsv.ON : Rsv.OFF;
}
}
#endregion
#region Public Properties

View File

@@ -1,6 +1,6 @@
<Type Name="WebSocket" FullName="WebSocketSharp.WebSocket">
<TypeSignature Language="C#" Value="public class WebSocket : IDisposable" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi WebSocket extends System.Object implements class System.IDisposable" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit WebSocket extends System.Object implements class System.IDisposable" />
<AssemblyInfo>
<AssemblyName>websocket-sharp</AssemblyName>
</AssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Overview>
<Assemblies>
<Assembly Name="websocket-sharp" Version="1.0.2.32570">
<Assembly Name="websocket-sharp" Version="1.0.2.4444">
<AssemblyPublicKey>[00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 00 24 00 00 52 53 41 31 00 04 00 00 11 00 00 00 29 17 fb 89 fe c3 91 f7 2b cb 8b e2 61 d2 3f 05 93 6d 65 a8 9e 63 72 a6 f5 d5 2c f2 9d 20 fa 0b c0 70 6a f6 88 7e 8b 90 3f 39 f5 76 c8 48 e0 bb 7b b2 7b ed d3 10 a7 1a 0f 70 98 0f 7f f4 4b 53 09 d2 a5 ef 36 c3 56 b4 aa f0 91 72 63 25 07 89 e0 93 3e 3f 2e f2 b9 73 0e 12 15 5d 43 56 c3 f4 70 a5 89 fe f7 f6 ac 3e 77 c2 d8 d0 84 91 f4 0c d1 f3 8e dc c3 c3 b8 38 3d 0c bf 17 de 20 78 c1 ]</AssemblyPublicKey>
<Attributes>
<Attribute>

Binary file not shown.