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",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.3;
MARKETING_VERSION = 1.3.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
PRODUCT_NAME = bitchat;
REGISTER_APP_GROUPS = YES;
@@ -1271,7 +1271,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.3;
MARKETING_VERSION = 1.3.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
PRODUCT_NAME = bitchat;
REGISTER_APP_GROUPS = YES;
+26 -7
View File
@@ -194,8 +194,9 @@ class NostrRelayManager: ObservableObject {
// SecureLogger.log("📋 Subscription filter JSON: \(messageString.prefix(200))...",
// category: SecureLogger.session, level: .debug)
// Target specific relays if provided; else all connections
let urls = relayUrls ?? Self.defaultRelays
// Target specific relays if provided; else default. Filter permanently failed relays.
let baseUrls = relayUrls ?? Self.defaultRelays
let urls = baseUrls.filter { !isPermanentlyFailed($0) }
ensureConnections(to: urls)
let targets: [(String, URLSessionWebSocketTask)] = urls.compactMap { url in
connections[url].map { (url, $0) }
@@ -278,6 +279,9 @@ class NostrRelayManager: ObservableObject {
if connections[urlString] != nil {
return
}
if isPermanentlyFailed(urlString) {
return
}
// Attempting to connect to Nostr relay via the proxied session
@@ -483,18 +487,21 @@ class NostrRelayManager: ObservableObject {
subscriptions.removeValue(forKey: relayUrl)
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 ns = error as NSError
if errorDescription.contains("hostname could not be found") ||
errorDescription.contains("dns") {
// Only log once for DNS failures
errorDescription.contains("dns") ||
(ns.domain == NSURLErrorDomain && ns.code == NSURLErrorBadServerResponse) {
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 }) {
relays[index].lastError = error
relays[index].reconnectAttempts = maxReconnectAttempts
relays[index].nextReconnectTime = nil
}
pendingSubscriptions[relayUrl] = nil
return
}
@@ -575,6 +582,18 @@ class NostrRelayManager: ObservableObject {
// Reconnect
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)