Relays: treat repeated -1011 handshake failures as permanent; skip reconnects

- Classify NSURLErrorBadServerResponse as permanent and stop retrying
- Filter permanently-failed relays from subscribe/connect attempts
- Avoid reconnect scheduling for permanently failed relays
This commit is contained in:
jack
2025-09-10 14:21:32 +02:00
parent 8dad90685d
commit 08d1fa7653
2 changed files with 28 additions and 9 deletions
+2 -2
View File
@@ -1182,7 +1182,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.3; MARKETING_VERSION = 1.3.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
PRODUCT_NAME = bitchat; PRODUCT_NAME = bitchat;
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;
@@ -1271,7 +1271,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.3; MARKETING_VERSION = 1.3.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
PRODUCT_NAME = bitchat; PRODUCT_NAME = bitchat;
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;
+26 -7
View File
@@ -194,8 +194,9 @@ class NostrRelayManager: ObservableObject {
// SecureLogger.log("📋 Subscription filter JSON: \(messageString.prefix(200))...", // SecureLogger.log("📋 Subscription filter JSON: \(messageString.prefix(200))...",
// category: SecureLogger.session, level: .debug) // category: SecureLogger.session, level: .debug)
// Target specific relays if provided; else all connections // Target specific relays if provided; else default. Filter permanently failed relays.
let urls = relayUrls ?? Self.defaultRelays let baseUrls = relayUrls ?? Self.defaultRelays
let urls = baseUrls.filter { !isPermanentlyFailed($0) }
ensureConnections(to: urls) ensureConnections(to: urls)
let targets: [(String, URLSessionWebSocketTask)] = urls.compactMap { url in let targets: [(String, URLSessionWebSocketTask)] = urls.compactMap { url in
connections[url].map { (url, $0) } connections[url].map { (url, $0) }
@@ -278,6 +279,9 @@ class NostrRelayManager: ObservableObject {
if connections[urlString] != nil { if connections[urlString] != nil {
return return
} }
if isPermanentlyFailed(urlString) {
return
}
// Attempting to connect to Nostr relay via the proxied session // Attempting to connect to Nostr relay via the proxied session
@@ -483,18 +487,21 @@ class NostrRelayManager: ObservableObject {
subscriptions.removeValue(forKey: relayUrl) subscriptions.removeValue(forKey: relayUrl)
updateRelayStatus(relayUrl, isConnected: false, error: error) updateRelayStatus(relayUrl, isConnected: false, error: error)
// Check if this is a DNS error // Check if this is a DNS or handshake error; treat as permanent
let errorDescription = error.localizedDescription.lowercased() let errorDescription = error.localizedDescription.lowercased()
let ns = error as NSError
if errorDescription.contains("hostname could not be found") || if errorDescription.contains("hostname could not be found") ||
errorDescription.contains("dns") { errorDescription.contains("dns") ||
// Only log once for DNS failures (ns.domain == NSURLErrorDomain && ns.code == NSURLErrorBadServerResponse) {
if relays.first(where: { $0.url == relayUrl })?.lastError == nil { if relays.first(where: { $0.url == relayUrl })?.lastError == nil {
SecureLogger.log("Nostr relay DNS failure for \(relayUrl) - not retrying", category: SecureLogger.session, level: .warning) SecureLogger.log("Nostr relay permanent failure for \(relayUrl) - not retrying (code=\(ns.code))", category: SecureLogger.session, level: .warning)
} }
// Mark relay as permanently failed
if let index = relays.firstIndex(where: { $0.url == relayUrl }) { if let index = relays.firstIndex(where: { $0.url == relayUrl }) {
relays[index].lastError = error relays[index].lastError = error
relays[index].reconnectAttempts = maxReconnectAttempts
relays[index].nextReconnectTime = nil
} }
pendingSubscriptions[relayUrl] = nil
return return
} }
@@ -575,6 +582,18 @@ class NostrRelayManager: ObservableObject {
// Reconnect // Reconnect
connect() connect()
} }
// MARK: - Failure classification
private func isPermanentlyFailed(_ url: String) -> Bool {
guard let r = relays.first(where: { $0.url == url }) else { return false }
if r.reconnectAttempts >= maxReconnectAttempts { return true }
if let ns = r.lastError as NSError?, ns.domain == NSURLErrorDomain {
if ns.code == NSURLErrorBadServerResponse || ns.code == NSURLErrorCannotFindHost {
return true
}
}
return false
}
} }
// MARK: - Off-main inbound parsing helpers (file scope, non-isolated) // MARK: - Off-main inbound parsing helpers (file scope, non-isolated)