Просмотр исходного кода

Never let an unbuildable address take down the page rendering it

BrowsableUrl built its result with UriBuilder, which throws UriFormatException
on a host it cannot parse — "a/b", a name with spaces, a bare run of dots. The
address field holds whatever a person typed or a collector read off a device,
and neither is obliged to produce something a URL can be built from.

That throw was already latent on the service card, where it would have spoiled
one card. It matters more now: the dependency trees call this for every service
they render, so a single malformed address would blank the entire hardware or
system page instead of costing one link.

A missing link costs a click. Letting this escape costs the page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tim Jones 20 часов назад
Родитель
Сommit
d801aaa685

+ 10 - 1
RackPeek.Domain/Resources/Services/ServiceEndpoint.cs

@@ -61,7 +61,16 @@ public static class ServiceEndpoint {
         if (scheme == null)
             return null;
 
-        return new UriBuilder(scheme, ip) { Port = port }.Uri.ToString();
+        try {
+            return new UriBuilder(scheme, ip) { Port = port }.Uri.ToString();
+        }
+        catch (UriFormatException) {
+            // Whatever is in the address field, a person put it there by hand or a
+            // collector read it off a device, and neither is obliged to produce something
+            // a URL can be built from. A missing link costs a click; letting this escape
+            // would take down every page that renders the resource.
+            return null;
+        }
     }
 
     private static string? SchemeFor(int port, string? protocol) {

+ 19 - 0
Tests.Discovery/ServiceEndpointTests.cs

@@ -93,4 +93,23 @@ public class ServiceEndpointTests {
     [Fact]
     public void A_service_with_no_port_is_not_guessed_at() =>
         Assert.Null(Svc(Net(null)).BrowsableUrl());
+
+    [Theory]
+    [InlineData("host name with spaces")]
+    [InlineData("...")]
+    [InlineData("a/b")]
+    [InlineData("under_score.local")]
+    [InlineData("fe80::1")]
+    [InlineData("[::1]")]
+    public void An_address_no_url_can_be_built_from_yields_no_link_rather_than_throwing(string ip) {
+        // The address field holds whatever a person typed or a device reported, and
+        // neither is obliged to produce something a URL can be built from. This is
+        // rendered inside the hardware and system trees, so an exception here would
+        // blank the whole page rather than spoil one link.
+        Service service = Svc(Net(8080, ip: ip));
+
+        Exception? thrown = Record.Exception(() => service.BrowsableUrl());
+
+        Assert.Null(thrown);
+    }
 }