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));
 }