@@ -15,8 +15,7 @@ internal enum HostKeyVerifier {
1515 private static let logger = Logger ( subsystem: " com.TablePro " , category: " HostKeyVerifier " )
1616
1717 /// Verify the host key, prompting the user if needed.
18- /// This method blocks the calling thread while showing UI prompts.
19- /// Must be called from a background thread.
18+ /// Uses `withCheckedContinuation` to await UI prompts without blocking the cooperative thread pool.
2019 /// - Parameters:
2120 /// - keyData: The raw host key bytes from the SSH session
2221 /// - keyType: The key type string (e.g. "ssh-rsa", "ssh-ed25519")
@@ -28,7 +27,7 @@ internal enum HostKeyVerifier {
2827 keyType: String ,
2928 hostname: String ,
3029 port: Int
31- ) throws {
30+ ) async throws {
3231 let result = HostKeyStore . shared. verify (
3332 keyData: keyData,
3433 keyType: keyType,
@@ -43,7 +42,7 @@ internal enum HostKeyVerifier {
4342
4443 case . unknown( let fingerprint, let keyType) :
4544 logger. info ( " Unknown host key for [ \( hostname) ]: \( port) , prompting user " )
46- let accepted = promptUnknownHost (
45+ let accepted = await promptUnknownHost (
4746 hostname: hostname,
4847 port: port,
4948 fingerprint: fingerprint,
@@ -62,7 +61,7 @@ internal enum HostKeyVerifier {
6261
6362 case . mismatch( let expected, let actual) :
6463 logger. warning ( " Host key mismatch for [ \( hostname) ]: \( port) " )
65- let accepted = promptHostKeyMismatch (
64+ let accepted = await promptHostKeyMismatch (
6665 hostname: hostname,
6766 port: port,
6867 expected: expected,
@@ -83,17 +82,14 @@ internal enum HostKeyVerifier {
8382
8483 // MARK: - UI Prompts
8584
86- /// Show a dialog asking the user whether to trust an unknown host
87- /// Blocks the calling thread until the user responds.
85+ /// Show a dialog asking the user whether to trust an unknown host.
86+ /// Suspends until the user responds, without blocking any thread .
8887 private static func promptUnknownHost(
8988 hostname: String ,
9089 port: Int ,
9190 fingerprint: String ,
9291 keyType: String
93- ) -> Bool {
94- let semaphore = DispatchSemaphore ( value: 0 )
95- var accepted = false
96-
92+ ) async -> Bool {
9793 let hostDisplay = " [ \( hostname) ]: \( port) "
9894 let title = String ( localized: " Unknown SSH Host " )
9995 let message = String ( localized: """
@@ -105,34 +101,29 @@ internal enum HostKeyVerifier {
105101 Are you sure you want to continue connecting?
106102 """ )
107103
108- DispatchQueue . main. async {
109- let alert = NSAlert ( )
110- alert. messageText = title
111- alert. informativeText = message
112- alert. alertStyle = . informational
113- alert. addButton ( withTitle: String ( localized: " Trust " ) )
114- alert. addButton ( withTitle: String ( localized: " Cancel " ) )
115-
116- let response = alert. runModal ( )
117- accepted = ( response == . alertFirstButtonReturn)
118- semaphore. signal ( )
104+ return await withCheckedContinuation { continuation in
105+ DispatchQueue . main. async {
106+ let alert = NSAlert ( )
107+ alert. messageText = title
108+ alert. informativeText = message
109+ alert. alertStyle = . informational
110+ alert. addButton ( withTitle: String ( localized: " Trust " ) )
111+ alert. addButton ( withTitle: String ( localized: " Cancel " ) )
112+
113+ let response = alert. runModal ( )
114+ continuation. resume ( returning: response == . alertFirstButtonReturn)
115+ }
119116 }
120-
121- semaphore. wait ( )
122- return accepted
123117 }
124118
125- /// Show a warning dialog about a changed host key (potential MITM attack)
126- /// Blocks the calling thread until the user responds.
119+ /// Show a warning dialog about a changed host key (potential MITM attack).
120+ /// Suspends until the user responds, without blocking any thread .
127121 private static func promptHostKeyMismatch(
128122 hostname: String ,
129123 port: Int ,
130124 expected: String ,
131125 actual: String
132- ) -> Bool {
133- let semaphore = DispatchSemaphore ( value: 0 )
134- var accepted = false
135-
126+ ) async -> Bool {
136127 let hostDisplay = " [ \( hostname) ]: \( port) "
137128 let title = String ( localized: " SSH Host Key Changed " )
138129 let message = String ( localized: """
@@ -144,24 +135,22 @@ internal enum HostKeyVerifier {
144135 Current fingerprint: \( actual)
145136 """ )
146137
147- DispatchQueue . main. async {
148- let alert = NSAlert ( )
149- alert. messageText = title
150- alert. informativeText = message
151- alert. alertStyle = . critical
152- alert. addButton ( withTitle: String ( localized: " Connect Anyway " ) )
153- alert. addButton ( withTitle: String ( localized: " Disconnect " ) )
154-
155- // Make "Disconnect" the default button (Return key) instead of "Connect Anyway"
156- alert. buttons [ 1 ] . keyEquivalent = " \r "
157- alert. buttons [ 0 ] . keyEquivalent = " "
158-
159- let response = alert. runModal ( )
160- accepted = ( response == . alertFirstButtonReturn)
161- semaphore. signal ( )
138+ return await withCheckedContinuation { continuation in
139+ DispatchQueue . main. async {
140+ let alert = NSAlert ( )
141+ alert. messageText = title
142+ alert. informativeText = message
143+ alert. alertStyle = . critical
144+ alert. addButton ( withTitle: String ( localized: " Connect Anyway " ) )
145+ alert. addButton ( withTitle: String ( localized: " Disconnect " ) )
146+
147+ // Make "Disconnect" the default button (Return key) instead of "Connect Anyway"
148+ alert. buttons [ 1 ] . keyEquivalent = " \r "
149+ alert. buttons [ 0 ] . keyEquivalent = " "
150+
151+ let response = alert. runModal ( )
152+ continuation. resume ( returning: response == . alertFirstButtonReturn)
153+ }
162154 }
163-
164- semaphore. wait ( )
165- return accepted
166155 }
167156}
0 commit comments