// We received a ping, send a pong in reply private async Task SendPongReplyAsync(CancellationToken cancellationToken) { await _writeLock.WaitAsync(cancellationToken); try { if (State != WebSocketState.Open) { // Output closed, discard the pong. return; } ArraySegment <byte> dataSegment = new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, (int)_frameBytesRemaining); if (_unmaskInput) { // _frameInProgress.Masked == _unmaskInput already verified Utilities.MaskInPlace(_frameInProgress.MaskKey, dataSegment); } int mask = GetNextMask(); FrameHeader header = new FrameHeader(true, Constants.OpCodes.PongFrame, _maskOutput, mask, _frameBytesRemaining); if (_maskOutput) { Utilities.MaskInPlace(mask, dataSegment); } ArraySegment <byte> headerSegment = header.Buffer; await _stream.WriteAsync(headerSegment.Array, headerSegment.Offset, headerSegment.Count, cancellationToken); await _stream.WriteAsync(dataSegment.Array, dataSegment.Offset, dataSegment.Count, cancellationToken); } finally { _writeLock.Release(); } }
private async Task ReadNextFrameAsync(CancellationToken cancellationToken) { await EnsureDataAvailableOrReadAsync(2, cancellationToken); int frameHeaderSize = FrameHeader.CalculateFrameHeaderSize(_receiveBuffer[_receiveBufferOffset + 1]); await EnsureDataAvailableOrReadAsync(frameHeaderSize, cancellationToken); _frameInProgress = new FrameHeader(new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, frameHeaderSize)); _receiveBufferOffset += frameHeaderSize; _receiveBufferBytes -= frameHeaderSize; _frameBytesRemaining = _frameInProgress.DataLength; if (_frameInProgress.AreReservedSet()) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Unexpected reserved bits set", cancellationToken); } if (_unmaskInput != _frameInProgress.Masked) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Incorrect masking", cancellationToken); } if (!ValidateOpCode(_frameInProgress.OpCode)) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid opcode: " + _frameInProgress.OpCode, cancellationToken); } if (_frameInProgress.IsControlFrame) { if (_frameBytesRemaining > 125) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid control frame size", cancellationToken); } if (!_frameInProgress.Fin) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Fragmented control frame", cancellationToken); } if (_frameInProgress.OpCode == Constants.OpCodes.PingFrame || _frameInProgress.OpCode == Constants.OpCodes.PongFrame) { // Drain it, should be less than 125 bytes await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, cancellationToken); if (_frameInProgress.OpCode == Constants.OpCodes.PingFrame) { await SendPongReplyAsync(cancellationToken); } _receiveBufferOffset += (int)_frameBytesRemaining; _receiveBufferBytes -= (int)_frameBytesRemaining; _frameBytesRemaining = 0; _frameInProgress = null; } } else if (_firstDataOpCode.HasValue && _frameInProgress.OpCode != Constants.OpCodes.ContinuationFrame) { // A data frame is already in progress, but this new frame is not a continuation frame. await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Expected a continuation frame: " + _frameInProgress.OpCode, cancellationToken); } }
public async override Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken) { ThrowIfDisposed(); ThrowIfInputClosed(); ValidateSegment(buffer); // TODO: InvalidOperationException if any receives are currently in progress. // No active frame. Loop because we may be discarding ping/pong frames. while (_frameInProgress == null) { await ReadNextFrameAsync(cancellationToken); } int opCode = _frameInProgress.OpCode; if (opCode == Constants.OpCodes.CloseFrame) { return(await ProcessCloseFrameAsync(cancellationToken)); } // Handle fragmentation, remember the first frame type if (opCode == Constants.OpCodes.ContinuationFrame) { if (!_firstDataOpCode.HasValue) { await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid continuation frame", cancellationToken); } opCode = _firstDataOpCode.Value; } else { _firstDataOpCode = opCode; } // Make sure there's at least some data in the buffer int bytesToBuffer = (int)Math.Min((long)_receiveBuffer.Length, _frameBytesRemaining); await EnsureDataAvailableOrReadAsync(bytesToBuffer, cancellationToken); // Copy buffered data to the users buffer int bytesToRead = (int)Math.Min((long)buffer.Count, _frameBytesRemaining); int bytesToCopy = Math.Min(bytesToRead, _receiveBufferBytes); Array.Copy(_receiveBuffer, _receiveBufferOffset, buffer.Array, buffer.Offset, bytesToCopy); if (_unmaskInput) { // _frameInProgress.Masked == _unmaskInput already verified Utilities.MaskInPlace(_frameInProgress.MaskKey, ref _dataUnmaskOffset, new ArraySegment <byte>(buffer.Array, buffer.Offset, bytesToCopy)); } WebSocketReceiveResult result; WebSocketMessageType messageType = Utilities.GetMessageType(opCode); if (messageType == WebSocketMessageType.Text && !Utilities.TryValidateUtf8(new ArraySegment <byte>(buffer.Array, buffer.Offset, bytesToCopy), _frameInProgress.Fin, _incomingUtf8MessageState)) { await SendErrorAbortAndThrow(WebSocketCloseStatus.InvalidPayloadData, "Invalid UTF-8", cancellationToken); } if (bytesToCopy == _frameBytesRemaining) { result = new WebSocketReceiveResult(bytesToCopy, messageType, _frameInProgress.Fin); if (_frameInProgress.Fin) { _firstDataOpCode = null; } _frameInProgress = null; _dataUnmaskOffset = 0; } else { result = new WebSocketReceiveResult(bytesToCopy, messageType, false); } _frameBytesRemaining -= bytesToCopy; _receiveBufferBytes -= bytesToCopy; _receiveBufferOffset += bytesToCopy; return(result); }