Ejemplo n.º 1
0
 public static void Sync(Action action) => TaskUtil.Sync(action);
Ejemplo n.º 2
0
 public static T Sync <T>(Func <T> func) => TaskUtil.Sync(func);
Ejemplo n.º 3
0
    async Task RecvLoopAsync()
    {
        try
        {
            CancellationToken cancel = this.GrandCancel;

            LowerStream.ReadTimeout = Options.TimeoutComm;

            MemoryBuffer <byte> currentRecvingMessage = new MemoryBuffer <byte>();

            while (true)
            {
                byte b1 = await LowerStream.ReceiveByteAsync(cancel).FlushOtherStreamIfPending(UpperStream);

                WebSocketOpcode opcode = (WebSocketOpcode)(b1 & 0x0f);

                byte b2 = await LowerStream.ReceiveByteAsync(cancel);

                bool isMasked = (b2 & 0b10000000)._ToBool();
                int  tmp      = (b2 & 0b01111111);

                ulong payloadLen64 = 0;
                if (tmp <= 125)
                {
                    payloadLen64 = (ulong)tmp;
                }
                else if (tmp == 126)
                {
                    payloadLen64 = await LowerStream.ReceiveUInt16Async(cancel).FlushOtherStreamIfPending(UpperStream);
                }
                else if (tmp == 127)
                {
                    payloadLen64 = await LowerStream.ReceiveUInt64Async(cancel).FlushOtherStreamIfPending(UpperStream);
                }

                if (payloadLen64 > (ulong)Options.RecvMaxPayloadLenPerFrame)
                {
                    throw new ApplicationException($"payloadLen64 {payloadLen64} > Options.RecvMaxPayloadLenPerFrame {Options.RecvMaxPayloadLenPerFrame}");
                }

                int payloadLen = (int)payloadLen64;

                Memory <byte> maskKey = default;

                if (isMasked)
                {
                    maskKey = await LowerStream.ReceiveAllAsync(4, cancel).FlushOtherStreamIfPending(UpperStream);
                }

                Memory <byte> data = await LowerStream.ReceiveAllAsync(payloadLen, cancel).FlushOtherStreamIfPending(UpperStream);

                if (isMasked)
                {
                    TaskUtil.Sync(() =>
                    {
                        Span <byte> maskKeySpan = maskKey.Span;
                        Span <byte> dataSpan    = data.Span;
                        for (int i = 0; i < dataSpan.Length; i++)
                        {
                            dataSpan[i] = (byte)(dataSpan[i] ^ maskKeySpan[i % 4]);
                        }
                    });
                }

                if (opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin, WebSocketOpcode.Continue))
                {
                    if (Options.RespectMessageDelimiter == false)
                    {
                        if (data.Length >= 1)
                        {
                            await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite);

                            UpperStream.FastSendNonBlock(data, false);
                        }
                    }
                    else
                    {
                        bool isFin = (b1 & 0b10000000)._ToBool();

                        if (isFin && opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin))
                        {
                            // Single message
                            if (data.Length >= 1)
                            {
                                await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite);

                                UpperStream.FastSendNonBlock(data, false);
                            }
                        }
                        else if (isFin == false && opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin))
                        {
                            // First message
                            currentRecvingMessage.Clear();

                            if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize)
                            {
                                throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize.");
                            }

                            currentRecvingMessage.Write(data);
                        }
                        else if (isFin && opcode == WebSocketOpcode.Continue)
                        {
                            // Final continuous message
                            if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize)
                            {
                                throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize.");
                            }

                            currentRecvingMessage.Write(data);

                            if (currentRecvingMessage.Length >= 1)
                            {
                                await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite);

                                UpperStream.FastSendNonBlock(data, false);
                            }

                            currentRecvingMessage.Clear();
                        }
                        else if (isFin == false && opcode == WebSocketOpcode.Continue)
                        {
                            // Intermediate continuous message
                            if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize)
                            {
                                throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize.");
                            }

                            currentRecvingMessage.Write(data);
                        }
                    }
                }
                else if (opcode == WebSocketOpcode.Pong)
                {
                    lock (this.PongQueueLock)
                    {
                        this.PongQueue.Enqueue(data);
                    }
                    this.SendPongEvent.Set(true);
                }
                else if (opcode == WebSocketOpcode.Ping)
                {
                    lock (this.PongQueueLock)
                    {
                        this.PongQueue.Enqueue(data);
                    }
                    this.SendPongEvent.Set(true);
                }
                else if (opcode == WebSocketOpcode.Close)
                {
                    throw new DisconnectedException();
                }
                else
                {
                    throw new ApplicationException($"WebSocket: Unknown Opcode: {(int)opcode}");
                }
            }
        }
        catch (Exception ex)
        {
            this.UpperStream.Disconnect();
            await this.CancelAsync(ex);
        }
    }