예제 #1
0
        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);
        }
예제 #3
0
 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();
 }
예제 #5
0
 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);
 }
예제 #7
0
		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);
 }
예제 #9
0
		public WebSocketReceiveResult (int count,
		                               WebSocketMessageType messageType,
		                               bool endOfMessage,
		                               WebSocketCloseStatus? closeStatus,
		                               string closeStatusDescription)
		{
			MessageType = messageType;
			CloseStatus = closeStatus;
			CloseStatusDescription = closeStatusDescription;
			Count = count;
			EndOfMessage = endOfMessage;
		}
예제 #10
0
        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;
        }
예제 #11
0
        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);
        }
예제 #13
0
        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);
            }
        }
예제 #14
0
 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();
        }
예제 #16
0
        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}");
            }
        }
예제 #17
0
        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));
            }
        }
예제 #18
0
        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;
            }
        }
예제 #19
0
        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);
        }
예제 #20
0
        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);
            }
        }
예제 #22
0
        /// <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();
            }
        }
예제 #23
0
        /// <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
            }
        }
예제 #24
0
        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");
            }
        }
예제 #26
0
        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();
            }
        }
예제 #27
0
        /// <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);
        }
예제 #28
0
 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
     });
 }
예제 #29
0
        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();
            }
        }
예제 #30
0
 public Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) =>
     _webSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
예제 #31
0
 public static Task SendCloseAsync(this WebSocket webSocket, WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string statusDescription = null, CancellationToken cancellationToken = default(CancellationToken))
 {
     return webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
 }
예제 #32
0
 /// <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);
예제 #33
0
 public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription,
                                       CancellationToken cancellationToken)
 {
     ThrowIfNotConnected();
     return(innerWebSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken));
 }
예제 #34
0
        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})");
        }
예제 #35
0
 public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription,
     CancellationToken cancellationToken)
 {
     ThrowIfNotConnected();
     WebSocketValidate.ValidateCloseStatus(closeStatus, statusDescription);
     return _innerWebSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
 }
예제 #36
0
 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;
 }
예제 #37
0
 public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
 {
     webSocket?.Close((ushort)closeStatus, statusDescription);
     return(Task.CompletedTask);
 }
예제 #38
0
 /// <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);
        }
예제 #40
0
        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;
        }
예제 #41
0
        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;
            }
        }
예제 #42
0
		public abstract Task CloseOutputAsync (WebSocketCloseStatus closeStatus,
		                                       string statusDescription,
		                                       CancellationToken cancellationToken);
예제 #43
0
 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);
            }
        }
예제 #47
0
 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());
 }
예제 #49
0
 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);
 }
예제 #51
0
 public void OnClose(IWebSocketContext context, WebSocketCloseStatus closeStatus)
 {
     handler.OnClose(context, closeStatus);
 }
예제 #52
0
        /// <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;
                }
            }
        }
예제 #53
0
 /// <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));
 }
예제 #54
0
        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);
        }
예제 #55
0
 public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) => _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
예제 #56
0
        /// <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;
            }
        }
예제 #57
0
        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());
        }
예제 #58
0
        /// <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);
        }
예제 #59
0
 public Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
 {
     Dispose();
     return(Task.CompletedTask);
 }
예제 #60
0
        /// <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;
                }
            }
        }