internal void WriteInternal(ArraySegment <Byte> buffer, Int32 count, Boolean isCompleted, Boolean headerSent, WebSocketFrameOption option, WebSocketExtensionFlags extensionFlags)
        {
            try
            {
                var header = WebSocketFrameHeader.Create(count, isCompleted, headerSent, option, extensionFlags);
                header.ToBytes(buffer.Array, buffer.Offset - header.HeaderLength);

                if (!_writeSemaphore.Wait(_options.WebSocketSendTimeout))
                {
                    throw new WebSocketException("Write timeout");
                }
                _clientStream.Write(buffer.Array, buffer.Offset - header.HeaderLength, count + header.HeaderLength);
            }
            catch (InvalidOperationException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (IOException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (Exception ex)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
                throw new WebSocketException("Cannot write on WebSocket", ex);
            }
            finally
            {
                if (_isClosed == 0)
                {
                    SafeEnd.ReleaseSemaphore(_writeSemaphore);
                }
            }
        }
        internal async Task WriteInternalAsync(ArraySegment <byte> buffer, int count, bool isCompleted, bool headerSent, WebSocketFrameOption option, WebSocketExtensionFlags extensionFlags, CancellationToken cancel)
        {
            try
            {
                var header = WebSocketFrameHeader.Create(count, isCompleted, headerSent, option, extensionFlags);
                header.ToBytes(buffer.Array, buffer.Offset - header.HeaderLength);

                if (!await _writeSemaphore.WaitAsync(_options.WebSocketSendTimeout, cancel).ConfigureAwait(false))
                {
                    throw new WebSocketException("Write timeout");
                }
                await _clientStream.WriteAsync(buffer.Array, buffer.Offset - header.HeaderLength, count + header.HeaderLength, cancel).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                Close(WebSocketCloseReasons.GoingAway);
            }
            catch (InvalidOperationException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (IOException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (Exception ex)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
                throw new WebSocketException("Cannot write on WebSocket", ex);
            }
            finally
            {
                SafeEnd.ReleaseSemaphore(_writeSemaphore);
            }
        }
Example #3
0
        public async Task CloseAsync(WebSocketCloseReason reason)
        {
            if (Interlocked.CompareExchange(ref this.closeState, WS_STATE_CLOSE_SENT, WS_STATE_OPEN) != WS_STATE_OPEN &&
                Interlocked.CompareExchange(ref this.closeState, WS_STATE_CLOSED, WS_STATE_CLOSE_RECEIVED) != WS_STATE_CLOSE_RECEIVED)
            {
                return;
            }

            if (this.log.IsDebugEnabled)
            {
                this.log.Debug($"A close is called on websocket. Switching state to '{(this.closeState == WS_STATE_CLOSE_SENT ? "CloseSent" : "Closed")}'.");
            }

            await this.writeSemaphore.WaitAsync().ConfigureAwait(false);

            try
            {
                EndianBitConverter.UInt16CopyBytesBe((ushort)reason, this.outCloseBuffer.Array, this.outCloseBuffer.Offset);
                var messageType = (WebSocketMessageType)WebSocketFrameOption.ConnectionClose;
                var closeFrame  = this.PrepareFrame(this.outCloseBuffer, 2, true, false, messageType, WebSocketExtensionFlags.None);

                await this.SendFrameAsync(closeFrame, Timeout.InfiniteTimeSpan, SendOptions.NoLock | SendOptions.NoErrors | SendOptions.IgnoreClose, CancellationToken.None).ConfigureAwait(false);

                await this.networkConnection.FlushAsync(CancellationToken.None).ConfigureAwait(false);

                if (this.closeState >= WS_STATE_CLOSED)
                {
                    await this.networkConnection.CloseAsync().ConfigureAwait(false);
                }
            }
            catch (Exception closeError) when(closeError.Unwrap() is ThreadAbortException == false)
            {
                var closeErrorUnwrap = closeError.Unwrap();

                if (closeErrorUnwrap is IOException || closeErrorUnwrap is OperationCanceledException || closeErrorUnwrap is InvalidOperationException)
                {
                    return; // ignore common IO exceptions while closing connection
                }
                if (this.log.IsDebugEnabled)
                {
                    this.log.Debug($"({this.GetHashCode():X}) An error occurred while closing connection.", closeError.Unwrap());
                }
            }
            finally
            {
                SafeEnd.ReleaseSemaphore(this.writeSemaphore, this.log);
            }
        }
        private async Task WriteInternalAsync(ArraySegment <Byte> buffer, Int32 count, Boolean isCompleted, Boolean headerSent, WebSocketFrameOption option, WebSocketExtensionFlags extensionFlags, CancellationToken cancellation)
        {
            CancellationTokenRegistration reg = cancellation.Register(this.Close, false);

            try
            {
                var header = WebSocketFrameHeader.Create(count, isCompleted, headerSent, option, extensionFlags);
                header.ToBytes(buffer.Array, buffer.Offset - header.HeaderLength);

                if (!_writeSemaphore.Wait(_options.WebSocketSendTimeout))
                {
                    throw new WebSocketException("Write timeout");
                }
                await _clientStream.WriteAsync(buffer.Array, buffer.Offset - header.HeaderLength, count + header.HeaderLength, cancellation).ConfigureAwait(false);
            }
            catch (InvalidOperationException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (IOException)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
            }
            catch (Exception ex)
            {
                Close(WebSocketCloseReasons.UnexpectedCondition);
                throw new WebSocketException("Cannot write on WebSocket", ex);
            }
            finally
            {
                reg.Dispose();
                if (_isClosed == 0)
                {
                    SafeEnd.ReleaseSemaphore(_writeSemaphore);
                }
            }
        }
Example #5
0
        private async Task <bool> SendFrameAsync(ArraySegment <byte> frame, TimeSpan timeout, SendOptions sendOptions, CancellationToken cancellation)
        {
            var noLock      = (sendOptions & SendOptions.NoLock) == SendOptions.NoLock;
            var noError     = (sendOptions & SendOptions.NoErrors) == SendOptions.NoErrors;
            var ignoreClose = (sendOptions & SendOptions.IgnoreClose) == SendOptions.IgnoreClose;

            try
            {
                if (!ignoreClose && this.IsClosed)
                {
                    if (noError)
                    {
                        return(false);
                    }

                    throw new WebSocketException("WebSocket connection is closed.");
                }

                var lockTaken = noLock || await this.writeSemaphore.WaitAsync(timeout, cancellation).ConfigureAwait(false);

                try
                {
                    if (!lockTaken)
                    {
                        if (noError)
                        {
                            return(false);
                        }

                        throw new WebSocketException($"Write operation lock timeout ({timeout.TotalMilliseconds:F2}ms).");
                    }

                    await this.networkConnection.WriteAsync(frame.Array, frame.Offset, frame.Count, cancellation).ConfigureAwait(false);

                    return(true);
                }
                finally
                {
                    if (lockTaken && !noLock)
                    {
                        SafeEnd.ReleaseSemaphore(this.writeSemaphore, this.log);
                    }
                }
            }
            catch (Exception writeError) when(writeError.Unwrap() is ThreadAbortException == false)
            {
                if (noError)
                {
                    return(false);
                }

                var writeErrorUnwrap = writeError.Unwrap();

                if (writeErrorUnwrap is ObjectDisposedException)
                {
                    writeErrorUnwrap = new IOException("Network connection has been closed.", writeErrorUnwrap);
                }

                if (this.log.IsDebugEnabled && writeErrorUnwrap is OperationCanceledException == false && this.IsConnected)
                {
                    this.log.Debug($"({this.GetHashCode():X}) Write operation on WebSocket stream is failed.", writeErrorUnwrap);
                }

                if (this.IsConnected)
                {
                    await this.CloseAsync(WebSocketCloseReasons.UnexpectedCondition).ConfigureAwait(false);
                }

                if (writeErrorUnwrap is WebSocketException == false && writeErrorUnwrap is OperationCanceledException == false)
                {
                    throw new WebSocketException("Write operation on WebSocket stream is failed: " + writeErrorUnwrap.Message, writeErrorUnwrap);
                }
                else
                {
                    throw;
                }
            }
        }