public Task SendAsync(ArraySegment <byte> buffer, WebSocketMessageType messageType, bool compressed, bool endOfMessage, CancellationToken cancellationToken) { if (messageType != WebSocketMessageType.Text && messageType != WebSocketMessageType.Binary) { throw new ArgumentException("Invalid message type", nameof(messageType)); } WebSocketValidationHelper.ValidateArraySegment(buffer, nameof(buffer)); try { WebSocketValidationHelper.ThrowIfInvalidState(_state, _disposed, _validSendStates); ThrowIfOperationInProgress(_lastSendTask); } catch (Exception ex) { return(TargetFrameworksHelper.FromException(ex)); } WebSocketMessageOpcode opcode = _lastSendWasFragment ? WebSocketMessageOpcode.Continuation : (messageType == WebSocketMessageType.Binary ? WebSocketMessageOpcode.Binary : WebSocketMessageOpcode.Text); Task sendTask = SendFrameAsync(opcode, compressed, endOfMessage, buffer, cancellationToken); _lastSendWasFragment = !endOfMessage; _lastSendTask = sendTask; return(sendTask); }
private int WriteFrameToSendBuffer(WebSocketMessageOpcode opcode, bool compressed, bool endOfMessage, ArraySegment <byte> payloadBuffer) { EnsureBufferLength(ref _sendBuffer, payloadBuffer.Count + MAX_MESSAGE_HEADER_LENGTH); int headerLength = WriteHeader(opcode, _sendBuffer, payloadBuffer, compressed, endOfMessage); if (payloadBuffer.Count > 0) { Buffer.BlockCopy(payloadBuffer.Array, payloadBuffer.Offset, _sendBuffer, headerLength, payloadBuffer.Count); } return(headerLength + payloadBuffer.Count); }
private Task SendFrameLockAcquiredNonCancelableAsync(WebSocketMessageOpcode opcode, bool compressed, bool endOfMessage, ArraySegment <byte> payloadBuffer) { Task writeTask = null; bool releaseSemaphore = true; try { int sendBytes = WriteFrameToSendBuffer(opcode, compressed, endOfMessage, payloadBuffer); writeTask = _stream.WriteAsync(_sendBuffer, 0, sendBytes, CancellationToken.None); if (writeTask.IsCompleted) { writeTask.GetAwaiter().GetResult(); return(TargetFrameworksHelper.CompletedTask); } releaseSemaphore = false; } catch (Exception ex) { return(TargetFrameworksHelper.FromException(_state == WebSocketState.Aborted ? CreateOperationCanceledException(ex) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, ex))); } finally { if (releaseSemaphore) { _sendFrameAsyncLock.Release(); } } return(writeTask.ContinueWith((task, source) => { CompressionWebSocket webSocket = (CompressionWebSocket)source; webSocket._sendFrameAsyncLock.Release(); try { task.GetAwaiter().GetResult(); } catch (Exception ex) { throw webSocket._state == WebSocketState.Aborted ? CreateOperationCanceledException(ex) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, ex); } }, this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }
private async Task SendFrameFallbackAsync(WebSocketMessageOpcode opcode, bool compressed, bool endOfMessage, ArraySegment <byte> payloadBuffer, CancellationToken cancellationToken) { await _sendFrameAsyncLock.WaitAsync().ConfigureAwait(false); try { int sendBytes = WriteFrameToSendBuffer(opcode, compressed, endOfMessage, payloadBuffer); using (cancellationToken.Register(source => ((CompressionWebSocket)source).Abort(), this)) { await _stream.WriteAsync(_sendBuffer, 0, sendBytes, cancellationToken).ConfigureAwait(false); } } catch (Exception exc) { throw _state == WebSocketState.Aborted ? CreateOperationCanceledException(exc, cancellationToken) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, exc); } finally { _sendFrameAsyncLock.Release(); } }
private static int WriteHeader(WebSocketMessageOpcode opcode, byte[] sendBuffer, ArraySegment <byte> payload, bool compressed, bool endOfMessage) { // Client header format: // 1 bit - FIN - 1 if this is the final fragment in the message (it could be the only fragment), otherwise 0 // 1 bit - RSV1 - Per-Message Compressed - 1 if this is the first fragment of compressed message, otherwise 0 // 1 bit - RSV2 - Reserved - 0 // 1 bit - RSV3 - Reserved - 0 // 4 bits - Opcode - How to interpret the payload // - 0x0 - continuation // - 0x1 - text // - 0x2 - binary // - 0x8 - connection close // - 0x9 - ping // - 0xA - pong // - (0x3 to 0x7, 0xB-0xF - reserved) // 1 bit - Masked - 1 if the payload is masked, 0 if it's not. Must be 1 for the client. This is only server and we use always 0 // 7 bits, 7+16 bits, or 7+64 bits - Payload length // - For length 0 through 125, 7 bits storing the length // - For lengths 126 through 2^16, 7 bits storing the value 126, followed by 16 bits storing the length // - For lengths 2^16+1 through 2^64, 7 bits storing the value 127, followed by 64 bytes storing the length // 0 or 4 bytes - Mask, if Masked is 1 - random value XOR'd with each 4 bytes of the payload, round-robin // Length bytes - Payload data sendBuffer[0] = (byte)opcode; if (compressed && (opcode != WebSocketMessageOpcode.Continuation)) { sendBuffer[0] |= 0x40; } if (endOfMessage) { sendBuffer[0] |= 0x80; } int headerLength; if (payload.Count <= 125) { sendBuffer[1] = (byte)payload.Count; headerLength = 2; } else if (payload.Count <= ushort.MaxValue) { sendBuffer[1] = 126; sendBuffer[2] = (byte)(payload.Count / 256); sendBuffer[3] = (byte)payload.Count; headerLength = 2 + sizeof(ushort); } else { sendBuffer[1] = 127; int length = payload.Count; for (int i = 9; i >= 2; i--) { sendBuffer[i] = (byte)length; length = length / 256; } headerLength = 2 + sizeof(ulong); } return(headerLength); }
private Task SendFrameAsync(WebSocketMessageOpcode opcode, bool compressed, bool endOfMessage, ArraySegment <byte> payloadBuffer, CancellationToken cancellationToken) { return(cancellationToken.CanBeCanceled || !_sendFrameAsyncLock.Wait(0) ? SendFrameFallbackAsync(opcode, compressed, endOfMessage, payloadBuffer, cancellationToken) : SendFrameLockAcquiredNonCancelableAsync(opcode, compressed, endOfMessage, payloadBuffer)); }