Replace Starscream with URLSessionWebSocketTask
Changelog-Fixed: Fix slow reconnection issues
This commit is contained in:
committed by
William Casarin
parent
6ac68b5a73
commit
0e94c48e26
@@ -1,4 +1,3 @@
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 4),
|
|
||||||
.Package(url: "https://github.com/jb55/secp256k1.swift.git", branch: "main")
|
.Package(url: "https://github.com/jb55/secp256k1.swift.git", branch: "main")
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -216,7 +216,6 @@
|
|||||||
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEF727F7A08200C66700 /* damusTests.swift */; };
|
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEF727F7A08200C66700 /* damusTests.swift */; };
|
||||||
4CE6DF0227F7A08200C66700 /* damusUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0127F7A08200C66700 /* damusUITests.swift */; };
|
4CE6DF0227F7A08200C66700 /* damusUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0127F7A08200C66700 /* damusUITests.swift */; };
|
||||||
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
||||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; };
|
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
||||||
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
||||||
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; };
|
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; };
|
||||||
@@ -253,6 +252,7 @@
|
|||||||
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
|
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
|
||||||
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||||
|
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50088DA029E8271A008A1FDF /* WebSocket.swift */; };
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
|
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
|
||||||
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
||||||
@@ -669,6 +669,7 @@
|
|||||||
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
|
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
||||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||||
|
50088DA029E8271A008A1FDF /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
|
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
|
||||||
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
||||||
@@ -708,7 +709,6 @@
|
|||||||
files = (
|
files = (
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
||||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */,
|
|
||||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -973,6 +973,7 @@
|
|||||||
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */,
|
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */,
|
||||||
4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */,
|
4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */,
|
||||||
4C363A8F28247A1D006E126D /* NostrLink.swift */,
|
4C363A8F28247A1D006E126D /* NostrLink.swift */,
|
||||||
|
50088DA029E8271A008A1FDF /* WebSocket.swift */,
|
||||||
);
|
);
|
||||||
path = Nostr;
|
path = Nostr;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1348,7 +1349,6 @@
|
|||||||
);
|
);
|
||||||
name = damus;
|
name = damus;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
4CE6DF1127F7A2B300C66700 /* Starscream */,
|
|
||||||
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
||||||
4C06670328FC7EC500038D2A /* Kingfisher */,
|
4C06670328FC7EC500038D2A /* Kingfisher */,
|
||||||
6C7DE41E2955169800E66263 /* Vault */,
|
6C7DE41E2955169800E66263 /* Vault */,
|
||||||
@@ -1454,7 +1454,6 @@
|
|||||||
);
|
);
|
||||||
mainGroup = 4CE6DEDA27F7A08100C66700;
|
mainGroup = 4CE6DEDA27F7A08100C66700;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */,
|
|
||||||
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
||||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
||||||
@@ -1661,6 +1660,7 @@
|
|||||||
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */,
|
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */,
|
||||||
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
|
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
|
||||||
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */,
|
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */,
|
||||||
|
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */,
|
||||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
||||||
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
||||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||||
@@ -2259,14 +2259,6 @@
|
|||||||
revision = 40b4b38b3b1c83f7088c76189a742870e0ca06a9;
|
revision = 40b4b38b3b1c83f7088c76189a742870e0ca06a9;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/daltoniam/Starscream";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 4.0.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */ = {
|
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/SparrowTek/Vault";
|
repositoryURL = "https://github.com/SparrowTek/Vault";
|
||||||
@@ -2288,11 +2280,6 @@
|
|||||||
package = 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */;
|
package = 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */;
|
||||||
productName = secp256k1;
|
productName = secp256k1;
|
||||||
};
|
};
|
||||||
4CE6DF1127F7A2B300C66700 /* Starscream */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */;
|
|
||||||
productName = Starscream;
|
|
||||||
};
|
|
||||||
6C7DE41E2955169800E66263 /* Vault */ = {
|
6C7DE41E2955169800E66263 /* Vault */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
||||||
|
|||||||
@@ -17,15 +17,6 @@
|
|||||||
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "starscream",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/daltoniam/Starscream",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
|
|
||||||
"version" : "4.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "vault",
|
"identity" : "vault",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Starscream
|
|
||||||
|
|
||||||
struct TimestampedProfile {
|
struct TimestampedProfile {
|
||||||
let profile: Profile
|
let profile: Profile
|
||||||
@@ -686,31 +685,6 @@ func get_since_time(last_event: NostrEvent?) -> Int64? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? {
|
|
||||||
switch ev {
|
|
||||||
case .binary(let dat):
|
|
||||||
return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay)
|
|
||||||
case .cancelled:
|
|
||||||
return NostrEvent(content: "cancelled", pubkey: relay)
|
|
||||||
case .connected:
|
|
||||||
return NostrEvent(content: "connected", pubkey: relay)
|
|
||||||
case .disconnected:
|
|
||||||
return NostrEvent(content: "disconnected", pubkey: relay)
|
|
||||||
case .error(let err):
|
|
||||||
return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay)
|
|
||||||
case .text(let txt):
|
|
||||||
return NostrEvent(content: "text \(txt)", pubkey: relay)
|
|
||||||
case .pong:
|
|
||||||
return NostrEvent(content: "pong", pubkey: relay)
|
|
||||||
case .ping:
|
|
||||||
return NostrEvent(content: "ping", pubkey: relay)
|
|
||||||
case .viabilityChanged(let b):
|
|
||||||
return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay)
|
|
||||||
case .reconnectSuggested(let b):
|
|
||||||
return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func is_notification(ev: NostrEvent, pubkey: String) -> Bool {
|
func is_notification(ev: NostrEvent, pubkey: String) -> Bool {
|
||||||
if ev.pubkey == pubkey {
|
if ev.pubkey == pubkey {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -290,17 +290,12 @@ class HomeModel: ObservableObject {
|
|||||||
send_home_filters(relay_id: relay_id)
|
send_home_filters(relay_id: relay_id)
|
||||||
}
|
}
|
||||||
case .error(let merr):
|
case .error(let merr):
|
||||||
let desc = merr.debugDescription
|
let desc = String(describing: merr)
|
||||||
if desc.contains("Software caused connection abort") {
|
if desc.contains("Software caused connection abort") {
|
||||||
pool.reconnect(to: [relay_id])
|
pool.reconnect(to: [relay_id])
|
||||||
}
|
}
|
||||||
case .disconnected: fallthrough
|
case .disconnected:
|
||||||
case .cancelled:
|
|
||||||
pool.reconnect(to: [relay_id])
|
pool.reconnect(to: [relay_id])
|
||||||
case .reconnectSuggested(let t):
|
|
||||||
if t {
|
|
||||||
pool.reconnect(to: [relay_id])
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,12 @@ class Relay: Identifiable {
|
|||||||
let descriptor: RelayDescriptor
|
let descriptor: RelayDescriptor
|
||||||
let connection: RelayConnection
|
let connection: RelayConnection
|
||||||
|
|
||||||
var last_pong: UInt32
|
|
||||||
var flags: Int
|
var flags: Int
|
||||||
|
|
||||||
init(descriptor: RelayDescriptor, connection: RelayConnection) {
|
init(descriptor: RelayDescriptor, connection: RelayConnection) {
|
||||||
self.flags = 0
|
self.flags = 0
|
||||||
self.descriptor = descriptor
|
self.descriptor = descriptor
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.last_pong = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mark_broken() {
|
func mark_broken() {
|
||||||
|
|||||||
@@ -5,26 +5,22 @@
|
|||||||
// Created by William Casarin on 2022-04-02.
|
// Created by William Casarin on 2022-04-02.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import Starscream
|
|
||||||
|
|
||||||
enum NostrConnectionEvent {
|
enum NostrConnectionEvent {
|
||||||
case ws_event(WebSocketEvent)
|
case ws_event(WebSocketEvent)
|
||||||
case nostr_event(NostrResponse)
|
case nostr_event(NostrResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RelayConnection: WebSocketDelegate {
|
final class RelayConnection {
|
||||||
private(set) var isConnected = false
|
private(set) var isConnected = false
|
||||||
private(set) var isConnecting = false
|
private(set) var isConnecting = false
|
||||||
private(set) var isReconnecting = false
|
|
||||||
|
|
||||||
private(set) var last_connection_attempt: TimeInterval = 0
|
private(set) var last_connection_attempt: TimeInterval = 0
|
||||||
private lazy var socket = {
|
private lazy var socket = WebSocket(url)
|
||||||
let req = URLRequest(url: url)
|
private var subscriptionToken: AnyCancellable?
|
||||||
let socket = WebSocket(request: req, compressionHandler: .none)
|
|
||||||
socket.delegate = self
|
|
||||||
return socket
|
|
||||||
}()
|
|
||||||
private var handleEvent: (NostrConnectionEvent) -> ()
|
private var handleEvent: (NostrConnectionEvent) -> ()
|
||||||
private let url: URL
|
private let url: URL
|
||||||
|
|
||||||
@@ -33,16 +29,6 @@ final class RelayConnection: WebSocketDelegate {
|
|||||||
self.handleEvent = handleEvent
|
self.handleEvent = handleEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconnect() {
|
|
||||||
if isConnected {
|
|
||||||
isReconnecting = true
|
|
||||||
disconnect()
|
|
||||||
} else {
|
|
||||||
// we're already disconnected, so just connect
|
|
||||||
connect(force: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connect(force: Bool = false) {
|
func connect(force: Bool = false) {
|
||||||
if !force && (isConnected || isConnecting) {
|
if !force && (isConnected || isConnecting) {
|
||||||
return
|
return
|
||||||
@@ -50,11 +36,27 @@ final class RelayConnection: WebSocketDelegate {
|
|||||||
|
|
||||||
isConnecting = true
|
isConnecting = true
|
||||||
last_connection_attempt = Date().timeIntervalSince1970
|
last_connection_attempt = Date().timeIntervalSince1970
|
||||||
|
|
||||||
|
subscriptionToken = socket.subject
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
self?.receive(event: .error(error))
|
||||||
|
case .finished:
|
||||||
|
self?.receive(event: .disconnected(.normalClosure, nil))
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] event in
|
||||||
|
self?.receive(event: event)
|
||||||
|
}
|
||||||
|
|
||||||
socket.connect()
|
socket.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
socket.disconnect()
|
socket.disconnect()
|
||||||
|
subscriptionToken = nil
|
||||||
|
|
||||||
isConnected = false
|
isConnected = false
|
||||||
isConnecting = false
|
isConnecting = false
|
||||||
}
|
}
|
||||||
@@ -64,34 +66,46 @@ final class RelayConnection: WebSocketDelegate {
|
|||||||
print("failed to encode nostr req: \(req)")
|
print("failed to encode nostr req: \(req)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
socket.send(.string(req))
|
||||||
socket.write(string: req)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - WebSocketDelegate
|
private func receive(event: WebSocketEvent) {
|
||||||
|
|
||||||
func didReceive(event: WebSocketEvent, client: WebSocket) {
|
|
||||||
switch event {
|
switch event {
|
||||||
case .connected:
|
case .connected:
|
||||||
self.isConnected = true
|
self.isConnected = true
|
||||||
self.isConnecting = false
|
self.isConnecting = false
|
||||||
|
case .message(let message):
|
||||||
case .disconnected:
|
self.receive(message: message)
|
||||||
self.isConnecting = false
|
case .disconnected(let closeCode, let reason):
|
||||||
self.isConnected = false
|
if closeCode != .normalClosure {
|
||||||
if self.isReconnecting {
|
print("⚠️ Warning: RelayConnection (\(self.url)) closed with code \(closeCode), reason: \(String(describing: reason))")
|
||||||
self.isReconnecting = false
|
|
||||||
self.connect()
|
|
||||||
}
|
}
|
||||||
|
isConnected = false
|
||||||
case .cancelled, .error:
|
isConnecting = false
|
||||||
self.isConnecting = false
|
reconnect()
|
||||||
self.isConnected = false
|
case .error(let error):
|
||||||
|
print("⚠️ Warning: RelayConnection (\(self.url)) error: \(error)")
|
||||||
case .text(let txt):
|
isConnected = false
|
||||||
if txt.utf8.count > 2000 {
|
isConnecting = false
|
||||||
|
reconnect()
|
||||||
|
}
|
||||||
|
self.handleEvent(.ws_event(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
func reconnect() {
|
||||||
|
guard !isConnecting else {
|
||||||
|
return // we're already trying to connect
|
||||||
|
}
|
||||||
|
disconnect()
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func receive(message: URLSessionWebSocketTask.Message) {
|
||||||
|
switch message {
|
||||||
|
case .string(let messageString):
|
||||||
|
if messageString.utf8.count > 2000 {
|
||||||
DispatchQueue.global(qos: .default).async {
|
DispatchQueue.global(qos: .default).async {
|
||||||
if let ev = decode_nostr_event(txt: txt) {
|
if let ev = decode_nostr_event(txt: messageString) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.handleEvent(.nostr_event(ev))
|
self.handleEvent(.nostr_event(ev))
|
||||||
}
|
}
|
||||||
@@ -99,18 +113,18 @@ final class RelayConnection: WebSocketDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let ev = decode_nostr_event(txt: txt) {
|
if let ev = decode_nostr_event(txt: messageString) {
|
||||||
handleEvent(.nostr_event(ev))
|
handleEvent(.nostr_event(ev))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case .data(let messageData):
|
||||||
|
if let messageString = String(data: messageData, encoding: .utf8) {
|
||||||
default:
|
receive(message: .string(messageString))
|
||||||
break
|
}
|
||||||
|
@unknown default:
|
||||||
|
print("An unexpected URLSessionWebSocketTask.Message was received.")
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(.ws_event(event))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
struct SubscriptionId: Identifiable, CustomStringConvertible {
|
struct SubscriptionId: Identifiable, CustomStringConvertible {
|
||||||
let id: String
|
let id: String
|
||||||
@@ -44,7 +45,24 @@ class RelayPool {
|
|||||||
var request_queue: [QueuedRequest] = []
|
var request_queue: [QueuedRequest] = []
|
||||||
var seen: Set<String> = Set()
|
var seen: Set<String> = Set()
|
||||||
var counts: [String: UInt64] = [:]
|
var counts: [String: UInt64] = [:]
|
||||||
|
|
||||||
|
private let network_monitor = NWPathMonitor()
|
||||||
|
private let network_monitor_queue = DispatchQueue(label: "io.damus.network_monitor")
|
||||||
|
private var last_network_status: NWPath.Status = .unsatisfied
|
||||||
|
|
||||||
|
init() {
|
||||||
|
network_monitor.pathUpdateHandler = { [weak self] path in
|
||||||
|
if (path.status == .satisfied || path.status == .requiresConnection) && self?.last_network_status != path.status {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.connect_to_disconnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.last_network_status = path.status
|
||||||
|
}
|
||||||
|
network_monitor.start(queue: network_monitor_queue)
|
||||||
|
}
|
||||||
|
|
||||||
var descriptors: [RelayDescriptor] {
|
var descriptors: [RelayDescriptor] {
|
||||||
relays.map { $0.descriptor }
|
relays.map { $0.descriptor }
|
||||||
}
|
}
|
||||||
@@ -106,11 +124,11 @@ class RelayPool {
|
|||||||
for relay in relays {
|
for relay in relays {
|
||||||
let c = relay.connection
|
let c = relay.connection
|
||||||
|
|
||||||
let is_connecting = c.isReconnecting || c.isConnecting
|
let is_connecting = c.isConnecting
|
||||||
|
|
||||||
if is_connecting && (Date.now.timeIntervalSince1970 - c.last_connection_attempt) > 5 {
|
if is_connecting && (Date.now.timeIntervalSince1970 - c.last_connection_attempt) > 5 {
|
||||||
print("stale connection detected (\(relay.descriptor.url.absoluteString)). retrying...")
|
print("stale connection detected (\(relay.descriptor.url.absoluteString)). retrying...")
|
||||||
relay.connection.connect(force: true)
|
relay.connection.reconnect()
|
||||||
} else if relay.is_broken || is_connecting || c.isConnected {
|
} else if relay.is_broken || is_connecting || c.isConnected {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@@ -208,19 +226,6 @@ class RelayPool {
|
|||||||
relays.first(where: { $0.id == id })
|
relays.first(where: { $0.id == id })
|
||||||
}
|
}
|
||||||
|
|
||||||
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
|
|
||||||
if case .ws_event(let ws_event) = event {
|
|
||||||
if case .pong = ws_event {
|
|
||||||
for relay in relays {
|
|
||||||
if relay.id == relay_id {
|
|
||||||
relay.last_pong = UInt32(Date.now.timeIntervalSince1970)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run_queue(_ relay_id: String) {
|
func run_queue(_ relay_id: String) {
|
||||||
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
|
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
|
||||||
guard req.relay == relay_id else {
|
guard req.relay == relay_id else {
|
||||||
@@ -250,7 +255,6 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
||||||
record_last_pong(relay_id: relay_id, event: event)
|
|
||||||
record_seen(relay_id: relay_id, event: event)
|
record_seen(relay_id: relay_id, event: event)
|
||||||
|
|
||||||
// run req queue when we reconnect
|
// run req queue when we reconnect
|
||||||
|
|||||||
87
damus/Nostr/WebSocket.swift
Normal file
87
damus/Nostr/WebSocket.swift
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// WebSocket.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Bryan Montz on 4/13/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum WebSocketEvent {
|
||||||
|
case connected
|
||||||
|
case message(URLSessionWebSocketTask.Message)
|
||||||
|
case disconnected(URLSessionWebSocketTask.CloseCode, String?)
|
||||||
|
case error(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class WebSocket: NSObject, URLSessionWebSocketDelegate {
|
||||||
|
|
||||||
|
private let url: URL
|
||||||
|
private let session: URLSession
|
||||||
|
private lazy var webSocketTask: URLSessionWebSocketTask = {
|
||||||
|
let task = session.webSocketTask(with: url)
|
||||||
|
task.delegate = self
|
||||||
|
return task
|
||||||
|
}()
|
||||||
|
|
||||||
|
let subject = PassthroughSubject<WebSocketEvent, Never>()
|
||||||
|
|
||||||
|
init(_ url: URL, session: URLSession = .shared) {
|
||||||
|
self.url = url
|
||||||
|
self.session = session
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect() {
|
||||||
|
resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect(closeCode: URLSessionWebSocketTask.CloseCode = .normalClosure, reason: Data? = nil) {
|
||||||
|
webSocketTask.cancel(with: closeCode, reason: reason)
|
||||||
|
|
||||||
|
// reset after disconnecting to be ready for reconnecting
|
||||||
|
let task = session.webSocketTask(with: url)
|
||||||
|
task.delegate = self
|
||||||
|
webSocketTask = task
|
||||||
|
|
||||||
|
let reason_str: String?
|
||||||
|
if let reason {
|
||||||
|
reason_str = String(data: reason, encoding: .utf8)
|
||||||
|
} else {
|
||||||
|
reason_str = nil
|
||||||
|
}
|
||||||
|
subject.send(.disconnected(closeCode, reason_str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(_ message: URLSessionWebSocketTask.Message) {
|
||||||
|
webSocketTask.send(message) { [weak self] error in
|
||||||
|
if let error {
|
||||||
|
self?.subject.send(.error(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resume() {
|
||||||
|
webSocketTask.receive { [weak self] result in
|
||||||
|
switch result {
|
||||||
|
case .success(let message):
|
||||||
|
self?.subject.send(.message(message))
|
||||||
|
self?.resume()
|
||||||
|
case .failure(let error):
|
||||||
|
self?.subject.send(.error(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webSocketTask.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionWebSocketDelegate
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol theProtocol: String?) {
|
||||||
|
subject.send(.connected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
|
||||||
|
disconnect(closeCode: closeCode, reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ struct RelayStatus: View {
|
|||||||
if c.isConnected {
|
if c.isConnected {
|
||||||
conn_image = "network"
|
conn_image = "network"
|
||||||
conn_color = .green
|
conn_color = .green
|
||||||
} else if c.isConnecting || c.isReconnecting {
|
} else if c.isConnecting {
|
||||||
connecting = true
|
connecting = true
|
||||||
} else {
|
} else {
|
||||||
conn_image = "exclamationmark.circle.fill"
|
conn_image = "exclamationmark.circle.fill"
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ struct SaveKeysView: View {
|
|||||||
|
|
||||||
case .error(let err):
|
case .error(let err):
|
||||||
self.loading = false
|
self.loading = false
|
||||||
self.error = "\(err.debugDescription)"
|
self.error = String(describing: err)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user