Пример #1
0
 public byte[] EncodeFrame(PongFrame frame)
 {
     if (!string.IsNullOrEmpty(frame.Data))
     {
         var data = Encoding.UTF8.GetBytes(frame.Data);
         if (data.Length > 125)
             throw new WebSocketException("All control frames must have a payload length of 125 bytes or less.");
         return Encode(frame.OpCode, data, 0, data.Length, isMasked: frame.IsMasked);
     }
     else
     {
         return Encode(frame.OpCode, EmptyArray, 0, 0, isMasked: frame.IsMasked);
     }
 }
Пример #2
0
        private async Task Process()
        {
            try
            {
                while (State == WebSocketState.Open || State == WebSocketState.Closing)
                {
                    int receiveCount = await _stream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length);
                    if (receiveCount == 0)
                        break;

                    _keepAliveTracker.OnDataReceived();
                    BufferDeflector.AppendBuffer(_bufferManager, ref _receiveBuffer, receiveCount, ref _sessionBuffer, ref _sessionBufferCount);

                    while (true)
                    {
                        Header frameHeader = null;
                        if (_frameBuilder.TryDecodeFrameHeader(_sessionBuffer, _sessionBufferCount, out frameHeader)
                            && frameHeader.Length + frameHeader.PayloadLength <= _sessionBufferCount)
                        {
                            try
                            {
                                if (!frameHeader.IsMasked)
                                {
                                    await Close(WebSocketCloseCode.ProtocolError, "A server MUST close the connection upon receiving a frame that is not masked.");
                                    throw new WebSocketException(string.Format(
                                        "Server received unmasked frame [{0}] from remote [{1}].", frameHeader.OpCode, RemoteEndPoint));
                                }

                                byte[] payload;
                                int payloadOffset;
                                int payloadCount;
                                _frameBuilder.DecodePayload(_sessionBuffer, frameHeader, out payload, out payloadOffset, out payloadCount);

                                switch (frameHeader.OpCode)
                                {
                                    case OpCode.Continuation:
                                        {
                                            if (!frameHeader.IsFIN)
                                            {
                                                try
                                                {
                                                    await _module.OnSessionFragmentationStreamContinued(this, payload, payloadOffset, payloadCount);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                            else
                                            {
                                                try
                                                {
                                                    await _module.OnSessionFragmentationStreamClosed(this, payload, payloadOffset, payloadCount);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                        }
                                        break;
                                    case OpCode.Text:
                                        {
                                            if (frameHeader.IsFIN)
                                            {
                                                try
                                                {
                                                    var text = Encoding.UTF8.GetString(payload, payloadOffset, payloadCount);
                                                    await _module.OnSessionTextReceived(this, text);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                            else
                                            {
                                                try
                                                {
                                                    await _module.OnSessionFragmentationStreamOpened(this, payload, payloadOffset, payloadCount);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                        }
                                        break;
                                    case OpCode.Binary:
                                        {
                                            if (frameHeader.IsFIN)
                                            {
                                                try
                                                {
                                                    await _module.OnSessionBinaryReceived(this, payload, payloadOffset, payloadCount);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                            else
                                            {
                                                try
                                                {
                                                    await _module.OnSessionFragmentationStreamOpened(this, payload, payloadOffset, payloadCount);
                                                }
                                                catch (Exception ex)
                                                {
                                                    HandleUserSideError(ex);
                                                }
                                            }
                                        }
                                        break;
                                    case OpCode.Close:
                                        {
                                            if (!frameHeader.IsFIN)
                                            {
                                                throw new WebSocketException(string.Format(
                                                    "Server received unfinished frame [{0}] from remote [{1}].", frameHeader.OpCode, RemoteEndPoint));
                                            }

                                            if (payloadCount > 1)
                                            {
                                                var statusCode = payload[0] * 256 + payload[1];
                                                var closeCode = (WebSocketCloseCode)statusCode;
                                                var closeReason = string.Empty;

                                                if (payloadCount > 2)
                                                {
                                                    closeReason = Encoding.UTF8.GetString(payload, 2, payloadCount - 2);
                                                }
#if DEBUG
                                                _log.DebugFormat("Session [{0}] received client side close frame [{1}] [{2}].", this, closeCode, closeReason);
#endif
                                                // If an endpoint receives a Close frame and did not previously send a
                                                // Close frame, the endpoint MUST send a Close frame in response.  (When
                                                // sending a Close frame in response, the endpoint typically echos the
                                                // status code it received.)  It SHOULD do so as soon as practical.
                                                await Close(closeCode, closeReason);
                                            }
                                            else
                                            {
#if DEBUG
                                                _log.DebugFormat("Session [{0}] received client side close frame but no status code.", this);
#endif
                                                await Close(WebSocketCloseCode.InvalidPayloadData);
                                            }
                                        }
                                        break;
                                    case OpCode.Ping:
                                        {
                                            if (!frameHeader.IsFIN)
                                            {
                                                throw new WebSocketException(string.Format(
                                                    "Server received unfinished frame [{0}] from remote [{1}].", frameHeader.OpCode, RemoteEndPoint));
                                            }

                                            // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
                                            // response, unless it already received a Close frame.  It SHOULD
                                            // respond with Pong frame as soon as is practical.  Pong frames are
                                            // discussed in Section 5.5.3.
                                            // 
                                            // An endpoint MAY send a Ping frame any time after the connection is
                                            // established and before the connection is closed.
                                            // 
                                            // A Ping frame may serve either as a keep-alive or as a means to
                                            // verify that the remote endpoint is still responsive.
                                            var ping = Encoding.UTF8.GetString(payload, payloadOffset, payloadCount);
#if DEBUG
                                            _log.DebugFormat("Session [{0}] received client side ping frame [{1}].", this, ping);
#endif
                                            if (State == WebSocketState.Open)
                                            {
                                                // A Pong frame sent in response to a Ping frame must have identical
                                                // "Application data" as found in the message body of the Ping frame being replied to.
                                                var pong = new PongFrame(ping, false).ToArray(_frameBuilder);
                                                await SendFrame(pong);
#if DEBUG
                                                _log.DebugFormat("Session [{0}] sends server side pong frame [{1}].", this, ping);
#endif
                                            }
                                        }
                                        break;
                                    case OpCode.Pong:
                                        {
                                            if (!frameHeader.IsFIN)
                                            {
                                                throw new WebSocketException(string.Format(
                                                    "Server received unfinished frame [{0}] from remote [{1}].", frameHeader.OpCode, RemoteEndPoint));
                                            }

                                            // If an endpoint receives a Ping frame and has not yet sent Pong
                                            // frame(s) in response to previous Ping frame(s), the endpoint MAY
                                            // elect to send a Pong frame for only the most recently processed Ping frame.
                                            // 
                                            // A Pong frame MAY be sent unsolicited.  This serves as a
                                            // unidirectional heartbeat.  A response to an unsolicited Pong frame is not expected.
                                            var pong = Encoding.UTF8.GetString(payload, payloadOffset, payloadCount);
                                            StopKeepAliveTimeoutTimer();
#if DEBUG
                                            _log.DebugFormat("Session [{0}] received client side pong frame [{1}].", this, pong);
#endif
                                        }
                                        break;
                                    default:
                                        {
                                            // Incoming data MUST always be validated by both clients and servers.
                                            // If, at any time, an endpoint is faced with data that it does not
                                            // understand or that violates some criteria by which the endpoint
                                            // determines safety of input, or when the endpoint sees an opening
                                            // handshake that does not correspond to the values it is expecting
                                            // (e.g., incorrect path or origin in the client request), the endpoint
                                            // MAY drop the TCP connection.  If the invalid data was received after
                                            // a successful WebSocket handshake, the endpoint SHOULD send a Close
                                            // frame with an appropriate status code (Section 7.4) before proceeding
                                            // to _Close the WebSocket Connection_.  Use of a Close frame with an
                                            // appropriate status code can help in diagnosing the problem.  If the
                                            // invalid data is sent during the WebSocket handshake, the server
                                            // SHOULD return an appropriate HTTP [RFC2616] status code.
                                            await Close(WebSocketCloseCode.InvalidMessageType);
                                            throw new NotSupportedException(
                                                string.Format("Not support received opcode [{0}].", (byte)frameHeader.OpCode));
                                        }
                                }
                            }
                            catch (Exception ex)
                            {
                                _log.Error(string.Format("Session [{0}] exception occurred, [{1}].", this, ex.Message), ex);
                                throw;
                            }

                            BufferDeflector.ShiftBuffer(_bufferManager, frameHeader.Length + frameHeader.PayloadLength, ref _sessionBuffer, ref _sessionBufferCount);
#if DEBUG
                            _log.DebugFormat("Session [{0}] buffer length [{1}].", this, _sessionBufferCount);
#endif
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            catch (Exception ex) when (!ShouldThrow(ex)) { }
            finally
            {
                await Abort();
            }
        }
Пример #3
0
        private async Task HandlePingFrame(Header frameHeader, byte[] payload, int payloadOffset, int payloadCount)
        {
            if (!frameHeader.IsFIN)
            {
                throw new WebSocketException(string.Format(
                    "Server received unfinished frame [{0}] from remote [{1}].", frameHeader.OpCode, RemoteEndPoint));
            }

            // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
            // response, unless it already received a Close frame.  It SHOULD
            // respond with Pong frame as soon as is practical.  Pong frames are
            // discussed in Section 5.5.3.
            // 
            // An endpoint MAY send a Ping frame any time after the connection is
            // established and before the connection is closed.
            // 
            // A Ping frame may serve either as a keep-alive or as a means to
            // verify that the remote endpoint is still responsive.
            var ping = Encoding.UTF8.GetString(payload, payloadOffset, payloadCount);
#if DEBUG
            _log.DebugFormat("Session [{0}] received client side ping frame [{1}].", this, ping);
#endif
            if (State == WebSocketState.Open)
            {
                // A Pong frame sent in response to a Ping frame must have identical
                // "Application data" as found in the message body of the Ping frame being replied to.
                var pong = new PongFrame(ping, false).ToArray(_frameBuilder);
                await SendFrame(pong);
#if DEBUG
                _log.DebugFormat("Session [{0}] sends server side pong frame [{1}].", this, ping);
#endif
            }
        }