Swap out LokiSnodeProxy with OnionRequestAPI
This commit is contained in:
parent
4f56307d39
commit
5812578a73
|
@ -29,6 +29,7 @@ public final class LokiAPI : NSObject {
|
|||
internal static let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
|
||||
|
||||
// MARK: Settings
|
||||
private static let useOnionRequests = true
|
||||
private static let maxRetryCount: UInt = 4
|
||||
private static let defaultTimeout: TimeInterval = 20
|
||||
private static let longPollingTimeout: TimeInterval = 40
|
||||
|
@ -99,9 +100,14 @@ public final class LokiAPI : NSObject {
|
|||
let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ])
|
||||
if let headers = headers { request.allHTTPHeaderFields = headers }
|
||||
request.timeoutInterval = timeout ?? defaultTimeout
|
||||
return LokiSnodeProxy(for: target).perform(request, withCompletionQueue: DispatchQueue.global())
|
||||
.handlingSnodeErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey)
|
||||
.recoveringNetworkErrorsIfNeeded()
|
||||
if useOnionRequests {
|
||||
return OnionRequestAPI.sendOnionRequest(invoking: method, on: target, with: parameters).map { $0 as Any }
|
||||
} else {
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global())
|
||||
.map { $0.responseObject }
|
||||
.handlingSnodeErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey)
|
||||
.recoveringNetworkErrorsIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
internal static func getRawMessages(from target: LokiAPITarget, usingLongPolling useLongPolling: Bool) -> RawResponsePromise {
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
import PromiseKit
|
||||
import SignalMetadataKit
|
||||
|
||||
internal class LokiSnodeProxy : LokiHTTPClient {
|
||||
private let target: LokiAPITarget
|
||||
private let keyPair = Curve25519.generateKeyPair()
|
||||
|
||||
// MARK: Error
|
||||
internal enum Error : LocalizedError {
|
||||
case targetPublicKeySetMissing
|
||||
case symmetricKeyGenerationFailed
|
||||
case proxyResponseParsingFailed
|
||||
case targetSnodeHTTPError(code: Int, message: Any?)
|
||||
|
||||
internal var errorDescription: String? {
|
||||
switch self {
|
||||
case .targetPublicKeySetMissing: return "Missing target public key set."
|
||||
case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key."
|
||||
case .proxyResponseParsingFailed: return "Couldn't parse snode proxy response."
|
||||
case .targetSnodeHTTPError(let httpStatusCode, let message): return "Target snode returned error \(httpStatusCode) with description: \(message ?? "no description provided.")."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Initialization
|
||||
internal init(for target: LokiAPITarget) {
|
||||
self.target = target
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: Proxying
|
||||
override internal func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> LokiAPI.RawResponsePromise {
|
||||
guard let targetHexEncodedPublicKeySet = target.publicKeySet else { return Promise(error: Error.targetPublicKeySetMissing) }
|
||||
let headers = getCanonicalHeaders(for: request)
|
||||
return Promise<LokiAPI.RawResponse> { [target = self.target, keyPair = self.keyPair, httpSession = self.httpSession] seal in
|
||||
DispatchQueue.global().async {
|
||||
let uncheckedSymmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: Data(hex: targetHexEncodedPublicKeySet.x25519Key), privateKey: keyPair.privateKey)
|
||||
guard let symmetricKey = uncheckedSymmetricKey else { return seal.reject(Error.symmetricKeyGenerationFailed) }
|
||||
LokiAPI.getRandomSnode().then(on: DispatchQueue.global()) { proxy -> Promise<LokiAPI.RawResponse> in
|
||||
let url = "\(proxy.address):\(proxy.port)/proxy"
|
||||
let parametersAsData = try JSONSerialization.data(withJSONObject: request.parameters, options: [])
|
||||
let proxyRequestParameters: JSON = [
|
||||
"method" : request.httpMethod,
|
||||
"body" : String(bytes: parametersAsData, encoding: .utf8),
|
||||
"headers" : headers
|
||||
]
|
||||
let proxyRequestParametersAsData = try JSONSerialization.data(withJSONObject: proxyRequestParameters, options: [])
|
||||
let ivAndCipherText = try DiffieHellman.encrypt(proxyRequestParametersAsData, using: symmetricKey)
|
||||
let proxyRequestHeaders = [
|
||||
"X-Sender-Public-Key" : keyPair.publicKey.toHexString(),
|
||||
"X-Target-Snode-Key" : targetHexEncodedPublicKeySet.ed25519Key
|
||||
]
|
||||
let (promise, resolver) = LokiAPI.RawResponsePromise.pending()
|
||||
let proxyRequest = AFHTTPRequestSerializer().request(withMethod: "POST", urlString: url, parameters: nil, error: nil)
|
||||
proxyRequest.allHTTPHeaderFields = proxyRequestHeaders
|
||||
proxyRequest.httpBody = ivAndCipherText
|
||||
proxyRequest.timeoutInterval = request.timeoutInterval
|
||||
var task: URLSessionDataTask!
|
||||
task = httpSession.dataTask(with: proxyRequest as URLRequest) { response, result, error in
|
||||
if let error = error {
|
||||
let nmError = NetworkManagerError.taskError(task: task, underlyingError: error)
|
||||
let nsError: NSError = nmError as NSError
|
||||
nsError.isRetryable = false
|
||||
resolver.reject(nsError)
|
||||
} else {
|
||||
resolver.fulfill(result)
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
return promise
|
||||
}.map(on: DispatchQueue.global()) { rawResponse in
|
||||
guard let responseAsData = rawResponse as? Data, let cipherText = Data(base64Encoded: responseAsData) else {
|
||||
print("[Loki] Received a non-string encoded response.")
|
||||
return rawResponse
|
||||
}
|
||||
let response = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
|
||||
let uncheckedJSON = try? JSONSerialization.jsonObject(with: response, options: .allowFragments) as? JSON
|
||||
guard let json = uncheckedJSON, let statusCode = json["status"] as? Int else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
|
||||
let isSuccess = (200...299) ~= statusCode
|
||||
var body: Any? = nil
|
||||
if let bodyAsString = json["body"] as? String {
|
||||
body = bodyAsString
|
||||
if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? JSON {
|
||||
body = bodyAsJSON
|
||||
}
|
||||
}
|
||||
guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: body, underlyingError: Error.targetSnodeHTTPError(code: statusCode, message: body)) }
|
||||
return body
|
||||
}.done { rawResponse in
|
||||
seal.fulfill(rawResponse)
|
||||
}.catch { error in
|
||||
print("[Loki] Proxy request failed with error: \(error.localizedDescription).")
|
||||
seal.reject(HTTPError.from(error: error) ?? error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ internal enum OnionRequestAPI {
|
|||
// MARK: Settings
|
||||
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 pathSize: UInt = 1
|
||||
|
||||
private static var guardSnodeCount: UInt { return pathCount } // One per path
|
||||
|
||||
|
@ -178,7 +178,7 @@ internal enum OnionRequestAPI {
|
|||
|
||||
// MARK: Internal API
|
||||
/// Sends an onion request to `snode`. Builds new paths as needed.
|
||||
internal static func invoke(_ method: LokiAPITarget.Method, on snode: LokiAPITarget, with parameters: JSON) -> Promise<JSON> {
|
||||
internal static func sendOnionRequest(invoking method: LokiAPITarget.Method, on snode: LokiAPITarget, with parameters: JSON) -> Promise<JSON> {
|
||||
let (promise, seal) = Promise<JSON>.pending()
|
||||
workQueue.async {
|
||||
let payload: JSON = [ "method" : method.rawValue, "params" : parameters ]
|
||||
|
|
Loading…
Reference in New Issue