Пример #1
0
        internal WebSocketImplementation(Guid guid, Func <MemoryStream> recycledStreamFactory, System.IO.Stream stream,
                                         TimeSpan keepAliveInterval, string secWebSocketExtensions, bool includeExceptionInCloseResponse,
                                         bool isClient, string subProtocol)
        {
            _guid = guid;
            _recycledStreamFactory = recycledStreamFactory;
            _stream          = stream;
            _isClient        = isClient;
            _subProtocol     = subProtocol;
            _internalReadCts = new CancellationTokenSource();
            _state           = WebSocketState.Open;
            _readCursor      = new WebSocketReadCursor(null, 0, 0);

            if (secWebSocketExtensions?.IndexOf("permessage-deflate") >= 0)
            {
                _usePerMessageDeflate = true;
            }

            KeepAliveInterval = keepAliveInterval;
            _includeExceptionInCloseResponse = includeExceptionInCloseResponse;
            if (keepAliveInterval.Ticks < 0)
            {
                throw new InvalidOperationException("KeepAliveInterval must be Zero or positive");
            }

            if (keepAliveInterval != TimeSpan.Zero)
            {
                _pingPongManager = new PingPongManager(guid, this, keepAliveInterval, _internalReadCts.Token);
            }
        }
Пример #2
0
        /// <summary>
        /// The last read could not be completed because the read buffer was too small.
        /// We need to continue reading bytes off the stream.
        /// Not to be confused with a continuation frame
        /// </summary>
        /// <param name="fromStream">The stream to read from</param>
        /// <param name="intoBuffer">The buffer to read into</param>
        /// <param name="readCursor">The previous partial websocket frame read plus cursor information</param>
        /// <param name="cancellationToken">the cancellation token</param>
        /// <returns>A websocket frame</returns>
        public static async Task <WebSocketReadCursor> ReadFromCursorAsync(System.IO.Stream fromStream,
                                                                           ArraySegment <byte> intoBuffer, WebSocketReadCursor readCursor, CancellationToken cancellationToken)
        {
            var remainingFrame = readCursor.WebSocketFrame;
            var minCount       = CalculateNumBytesToRead(readCursor.NumBytesLeftToRead, intoBuffer.Count);
            await BinaryReaderWriter.ReadExactly(minCount, fromStream, intoBuffer, cancellationToken);

            if (remainingFrame.MaskKey.Count > 0)
            {
                ArraySegment <byte> payloadToMask =
                    new ArraySegment <byte>(intoBuffer.Array, intoBuffer.Offset, minCount);
                WebSocketFrameCommon.ToggleMask(remainingFrame.MaskKey, payloadToMask);
            }

            return(new WebSocketReadCursor(remainingFrame, minCount, readCursor.NumBytesLeftToRead - minCount));
        }
Пример #3
0
        /// <summary>
        /// Receive web socket result
        /// </summary>
        /// <param name="buffer">The buffer to copy data into</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <returns>The web socket result details</returns>
        public override async Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer,
                                                                         CancellationToken cancellationToken)
        {
            try
            {
                // we may receive control frames so reading needs to happen in an infinite loop
                while (true)
                {
                    // allow this operation to be cancelled from iniside OR outside this instance
                    using (CancellationTokenSource linkedCts =
                               CancellationTokenSource.CreateLinkedTokenSource(_internalReadCts.Token, cancellationToken))
                    {
                        WebSocketFrame frame;

                        try
                        {
                            if (_readCursor.NumBytesLeftToRead > 0)
                            {
                                // If the buffer used to read the frame was too small to fit the whole frame then we need to "remember" this frame
                                // and return what we have. Subsequent calls to the read function will simply continue reading off the stream without
                                // decoding the first few bytes as a websocket header.
                                _readCursor =
                                    await WebSocketFrameReader.ReadFromCursorAsync(_stream, buffer, _readCursor,
                                                                                   linkedCts.Token);

                                frame = _readCursor.WebSocketFrame;
                            }
                            else
                            {
                                _readCursor = await WebSocketFrameReader.ReadAsync(_stream, buffer, linkedCts.Token);

                                frame = _readCursor.WebSocketFrame;
                            }
                        }
                        catch (InternalBufferOverflowException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.MessageTooBig,
                                                              "Frame too large to fit in buffer. Use message fragmentation", ex);

                            throw;
                        }
                        catch (ArgumentOutOfRangeException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError,
                                                              "Payload length out of range", ex);

                            throw;
                        }
                        catch (EndOfStreamException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InvalidPayloadData,
                                                              "Unexpected end of stream encountered", ex);

                            throw;
                        }
                        catch (OperationCanceledException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.EndpointUnavailable,
                                                              "Operation cancelled", ex);

                            throw;
                        }
                        catch (Exception ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError,
                                                              "Error reading WebSocket frame", ex);

                            throw;
                        }

                        var endOfMessage = frame.IsFinBitSet && _readCursor.NumBytesLeftToRead == 0;
                        switch (frame.OpCode)
                        {
                        case WebSocketOpCode.ConnectionClose:
                            return(await RespondToCloseFrame(frame, buffer, linkedCts.Token));

                        case WebSocketOpCode.Ping:
                            ArraySegment <byte> pingPayload = new ArraySegment <byte>(buffer.Array, buffer.Offset,
                                                                                      _readCursor.NumBytesRead);
                            await SendPongAsync(pingPayload, linkedCts.Token);

                            break;

                        case WebSocketOpCode.Pong:
                            ArraySegment <byte> pongBuffer = new ArraySegment <byte>(buffer.Array,
                                                                                     _readCursor.NumBytesRead, buffer.Offset);
                            Pong?.Invoke(this, new PongEventArgs(pongBuffer));
                            break;

                        case WebSocketOpCode.TextFrame:
                            if (!frame.IsFinBitSet)
                            {
                                // continuation frames will follow, record the message type Text
                                _continuationFrameMessageType = WebSocketMessageType.Text;
                            }

                            return(new WebSocketReceiveResult(_readCursor.NumBytesRead, WebSocketMessageType.Text,
                                                              endOfMessage));

                        case WebSocketOpCode.BinaryFrame:
                            if (!frame.IsFinBitSet)
                            {
                                // continuation frames will follow, record the message type Binary
                                _continuationFrameMessageType = WebSocketMessageType.Binary;
                            }

                            return(new WebSocketReceiveResult(_readCursor.NumBytesRead, WebSocketMessageType.Binary,
                                                              endOfMessage));

                        case WebSocketOpCode.ContinuationFrame:
                            return(new WebSocketReceiveResult(_readCursor.NumBytesRead,
                                                              _continuationFrameMessageType, endOfMessage));

                        default:
                            Exception ex = new NotSupportedException($"Unknown WebSocket opcode {frame.OpCode}");
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, ex.Message, ex);

                            throw ex;
                        }
                    }
                }
            }
            catch (Exception catchAll)
            {
                // Most exceptions will be caught closer to their source to send an appropriate close message (and set the WebSocketState)
                // However, if an unhandled exception is encountered and a close message not sent then send one here
                if (_state == WebSocketState.Open)
                {
                    await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError,
                                                      "Unexpected error reading from WebSocket", catchAll);
                }

                throw;
            }
        }