Exemple #1
0
        // the `unsafe` here is so that in the "multiple spans, header crosses spans", we can use stackalloc to
        // collate the header bytes in one place, and pass that down for analysis
        internal unsafe static bool TryReadFrameHeader(ref ReadableBuffer buffer, out WebSocketsFrame frame)
        {
            int bytesAvailable = buffer.Length;

            if (bytesAvailable < 2)
            {
                frame = default(WebSocketsFrame);
                return(false); // can't read that; frame takes at minimum two bytes
            }

            var firstSpan = buffer.First;

            if (buffer.IsSingleSpan || firstSpan.Length >= MaxHeaderLength)
            {
                return(TryReadFrameHeader(firstSpan.Length, firstSpan.Span, ref buffer, out frame));
            }
            else
            {
                // header is at most 14 bytes; can afford the stack for that - but note that if we aim for 16 bytes instead,
                // we will usually benefit from using 2 qword copies
                byte *header     = stackalloc byte[16];
                var   slice      = buffer.Slice(0, Math.Min(16, bytesAvailable));
                var   headerSpan = new Span <byte>(header, slice.Length);
                slice.CopyTo(headerSpan);

                // note that we're using the "slice" above to preview the header, but we
                // still want to pass the *original* buffer down below, so that we can
                // check the overall length (payload etc)
                return(TryReadFrameHeader(slice.Length, headerSpan, ref buffer, out frame));
            }
        }
Exemple #2
0
        internal static Task WriteAsync <T>(WebSocketConnection connection,
                                            WebSocketsFrame.OpCodes opCode,
                                            WebSocketsFrame.FrameFlags flags, ref T message)
            where T : struct, IMessageWriter
        {
            int payloadLength = message.GetPayloadLength();
            var buffer        = connection.Connection.Output.Alloc(MaxHeaderLength + payloadLength);
            int mask          = connection.ConnectionType == ConnectionType.Client ? CreateMask() : 0;

            WriteFrameHeader(ref buffer, WebSocketsFrame.FrameFlags.IsFinal, opCode, payloadLength, mask);
            if (payloadLength != 0)
            {
                if (mask == 0)
                {
                    message.WritePayload(buffer);
                }
                else
                {
                    var payloadStart = buffer.AsReadableBuffer().End;
                    message.WritePayload(buffer);
                    var payload = buffer.AsReadableBuffer().Slice(payloadStart); // note that this is a different AsReadableBuffer; call twice is good
                    WebSocketsFrame.ApplyMask(ref payload, mask);
                }
            }
            return(buffer.FlushAsync().AsTask());
        }
        // TODO: not sure how to do this; basically I want to lease a writable, expandable area
        // for a duration, and be able to access it for reading, and release
        internal void AddBacklog(ref ReadableBuffer buffer, ref WebSocketsFrame frame)
        {
            var length = frame.PayloadLength;

            if (length == 0)
            {
                return;              // nothing to store!
            }
            var slicedBuffer = buffer.Slice(0, length);

            // unscramble the data
            if (frame.MaskLittleEndian != 0)
            {
                WebSocketsFrame.ApplyMask(ref slicedBuffer, frame.MaskLittleEndian);
            }

            var backlog = this.backlog;

            if (backlog == null)
            {
                var newBacklog = new List <PreservedBuffer>();
                backlog = Interlocked.CompareExchange(ref this.backlog, newBacklog, null) ?? newBacklog;
            }
            backlog.Add(slicedBuffer.Preserve());
        }
