From 376e7b55b4a0c4f54faa0c2828dea4fd17dff288 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Wed, 18 Mar 2026 11:59:38 -0500 Subject: realigning commits --- DomainDig/ReachabilityService.swift | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 DomainDig/ReachabilityService.swift (limited to 'DomainDig/ReachabilityService.swift') diff --git a/DomainDig/ReachabilityService.swift b/DomainDig/ReachabilityService.swift new file mode 100644 index 0000000..0bb30ae --- /dev/null +++ b/DomainDig/ReachabilityService.swift @@ -0,0 +1,67 @@ +import Foundation +import Network + +struct ReachabilityService { + static func check(domain: String, port: UInt16) async -> PortReachability { + await withCheckedContinuation { continuation in + let host = NWEndpoint.Host(domain) + let nwPort = NWEndpoint.Port(rawValue: port)! + let connection = NWConnection(host: host, port: nwPort, using: .tcp) + let context = ConnectionContext(port: port, connection: connection, continuation: continuation) + + connection.stateUpdateHandler = { state in + switch state { + case .ready: + context.finish(reachable: true) + case .failed, .cancelled: + context.finish(reachable: false) + default: + break + } + } + + let queue = DispatchQueue(label: "reachability.\(port)") + connection.start(queue: queue) + + queue.asyncAfter(deadline: .now() + 5) { + context.finish(reachable: false) + } + } + } + + static func checkAll(domain: String) async -> [PortReachability] { + async let port443 = check(domain: domain, port: 443) + async let port80 = check(domain: domain, port: 80) + return await [port443, port80] + } +} + +private final class ConnectionContext: @unchecked Sendable { + private let port: UInt16 + private let connection: NWConnection + private let continuation: CheckedContinuation + private let start = CFAbsoluteTimeGetCurrent() + private let lock = NSLock() + private nonisolated(unsafe) var resumed = false + + init(port: UInt16, connection: NWConnection, continuation: CheckedContinuation) { + self.port = port + self.connection = connection + self.continuation = continuation + } + + nonisolated func finish(reachable: Bool) { + lock.lock() + guard !resumed else { + lock.unlock() + return + } + resumed = true + lock.unlock() + + let elapsed = CFAbsoluteTimeGetCurrent() - start + let ms = reachable ? Int(elapsed * 1000) : nil + connection.cancel() + continuation.resume(returning: PortReachability(port: port, reachable: reachable, latencyMs: ms)) + } +} -- cgit v1.2.3