You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/SessionSnodeKit/Networking/PreparedRequest+OnionReques...

135 lines
6.7 KiB
Swift

// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Combine
import SessionUtilitiesKit
public extension Network.PreparedRequest {
/// Send an onion request for the prepared data
func send(using dependencies: Dependencies) -> AnyPublisher<(ResponseInfoType, R), Error> {
// If we have a cached response then user that directly
if let cachedResponse: Network.PreparedRequest<R>.CachedResponse = self.cachedResponse {
return Just(cachedResponse)
.setFailureType(to: Error.self)
.handleEvents(
receiveSubscription: { _ in self.subscriptionHandler?() },
receiveOutput: self.outputEventHandler,
receiveCompletion: self.completionEventHandler,
receiveCancel: self.cancelEventHandler
)
.eraseToAnyPublisher()
}
// Otherwise trigger the request
return Just(())
.setFailureType(to: Error.self)
.tryFlatMap { _ in
switch target {
case let serverTarget as any ServerRequestTarget:
return dependencies.network
.send(
.onionRequest(
request,
to: serverTarget.server,
with: serverTarget.x25519PublicKey,
timeout: timeout
),
using: dependencies
)
case let snodeTarget as Network.SnodeTarget:
guard let payload: Data = request.httpBody else { throw NetworkError.invalidPreparedRequest }
return dependencies.network
.send(
.onionRequest(
payload,
to: snodeTarget.snode,
swarmPublicKey: snodeTarget.swarmPublicKey,
timeout: timeout
),
using: dependencies
)
case let randomTarget as Network.RandomSnodeTarget:
guard let payload: Data = request.httpBody else { throw NetworkError.invalidPreparedRequest }
return LibSession.getSwarm(swarmPublicKey: randomTarget.swarmPublicKey)
.tryFlatMapWithRandomSnode(retry: randomTarget.retryCount, using: dependencies) { snode in
dependencies.network
.send(
.onionRequest(
payload,
to: snode,
swarmPublicKey: randomTarget.swarmPublicKey,
timeout: timeout
),
using: dependencies
)
}
case let randomTarget as Network.RandomSnodeLatestNetworkTimeTarget:
guard request.httpBody != nil else { throw NetworkError.invalidPreparedRequest }
return LibSession.getSwarm(swarmPublicKey: randomTarget.swarmPublicKey)
.tryFlatMapWithRandomSnode(retry: randomTarget.retryCount, using: dependencies) { snode in
SnodeAPI
.getNetworkTime(from: snode, using: dependencies)
.tryFlatMap { timestampMs in
guard
let updatedRequest: URLRequest = try? randomTarget
.urlRequestWithUpdatedTimestampMs(timestampMs, dependencies),
let payload: Data = updatedRequest.httpBody
else { throw NetworkError.invalidPreparedRequest }
return dependencies.network
.send(
.onionRequest(
payload,
to: snode,
swarmPublicKey: randomTarget.swarmPublicKey,
timeout: timeout
),
using: dependencies
)
.map { info, response -> (ResponseInfoType, Data?) in
(
SnodeAPI.LatestTimestampResponseInfo(
code: info.code,
headers: info.headers,
timestampMs: timestampMs
),
response
)
}
}
}
default: throw NetworkError.invalidPreparedRequest
}
}
.decoded(with: self, using: dependencies)
.retry(retryCount, using: dependencies)
.handleEvents(
receiveSubscription: { _ in self.subscriptionHandler?() },
receiveOutput: self.outputEventHandler,
receiveCompletion: self.completionEventHandler,
receiveCancel: self.cancelEventHandler
)
.eraseToAnyPublisher()
}
}
public extension Optional {
func send<R>(
using dependencies: Dependencies
) -> AnyPublisher<(ResponseInfoType, R), Error> where Wrapped == Network.PreparedRequest<R> {
guard let instance: Wrapped = self else {
return Fail(error: NetworkError.invalidPreparedRequest)
.eraseToAnyPublisher()
}
return instance.send(using: dependencies)
}
}