Exemple #4
0
        internal static bool TryReadFrameHeader(int inspectableBytes, Span <byte> header, ref ReadableBuffer buffer, out WebSocketsFrame frame)
        {
            bool masked = (header[1] & 128) != 0;
            int  tmp = header[1] & 127;
            int  headerLength, maskOffset, payloadLength;

            switch (tmp)
            {
            case 126:
                headerLength = masked ? 8 : 4;
                if (inspectableBytes < headerLength)
                {
                    frame = default(WebSocketsFrame);
                    return(false);
                }
                payloadLength = (header[2] << 8) | header[3];
                maskOffset    = 4;
                break;

            case 127:
                headerLength = masked ? 14 : 10;
                if (inspectableBytes < headerLength)
                {
                    frame = default(WebSocketsFrame);
                    return(false);
                }
                int big = header.Slice(2).ReadBigEndian <int>(), little = header.Slice(6).ReadBigEndian <int>();
                if (big != 0 || little < 0)
                {
                    throw new ArgumentOutOfRangeException();                             // seriously, we're not going > 2GB
                }
                payloadLength = little;
                maskOffset    = 10;
                break;

            default:
                headerLength = masked ? 6 : 2;
                if (inspectableBytes < headerLength)
                {
                    frame = default(WebSocketsFrame);
                    return(false);
                }
                payloadLength = tmp;
                maskOffset    = 2;
                break;
            }
            if (buffer.Length < headerLength + payloadLength)
            {
                frame = default(WebSocketsFrame);
                return(false); // frame (header+body) isn't intact
            }


            frame = new WebSocketsFrame(header[0], masked,
                                        masked ? header.Slice(maskOffset).ReadLittleEndian <int>() : 0,
                                        payloadLength);
            buffer = buffer.Slice(headerLength); // header is fully consumed now
            return(true);
        }
Exemple #5
0
 private void ApplyMask()
 {
     if (_mask != 0)
     {
         WebSocketsFrame.ApplyMask(ref _buffer, _mask);
         _mask = 0;
     }
 }
        internal Task OnFrameReceivedAsync(ref WebSocketsFrame frame, ref ReadableBuffer buffer, WebSocketServer server)
        {
            WebSocketServer.WriteStatus(ConnectionType, frame.ToString());


            // note that this call updates the connection state; must be called, even
            // if we don't get as far as the 'switch'
            var opCode = GetEffectiveOpCode(ref frame);

            Message msg;

            if (!frame.IsControlFrame)
            {
                if (frame.IsFinal)
                {
                    if (this.HasBacklog)
                    {
                        try
                        {
                            // add our data to the existing backlog
                            this.AddBacklog(ref buffer, ref frame);

                            // use the backlog buffer to execute the method; note that
                            // we un-masked *while adding*; don't need mask here
                            msg = new Message(this.GetBacklog());
                            if (server != null)
                            {
                                return(OnServerAndConnectionFrameReceivedImplAsync(opCode, msg, server));
                            }
                            else
                            {
                                return(OnConectionFrameReceivedImplAsync(opCode, ref msg));
                            }
                        }
                        finally
                        {
                            // and release the backlog
                            this.ClearBacklog();
                        }
                    }
                }
                else if (BufferFragments)
                {
                    // need to buffer this data against the connection
                    this.AddBacklog(ref buffer, ref frame);
                    return(Task.CompletedTask);
                }
            }
            msg = new Message(buffer.Slice(0, frame.PayloadLength), frame.MaskLittleEndian, frame.IsFinal);
            if (server != null)
            {
                return(OnServerAndConnectionFrameReceivedImplAsync(opCode, msg, server));
            }
            else
            {
                return(OnConectionFrameReceivedImplAsync(opCode, ref msg));
            }
        }
        internal WebSocketsFrame.OpCodes GetEffectiveOpCode(ref WebSocketsFrame frame)
        {
            if (frame.IsControlFrame)
            {
                // doesn't change state
                return(frame.OpCode);
            }

            var frameOpCode = frame.OpCode;

            // re-use the previous opcode if we are a continuation
            var result = frameOpCode == WebSocketsFrame.OpCodes.Continuation ? lastOpCode : frameOpCode;

            // if final, clear the opcode; otherwise: use what we thought of
            lastOpCode = frame.IsFinal ? WebSocketsFrame.OpCodes.Continuation : result;
            return(result);
        }