private async Task <WebSocketReceiveResult> HandleReceivedCloseAsync(CompressionWebSocketMessageHeader header, CancellationToken cancellationToken) { lock (StateUpdateLock) { _receivedCloseFrame = true; if (_state < WebSocketState.CloseReceived) { _state = WebSocketState.CloseReceived; } } WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure; string closeStatusDescription = string.Empty; if (header.PayloadLength == 1) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); } else if (header.PayloadLength >= 2) { if (_receiveBufferCount < header.PayloadLength) { await EnsureBufferContainsAsync((int)header.PayloadLength, cancellationToken).ConfigureAwait(false); } ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength); closeStatus = (WebSocketCloseStatus)(_receiveBuffer[_receiveBufferOffset] << 8 | _receiveBuffer[_receiveBufferOffset + 1]); if (!IsValidCloseStatus(closeStatus)) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); } if (header.PayloadLength > 2) { try { closeStatusDescription = _textEncoding.GetString(_receiveBuffer, _receiveBufferOffset + 2, (int)header.PayloadLength - 2); } catch (DecoderFallbackException exc) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken, exc).ConfigureAwait(false); } } ConsumeFromBuffer((int)header.PayloadLength); } _closeStatus = closeStatus; _closeStatusDescription = closeStatusDescription; return(new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, closeStatus, closeStatusDescription)); }
private async Task HandleReceivedPingPongAsync(CompressionWebSocketMessageHeader header, CancellationToken cancellationToken) { if (header.PayloadLength > 0 && _receiveBufferCount < header.PayloadLength) { await EnsureBufferContainsAsync((int)header.PayloadLength, cancellationToken).ConfigureAwait(false); } if (header.Opcode == WebSocketMessageOpcode.Ping) { ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength); await SendFrameAsync(WebSocketMessageOpcode.Pong, false, true, new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, (int)header.PayloadLength), cancellationToken).ConfigureAwait(false); } if (header.PayloadLength > 0) { ConsumeFromBuffer((int)header.PayloadLength); } }
private bool TryParseMessageHeaderFromReceiveBuffer(out CompressionWebSocketMessageHeader resultHeader) { var header = new CompressionWebSocketMessageHeader(); header.Fin = (_receiveBuffer[_receiveBufferOffset] & 0x80) != 0; header.Compressed = (_receiveBuffer[_receiveBufferOffset] & 0x40) != 0; bool reservedSet = (_receiveBuffer[_receiveBufferOffset] & 0x70) != 0; bool reservedExceptCompressedSet = (_receiveBuffer[_receiveBufferOffset] & 0x30) != 0; header.Opcode = (WebSocketMessageOpcode)(_receiveBuffer[_receiveBufferOffset] & 0xF); bool masked = (_receiveBuffer[_receiveBufferOffset + 1] & 0x80) != 0; header.PayloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F; ConsumeFromBuffer(2); if (header.PayloadLength == 126) { header.PayloadLength = (_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1]; ConsumeFromBuffer(2); } else if (header.PayloadLength == 127) { header.PayloadLength = 0; for (int i = 0; i < 8; i++) { header.PayloadLength = (header.PayloadLength << 8) | _receiveBuffer[_receiveBufferOffset + i]; } ConsumeFromBuffer(8); } bool shouldFail = (!header.Compressed && reservedSet) || reservedExceptCompressedSet; if (masked) { header.Mask = BitConverter.ToInt32(_receiveBuffer, _receiveBufferOffset); ConsumeFromBuffer(4); } switch (header.Opcode) { case WebSocketMessageOpcode.Continuation: if (_lastReceiveHeader.Fin) { shouldFail = true; } break; case WebSocketMessageOpcode.Binary: case WebSocketMessageOpcode.Text: if (!_lastReceiveHeader.Fin) { shouldFail = true; } break; case WebSocketMessageOpcode.Close: case WebSocketMessageOpcode.Ping: case WebSocketMessageOpcode.Pong: if (header.PayloadLength > MAX_CONTROL_PAYLOAD_LENGTH || !header.Fin) { shouldFail = true; } break; default: shouldFail = true; break; } resultHeader = header; return(!shouldFail); }
private async Task <WebSocketReceiveResult> InternalReceiveAsync(ArraySegment <byte> payloadBuffer, CancellationToken cancellationToken) { CancellationTokenRegistration registration = cancellationToken.Register(s => ((CompressionWebSocket)s).Abort(), this); try { while (true) { CompressionWebSocketMessageHeader header = _lastReceiveHeader; if (header.PayloadLength == 0) { if (_receiveBufferCount < (MAX_MESSAGE_HEADER_LENGTH - MASK_LENGTH)) { if (_receiveBufferCount < 2) { await EnsureBufferContainsAsync(2, cancellationToken, throwOnPrematureClosure : true).ConfigureAwait(false); } long payloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F; int minNeeded = 2 + MASK_LENGTH + (payloadLength <= 125 ? 0 : payloadLength == 126 ? sizeof(ushort) : sizeof(ulong)); await EnsureBufferContainsAsync(minNeeded, cancellationToken).ConfigureAwait(false); } if (!TryParseMessageHeaderFromReceiveBuffer(out header)) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); } _receivedMaskOffsetOffset = 0; } if (header.Opcode == WebSocketMessageOpcode.Ping || header.Opcode == WebSocketMessageOpcode.Pong) { await HandleReceivedPingPongAsync(header, cancellationToken).ConfigureAwait(false); continue; } else if (header.Opcode == WebSocketMessageOpcode.Close) { return(await HandleReceivedCloseAsync(header, cancellationToken).ConfigureAwait(false)); } if (header.Opcode == WebSocketMessageOpcode.Continuation) { header.Compressed = _lastReceiveHeader.Compressed; header.Opcode = _lastReceiveHeader.Opcode; } int bytesToRead = (int)Math.Min(payloadBuffer.Count, header.PayloadLength); if (bytesToRead == 0) { _lastReceiveHeader = header; return(new WebSocketReceiveResult( 0, header.Opcode == WebSocketMessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary, header.PayloadLength == 0 ? header.Fin : false)); } if (_receiveBufferCount == 0) { await EnsureBufferContainsAsync(1, cancellationToken, throwOnPrematureClosure : false).ConfigureAwait(false); } int bytesToCopy = Math.Min(bytesToRead, _receiveBufferCount); _receivedMaskOffsetOffset = ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, _receivedMaskOffsetOffset, bytesToCopy); Buffer.BlockCopy(_receiveBuffer, _receiveBufferOffset, payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy); ConsumeFromBuffer(bytesToCopy); header.PayloadLength -= bytesToCopy; if ((header.Opcode == WebSocketMessageOpcode.Text) && !header.Compressed && !TryValidateUtf8(new ArraySegment <byte>(payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy), header.Fin, _utf8TextState)) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.InvalidPayloadData, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); } _lastReceiveHeader = header; return(new CompressionWebSocketReceiveResult( bytesToCopy, header.Opcode == WebSocketMessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary, header.Compressed, bytesToCopy == 0 || (header.Fin && header.PayloadLength == 0))); } } catch (Exception ex) { throw _state == WebSocketState.Aborted ? new WebSocketException(WebSocketError.InvalidState, "Invalid state.", ex) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, ex); } finally { registration.Dispose(); } }