protected void Push(NetContext context, WebSocketsFrame frame) { lock (this) { int maxQuota = context.Handler.MaxIncomingQuota; if (maxQuota > 0 && ProtocolProcessor.Sum(holder, f => { var typed = f as WebSocketsFrame; return(typed == null ? 0 : typed.GetLengthEstimate()); }) + frame.GetLengthEstimate() > maxQuota) { throw new InvalidOperationException("Inbound quota exceeded"); } ProtocolProcessor.AddFrame(context, ref holder, frame); } }
public static WebSocketsFrame TryParseFrameHeader(byte[] buffer, int bytesAvailable, out int headerLength) { int maskOffset; if (bytesAvailable < 2) { headerLength = 0; return null; } bool masked = (buffer[1] & 128) != 0; int tmp = buffer[1] & 127; int payloadLen; switch (tmp) { case 126: headerLength = masked ? 8 : 4; if (bytesAvailable < headerLength) return null; payloadLen = (buffer[2] << 8) | buffer[3]; maskOffset = 4; break; case 127: headerLength = masked ? 14 : 10; if (bytesAvailable < headerLength) return null; int big = WebSocketsProcessor.ReadInt32(buffer, 2), little = WebSocketsProcessor.ReadInt32(buffer, 6); if (big != 0 || little < 0) throw new ArgumentOutOfRangeException(); // seriously, we're not going > 2GB payloadLen = little; maskOffset = 10; break; default: headerLength = masked ? 6 : 2; if (bytesAvailable < headerLength) return null; payloadLen = tmp; maskOffset = 2; break; } var frame = new WebSocketsFrame(); frame.IsFinal = (buffer[0] & 128) != 0; frame.Reserved1 = (buffer[0] & 64) != 0; frame.Reserved2 = (buffer[0] & 32) != 0; frame.Reserved3 = (buffer[0] & 16) != 0; frame.OpCode = (WebSocketsFrame.OpCodes)(buffer[0] & 15); frame.Mask = masked ? (int?)WebSocketsProcessor.ReadInt32(buffer, maskOffset) : null; frame.PayloadLength = payloadLen; return frame; }
protected override void GracefulShutdown(NetContext context, Connection connection) { connection.DisableRead(); var ws = (WebSocketConnection)connection; if (!ws.HasSentClose) { ws.HasSentClose = true; var close = new WebSocketsFrame(); if (IsClient) { close.Mask = CreateMask(); } close.OpCode = WebSocketsFrame.OpCodes.Close; close.PayloadLength = 0; SendControl(context, close); } SendShutdown(context); connection.PromptToSend(context); }
private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame) { if(IsClient) { if(frame.Mask.HasValue) throw new InvalidOperationException("Clients only expect unmasked frames"); } else { if (!frame.Mask.HasValue) throw new InvalidOperationException("Servers only expect masked frames"); } var ws = (WebSocketConnection) connection; if(frame.IsControlFrame) { if(!frame.IsFinal) throw new InvalidOperationException("control frames cannot be fragmented"); switch(frame.OpCode) { case WebSocketsFrame.OpCodes.Close: // respond with a closing handshake GracefulShutdown(context, connection); break; case WebSocketsFrame.OpCodes.Pong: // could add some reaction here break; case WebSocketsFrame.OpCodes.Ping: // send a "pong" with the same payload var pong = new WebSocketsFrame(); if (IsClient) pong.Mask = CreateMask(); pong.OpCode = WebSocketsFrame.OpCodes.Binary; pong.PayloadLength = frame.PayloadLength; if(frame.Payload != null) { var ms = new MemoryStream((int)frame.Payload.Length); byte[] buffer = null; try { buffer = context.GetBuffer(); Copy(frame.Payload, ms, buffer, null, frame.PayloadLength); } finally { context.Recycle(buffer); } pong.Payload = ms; } SendControl(context, pong); connection.PromptToSend(context); break; default: throw new InvalidOperationException(); } } else { if (!ws.AllowIncomingDataFrames) throw new InvalidOperationException("Data frames disabled"); int pendingCount = FrameCount; if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation) { if(pendingCount == 0) throw new InvalidOperationException("Continuation of nothing"); } else if (pendingCount != 0) { throw new InvalidOperationException("Should be a continuation"); } if (!frame.IsFinal) { Push(context, frame); } else { if(pendingCount == 0) { Process(context, connection, frame.Payload, frame.OpCode); } else { throw new NotImplementedException(); } } } if (frame.OpCode == WebSocketsFrame.OpCodes.Close) { Debug.WriteLine("Sent close frame: " + connection); } }
protected override void GracefulShutdown(NetContext context, Connection connection) { connection.DisableRead(); var ws = (WebSocketConnection) connection; if(!ws.HasSentClose) { ws.HasSentClose = true; var close = new WebSocketsFrame(); if (IsClient) close.Mask = CreateMask(); close.OpCode = WebSocketsFrame.OpCodes.Close; close.PayloadLength = 0; SendControl(context, close); } SendShutdown(context); connection.PromptToSend(context); }
static string ReadFrameMessage(WebSocketsFrame frame) { var ms = new MemoryStream(); frame.Payload.CopyTo(ms); return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); }
static string DecompressFrame(IExtension extn, params byte[] data) { var handler = new TcpHandler(); var ctx = new NetContext(delegate { }, handler); if (extn == null) extn = PerFrameDeflate.Default.CreateExtension("deflate-frame"); var frame = new WebSocketsFrame { OpCode = WebSocketsFrame.OpCodes.Text, Payload = new MemoryStream(data), PayloadLength = data.Length, Reserved1 = true }; var connection = new WebSocketConnection(new IPEndPoint(IPAddress.Loopback, 20000)); var decoded = extn.ApplyIncoming(ctx, connection, frame).Single(); return ReadFrameMessage(decoded); }
IEnumerable <WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes) { if (frame.Reserved1) { throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?"); } int headerBytes = 0; if (outbound == null) { outbound = new ZStream(); const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15) outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS); headerBytes = 2; } BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { inBuffer = context.GetBuffer(); outBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); outbound.next_out = outBuffer; outbound.next_in = inBuffer; int remaining = frame.PayloadLength; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) { break; } remaining -= readCount; outbound.next_in_index = 0; outbound.avail_in = readCount; do { outbound.next_out_index = 0; outbound.avail_out = outBuffer.Length; long priorOut = outbound.total_out; int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH); if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) { throw new ZStreamException("deflating: " + outbound.msg); } int outCount = (int)(outbound.total_out - priorOut); if (outCount > 0) { if (headerBytes == 0) { tmp.Write(outBuffer, 0, outCount); } else { if (outCount < headerBytes) { throw new InvalidOperationException("Failed to write entire header"); } // check the generated header meets our expectations // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window) if ((outBuffer[0] & 15) != 8) { throw new InvalidOperationException("Zlib CM header was incorrect"); } if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid { throw new InvalidOperationException("Zlib CINFO header was incorrect"); } // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary if ((outBuffer[1] & 32) != 0) { throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)"); } // skip the header, and write anything else outCount -= headerBytes; if (outCount > 0) { tmp.Write(outBuffer, headerBytes, outCount); } headerBytes = 0; // all written now } } } while (outbound.avail_in > 0 || outbound.avail_out == 0); } if (remaining != 0) { throw new EndOfStreamException(); } if (headerBytes != 0) { throw new InvalidOperationException("Zlib header was not written"); } // verify the last 4 bytes, then drop them tmp.Position = tmp.Length - 4; NetContext.Fill(tmp, outBuffer, 4); if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF)) { throw new InvalidOperationException("expectation failed: 0000FFFF in the tail"); } if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength) { // compressing it didn't do anything useful; since we're going to discard // the compression context, we might as well stick with the original data payload.Position = 0; payload = null; // so that it doesn't get disposed } else { // set our final output tmp.Position = 0; tmp.SetLength(tmp.Length - 4); long bytesSaved = frame.PayloadLength - tmp.Length; frame.Payload = tmp; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = true; tmp = payload as BufferStream; parent.RegisterOutboundBytesSaved(bytesSaved); } } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (outbound != null) { outbound.next_out = null; outbound.next_in = null; } if (tmp != null) { tmp.Dispose(); } if (inBuffer != null) { context.Recycle(inBuffer); } if (outBuffer != null) { context.Recycle(outBuffer); } if (parent.disableContextTakeover) { ClearContext(false, true); } } } yield return(frame); }
IEnumerable<WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes) { if (frame.Reserved1) { throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?"); } int headerBytes = 0; if (outbound == null) { outbound = new ZStream(); const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15) outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS); headerBytes = 2; } BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { inBuffer = context.GetBuffer(); outBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); outbound.next_out = outBuffer; outbound.next_in = inBuffer; int remaining = frame.PayloadLength; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) break; remaining -= readCount; outbound.next_in_index = 0; outbound.avail_in = readCount; do { outbound.next_out_index = 0; outbound.avail_out = outBuffer.Length; long priorOut = outbound.total_out; int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH); if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) throw new ZStreamException("deflating: " + outbound.msg); int outCount = (int)(outbound.total_out - priorOut); if (outCount > 0) { if (headerBytes == 0) { tmp.Write(outBuffer, 0, outCount); } else { if (outCount < headerBytes) { throw new InvalidOperationException("Failed to write entire header"); } // check the generated header meets our expectations // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window) if ((outBuffer[0] & 15) != 8) { throw new InvalidOperationException("Zlib CM header was incorrect"); } if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid { throw new InvalidOperationException("Zlib CINFO header was incorrect"); } // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary if ((outBuffer[1] & 32) != 0) { throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)"); } // skip the header, and write anything else outCount -= headerBytes; if (outCount > 0) { tmp.Write(outBuffer, headerBytes, outCount); } headerBytes = 0; // all written now } } } while (outbound.avail_in > 0 || outbound.avail_out == 0); } if (remaining != 0) throw new EndOfStreamException(); if (headerBytes != 0) throw new InvalidOperationException("Zlib header was not written"); // verify the last 4 bytes, then drop them tmp.Position = tmp.Length - 4; NetContext.Fill(tmp, outBuffer, 4); if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF)) { throw new InvalidOperationException("expectation failed: 0000FFFF in the tail"); } if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength) { // compressing it didn't do anything useful; since we're going to discard // the compression context, we might as well stick with the original data payload.Position = 0; payload = null; // so that it doesn't get disposed } else { // set our final output tmp.Position = 0; tmp.SetLength(tmp.Length - 4); long bytesSaved = frame.PayloadLength - tmp.Length; frame.Payload = tmp; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = true; tmp = payload as BufferStream; parent.RegisterOutboundBytesSaved(bytesSaved); } } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (outbound != null) { outbound.next_out = null; outbound.next_in = null; } if (tmp != null) tmp.Dispose(); if (inBuffer != null) context.Recycle(inBuffer); if (outBuffer != null) context.Recycle(outBuffer); if (parent.disableContextTakeover) ClearContext(false, true); } } yield return frame; }
private static IEnumerable<WebSocketsFrame> ApplyExtensions(NetContext context, Connection connection, WebSocketsFrame frame, bool inbound) { var wsc = connection as WebSocketConnection; var extn = wsc == null ? null : wsc.Extensions; IEnumerable<WebSocketsFrame> frames = Enumerable.Repeat(frame, 1); if (extn != null && extn.Length != 0) { for (int i = 0; i < extn.Length; i++) { var actualExtension = extn[i]; if (inbound) { frames = frames.SelectMany(x => actualExtension.ApplyIncoming(context, wsc, x)); } else { frames = frames.SelectMany(x => actualExtension.ApplyOutgoing(context, wsc, x)); } } } return frames; }
private static IEnumerable <WebSocketsFrame> ApplyExtensions(NetContext context, Connection connection, WebSocketsFrame frame, bool inbound) { var wsc = connection as WebSocketConnection; var extn = wsc == null ? null : wsc.Extensions; IEnumerable <WebSocketsFrame> frames = Enumerable.Repeat(frame, 1); if (extn != null && extn.Length != 0) { for (int i = 0; i < extn.Length; i++) { var actualExtension = extn[i]; if (inbound) { frames = frames.SelectMany(x => actualExtension.ApplyIncoming(context, wsc, x)); } else { frames = frames.SelectMany(x => actualExtension.ApplyOutgoing(context, wsc, x)); } } } return(frames); }
public override void Send(NetContext context, Connection connection, object message) { if (message == null) { throw new ArgumentNullException("message"); } string s; WebSocketConnection wsConnection; WebSocketsFrame frame; var encoding = Encoding.UTF8; if ((s = message as string) != null && (wsConnection = connection as WebSocketConnection) != null && wsConnection.MaxCharactersPerFrame > 0 && s.Length > wsConnection.MaxCharactersPerFrame) { int remaining = s.Length, charIndex = 0; bool isFirst = true; char[] charBuffer = s.ToCharArray(); var batch = new List <IFrame>(); while (remaining > 0) { int charCount = Math.Min(remaining, wsConnection.MaxCharactersPerFrame); var buffer = encoding.GetBytes(charBuffer, charIndex, charCount); //Debug.WriteLine("> " + new string(charBuffer, charIndex, charCount)); remaining -= charCount; charIndex += charCount; frame = new WebSocketsFrame(); frame.OpCode = isFirst ? WebSocketsFrame.OpCodes.Text : WebSocketsFrame.OpCodes.Continuation; isFirst = false; frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota); frame.Payload.Write(buffer, 0, buffer.Length); frame.PayloadLength = buffer.Length; frame.Mask = IsClient ? (int?)CreateMask() : null; frame.Payload.Position = 0; frame.IsFinal = remaining <= 0; foreach (var final in ApplyExtensions(context, connection, frame, false)) { batch.Add(final); } } SendData(context, batch); return; } byte[] blob; frame = new WebSocketsFrame(); frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota); if (s != null) { frame.OpCode = WebSocketsFrame.OpCodes.Text; var buffer = encoding.GetBytes(s); //Debug.WriteLine("> " + s); frame.Payload.Write(buffer, 0, buffer.Length); frame.PayloadLength = buffer.Length; } else if ((blob = message as byte[]) != null) { frame.OpCode = WebSocketsFrame.OpCodes.Binary; frame.Payload.Write(blob, 0, blob.Length); frame.PayloadLength = blob.Length; } else { throw new NotSupportedException(message.ToString()); } frame.Mask = IsClient ? (int?)CreateMask() : null; frame.Payload.Position = 0; frame.IsFinal = true; foreach (var final in ApplyExtensions(context, connection, frame, false)) { SendData(context, final); } }
private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame) { if (IsClient) { if (frame.Mask.HasValue) { throw new InvalidOperationException("Clients only expect unmasked frames"); } } else { if (!frame.Mask.HasValue) { throw new InvalidOperationException("Servers only expect masked frames"); } } var ws = (WebSocketConnection)connection; if (frame.IsControlFrame) { if (!frame.IsFinal) { throw new InvalidOperationException("control frames cannot be fragmented"); } switch (frame.OpCode) { case WebSocketsFrame.OpCodes.Close: // respond with a closing handshake GracefulShutdown(context, connection); break; case WebSocketsFrame.OpCodes.Pong: // could add some reaction here break; case WebSocketsFrame.OpCodes.Ping: // send a "pong" with the same payload var pong = new WebSocketsFrame(); if (IsClient) { pong.Mask = CreateMask(); } pong.OpCode = WebSocketsFrame.OpCodes.Binary; pong.PayloadLength = frame.PayloadLength; if (frame.Payload != null) { var ms = new MemoryStream((int)frame.Payload.Length); byte[] buffer = null; try { buffer = context.GetBuffer(); Copy(frame.Payload, ms, buffer, null, frame.PayloadLength); } finally { context.Recycle(buffer); } pong.Payload = ms; } SendControl(context, pong); connection.PromptToSend(context); break; default: throw new InvalidOperationException(); } } else { if (!ws.AllowIncomingDataFrames) { throw new InvalidOperationException("Data frames disabled"); } int pendingCount = FrameCount; if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation) { if (pendingCount == 0) { throw new InvalidOperationException("Continuation of nothing"); } } else if (pendingCount != 0) { throw new InvalidOperationException("Should be a continuation"); } if (!frame.IsFinal) { Push(context, frame); } else { if (pendingCount == 0) { Process(context, connection, frame.Payload, frame.OpCode); } else { throw new NotImplementedException(); } } } if (frame.OpCode == WebSocketsFrame.OpCodes.Close) { Debug.WriteLine("Sent close frame: " + connection); } }
protected override int ProcessIncoming(NetContext context, Connection connection, Stream incoming) { if (incoming.Length < 2) { return(0); // can't read that; frame takes at minimum two bytes } byte[] buffer = null; try { buffer = context.GetBuffer(); int read; // read as much as possible up to 14 bytes; that gives us space for 2 bytes frame header, 8 bytes length, and 4 bytes mask read = NetContext.TryFill(incoming, buffer, 14); int headerLength; var frame = WebSocketsFrame.TryParseFrameHeader(buffer, read, out headerLength); if (frame == null) { return(0); } int payloadLen = frame.PayloadLength; #if VERBOSE Debug.WriteLine("Parsed header from: " + BitConverter.ToString(buffer, 0, headerLength)); #endif if (incoming.Length < headerLength + payloadLen) { return(0); // not enough data to read the payload } if (payloadLen != 0) { Stream payload = null; try { payload = new BufferStream(context, context.Handler.MaxIncomingQuota); if (read != headerLength) { incoming.Position -= (read - headerLength); // we got it wrong... } Copy(incoming, payload, buffer, frame.Mask, payloadLen); if (payloadLen != payload.Length) { throw new InvalidOperationException("I munged the data"); } frame.Payload = payload; payload = null; } finally { if (payload != null) { payload.Dispose(); } } } foreach (var final in ApplyExtensions(context, connection, frame, true)) { ProcessFrame(context, connection, final); } return(headerLength + payloadLen); } finally { context.Recycle(buffer); } }
IEnumerable <WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (frame.Reserved1 && !frame.IsControlFrame) { BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { outBuffer = context.GetBuffer(); inBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); if (inbound == null) { inbound = new ZStream(); inbound.inflateInit(); // fake a zlib header with: // CMF: // CM = 8 (deflate) // CINFO = 7 (32k window) // FLG: // FCHECK: 26 (checksum of other bits) // FDICT: 0 (no dictionary) // FLEVEL: 3 (maximum) inBuffer[0] = 120; inBuffer[1] = 218; inbound.next_in = inBuffer; int chk = Inflate(tmp, outBuffer, 2); if (chk != 0) { throw new InvalidOperationException("Spoofed zlib header suggested data"); } } inbound.next_in = inBuffer; int remaining = frame.PayloadLength; //bool first = true; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) { break; } remaining -= readCount; //if (first) //{ // kill the BFINAL flag from the first block, if set; we don't want zlib // // trying to verify the ADLER checksum; unfortunately, a frame can contain // // multiple blocks, and a *later* block could have BFINAL set. That sucks. // inBuffer[0] &= 254; // first = false; //} Inflate(tmp, outBuffer, readCount); } if (remaining != 0) { throw new EndOfStreamException(); } // spoof the missing 4 bytes from the tail inBuffer[0] = inBuffer[1] = 0x00; inBuffer[2] = inBuffer[3] = 0xFF; Inflate(tmp, outBuffer, 4); // set our final output tmp.Position = 0; frame.Payload = tmp; long bytesSaved = tmp.Length - frame.PayloadLength; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = false; tmp = payload as BufferStream; parent.RegisterInboundBytesSaved(bytesSaved); } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (inbound != null) { inbound.next_out = null; inbound.next_in = null; } if (tmp != null) { tmp.Dispose(); } if (inBuffer != null) { context.Recycle(inBuffer); } if (outBuffer != null) { context.Recycle(outBuffer); } if (parent.disableContextTakeover) { ClearContext(true, false); } } } yield return(frame); }
private void Process(NetContext context, Connection connection, Stream stream, WebSocketsFrame.OpCodes opCode) { using (stream) { switch (opCode) { case WebSocketsFrame.OpCodes.Text: string s; if (stream == null || stream.Length == 0) s = ""; else { stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { s = reader.ReadToEnd(); } } context.Handler.OnReceived(connection, s); break; case WebSocketsFrame.OpCodes.Binary: byte[] blob; int len; if (stream == null || (len = ((int)stream.Length - (int)stream.Position)) == 0) blob = new byte[0]; else { blob = new byte[len]; byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = 0, read; stream.Position = 0; while((read = stream.Read(buffer, offset, Math.Min(len, buffer.Length))) > 0) { offset += read; len -= read; } if(len != 0) throw new EndOfStreamException(); } finally { context.Recycle(buffer); } } context.Handler.OnReceived(connection, blob); break; default: throw new InvalidOperationException(); } } }
public override void Send(NetContext context, Connection connection, object message) { if(message == null) throw new ArgumentNullException("message"); string s; WebSocketConnection wsConnection; WebSocketsFrame frame; var encoding = Encoding.UTF8; if ((s = message as string) != null && (wsConnection = connection as WebSocketConnection) != null && wsConnection.MaxCharactersPerFrame > 0 && s.Length > wsConnection.MaxCharactersPerFrame) { int remaining = s.Length, charIndex = 0; bool isFirst = true; char[] charBuffer = s.ToCharArray(); var batch = new List<IFrame>(); while(remaining > 0) { int charCount = Math.Min(remaining, wsConnection.MaxCharactersPerFrame); var buffer = encoding.GetBytes(charBuffer, charIndex, charCount); //Debug.WriteLine("> " + new string(charBuffer, charIndex, charCount)); remaining -= charCount; charIndex += charCount; frame = new WebSocketsFrame(); frame.OpCode = isFirst ? WebSocketsFrame.OpCodes.Text : WebSocketsFrame.OpCodes.Continuation; isFirst = false; frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota); frame.Payload.Write(buffer, 0, buffer.Length); frame.PayloadLength = buffer.Length; frame.Mask = IsClient ? (int?)CreateMask() : null; frame.Payload.Position = 0; frame.IsFinal = remaining <= 0; foreach (var final in ApplyExtensions(context, connection, frame, false)) { batch.Add(final); } } SendData(context, batch); return; } byte[] blob; frame = new WebSocketsFrame(); frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota); if(s != null) { frame.OpCode = WebSocketsFrame.OpCodes.Text; var buffer = encoding.GetBytes(s); //Debug.WriteLine("> " + s); frame.Payload.Write(buffer, 0, buffer.Length); frame.PayloadLength = buffer.Length; } else if ((blob = message as byte[]) != null) { frame.OpCode = WebSocketsFrame.OpCodes.Binary; frame.Payload.Write(blob, 0, blob.Length); frame.PayloadLength = blob.Length; } else { throw new NotSupportedException(message.ToString()); } frame.Mask = IsClient ? (int?)CreateMask() : null; frame.Payload.Position = 0; frame.IsFinal = true; foreach(var final in ApplyExtensions(context, connection, frame, false)) { SendData(context, final); } }
protected void Push(NetContext context, WebSocketsFrame frame) { lock (this) { int maxQuota = context.Handler.MaxIncomingQuota; if (maxQuota > 0 && ProtocolProcessor.Sum(holder, f => { var typed = f as WebSocketsFrame; return typed == null ? 0 : typed.GetLengthEstimate(); }) + frame.GetLengthEstimate() > maxQuota) { throw new InvalidOperationException("Inbound quota exceeded"); } ProtocolProcessor.AddFrame(context, ref holder, frame); } }
public static WebSocketsFrame TryParseFrameHeader(byte[] buffer, int bytesAvailable, out int headerLength) { int maskOffset; if (bytesAvailable < 2) { headerLength = 0; return(null); } bool masked = (buffer[1] & 128) != 0; int tmp = buffer[1] & 127; int payloadLen; switch (tmp) { case 126: headerLength = masked ? 8 : 4; if (bytesAvailable < headerLength) { return(null); } payloadLen = (buffer[2] << 8) | buffer[3]; maskOffset = 4; break; case 127: headerLength = masked ? 14 : 10; if (bytesAvailable < headerLength) { return(null); } int big = WebSocketsProcessor.ReadInt32(buffer, 2), little = WebSocketsProcessor.ReadInt32(buffer, 6); if (big != 0 || little < 0) { throw new ArgumentOutOfRangeException(); // seriously, we're not going > 2GB } payloadLen = little; maskOffset = 10; break; default: headerLength = masked ? 6 : 2; if (bytesAvailable < headerLength) { return(null); } payloadLen = tmp; maskOffset = 2; break; } var frame = new WebSocketsFrame(); frame.IsFinal = (buffer[0] & 128) != 0; frame.Reserved1 = (buffer[0] & 64) != 0; frame.Reserved2 = (buffer[0] & 32) != 0; frame.Reserved3 = (buffer[0] & 16) != 0; frame.OpCode = (WebSocketsFrame.OpCodes)(buffer[0] & 15); frame.Mask = masked ? (int?)WebSocketsProcessor.ReadInt32(buffer, maskOffset) : null; frame.PayloadLength = payloadLen; return(frame); }
public void CompressFrame() { var handler = new TcpHandler(); var ctx = new NetContext(delegate { }, handler); IExtensionFactory factory = new PerFrameDeflate(0, false); var extn = factory.CreateExtension("deflate-frame"); var data = Encoding.UTF8.GetBytes("Hello"); var frame = new WebSocketsFrame { OpCode = WebSocketsFrame.OpCodes.Text, Payload = new MemoryStream(data), PayloadLength = data.Length, Reserved1 = false }; var connection = new WebSocketConnection(new IPEndPoint(IPAddress.Loopback, 20000)); var encoded = extn.ApplyOutgoing(ctx, connection, frame).Single(); var ms = new MemoryStream(); encoded.Payload.CopyTo(ms); string hex = BitConverter.ToString(ms.GetBuffer(), 0, (int)ms.Length); Assert.AreEqual("F2-48-CD-C9-C9-07-00", hex); // unrelated decoder extn = PerFrameDeflate.Default.CreateExtension("deflate-frame"); var decoded = extn.ApplyIncoming(ctx, connection, frame).Single(); ms = new MemoryStream(); decoded.Payload.Position = 0; decoded.Payload.CopyTo(ms); string s = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); Assert.AreEqual("Hello", s); }
IEnumerable<WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (frame.Reserved1 && !frame.IsControlFrame) { BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { outBuffer = context.GetBuffer(); inBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); if (inbound == null) { inbound = new ZStream(); inbound.inflateInit(); // fake a zlib header with: // CMF: // CM = 8 (deflate) // CINFO = 7 (32k window) // FLG: // FCHECK: 26 (checksum of other bits) // FDICT: 0 (no dictionary) // FLEVEL: 3 (maximum) inBuffer[0] = 120; inBuffer[1] = 218; inbound.next_in = inBuffer; int chk = Inflate(tmp, outBuffer, 2); if (chk != 0) throw new InvalidOperationException("Spoofed zlib header suggested data"); } inbound.next_in = inBuffer; int remaining = frame.PayloadLength; //bool first = true; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) break; remaining -= readCount; //if (first) //{ // kill the BFINAL flag from the first block, if set; we don't want zlib // // trying to verify the ADLER checksum; unfortunately, a frame can contain // // multiple blocks, and a *later* block could have BFINAL set. That sucks. // inBuffer[0] &= 254; // first = false; //} Inflate(tmp, outBuffer, readCount); } if (remaining != 0) throw new EndOfStreamException(); // spoof the missing 4 bytes from the tail inBuffer[0] = inBuffer[1] = 0x00; inBuffer[2] = inBuffer[3] = 0xFF; Inflate(tmp, outBuffer, 4); // set our final output tmp.Position = 0; frame.Payload = tmp; long bytesSaved = tmp.Length - frame.PayloadLength; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = false; tmp = payload as BufferStream; parent.RegisterInboundBytesSaved(bytesSaved); } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (inbound != null) { inbound.next_out = null; inbound.next_in = null; } if (tmp != null) tmp.Dispose(); if (inBuffer != null) context.Recycle(inBuffer); if (outBuffer != null) context.Recycle(outBuffer); if (parent.disableContextTakeover) ClearContext(true, false); } } yield return frame; }
static WebSocketsFrame CreateFrame(string text) { var bytes = Encoding.UTF8.GetBytes(text); var frame = new WebSocketsFrame { OpCode = WebSocketsFrame.OpCodes.Text, Payload = new MemoryStream(bytes), PayloadLength = bytes.Length }; return frame; }