public Task WriteCloseFragmentAsync(WebSocketCloseStatus closeStatus, string statusDescription) { TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); // Callback will always be called (since it is responsible for cleanup), even if completed synchronously CompletionCallback callback = (hrError, cbIO, fUtf8Encoded, fFinalFragment, fClose) => { try { ThrowExceptionForHR(hrError); tcs.TrySetResult(null); // regular completion } catch (Exception ex) { tcs.TrySetException(ex); // exceptional completion } }; IntPtr completionContext = GCUtil.RootObject(callback); // Call the underlying implementation; SendConnectionClose should never throw an exception bool completionExpected; int hr = _context.SendConnectionClose( fAsync: true, uStatusCode: (ushort)closeStatus, szReason: statusDescription, // don't need to pin string: CLR marshaler handles managed to unmanaged conversion, and IIS makes local copy for duration of async operation pfnCompletion: _asyncThunkAddress, pvCompletionContext: completionContext, pfCompletionExpected: out completionExpected); if (!completionExpected) { // Completed synchronously or error; the thunk and callback together handle cleanup AsyncCallbackThunk(hr, completionContext, cbIO: 0, fUtf8Encoded: true, fFinalFragment: true, fClose: false); } return tcs.Task; }
public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription) { if (this.SocketDisconnected != null) this.SocketDisconnected(this, this); base.OnClose(closeStatus, closeStatusDescription); }
public Task CloseAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { throw new PlatformNotSupportedException(SR.net_WebSockets_UnsupportedPlatform); }
public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription) { _isAvailable = false; _context.CloseStatus = closeStatus; _context.CloseStatusDescription = closeStatusDescription; _connection.OnClose?.Invoke(); }
public Task CloseAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { return _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken); }
public Task CloseAsync( WebSocketCloseStatus webSocketCloseStatus, string statusDescription, CancellationToken cancellationToken) { return Task.Run(() => this.socket.Close(), cancellationToken); }
public WebSocketReceiveResult (int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeStatusDescription) { throw new NotImplementedException (); }
public void ConstructorTest_Success(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeStatusDescription) { var wsrr = new WebSocketReceiveResult(count, messageType, endOfMessage, closeStatus, closeStatusDescription); Assert.Equal(wsrr.Count, count); Assert.Equal(wsrr.MessageType, messageType); Assert.Equal(wsrr.EndOfMessage, endOfMessage); Assert.Equal(wsrr.CloseStatus, closeStatus); Assert.Equal(wsrr.CloseStatusDescription, closeStatusDescription); }
public WebSocketReceiveResult (int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeStatusDescription) { MessageType = messageType; CloseStatus = closeStatus; CloseStatusDescription = closeStatusDescription; Count = count; EndOfMessage = endOfMessage; }
public WebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeStatusDescription) { if (count < 0) { throw new ArgumentOutOfRangeException("count"); } Count = count; EndOfMessage = endOfMessage; MessageType = messageType; CloseStatus = closeStatus; CloseStatusDescription = closeStatusDescription; }
public async override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { ThrowIfDisposed(); ThrowIfOutputClosed(); var message = new Message(closeStatus, statusDescription); await _sendBuffer.SendAsync(message, cancellationToken); if (State == WebSocketState.Open) { _state = WebSocketState.CloseSent; } else if (State == WebSocketState.CloseReceived) { _state = WebSocketState.Closed; Close(); } }
public void ConstructorTest_Success(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeStatusDescription) { WebSocketReceiveResult wsrr; if (closeStatus == null && closeStatusDescription == null) { wsrr = new WebSocketReceiveResult(count, messageType, endOfMessage); Assert.Equal(count, wsrr.Count); Assert.Equal(messageType, wsrr.MessageType); Assert.Equal(endOfMessage, wsrr.EndOfMessage); Assert.Equal(null, wsrr.CloseStatus); Assert.Equal(null, wsrr.CloseStatusDescription); } wsrr = new WebSocketReceiveResult(count, messageType, endOfMessage, closeStatus, closeStatusDescription); Assert.Equal(count, wsrr.Count); Assert.Equal(messageType, wsrr.MessageType); Assert.Equal(endOfMessage, wsrr.EndOfMessage); Assert.Equal(closeStatus, wsrr.CloseStatus); Assert.Equal(closeStatusDescription, wsrr.CloseStatusDescription); }
public async override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { ThrowIfDisposed(); if (State == WebSocketState.Open || State == WebSocketState.CloseReceived) { // Send a close message. await CloseOutputAsync(closeStatus, statusDescription, cancellationToken); } if (State == WebSocketState.CloseSent) { // Do a receiving drain var data = new byte[1024]; WebSocketReceiveResult result; do { result = await ReceiveAsync(new ArraySegment<byte>(data), cancellationToken); } while (result.MessageType != WebSocketMessageType.Close); } }
public static async Task CloseSocket(this WebSocket socket, WebSocketCloseStatus status, string statusDescription, CancellationToken cancellation = default) { try { if (socket.State == WebSocketState.Open) { using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.CancelAfter(5000); using (var cts2 = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellation)) { try { await socket.CloseAsync(status, statusDescription, cts2.Token).ConfigureAwait(false); } catch (ObjectDisposedException) { } } } } } catch { } finally { socket.Dispose(); } }
/// <summary> /// Fire and forget close /// </summary> public override async Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { if (_state == WebSocketState.Open) { _state = WebSocketState.Closed; // set this before we write to the network because the write may fail using (MemoryStream stream = _recycledStreamFactory()) { ArraySegment <byte> buffer = BuildClosePayload(closeStatus, statusDescription); WebSocketFrameWriter.Write(WebSocketOpCode.ConnectionClose, buffer, stream, true, _isClient); Events.Log.CloseOutputNoHandshake(_guid, closeStatus, statusDescription); Events.Log.SendingFrame(_guid, WebSocketOpCode.ConnectionClose, true, buffer.Count, true); await WriteStreamToNetwork(stream, cancellationToken); } } else { Events.Log.InvalidStateBeforeCloseOutput(_guid, _state); } // cancel pending reads _internalReadCts.Cancel(); }
private async Task Close(WebSocketCloseStatus status, string reason) { Log.Debug($"{nameof(this.Close)}: {status} {reason}"); try { switch (this._socket.State) { case WebSocketState.Aborted: case WebSocketState.Closed: case WebSocketState.CloseReceived: case WebSocketState.CloseSent: case WebSocketState.None: return; } await this._socket.CloseAsync(status, reason, this._cancel.Token); this._cancel.Cancel(); } catch (Exception ex) { Log.Error($"{nameof(this.Close)}: {ex}"); } }
internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, string statusDescription) { if (closeStatus == WebSocketCloseStatus.Empty && !string.IsNullOrEmpty(statusDescription)) { throw new ArgumentException(SR.Format(SR.net_WebSockets_ReasonNotNull, statusDescription, WebSocketCloseStatus.Empty), nameof(statusDescription)); } int closeStatusCode = (int)closeStatus; if ((closeStatusCode >= InvalidCloseStatusCodesFrom && closeStatusCode <= InvalidCloseStatusCodesTo) || closeStatusCode == CloseStatusCodeAbort || closeStatusCode == CloseStatusCodeFailedTLSHandshake) { // CloseStatus 1006 means Aborted - this will never appear on the wire and is reflected by calling WebSocket.Abort throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusCode, closeStatusCode), nameof(closeStatus)); } int length = 0; if (!string.IsNullOrEmpty(statusDescription)) { length = Encoding.UTF8.GetByteCount(statusDescription); } if (length > MaxControlFramePayloadLength) { throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusDescription, statusDescription, MaxControlFramePayloadLength), nameof(statusDescription)); } }
protected override void OnCloseReceived(WebSocketCloseStatus closeStatus, string closeDescription) { if (closeStatus == WebSocketCloseStatus.NormalClosure) { return; } VoiceCloseCode voiceCloseCode = (VoiceCloseCode)closeStatus; switch (voiceCloseCode) { case VoiceCloseCode.Disconnected: case VoiceCloseCode.VoiceServerCrashed: heartbeatCancellationSource?.Cancel(); OnResumeRequested?.Invoke(this, EventArgs.Empty); break; case VoiceCloseCode.InvalidSession: case VoiceCloseCode.SessionTimeout: heartbeatCancellationSource?.Cancel(); OnNewSessionRequested?.Invoke(this, EventArgs.Empty); break; default: if ((int)voiceCloseCode >= 4000) { log.LogVerbose($"Fatal close code: {voiceCloseCode} ({(int)voiceCloseCode}), {closeDescription}"); } else { log.LogVerbose($"Fatal close code: {closeStatus} ({(int)closeStatus}), {closeDescription}"); } OnUnexpectedClose?.Invoke(this, EventArgs.Empty); break; } }
public void TestRfc6455CodeValidation() { var knownCodes = new List <int> { WebSocketCloseStatus.NormalClosure.Code, WebSocketCloseStatus.EndpointUnavailable.Code, WebSocketCloseStatus.ProtocolError.Code, WebSocketCloseStatus.InvalidMessageType.Code, WebSocketCloseStatus.InvalidPayloadData.Code, WebSocketCloseStatus.PolicyViolation.Code, WebSocketCloseStatus.MessageTooBig.Code, WebSocketCloseStatus.MandatoryExtension.Code, WebSocketCloseStatus.InternalServerError.Code, WebSocketCloseStatus.ServiceRestart.Code, WebSocketCloseStatus.TryAgainLater.Code, WebSocketCloseStatus.BadGateway.Code }; SortedSet <int> invalidCodes = new SortedSet <int>(); // When for (int statusCode = short.MinValue; statusCode < short.MaxValue; statusCode++) { if (!WebSocketCloseStatus.IsValidStatusCode(statusCode)) { invalidCodes.Add(statusCode); } } // Then Assert.Equal(0, invalidCodes.First()); Assert.Equal(2999, invalidCodes.Last()); Assert.Equal(3000 - s_validCodes.Count, invalidCodes.Count); invalidCodes.IntersectWith(knownCodes); Assert.Empty(invalidCodes); }
public override async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { ThrowIfNotConnected(); ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived, WebSocketState.CloseSent); tcsClose = new TaskCompletionSource <bool> (); // Wrap the cancellationToken in a using so that it can be disposed of whether // we successfully connected or failed trying. // Otherwise any timeout/cancellation would apply to the full session. // In the failure case we need to release the references and dispose of the objects. using (cancellationToken.Register(() => tcsClose.TrySetCanceled())) { innerWebSocketCloseStatus = closeStatus; innerWebSocketCloseStatusDescription = statusDescription; try { innerWebSocket.Invoke("close", (int)closeStatus, statusDescription); } catch (Exception exc) { throw exc; } await tcsClose.Task; } }
private static bool IsValidCloseStatus(WebSocketCloseStatus closeStatus) { // 0-999: "not used" // 1000-2999: reserved for the protocol; we need to check individual codes manually // 3000-3999: reserved for use by higher-level code // 4000-4999: reserved for private use // 5000-: not mentioned in RFC if (closeStatus < (WebSocketCloseStatus)1000 || closeStatus >= (WebSocketCloseStatus)5000) { return(false); } if (closeStatus >= (WebSocketCloseStatus)3000) { return(true); } // Check for the 1000-2999 range known codes switch (closeStatus) { case WebSocketCloseStatus.EndpointUnavailable: case WebSocketCloseStatus.InternalServerError: case WebSocketCloseStatus.InvalidMessageType: case WebSocketCloseStatus.InvalidPayloadData: case WebSocketCloseStatus.MandatoryExtension: case WebSocketCloseStatus.MessageTooBig: case WebSocketCloseStatus.NormalClosure: case WebSocketCloseStatus.PolicyViolation: case WebSocketCloseStatus.ProtocolError: return(true); default: return(false); } }
/// <summary> /// Closes the connection. /// </summary> /// <param name="closeStatus">Close reason.</param> /// <param name="statusDescription">Status description.</param> /// <returns>Task.</returns> public async Task CloseAsync(WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string statusDescription = "") { if (statusDescription == null) { throw new ArgumentNullException(nameof(statusDescription), "The value may be empty but not null."); } try { if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) { await socket.CloseOutputAsync(closeStatus, statusDescription, CancellationToken.None); } } catch (Exception ex) { InvokeOnError(ex); } finally { invokeOnClose(closeStatus, statusDescription); clearEvents(); } }
/// <summary> /// Automatic WebSocket close in response to some invalid data from the remote websocket host /// </summary> /// <param name="closeStatus">The close status to use</param> /// <param name="statusDescription">A description of why we are closing</param> /// <param name="ex">The exception (for logging)</param> private async Task CloseOutputAutoTimeoutAsync(WebSocketCloseStatus closeStatus, string statusDescription, Exception ex) { TimeSpan timeSpan = TimeSpan.FromSeconds(5); try { // we may not want to send sensitive information to the client / server if (_includeExceptionInCloseResponse) { statusDescription = statusDescription + "\r\n\r\n" + ex.ToString(); } var autoCancel = new CancellationTokenSource(timeSpan); await CloseOutputAsync(closeStatus, statusDescription, autoCancel.Token); } catch (OperationCanceledException) { // do not throw an exception because that will mask the original exception } catch { // do not throw an exception because that will mask the original exception } }
private Task <bool> InternalCloseAsync(WebSocketCloseStatus closeStatus, string statusDescription) { uint ret; _operation.TcsClose = new TaskCompletionSource <bool>(); lock (_operation.Lock) { _operation.CheckValidState(s_validCloseStates); if (!string.IsNullOrEmpty(statusDescription)) { byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription); ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, statusDescriptionBuffer, (uint)statusDescriptionBuffer.Length); } else { ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, IntPtr.Zero, 0); } if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } return(_operation.TcsClose.Task); }
public async Task CloseConnection(WebSocketCloseStatus status = WebSocketCloseStatus.NormalClosure, string desc = null) { try { if (webSocket.State == WebSocketState.Open) { await sendMessageSemaphore.WaitAsync(); try { var timeoutTokenSource = new CancellationTokenSource(1000); await webSocket.CloseAsync(status, desc, timeoutTokenSource.Token); cancellationTokenSource?.Cancel(); } catch (WebSocketException exc) { //ignore client disconnection if (exc.WebSocketErrorCode != WebSocketError.ConnectionClosedPrematurely && exc.WebSocketErrorCode != WebSocketError.InvalidState) { throw; } } catch (OperationCanceledException) { } finally { sendMessageSemaphore?.Release(); } } } catch (Exception e) { logger.Error(e, "Error on close connection"); } }
public async Task Complete(WebSocketCloseStatus closeStatus, string statusDescription) { if (_socket.State != WebSocketState.Closed && _socket.State != WebSocketState.CloseSent) { if (closeStatus == WebSocketCloseStatus.NormalClosure) { // If nothing went wrong, close connection with handshakes. await _socket.CloseOutputAsync( closeStatus, statusDescription, CancellationToken.None); } else { // Something went wrong, so don't wait for answer from the other side, just close the connection. await _socket.CloseAsync( closeStatus, statusDescription, CancellationToken.None); } _startBlock.Complete(); } }
/// <summary> /// Creates a failed result. /// </summary> /// <param name="errorReason">A more detailed error reason.</param> /// <param name="closeStatus">The Discord error that caused the failure, if any.</param> /// <returns>A failed result.</returns> public static TActualResult FromError ( string errorReason, WebSocketCloseStatus closeStatus ) { var constructor = typeof(TActualResult).GetConstructor ( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(WebSocketCloseStatus) }, null ); if (constructor is null) { var typeName = typeof(TActualResult).Name; throw new MissingMethodException(typeName, $"{typeName}(string, WebSocketCloseStatus)"); } var resultInstance = constructor.Invoke(new object?[] { errorReason, closeStatus }); return((TActualResult)resultInstance); }
protected WebSocketTransport( System.Net.WebSockets.WebSocket webSocket, IEnvelopeSerializer envelopeSerializer, ITraceWriter traceWriter = null, int bufferSize = DEFAULT_BUFFER_SIZE, int receiveBoundedCapacity = -1, WebSocketMessageType webSocketMessageType = WebSocketMessageType.Text) { WebSocket = webSocket; _envelopeSerializer = envelopeSerializer; _traceWriter = traceWriter; _arrayPool = ArrayPool <byte> .Shared; _bufferSize = bufferSize; _jsonBuffer = new JsonBuffer(bufferSize, DEFAULT_MAX_BUFFER_SIZE, _arrayPool); _webSocketMessageType = webSocketMessageType; _sendSemaphore = new SemaphoreSlim(1); CloseStatus = WebSocketCloseStatus.NormalClosure; CloseStatusDescription = string.Empty; _receivedEnvelopeBufferBlock = new BufferBlock <Envelope>( new DataflowBlockOptions() { BoundedCapacity = receiveBoundedCapacity }); }
public override async Task CloseAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { _operation.InterlockedCheckAndUpdateState(WebSocketState.CloseSent, s_validCloseStates); using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) { _operation.TcsClose = new TaskCompletionSource<bool>(); await InternalCloseAsync(closeStatus, statusDescription).ConfigureAwait(false); UpdateServerCloseStatus(); } }
public Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) => _webSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
public static Task SendCloseAsync(this WebSocket webSocket, WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string statusDescription = null, CancellationToken cancellationToken = default(CancellationToken)) { return webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken); }
/// <summary> /// Close the output for the instance as an asynchronous operation. /// </summary> /// <param name="closeStatus">The WebSocket close status.</param> /// <param name="statusDescription">A description of the close status.</param> /// <param name="cancellationToken">Propagates the notification that operations should be canceled.</param> /// <returns>The task object representing the asynchronous operation.</returns> public async Task CloseOutputAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) => await this.webSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { ThrowIfNotConnected(); return(innerWebSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)); }
public static async Task CloseSocketAndThrow(ArraySegment <byte> buffer, ClientWebSocket socket, WebSocketCloseStatus status, string description, CancellationToken cancellation) { var array = buffer.Array; if (array.Length != WebsocketHelper.ORIGINAL_BUFFER_SIZE) { Array.Resize(ref array, WebsocketHelper.ORIGINAL_BUFFER_SIZE); } await WebsocketHelper.CloseSocket(socket, status, description, cancellation); throw new WebSocketException($"The socket has been closed ({status}: {description})"); }
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { ThrowIfNotConnected(); WebSocketValidate.ValidateCloseStatus(closeStatus, statusDescription); return _innerWebSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken); }
public WebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus closeStatus, string closeStatusDescription) { this.Count = count; this.MessageType = messageType; this.EndOfMessage = endOfMessage; this.Bytes = null; this.CloseStatus = closeStatus; this.CloseStatusDescription = closeStatusDescription; }
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { webSocket?.Close((ushort)closeStatus, statusDescription); return(Task.CompletedTask); }
/// <inheritdoc /> public async Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { await _client.CloseOutputAsync(closeStatus, statusDescription, cancellationToken); }
private void Overlay_OnWebSocketDisconnectedOccurred(object sender, WebSocketCloseStatus closeStatus) { IOverlayService overlay = (IOverlayService)sender; this.OnOverlayDisconnectedOccurred(overlay, closeStatus); }
private Task<bool> InternalCloseAsync(WebSocketCloseStatus closeStatus, string statusDescription) { uint ret; _operation.TcsClose = new TaskCompletionSource<bool>(); lock (_operation.Lock) { _operation.CheckValidState(s_validCloseStates); if (!string.IsNullOrEmpty(statusDescription)) { byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription); ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, statusDescriptionBuffer, (uint)statusDescriptionBuffer.Length); } else { ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, IntPtr.Zero, 0); } if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } return _operation.TcsClose.Task; }
public override Task CloseOutputAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { _operation.InterlockedCheckAndUpdateState(WebSocketState.CloseSent, s_validCloseOutputStates); using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) { lock (_operation.Lock) { _operation.CheckValidState(s_validCloseOutputStatesAfterUpdate); uint ret; _operation.TcsCloseOutput = new TaskCompletionSource<bool>(); if (!string.IsNullOrEmpty(statusDescription)) { byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription); ret = Interop.WinHttp.WinHttpWebSocketShutdown( _operation.WebSocketHandle, (ushort)closeStatus, statusDescriptionBuffer, (uint)statusDescriptionBuffer.Length); } else { ret = Interop.WinHttp.WinHttpWebSocketShutdown( _operation.WebSocketHandle, (ushort)closeStatus, IntPtr.Zero, 0); } if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } return _operation.TcsCloseOutput.Task; } }
public abstract Task CloseOutputAsync (WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken);
public async Task Disconnect(WebSocketCloseStatus closeStatus, string?reason) { await CloseInner(closeStatus, reason); }
public override Task OnCloseAsync(WebSocketCloseStatus? closeStatus, string closeStatusDescription) { Server.Stop(); return base.OnCloseAsync(closeStatus, closeStatusDescription); }
private void XSplitWebServer_OnDisconnectOccurred(object sender, WebSocketCloseStatus e) { this.Disconnected(sender, new EventArgs()); }
internal void ConvertCloseBuffer(WebSocketProtocolComponent.Action action, WebSocketProtocolComponent.Buffer buffer, out WebSocketCloseStatus closeStatus, out string reason) { ThrowIfDisposed(); IntPtr bufferData; uint bufferLength; closeStatus = (WebSocketCloseStatus)buffer.CloseStatus.CloseStatus; UnwrapWebSocketBuffer(buffer, WebSocketProtocolComponent.BufferType.Close, out bufferData, out bufferLength); if (bufferData == IntPtr.Zero) { reason = null; } else { ArraySegment<byte> reasonBlob; if (this.IsNativeBuffer(bufferData, bufferLength)) { reasonBlob = new ArraySegment<byte>(m_InternalBuffer.Array, this.GetOffset(bufferData), (int)bufferLength); } else { Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer."); // Indicates a violation in the contract with native Websocket.dll and could indicate // memory corruption because the internal buffer is shared between managed and native code throw new AccessViolationException(); } // No need to wrap DecoderFallbackException for invalid UTF8 chacters, because // Encoding.UTF8 will not throw but replace invalid characters instead. reason = Encoding.UTF8.GetString(reasonBlob.Array, reasonBlob.Offset, reasonBlob.Count); } }
public static Task SendCloseAsync(this WebSocket webSocket, WebSocketCloseStatus closeStatus, CancellationToken cancellationToken) { return webSocket.CloseAsync(closeStatus, null, cancellationToken); }
public void ToClientConnectionCloseStatus(ClientConnectionCloseStatus socketStatus, WebSocketCloseStatus expectedCloseStatus) { Assert.AreEqual(expectedCloseStatus, socketStatus.ToWebSocketCloseStatus()); }
public override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { return(Task.CompletedTask); }
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { // TODO: Validate state if (State == WebSocketState.Open) { _state = WebSocketState.CloseSent; } else if (State == WebSocketState.CloseReceived) { _state = WebSocketState.Closed; } return _closeAsync((int)closeStatus, statusDescription, cancellationToken); }
public void OnClose(IWebSocketContext context, WebSocketCloseStatus closeStatus) { handler.OnClose(context, closeStatus); }
/// <summary>Sends a close message to the server.</summary> /// <param name="closeStatus">The close status to send.</param> /// <param name="closeStatusDescription">The close status description to send.</param> /// <param name="cancellationToken">The CancellationToken to use to cancel the websocket.</param> private async Task SendCloseFrameAsync(WebSocketCloseStatus closeStatus, string closeStatusDescription, CancellationToken cancellationToken) { // Close payload is two bytes containing the close status followed by a UTF8-encoding of the status description, if it exists. byte[] buffer; if (string.IsNullOrEmpty(closeStatusDescription)) { buffer = new byte[2]; } else { buffer = new byte[2 + s_textEncoding.GetByteCount(closeStatusDescription)]; int encodedLength = s_textEncoding.GetBytes(closeStatusDescription, 0, closeStatusDescription.Length, buffer, 2); Debug.Assert(buffer.Length - 2 == encodedLength, $"GetByteCount and GetBytes encoded count didn't match"); } ushort closeStatusValue = (ushort)closeStatus; buffer[0] = (byte)(closeStatusValue >> 8); buffer[1] = (byte)(closeStatusValue & 0xFF); await SendFrameAsync(MessageOpcode.Close, true, new ArraySegment<byte>(buffer), cancellationToken).ConfigureAwait(false); lock (StateUpdateLock) { _sentCloseFrame = true; if (_state <= WebSocketState.CloseReceived) { _state = WebSocketState.CloseSent; } } }
/// <summary>Disconnects the WebSocket gracefully from the server.</summary> public Task CloseAsync(WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string statusDescription = "Client closed the connection", CancellationToken cancellationToken = default(CancellationToken)) { return(_clientWebSocket.CloseAsync(closeStatus, statusDescription, cancellationToken) .ContinueWith(task => Closed?.Invoke(this, EventArgs.Empty), cancellationToken)); }
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { WebSocketValidate.ValidateCloseStatus(closeStatus, statusDescription); try { WebSocketValidate.ThrowIfInvalidState(_state, _disposed, s_validCloseOutputStates); } catch (Exception exc) { return Task.FromException(exc); } return SendCloseFrameAsync(closeStatus, statusDescription, cancellationToken); }
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) => _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
/// <summary>Check whether a close status is valid according to the RFC.</summary> /// <param name="closeStatus">The status to validate.</param> /// <returns>true if the status if valid; otherwise, false.</returns> private static bool IsValidCloseStatus(WebSocketCloseStatus closeStatus) { // 0-999: "not used" // 1000-2999: reserved for the protocol; we need to check individual codes manually // 3000-3999: reserved for use by higher-level code // 4000-4999: reserved for private use // 5000-: not mentioned in RFC if (closeStatus < (WebSocketCloseStatus)1000 || closeStatus >= (WebSocketCloseStatus)5000) { return false; } if (closeStatus >= (WebSocketCloseStatus)3000) { return true; } switch (closeStatus) // check for the 1000-2999 range known codes { case WebSocketCloseStatus.EndpointUnavailable: case WebSocketCloseStatus.InternalServerError: case WebSocketCloseStatus.InvalidMessageType: case WebSocketCloseStatus.InvalidPayloadData: case WebSocketCloseStatus.MandatoryExtension: case WebSocketCloseStatus.MessageTooBig: case WebSocketCloseStatus.NormalClosure: case WebSocketCloseStatus.PolicyViolation: case WebSocketCloseStatus.ProtocolError: return true; default: return false; } }
public async Task CloseAsync_HandshakeStartedFromClient_Success(WebSocketCloseStatus status, string statusDescription, WebSocketCloseStatus expectedCloseStatus) { // [ActiveIssue(20392, TargetFrameworkMonikers.Netcoreapp)] string expectedStatusDescription = statusDescription; if (!PlatformDetection.IsFullFramework && statusDescription == null) { expectedStatusDescription = string.Empty; } HttpListenerWebSocketContext context = await GetWebSocketContext(); await ClientConnectTask; // Close the client output. Task clientCloseTask = Client.CloseOutputAsync(status, statusDescription, new CancellationToken()); byte[] receivedServerBytes = new byte[10]; Task <WebSocketReceiveResult> serverReceiveTask = context.WebSocket.ReceiveAsync(new ArraySegment <byte>(receivedServerBytes), new CancellationToken()); await Task.WhenAll(clientCloseTask, serverReceiveTask); WebSocketReceiveResult serverResult = await serverReceiveTask; Assert.Equal(new byte[10], receivedServerBytes); Assert.Equal(expectedCloseStatus, serverResult.CloseStatus); Assert.Equal(statusDescription, serverResult.CloseStatusDescription); Assert.Equal(WebSocketMessageType.Close, serverResult.MessageType); Assert.True(serverResult.EndOfMessage); Assert.Equal(expectedCloseStatus, context.WebSocket.CloseStatus); Assert.Equal(statusDescription, context.WebSocket.CloseStatusDescription); Assert.Equal(WebSocketState.CloseReceived, context.WebSocket.State); // Trying to read if the server received a close handshake should fail. await Assert.ThrowsAsync <WebSocketException>(() => context.WebSocket.ReceiveAsync(new ArraySegment <byte>(receivedServerBytes), new CancellationToken())); // Close the server. Task serverCloseTask = context.WebSocket.CloseAsync(status, statusDescription, new CancellationToken()); byte[] receivedClientBytes = new byte[10]; Task <WebSocketReceiveResult> clientReceiveTask = Client.ReceiveAsync(new ArraySegment <byte>(receivedClientBytes), new CancellationToken()); await Task.WhenAll(serverCloseTask, clientReceiveTask); WebSocketReceiveResult clientResult = await clientReceiveTask; Assert.Equal(new byte[10], receivedClientBytes); Assert.Equal(expectedCloseStatus, clientResult.CloseStatus); Assert.Equal(expectedStatusDescription, clientResult.CloseStatusDescription); Assert.Equal(WebSocketMessageType.Close, clientResult.MessageType); Assert.True(clientResult.EndOfMessage); Assert.Equal(expectedCloseStatus, context.WebSocket.CloseStatus); Assert.Equal(statusDescription, context.WebSocket.CloseStatusDescription); Assert.Equal(WebSocketState.Closed, context.WebSocket.State); // Trying to read or write if closed should fail. await Assert.ThrowsAsync <WebSocketException>(() => context.WebSocket.ReceiveAsync(new ArraySegment <byte>(receivedServerBytes), new CancellationToken())); await Assert.ThrowsAsync <WebSocketException>(() => context.WebSocket.SendAsync(new ArraySegment <byte>(receivedServerBytes), WebSocketMessageType.Binary, false, new CancellationToken())); // Trying to close again should be a nop. await context.WebSocket.CloseAsync(WebSocketCloseStatus.Empty, null, new CancellationToken()); await context.WebSocket.CloseOutputAsync(WebSocketCloseStatus.Empty, null, new CancellationToken()); }
/// <summary>Send a close message to the server and throw an exception, in response to getting bad data from the server.</summary> /// <param name="closeStatus">The close status code to use.</param> /// <param name="error">The error reason.</param> /// <param name="cancellationToken">The CancellationToken used to cancel the websocket.</param> /// <param name="innerException">An optional inner exception to include in the thrown exception.</param> private async Task CloseWithReceiveErrorAndThrowAsync( WebSocketCloseStatus closeStatus, WebSocketError error, CancellationToken cancellationToken, Exception innerException = null) { // Close the connection if it hasn't already been closed if (!_sentCloseFrame) { await CloseOutputAsync(closeStatus, string.Empty, cancellationToken).ConfigureAwait(false); } // Dump our receive buffer; we're in a bad state to do any further processing _receiveBufferCount = 0; // Let the caller know we've failed throw new WebSocketException(error, innerException); }
public Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { Dispose(); return(Task.CompletedTask); }
/// <summary>Send a close message, then receive until we get a close response message.</summary> /// <param name="closeStatus">The close status to send.</param> /// <param name="statusDescription">The close status description to send.</param> /// <param name="cancellationToken">The CancellationToken to use to cancel the websocket.</param> private async Task CloseAsyncPrivate(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { // Send the close message. Skip sending a close frame if we're currently in a CloseSent state, // for example having just done a CloseOutputAsync. if (!_sentCloseFrame) { await SendCloseFrameAsync(closeStatus, statusDescription, cancellationToken).ConfigureAwait(false); } // We should now either be in a CloseSent case (because we just sent one), or in a CloseReceived state, in case // there was a concurrent receive that ended up handling an immediate close frame response from the server. // Of course it could also be Aborted if something happened concurrently to cause things to blow up. Debug.Assert( State == WebSocketState.CloseSent || State == WebSocketState.CloseReceived || State == WebSocketState.Aborted, $"Unexpected state {State}."); // Wait until we've received a close response byte[] closeBuffer = new byte[MaxMessageHeaderLength + MaxControlPayloadLength]; while (!_receivedCloseFrame) { Debug.Assert(!Monitor.IsEntered(StateUpdateLock), $"{nameof(StateUpdateLock)} must never be held when acquiring {nameof(ReceiveAsyncLock)}"); Task<WebSocketReceiveResult> receiveTask; lock (ReceiveAsyncLock) { // Now that we're holding the ReceiveAsyncLock, double-check that we've not yet received the close frame. // It could have been received between our check above and now due to a concurrent receive completing. if (_receivedCloseFrame) { break; } // We've not yet processed a received close frame, which means we need to wait for a received close to complete. // There may already be one in flight, in which case we want to just wait for that one rather than kicking off // another (we don't support concurrent receive operations). We need to kick off a new receive if either we've // never issued a receive or if the last issued receive completed for reasons other than a close frame. There is // a race condition here, e.g. if there's a in-flight receive that completes after we check, but that's fine: worst // case is we then await it, find that it's not what we need, and try again. receiveTask = _lastReceiveAsync; if (receiveTask == null || (receiveTask.Status == TaskStatus.RanToCompletion && receiveTask.Result.MessageType != WebSocketMessageType.Close)) { _lastReceiveAsync = receiveTask = ReceiveAsyncPrivate(new ArraySegment<byte>(closeBuffer), cancellationToken); } } // Wait for whatever receive task we have. We'll then loop around again to re-check our state. Debug.Assert(receiveTask != null); await receiveTask.ConfigureAwait(false); } // We're closed. Close the connection and update the status. lock (StateUpdateLock) { DisposeCore(); if (_state < WebSocketState.Closed) { _state = WebSocketState.Closed; } } }