Commit 834b52d1 authored by Chris Baudin's avatar Chris Baudin
Browse files

Refactor the WebSocket class implementations to decouple the Connect, Close,...

Refactor the WebSocket class implementations to decouple the Connect, Close, Receive and Send actions. (Issue #31)
parent 5861be21
Loading
Loading
Loading
Loading
+60 −12
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// </summary>
        private Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> serverCertificateValidationHandler;

        /// <summary>
        /// The connection to the websocket.
        /// </summary>
        private Task<HttpResponseMessage> webSocketTask;

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSocket{T}" /> class.
        /// </summary>
@@ -50,14 +55,40 @@ namespace Microsoft.Tools.WindowsDevicePortal
            this.serverCertificateValidationHandler = serverCertificateValidationHandler;
        }

        /// <summary>
        /// Initialize a connection to the websocket.
        /// </summary>
        /// <param name="endpoint">The uri that the weboscket should connect to.</param>
        /// <returns>The task of opening a connection to the websocket.</returns>
        private async Task ConnectInternalAsync(Uri endpoint)
        {
            await Task.Run(() =>
            {
                webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint);
                this.IsConnected = true;
            });
        }

        /// <summary>
        /// Closes the connection to the websocket.
        /// </summary>
        /// <returns>The task of closing the websocket connection.</returns>
        private async Task CloseInternalAsync()
        {
            await Task.Run(() =>
            {
                webSocketTask.Dispose();
                webSocketTask = null;
                this.IsConnected = false;
            });
        }

        /// <summary>
        /// Stops listneing for messages from the websocket and closes the connection to the websocket.
        /// </summary>
        /// <returns>The task of closing the websocket connection.</returns>
#pragma warning disable 1998
        private async Task StopListeningForMessagesInternalAsync()
        {
            if (this.IsListeningForMessages)
        {
            this.keepListeningForMessages = false;

@@ -68,24 +99,22 @@ namespace Microsoft.Tools.WindowsDevicePortal
                this.stoppedReceivingMessages.Reset();
            }
        }
        }
#pragma warning restore 1998

        /// <summary>
        /// Connects to the websocket and starts listening for messages from the websocket.
        /// Starts listening for messages from the websocket.
        /// Once they are received they are parsed and the WebSocketMessageReceived event is raised.
        /// </summary>
        /// <param name="endpoint">The uri that the weboscket should connect to</param>
        /// <returns>The task of listening for messages from the websocket.</returns>
        private async Task StartListeningForMessagesInternalAsync(Uri endpoint)
        private async Task StartListeningForMessagesInternalAsync()
        {
            this.IsListeningForMessages = true;
            this.keepListeningForMessages = true;

            try
            {
                while (this.keepListeningForMessages)
                {
                    Task<HttpResponseMessage> webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint);
                    await webSocketTask.ConfigureAwait(false);
                    webSocketTask.Wait();

@@ -121,5 +150,24 @@ namespace Microsoft.Tools.WindowsDevicePortal
                this.IsListeningForMessages = false;
            }
        }

        /// <summary>
        /// Sends a message to the server.
        /// </summary>
        /// <param name="message">The message to send.</param>
        /// <returns>The task of sending the message to the websocket</returns>
        private async Task SendMessageInternalAsync(string message)
        {
            await webSocketTask.ConfigureAwait(false);
            webSocketTask.Wait();

            using (HttpResponseMessage response = webSocketTask.Result)
            {
                if (!response.IsSuccessStatusCode)
                {
                    throw new DevicePortalException(response);
                }
            }
        }
    }
}
+48 −19
Original line number Diff line number Diff line
@@ -32,10 +32,15 @@ namespace Microsoft.Tools.WindowsDevicePortal
        public static readonly string EtwProvidersApi = "api/etw/providers";

        /// <summary>
        /// Web socket to get ETW events.!
        /// Web socket to get ETW events.
        /// </summary>
        private WebSocket<EtwEvents> realtimeEventsWebSocket;

        /// <summary>
        /// Determines if the event listener has been registered
        /// </summary>
        private bool isListeningForRealtimeEvents = false;

        /// <summary>
        /// The ETW event message received handler
        /// </summary>
