Don't use TSNetworkManager for LokiAPI.getRandomSnode()

This commit is contained in:
gmbnt 2020-04-02 15:09:11 +11:00
parent 651aa7039d
commit 2fa1391341
3 changed files with 19 additions and 14 deletions

View File

@ -35,8 +35,8 @@ public extension LokiAPI {
// All of this has to happen on DispatchQueue.global() due to the way OWSMessageManager works
if randomSnodePool.isEmpty {
let target = seedNodePool.randomElement()!
let url = URL(string: "\(target)/json_rpc")!
let request = TSRequest(url: url, method: "POST", parameters: [
let url = "\(target)/json_rpc"
let parameters: JSON = [
"method" : "get_service_nodes",
"params" : [
"active_only" : true,
@ -47,11 +47,12 @@ public extension LokiAPI {
"pubkey_x25519": true
]
]
])
]
print("[Loki] Populating snode pool using: \(target).")
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map(on: DispatchQueue.global()) { intermediate in
let rawResponse = intermediate.responseObject
guard let json = rawResponse as? JSON, let intermediate = json["result"] as? JSON, let rawTargets = intermediate["service_node_states"] as? [JSON] else { throw LokiAPIError.randomSnodePoolUpdatingFailed }
let (promise, seal) = Promise<LokiAPITarget>.pending()
let queue = DispatchQueue.global()
HTTP.execute(.post, url, parameters: parameters).map(on: queue) { json in
guard let intermediate = json["result"] as? JSON, let rawTargets = intermediate["service_node_states"] as? [JSON] else { throw LokiAPIError.randomSnodePoolUpdatingFailed }
randomSnodePool = try Set(rawTargets.flatMap { rawTarget in
guard let address = rawTarget["public_ip"] as? String, let port = rawTarget["storage_port"] as? Int, let ed25519PublicKey = rawTarget["pubkey_ed25519"] as? String, let x25519PublicKey = rawTarget["pubkey_x25519"] as? String, address != "0.0.0.0" else {
print("[Loki] Failed to parse target from: \(rawTarget).")
@ -61,10 +62,13 @@ public extension LokiAPI {
})
// randomElement() uses the system's default random generator, which is cryptographically secure
return randomSnodePool.randomElement()!
}.recover(on: DispatchQueue.global()) { error -> Promise<LokiAPITarget> in
}.retryingIfNeeded(maxRetryCount: 4).done(on: queue) { snode in
seal.fulfill(snode)
}.catch(on: queue) { error in
print("[Loki] Failed to contact seed node at: \(target).")
throw error
}.retryingIfNeeded(maxRetryCount: 16) // The seed nodes have historically been unreliable
seal.reject(error)
}
return promise
} else {
return Promise<LokiAPITarget> { seal in
// randomElement() uses the system's default random generator, which is cryptographically secure
@ -141,10 +145,10 @@ public extension LokiAPI {
}
}
// MARK: Error Handling
// MARK: Snode Error Handling
internal extension Promise {
internal func handlingSwarmSpecificErrorsIfNeeded(for target: LokiAPITarget, associatedWith hexEncodedPublicKey: String) -> Promise<T> {
internal func handlingSnodeErrorsIfNeeded(for target: LokiAPITarget, associatedWith hexEncodedPublicKey: String) -> Promise<T> {
return recover(on: LokiAPI.errorHandlingQueue) { error -> Promise<T> in
if let error = error as? LokiHTTPClient.HTTPError {
switch error.statusCode {

View File

@ -99,7 +99,7 @@ public final class LokiAPI : NSObject {
if let headers = headers { request.allHTTPHeaderFields = headers }
request.timeoutInterval = timeout ?? defaultTimeout
return LokiSnodeProxy(for: target).perform(request, withCompletionQueue: DispatchQueue.global())
.handlingSwarmSpecificErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey)
.handlingSnodeErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey)
.recoveringNetworkErrorsIfNeeded()
}

View File

@ -19,7 +19,6 @@ internal enum OnionRequestAPI {
private static let pathCount: UInt = 2
/// The number of snodes (including the guard snode) in a path.
private static let pathSize: UInt = 3
private static let timeout: TimeInterval = 20
private static var guardSnodeCount: UInt { return pathCount } // One per path
@ -124,7 +123,7 @@ internal enum OnionRequestAPI {
}
/// Returns a `Path` to be used for building an onion request. Builds new paths as needed.
private static func getPath() -> Promise<Path> {
internal static func getPath() -> Promise<Path> {
guard pathSize >= 1 else { preconditionFailure("Cannot build path of size zero.") }
// randomElement() uses the system's default random generator, which is cryptographically secure
if paths.count >= pathCount {
@ -198,8 +197,10 @@ internal enum OnionRequestAPI {
let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined)
let aes = try AES(key: targetSnodeSymmetricKey.bytes, blockMode: gcm, padding: .pkcs7)
let result = try aes.decrypt(ciphertext.bytes)
print("[Loki] [Onion Request API] Succeeded with result: \(String(data: Data(bytes: result), encoding: .utf8)!).")
seal.fulfill(Data(bytes: result))
} catch (let error) {
print("[Loki] [Onion Request API] Decryption failed.")
seal.reject(error)
}
}.catch(on: workQueue) { error in