diff --git a/WirelessTextSyncer.Windows.Tests/SyncMessageTests.cs b/WirelessTextSyncer.Windows.Tests/SyncMessageTests.cs index 478f5b8..20f7407 100644 --- a/WirelessTextSyncer.Windows.Tests/SyncMessageTests.cs +++ b/WirelessTextSyncer.Windows.Tests/SyncMessageTests.cs @@ -74,6 +74,20 @@ public sealed class SyncMessageTests Assert.AreEqual(8181, root.GetProperty("port").GetInt32()); } + [TestMethod] + public void WebSocketServiceInfoContainsDeviceName() + { + using var server = new WebSocketServerService(new SyncMessageHandler(new RecordingKeyboardInjectionService()), "Desktop-WIN11"); + + using var document = JsonDocument.Parse(server.BuildServiceInfoJson()); + var root = document.RootElement; + + Assert.AreEqual("wirelessTextSyncer.service", root.GetProperty("type").GetString()); + Assert.AreEqual(1, root.GetProperty("version").GetInt32()); + Assert.AreEqual("Desktop-WIN11", root.GetProperty("name").GetString()); + Assert.AreEqual(8181, root.GetProperty("port").GetInt32()); + } + private sealed class RecordingKeyboardInjectionService : IKeyboardInjectionService { public List Calls { get; } = []; diff --git a/WirelessTextSyncer.Windows/Services/WebSocketServerService.cs b/WirelessTextSyncer.Windows/Services/WebSocketServerService.cs index 900fbee..99ef337 100644 --- a/WirelessTextSyncer.Windows/Services/WebSocketServerService.cs +++ b/WirelessTextSyncer.Windows/Services/WebSocketServerService.cs @@ -17,14 +17,16 @@ public sealed class WebSocketServerService : IDisposable private readonly TimeSpan heartbeatInterval = TimeSpan.FromSeconds(5); private readonly SyncMessageHandler messageHandler; + private readonly string deviceName; private readonly List clients = []; private readonly object clientsLock = new(); private WebSocketServer? server; private System.Threading.Timer? heartbeatTimer; - public WebSocketServerService(SyncMessageHandler messageHandler) + public WebSocketServerService(SyncMessageHandler messageHandler, string? deviceName = null) { this.messageHandler = messageHandler; + this.deviceName = string.IsNullOrWhiteSpace(deviceName) ? Environment.MachineName : deviceName; } public int Port { get; private set; } = 8181; @@ -45,6 +47,18 @@ public sealed class WebSocketServerService : IDisposable public event EventHandler? StatusChanged; + public string BuildServiceInfoJson() + { + return JsonSerializer.Serialize( + new ServiceInfoMessage( + DiscoveryResponderService.ServiceResponseType, + 1, + deviceName, + LocalIpAddress, + Port), + JsonOptions); + } + public void Start(int port) { Port = port; @@ -72,6 +86,7 @@ public sealed class WebSocketServerService : IDisposable } AppLogger.Info($"Client connected: {socket.ConnectionInfo.ClientIpAddress}:{socket.ConnectionInfo.ClientPort}"); + _ = SendServiceInfoAsync(socket); StatusChanged?.Invoke(this, EventArgs.Empty); }; socket.OnClose = () => @@ -155,6 +170,19 @@ public sealed class WebSocketServerService : IDisposable } } + private async Task SendServiceInfoAsync(IWebSocketConnection client) + { + try + { + await client.Send(BuildServiceInfoJson()); + } + catch (Exception exception) + { + AppLogger.Error("Failed to send WebSocket service info.", exception); + RemoveClient(client, "service info failure"); + } + } + private void RemoveClient(IWebSocketConnection client, string reason) { var removed = false; @@ -215,4 +243,6 @@ public sealed class WebSocketServerService : IDisposable return address?.ToString() ?? "127.0.0.1"; } + + private sealed record ServiceInfoMessage(string Type, int Version, string Name, string Host, int Port); }