@@ -59,31 +64,36 @@ namespace Microsoft.Tools.WindowsDevicePortal
            return await this.GetAsync<EtwProviders>(EtwProvidersApi);
        }

        /// <summary>
        /// Toggles the listening state of a specific provider on the realtime events WebSocket.
        /// </summary>
        /// <param name="etwProvider">The provider to update the listening state of.</param>
        /// <param name="isEnabled">Determines whether the listening state should be enabled or disabled.</param>
        /// <returns>Task for toggling the listening state of the specified provider.</returns>
        public async Task ToggleEtwProviderAsync(EtwProviderInfo etwProvider, bool isEnabled = true)
        {
            string action = isEnabled ? "enable" : "disable";
            string message = $"provider {etwProvider.GUID} {action}";

            await this.InitializeRealtimeEventsWebSocket();
            await this.realtimeEventsWebSocket.SendMessageAsync(message);
        }

        /// <summary>
        /// Starts listening for ETW events with it being returned via the RealtimeEventsMessageReceived event handler.
        /// </summary>
        /// <returns>Task for connecting to the WebSocket but not for listening to it.</returns>
        public async Task StartListeningForEtwEventsAsync()
        {
            if (this.realtimeEventsWebSocket == null)
            {
#if WINDOWS_UWP
                this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection);
#else
                this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection, this.ServerCertificateValidation);
#endif
            await this.InitializeRealtimeEventsWebSocket();

                this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler;
            }
            else
            if (!this.isListeningForRealtimeEvents)
            {
                if (this.realtimeEventsWebSocket.IsListeningForMessages)
                {
                    return;
                }
                this.isListeningForRealtimeEvents = true;
                this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler;
            }

            await this.realtimeEventsWebSocket.StartListeningForMessagesAsync(RealtimeEtwSessionApi);
            await this.realtimeEventsWebSocket.ReceiveMessagesAsync();
        }

        /// <summary>
@@ -92,12 +102,31 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// <returns>Task for stop listening for ETW events and disconnecting from the WebSocket.</returns>
        public async Task StopListeningForEtwEventsAsync()
        {
            if (this.realtimeEventsWebSocket == null || !this.realtimeEventsWebSocket.IsListeningForMessages)
            if (this.isListeningForRealtimeEvents)
            {
                return;
                this.isListeningForRealtimeEvents = false;
                this.realtimeEventsWebSocket.WebSocketMessageReceived -= this.EtwEventsReceivedHandler;
            }

            await this.realtimeEventsWebSocket.CloseAsync();
        }

        /// <summary>
        /// Creates a new <see cref="WebSocket{EtwEvents}"/> if it hasn't already been initialized.
        /// </summary>
        /// <returns>Task for connecting the ETW realtime event WebSocket.</returns>
        private async Task InitializeRealtimeEventsWebSocket()
        {
            if (this.realtimeEventsWebSocket == null)
            {
#if WINDOWS_UWP
                this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection);
#else
                this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection, this.ServerCertificateValidation);
#endif
            }

            await this.realtimeEventsWebSocket.StopListeningForMessagesAsync();
            await this.realtimeEventsWebSocket.ConnectAsync(RealtimeEtwSessionApi);
        }

        /// <summary>
+8 −14
Original line number Diff line number Diff line
@@ -79,7 +79,8 @@ namespace Microsoft.Tools.WindowsDevicePortal
                }
            }

            await this.deviceProcessesWebSocket.StartListeningForMessagesAsync(RunningProcessApi);
            await this.deviceProcessesWebSocket.ConnectAsync(RunningProcessApi);
            await this.deviceProcessesWebSocket.ReceiveMessagesAsync();
        }

        /// <summary>
@@ -88,12 +89,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// <returns>Task for stop listening for processes and disconnecting from the websocket .</returns>
        public async Task StopListeningForRunningProcessesAsync()
        {
            if (this.deviceProcessesWebSocket == null || !this.deviceProcessesWebSocket.IsListeningForMessages)
            {
                return;
            }

            await this.deviceProcessesWebSocket.StopListeningForMessagesAsync();
            await this.deviceProcessesWebSocket.CloseAsync();
        }

        /// <summary>
