|
|
|
|
@ -548,6 +548,56 @@ namespace WebSocketSharp {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool concatenateFragments(Stream dest)
|
|
|
|
|
{
|
|
|
|
|
Func<WsFrame, bool> processContinuation = contFrame =>
|
|
|
|
|
{
|
|
|
|
|
if (!contFrame.IsContinuation)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
dest.WriteBytes(contFrame.PayloadData.ApplicationData);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var frame = readFrame();
|
|
|
|
|
if (processAbnormal(frame))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!frame.IsFinal)
|
|
|
|
|
{
|
|
|
|
|
// MORE & CONT
|
|
|
|
|
if (processContinuation(frame))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// FINAL & CONT
|
|
|
|
|
if (processContinuation(frame))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// FINAL & PING
|
|
|
|
|
if (processPing(frame))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// FINAL & PONG
|
|
|
|
|
if (processPong(frame))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// FINAL & CLOSE
|
|
|
|
|
if (processClose(frame))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ?
|
|
|
|
|
processIncorrectFrame();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool connect()
|
|
|
|
|
{
|
|
|
|
|
return _client
|
|
|
|
|
@ -565,11 +615,9 @@ namespace WebSocketSharp {
|
|
|
|
|
return Convert.ToBase64String(src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string createCompressExtension(CompressionMethod method)
|
|
|
|
|
private static string createCompressionExtension(CompressionMethod method)
|
|
|
|
|
{
|
|
|
|
|
return method != CompressionMethod.NONE
|
|
|
|
|
? String.Format("permessage-compress; method={0}", method.ToString().ToLower())
|
|
|
|
|
: String.Empty;
|
|
|
|
|
return createCurrentCompressionExtension(method);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static WsFrame createControlFrame(Opcode opcode, PayloadData payloadData, bool client)
|
|
|
|
|
@ -577,6 +625,20 @@ namespace WebSocketSharp {
|
|
|
|
|
return createFrame(Fin.FINAL, opcode, payloadData, false, client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string createCurrentCompressionExtension(CompressionMethod method)
|
|
|
|
|
{
|
|
|
|
|
return method != CompressionMethod.NONE
|
|
|
|
|
? String.Format("permessage-{0}", method.ToString().ToLower())
|
|
|
|
|
: String.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string createDeprecatedCompressionExtension(CompressionMethod method)
|
|
|
|
|
{
|
|
|
|
|
return method != CompressionMethod.NONE
|
|
|
|
|
? String.Format("permessage-compress; method={0}", method.ToString().ToLower())
|
|
|
|
|
: String.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static WsFrame createFrame(
|
|
|
|
|
Fin fin, Opcode opcode, PayloadData payloadData, bool compressed, bool client)
|
|
|
|
|
{
|
|
|
|
|
@ -590,9 +652,9 @@ namespace WebSocketSharp {
|
|
|
|
|
private string createRequestExtensions()
|
|
|
|
|
{
|
|
|
|
|
var extensions = new StringBuilder(64);
|
|
|
|
|
var compress = createCompressExtension(_compression);
|
|
|
|
|
if (!compress.IsEmpty())
|
|
|
|
|
extensions.Append(compress);
|
|
|
|
|
var comp = createCompressionExtension(_compression);
|
|
|
|
|
if (!comp.IsEmpty())
|
|
|
|
|
extensions.Append(comp);
|
|
|
|
|
|
|
|
|
|
return extensions.Length > 0
|
|
|
|
|
? extensions.ToString()
|
|
|
|
|
@ -671,6 +733,19 @@ namespace WebSocketSharp {
|
|
|
|
|
return processResponseHandshake();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static CompressionMethod getCompressionMethod(string value)
|
|
|
|
|
{
|
|
|
|
|
var deprecated = createDeprecatedCompressionExtension(CompressionMethod.DEFLATE);
|
|
|
|
|
if (value.Equals(deprecated))
|
|
|
|
|
return CompressionMethod.DEFLATE;
|
|
|
|
|
|
|
|
|
|
foreach (CompressionMethod method in Enum.GetValues(typeof(CompressionMethod)))
|
|
|
|
|
if (isCompressionExtension(value, method))
|
|
|
|
|
return method;
|
|
|
|
|
|
|
|
|
|
return CompressionMethod.NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// As client
|
|
|
|
|
private void init()
|
|
|
|
|
{
|
|
|
|
|
@ -691,9 +766,14 @@ namespace WebSocketSharp {
|
|
|
|
|
_client = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isCompressExtension(string value, CompressionMethod method)
|
|
|
|
|
private static bool isCompressionExtension(string value)
|
|
|
|
|
{
|
|
|
|
|
return value.StartsWith("permessage-");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isCompressionExtension(string value, CompressionMethod method)
|
|
|
|
|
{
|
|
|
|
|
var expected = createCompressExtension(method);
|
|
|
|
|
var expected = createCompressionExtension(method);
|
|
|
|
|
return !expected.IsEmpty()
|
|
|
|
|
? value.Equals(expected)
|
|
|
|
|
: false;
|
|
|
|
|
@ -751,56 +831,6 @@ namespace WebSocketSharp {
|
|
|
|
|
response.HeaderExists("Sec-WebSocket-Version", _version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool mergeFragments(Stream dest)
|
|
|
|
|
{
|
|
|
|
|
Func<WsFrame, bool> processContinuation = contFrame =>
|
|
|
|
|
{
|
|
|
|
|
if (!contFrame.IsContinuation)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
dest.WriteBytes(contFrame.PayloadData.ApplicationData);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var frame = readFrame();
|
|
|
|
|
if (processAbnormal(frame))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!frame.IsFinal)
|
|
|
|
|
{
|
|
|
|
|
// MORE & CONT
|
|
|
|
|
if (processContinuation(frame))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// FINAL & CONT
|
|
|
|
|
if (processContinuation(frame))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// FINAL & PING
|
|
|
|
|
if (processPing(frame))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// FINAL & PONG
|
|
|
|
|
if (processPong(frame))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// FINAL & CLOSE
|
|
|
|
|
if (processClose(frame))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ?
|
|
|
|
|
processIncorrectFrame();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onClose(CloseEventArgs eventArgs)
|
|
|
|
|
{
|
|
|
|
|
if (!Thread.CurrentThread.IsBackground)
|
|
|
|
|
@ -931,21 +961,21 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
private void processFragments(WsFrame first)
|
|
|
|
|
{
|
|
|
|
|
using (var merge = new MemoryStream())
|
|
|
|
|
using (var concatenated = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
merge.WriteBytes(first.PayloadData.ApplicationData);
|
|
|
|
|
if (!mergeFragments(merge))
|
|
|
|
|
concatenated.WriteBytes(first.PayloadData.ApplicationData);
|
|
|
|
|
if (!concatenateFragments(concatenated))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
byte[] data;
|
|
|
|
|
if (_compression != CompressionMethod.NONE)
|
|
|
|
|
{
|
|
|
|
|
data = merge.DecompressToArray(_compression);
|
|
|
|
|
data = concatenated.DecompressToArray(_compression);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
merge.Close();
|
|
|
|
|
data = merge.ToArray();
|
|
|
|
|
concatenated.Close();
|
|
|
|
|
data = concatenated.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMessage(new MessageEventArgs(first.Opcode, data));
|
|
|
|
|
@ -1005,17 +1035,21 @@ namespace WebSocketSharp {
|
|
|
|
|
if (extensions.IsNullOrEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var compress = false;
|
|
|
|
|
var comp = false;
|
|
|
|
|
var buffer = new List<string>();
|
|
|
|
|
foreach (var extension in extensions.SplitHeaderValue(','))
|
|
|
|
|
{
|
|
|
|
|
var e = extension.Trim();
|
|
|
|
|
var tmp = e.RemovePrefix("x-webkit-");
|
|
|
|
|
if (!compress && isCompressExtension(tmp, CompressionMethod.DEFLATE))
|
|
|
|
|
if (!comp && isCompressionExtension(tmp))
|
|
|
|
|
{
|
|
|
|
|
_compression = CompressionMethod.DEFLATE;
|
|
|
|
|
compress = true;
|
|
|
|
|
buffer.Add(e);
|
|
|
|
|
var method = getCompressionMethod(tmp);
|
|
|
|
|
if (method != CompressionMethod.NONE)
|
|
|
|
|
{
|
|
|
|
|
_compression = method;
|
|
|
|
|
comp = true;
|
|
|
|
|
buffer.Add(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1062,20 +1096,26 @@ namespace WebSocketSharp {
|
|
|
|
|
// As client
|
|
|
|
|
private void processResponseExtensions(string extensions)
|
|
|
|
|
{
|
|
|
|
|
var compress = false;
|
|
|
|
|
var checkComp = _compression != CompressionMethod.NONE
|
|
|
|
|
? true
|
|
|
|
|
: false;
|
|
|
|
|
|
|
|
|
|
var comp = false;
|
|
|
|
|
if (!extensions.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
foreach (var extension in extensions.SplitHeaderValue(','))
|
|
|
|
|
{
|
|
|
|
|
var e = extension.Trim();
|
|
|
|
|
if (!compress && isCompressExtension(e, _compression))
|
|
|
|
|
compress = true;
|
|
|
|
|
if (checkComp &&
|
|
|
|
|
!comp &&
|
|
|
|
|
isCompressionExtension(e, _compression))
|
|
|
|
|
comp = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_extensions = extensions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!compress)
|
|
|
|
|
if (checkComp && !comp)
|
|
|
|
|
_compression = CompressionMethod.NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|