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