@@ -129,7 +125,8 @@ namespace Microsoft.Tools.WindowsDevicePortal
                }
            }

            await this.systemPerfWebSocket.StartListeningForMessagesAsync(SystemPerfApi);
            await this.systemPerfWebSocket.ConnectAsync(SystemPerfApi);
            await this.systemPerfWebSocket.ReceiveMessagesAsync();
        }

        /// <summary>
@@ -138,12 +135,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// <returns>Task for stop listening for system perf and disconnecting from the websocket .</returns>
        public async Task StopListeningForSystemPerfAsync()
        {
            if (this.systemPerfWebSocket == null || !this.systemPerfWebSocket.IsListeningForMessages)
            {
                return;
            }

            await this.systemPerfWebSocket.StopListeningForMessagesAsync();
            await this.systemPerfWebSocket.CloseAsync();
        }

        /// <summary>
@@ -431,6 +423,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
                        break;
                    }
                }

                return found;
            }

@@ -455,6 +448,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
                        break;
                    }
                }

                return found;
            }
        }
+5 −2
Original line number Diff line number Diff line
@@ -372,12 +372,15 @@ namespace Microsoft.Tools.WindowsDevicePortal

                websocket.WebSocketStreamReceived += streamReceivedHandler;

                Task startListeningForStreamTask = websocket.StartListeningForMessagesAsync(endpoint);
                Task connect = websocket.ConnectAsync(endpoint);
                connect.Wait();

                Task startListeningForStreamTask = websocket.ReceiveMessagesAsync();
                startListeningForStreamTask.Wait();

                streamReceived.WaitOne();

                Task stopListeningForStreamTask = websocket.StopListeningForMessagesAsync();
                Task stopListeningForStreamTask = websocket.CloseAsync();
                stopListeningForStreamTask.Wait();

                websocket.WebSocketStreamReceived -= streamReceivedHandler;
+53 −14
Original line number Diff line number Diff line
@@ -53,6 +53,15 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// </summary>
        public event WebSocketStreamReceivedEventInternalHandler<T> WebSocketStreamReceived;

        /// <summary>
        /// Gets a value indicating whether the web socket is connected.
        /// </summary>
        public bool IsConnected
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets a value indicating whether the web socket is listening for messages.
        /// </summary>
@@ -62,35 +71,65 @@ namespace Microsoft.Tools.WindowsDevicePortal
            private set;
        }

        /// <summary>
        /// Initialize a connection to the websocket.
        /// </summary>
        /// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
        /// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
        /// <returns>The task of opening the websocket connection.</returns>
        internal async Task ConnectAsync(string apiPath, string payload = null)
        {
            if (this.IsConnected)
            {
                return;
            }

            Uri uri = Utilities.BuildEndpoint(
                this.deviceConnection.WebSocketConnection,
                apiPath,
                payload);
            await this.ConnectInternalAsync(uri);
        }

        /// <summary>
        /// Closes the connection to the websocket and stop listening for messages.
        /// </summary>
        /// <returns>The task of closing the websocket connection.</returns>
        internal async Task StopListeningForMessagesAsync()
        internal async Task CloseAsync()
        {
            if (this.IsConnected)
            {
                if (this.IsListeningForMessages)
                {
                    await this.StopListeningForMessagesInternalAsync();
                }

                await this.CloseInternalAsync();
            }
        }

        /// <summary>
        /// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised.
        /// </summary>
        /// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
        /// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
        /// <returns>The task of listening for messages from the websocket.</returns>
        internal async Task StartListeningForMessagesAsync(
            string apiPath,
            string payload = null)
        internal async Task ReceiveMessagesAsync()
        {
            if (!this.IsListeningForMessages)
            if (this.IsConnected && !this.IsListeningForMessages)
            {
                Uri uri = Utilities.BuildEndpoint(
                    this.deviceConnection.WebSocketConnection,
                    apiPath,
                    payload);
                await this.StartListeningForMessagesInternalAsync(uri);
                await this.StartListeningForMessagesInternalAsync();
            }
        }

        /// <summary>
        /// Sends a message to the server.
        /// </summary>
        /// <param name="message">The message to send.</param>
        /// <returns>The task of sending a message to the websocket.</returns>
        internal async Task SendMessageAsync(string message)
        {
            if (this.IsConnected)
            {
                await this.SendMessageInternalAsync(message);
            }
        }

Loading