From fa7bd3dda81718a5ad668f45afaf159ea3d703c3 Mon Sep 17 00:00:00 2001
From: pavelsavara <pavel.savara@gmail.com>
Date: Tue, 25 Mar 2025 15:44:46 +0100
Subject: [PATCH 1/5] rebase

---
 .../WebSocketServerImpl.cs                    | 22 +++++++++----------
 src/Tools/dotnet-dsrouter/Program.cs          |  3 ++-
 2 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs b/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
index f34e5907ce..5d7b821ba3 100644
--- a/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
+++ b/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
@@ -172,24 +172,24 @@ private static void ParseWebSocketURL(string endPoint, out Uri uri)
             uriToParse = endPoint;
         }
 
-        string[] supportedSchemes = new string[] { "ws", "wss", "http", "https" };
 
         if (!string.IsNullOrEmpty(uriToParse) && Uri.TryCreate(uriToParse, UriKind.Absolute, out uri))
         {
-            bool supported = false;
-            foreach (string scheme in supportedSchemes)
+            if (uri.Scheme == "ws")
             {
-                if (string.Equals(uri.Scheme, scheme, StringComparison.InvariantCultureIgnoreCase))
-                {
-                    supported = true;
-                    break;
-                }
+                uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
+                return;
             }
-            if (!supported)
+            if (uri.Scheme == "wss")
             {
-                throw new ArgumentException(string.Format("Unsupported Uri schema, \"{0}\"", uri.Scheme));
+                uri = new UriBuilder(uri) { Scheme = "https" }.Uri;
+                return;
             }
-            return;
+            if (uri.Scheme == "http" || uri.Scheme == "https")
+            {
+                return;
+            }
+            throw new ArgumentException(string.Format("Unsupported Uri schema, \"{0}\"", uri.Scheme));
         }
         else
         {
diff --git a/src/Tools/dotnet-dsrouter/Program.cs b/src/Tools/dotnet-dsrouter/Program.cs
index 253ad35b4a..27b347c053 100644
--- a/src/Tools/dotnet-dsrouter/Program.cs
+++ b/src/Tools/dotnet-dsrouter/Program.cs
@@ -267,7 +267,8 @@ private static Command AndroidRouterCommand()
             new("--web-socket", "-ws")
             {
                 Description = "The router WebSocket address using format ws://[host]:[port]/[path] or wss://[host]:[port]/[path]. " +
-                                "Launch app with WasmExtraConfig property specifying diagnostic_options with a server connect_url"
+                                "Launch app with WasmExtraConfig property specifying diagnostic_options with a server connect_url",
+                DefaultValueFactory = _ => "ws://127.0.0.1:8088/diagnostics"
             };
 
         private static readonly Option<int> RuntimeTimeoutOption =

From f1797a4b9c036ba603d385f0ef74c913e91468cb Mon Sep 17 00:00:00 2001
From: pavelsavara <pavel.savara@gmail.com>
Date: Tue, 25 Mar 2025 16:17:29 +0100
Subject: [PATCH 2/5] longer timeout for the browser WS

---
 .../ReversedServer/ReversedDiagnosticsServer.cs                 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
index b0ea799412..b0b222e1de 100644
--- a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
+++ b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
@@ -21,7 +21,7 @@ internal sealed class ReversedDiagnosticsServer : IAsyncDisposable
     {
         // The amount of time to allow parsing of the advertise data before cancelling. This allows the server to
         // remain responsive in case the advertise data is incomplete and the stream is not closed.
-        private static readonly TimeSpan ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(250);
+        private static readonly TimeSpan ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
 
         private readonly CancellationTokenSource _disposalSource = new();
         private readonly HandleableCollection<IpcEndpointInfo> _endpointInfos = new();

From 8e52e38d3512d142d1e025e1ccce443f39882e80 Mon Sep 17 00:00:00 2001
From: pavelsavara <pavel.savara@gmail.com>
Date: Thu, 27 Mar 2025 11:15:50 +0100
Subject: [PATCH 3/5] feedback

---
 .../DiagnosticsServerRouterFactory.cs         |  6 ++--
 .../ReversedDiagnosticsServer.cs              | 31 +++++++++++++++++--
 src/Tools/dotnet-dsrouter/Program.cs          |  4 +--
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs
index c79bd3c15e..e7287c0fd9 100644
--- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs
+++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs
@@ -356,9 +356,11 @@ public static WebSocketServerRouterFactory CreateDefaultInstance(string webSocke
 
         public WebSocketServerRouterFactory(string webSocketURL, int runtimeTimeoutMs, ILogger logger) : base(runtimeTimeoutMs, logger)
         {
-            _webSocketURL = string.IsNullOrEmpty(webSocketURL) ? "ws://127.0.0.1:8088/diagnostics" : webSocketURL;
+            Debug.Assert(!string.IsNullOrEmpty(webSocketURL));
 
-            _webSocketServer = new ReversedDiagnosticsServer(_webSocketURL, ReversedDiagnosticsServer.Kind.WebSocket);
+            _webSocketURL = webSocketURL;
+
+            _webSocketServer = new ReversedDiagnosticsServer(_webSocketURL, ReversedDiagnosticsServer.Kind.WebSocket, TimeSpan.FromMilliseconds(750));
             _webSocketServer.TransportCallback = this;
         }
 
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
index b0b222e1de..ebdc949e43 100644
--- a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
+++ b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
@@ -21,7 +21,7 @@ internal sealed class ReversedDiagnosticsServer : IAsyncDisposable
     {
         // The amount of time to allow parsing of the advertise data before cancelling. This allows the server to
         // remain responsive in case the advertise data is incomplete and the stream is not closed.
-        private static readonly TimeSpan ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
+        private readonly TimeSpan ParseAdvertiseTimeout;
 
         private readonly CancellationTokenSource _disposalSource = new();
         private readonly HandleableCollection<IpcEndpointInfo> _endpointInfos = new();
@@ -52,6 +52,7 @@ public enum Kind
         public ReversedDiagnosticsServer(string address)
         {
             _address = address;
+            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
         }
 
         /// <summary>
@@ -69,12 +70,38 @@ public ReversedDiagnosticsServer(string address)
         /// Otherwise if kind is TcpIp as a supported protocol for ReversedDiagnosticServer. When Kind is Tcp, address will
         /// be analyzed and if on format host:port, ReversedDiagnosticServer will try to bind
         /// a TcpIp listener to host and port, otherwise it will use a Unix domain socket or a Windows named pipe.
-        ///
         /// </param>
         public ReversedDiagnosticsServer(string address, Kind kind)
         {
             _address = address;
             _kind = kind;
+            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
+        }
+
+        /// <summary>
+        /// Constructs the <see cref="ReversedDiagnosticsServer"/> instance with an endpoint bound
+        /// to the location specified by <paramref name="address"/>.
+        /// </summary>
+        /// <param name="address">
+        /// The server endpoint.
+        /// On Windows, this can be a full pipe path or the name without the "\\.\pipe\" prefix.
+        /// On all other systems, this must be the full file path of the socket.
+        /// </param>
+        /// <param name="kind">
+        /// If kind is WebSocket, start a Kestrel web server.
+        /// Otherwise if kind is TcpIp as a supported protocol for ReversedDiagnosticServer. When Kind is Tcp, address will
+        /// be analyzed and if on format host:port, ReversedDiagnosticServer will try to bind
+        /// a TcpIp listener to host and port, otherwise it will use a Unix domain socket or a Windows named pipe.
+        /// </param>
+        /// <param name="timeout">
+        /// The amount of time to allow parsing of the advertise data before cancelling. This allows the server to
+        /// remain responsive in case the advertise data is incomplete and the stream is not closed.
+        /// </param>
+        public ReversedDiagnosticsServer(string address, Kind kind, TimeSpan timeout)
+        {
+            _address = address;
+            _kind = kind;
+            ParseAdvertiseTimeout = timeout;
         }
 
         public async ValueTask DisposeAsync()
diff --git a/src/Tools/dotnet-dsrouter/Program.cs b/src/Tools/dotnet-dsrouter/Program.cs
index 27b347c053..b5cd886ab7 100644
--- a/src/Tools/dotnet-dsrouter/Program.cs
+++ b/src/Tools/dotnet-dsrouter/Program.cs
@@ -95,7 +95,7 @@ private static Command IpcServerWebSocketServerRouterCommand()
             command.SetAction((parseResult, ct) => new DiagnosticsServerRouterCommands().RunIpcServerWebSocketServerRouter(
                 ct,
                 ipcServer: parseResult.GetValue(IpcServerAddressOption) ?? "",
-                webSocket: parseResult.GetValue(WebSocketURLAddressOption) ?? "",
+                webSocket: parseResult.GetValue(WebSocketURLAddressOption),
                 runtimeTimeout: parseResult.GetValue(RuntimeTimeoutOption),
                 verbose: parseResult.GetValue(VerboseOption)
             ));
@@ -117,7 +117,7 @@ private static Command IpcClientWebSocketServerRouterCommand()
             command.SetAction((parseResult, ct) => new DiagnosticsServerRouterCommands().RunIpcClientWebSocketServerRouter(
                 ct,
                 ipcClient: parseResult.GetValue(IpcClientAddressOption) ?? "",
-                webSocket: parseResult.GetValue(WebSocketURLAddressOption) ?? "",
+                webSocket: parseResult.GetValue(WebSocketURLAddressOption),
                 runtimeTimeout: parseResult.GetValue(RuntimeTimeoutOption),
                 verbose: parseResult.GetValue(VerboseOption)
             ));

From 51d458c85368654907c0a50caf13e1ec3f47b2d2 Mon Sep 17 00:00:00 2001
From: pavelsavara <pavel.savara@gmail.com>
Date: Thu, 27 Mar 2025 12:07:05 +0100
Subject: [PATCH 4/5] feedback

---
 .../ReversedServer/ReversedDiagnosticsServer.cs               | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
index ebdc949e43..72c690c49e 100644
--- a/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
+++ b/src/Microsoft.Diagnostics.NETCore.Client/ReversedServer/ReversedDiagnosticsServer.cs
@@ -52,7 +52,7 @@ public enum Kind
         public ReversedDiagnosticsServer(string address)
         {
             _address = address;
-            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
+            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(250);
         }
 
         /// <summary>
@@ -75,7 +75,7 @@ public ReversedDiagnosticsServer(string address, Kind kind)
         {
             _address = address;
             _kind = kind;
-            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(750);
+            ParseAdvertiseTimeout = TimeSpan.FromMilliseconds(250);
         }
 
         /// <summary>

From cb73c323ebe331cb060f912f0ec5b26a9a181c6f Mon Sep 17 00:00:00 2001
From: pavelsavara <pavel.savara@gmail.com>
Date: Thu, 27 Mar 2025 16:49:22 +0100
Subject: [PATCH 5/5] feedback

---
 .../WebSocketServerImpl.cs                    | 33 ++++++++++++-------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs b/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
index 5d7b821ba3..e7e53cf986 100644
--- a/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
+++ b/src/Microsoft.Diagnostics.WebSocketServer/WebSocketServerImpl.cs
@@ -40,9 +40,18 @@ public async Task StartServer(string endpoint, CancellationToken cancellationTok
 
         ParseWebSocketURL(endpoint, out Uri uri);
 
+        string scheme = uri.Scheme switch
+        {
+            "ws" => "http",
+            "http" => "http",
+            "wss" => "https",
+            "https" => "https",
+            _ => throw new ArgumentException(string.Format("Unsupported Uri schema, \"{0}\"", uri.Scheme))
+        };
+
         EmbeddedWebSocketServer.Options options = new()
         {
-            Scheme = uri.Scheme,
+            Scheme = scheme,
             Host = uri.Host,
             Port = uri.Port.ToString(),
             Path = uri.PathAndQuery,
@@ -172,24 +181,24 @@ private static void ParseWebSocketURL(string endPoint, out Uri uri)
             uriToParse = endPoint;
         }
 
+        string[] supportedSchemes = new string[] { "ws", "wss", "http", "https" };
 
         if (!string.IsNullOrEmpty(uriToParse) && Uri.TryCreate(uriToParse, UriKind.Absolute, out uri))
         {
-            if (uri.Scheme == "ws")
-            {
-                uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
-                return;
-            }
-            if (uri.Scheme == "wss")
+            bool supported = false;
+            foreach (string scheme in supportedSchemes)
             {
-                uri = new UriBuilder(uri) { Scheme = "https" }.Uri;
-                return;
+                if (string.Equals(uri.Scheme, scheme, StringComparison.InvariantCultureIgnoreCase))
+                {
+                    supported = true;
+                    break;
+                }
             }
-            if (uri.Scheme == "http" || uri.Scheme == "https")
+            if (!supported)
             {
-                return;
+                throw new ArgumentException(string.Format("Unsupported Uri schema, \"{0}\"", uri.Scheme));
             }
-            throw new ArgumentException(string.Format("Unsupported Uri schema, \"{0}\"", uri.Scheme));
+            return;
         }
         else
         {
