From fa81ba41c220b593bd5a06ebeb65e8869201d1b0 Mon Sep 17 00:00:00 2001 From: Jan Kobersky <5406945+kober32@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:26:24 +0200 Subject: [PATCH] Removed serialAll option from concurrency settings (#63) --- README.md | 8 +- .../WultraPowerauthNetworking/WPNError.swift | 4 + .../WPNNetworkingService.swift | 154 ++++++++++++------ 3 files changed, 110 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 08bf5bb..1ac22d2 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ pod 'WultraPowerAuthNetworking' |---|---| | `1.0.x` - `1.2.x` | `1.7.x` | | `1.3.x` | `1.8.x` | +| `1.4.x` | `1.8.x` | ### Xcode Compatibility @@ -259,11 +260,10 @@ class MyResponseDelegateLogger: WPNResponseDelegate { By default, the SDK is serializing all signed requests. This means that the requests signed with the PowerAuthSDK are put into the queue and executed one by one (meaning that the HTTP request is not made until the previous one is finished). Other requests will be parallel. -This behavior can be changed via `WPNNetworkingService.concurencyStrategy` with the following possible values: +This behavior can be changed via `WPNNetworkingService.concurrencyStrategy` with the following possible values: -- `serialSigned` - Default behavior. Only requests that need a PowerAuth signature will be put into the serial queue. -- `serialAll` - All requests will be put into a serial queue. -- `concurentAll` - All requests will be put into the concurrent queue. This behavior is not recommended unless you know exactly why you want this. +- `serialSigned` - Default behavior. Only requests that need a PowerAuth signature will be put into the serial queue that is shared with the `PowerAuthSDK` instance to ensure all signed requests are in proper order. +- `concurrentAll` - All requests will be put into the concurrent queue. This behavior is not recommended unless you know exactly why you want this. More about this topic can be found in the [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization). diff --git a/Sources/WultraPowerauthNetworking/WPNError.swift b/Sources/WultraPowerauthNetworking/WPNError.swift index e0127aa..71d8b2d 100644 --- a/Sources/WultraPowerauthNetworking/WPNError.swift +++ b/Sources/WultraPowerauthNetworking/WPNError.swift @@ -154,8 +154,12 @@ public class WPNError: Error { /// Reason of the error. public struct WPNErrorReason: RawRepresentable, Equatable, Hashable { + /// PowerAuth instance does not have a valid activation public static let missingActivation = WPNErrorReason(rawValue: "missingActivation") + /// Unknown (unrecognized) error occured. public static let unknown = WPNErrorReason(rawValue: "unknown") + /// The operation was canceled. + public static let canceled = WPNErrorReason(rawValue: "canceled") public typealias RawValue = String public var rawValue: RawValue diff --git a/Sources/WultraPowerauthNetworking/WPNNetworkingService.swift b/Sources/WultraPowerauthNetworking/WPNNetworkingService.swift index e3ec9e4..4be90af 100644 --- a/Sources/WultraPowerauthNetworking/WPNNetworkingService.swift +++ b/Sources/WultraPowerauthNetworking/WPNNetworkingService.swift @@ -18,22 +18,25 @@ import Foundation import PowerAuth2 import PowerAuthCore -/// Strategy that decides if request will be put in serial or concurent queue. +/// Strategy that decides if request will be put in serial or concurrent queue. +/// +/// For serial queue, `PowerAuthSDK.executeOperation` is used. /// /// More about this topic can be found in the /// [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization) -public enum WPNRequestConcurencyStrategy { - /// All requests will be put into concurent queue. +public enum WPNRequestConcurrencyStrategy { + /// All requests will be put into concurrent queue. /// /// We recommend not using this option unless you're managing theserialization of requests yourself. /// /// More about this topic can be found in the /// [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization) - case concurentAll - /// Only request that needs PowerAuth signature will be put into serial queue. + case concurrentAll + /// Only request that needs PowerAuth signature will be put into serial queue provided by the PowerAuth library. + /// + /// Note that serial queue for signed requests is shared with the `PowerAuthSDK` instance + /// to ensure all signed requests are in proper order. case serialSigned - /// All requests will be put into serial queue. - case serialAll } /// Networking service for dispatching PowerAuth signed requests. @@ -50,10 +53,10 @@ public class WPNNetworkingService { /// Response delegate is called on each received response public weak var responseDelegate: WPNResponseDelegate? - /// Strategy that decides if request will be put in serial or concurent queue. + /// Strategy that decides if request will be put in serial or concurrent queue. /// /// Default value is `serialSigned` - public var concurencyStrategy = WPNRequestConcurencyStrategy.serialSigned + public var concurrencyStrategy = WPNRequestConcurrencyStrategy.serialSigned /// PowerAuth instance that will be used for this networking. public let powerAuth: PowerAuthSDK @@ -68,7 +71,12 @@ public class WPNNetworkingService { /// - serviceName: Name of the service. Will be reflected in the OperationQueue name and logs. /// - acceptLanguage: Language sent to the server for request localized response. /// Compliant with standard RFC Accept-Language. Default value is "en". - public init(powerAuth: PowerAuthSDK, config: WPNConfig, serviceName: String, acceptLanguage: String = "en") { + public init( + powerAuth: PowerAuthSDK, + config: WPNConfig, + serviceName: String, + acceptLanguage: String = "en" + ) { self.acceptLanguage = acceptLanguage self.powerAuth = powerAuth self.httpClient = WPNHttpClient(sslValidation: config.sslValidation, timeout: config.timeoutIntervalForRequest) @@ -88,20 +96,29 @@ public class WPNNetworkingService { /// - completionQueue: Queue on wich the completion will be executed. /// Default value is .main /// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter. - /// - Returns: Operation for observation or operation chaining. + /// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own. @discardableResult - public func post>(data: Req, - to endpoint: Endpoint, - with headers: [String: String]? = nil, - encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, - timeoutInterval: TimeInterval? = nil, - progressCallback: ((Double) -> Void)? = nil, - completionQueue: DispatchQueue = .main, - completion: @escaping Endpoint.Completion) -> Operation { + public func post>( + data: Req, + to endpoint: Endpoint, + with headers: [String: String]? = nil, + encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, + timeoutInterval: TimeInterval? = nil, + progressCallback: ((Double) -> Void)? = nil, + completionQueue: DispatchQueue = .main, + completion: @escaping Endpoint.Completion + ) -> Operation { + let url = config.buildURL(endpoint.endpointURLPath) let request = Endpoint.Request(url, requestData: data, encryptor: encryptor) request.timeoutInterval = timeoutInterval - return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion) + return post( + request: request, + headers: headers, + progressCallback: progressCallback, + completionQueue: completionQueue, + completion: completion + ) } /// Sends signed request with provided authentication. @@ -117,21 +134,30 @@ public class WPNNetworkingService { /// - completionQueue: Queue on wich the completion will be executed. /// Default value is .main /// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter. - /// - Returns: Operation for observation or operation chaining. + /// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own. @discardableResult - public func post>(data: Req, - signedWith auth: PowerAuthAuthentication, - to endpoint: Endpoint, - with headers: [String: String]? = nil, - encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, - timeoutInterval: TimeInterval? = nil, - progressCallback: ((Double) -> Void)? = nil, - completionQueue: DispatchQueue = .main, - completion: @escaping Endpoint.Completion) -> Operation { + public func post>( + data: Req, + signedWith auth: PowerAuthAuthentication, + to endpoint: Endpoint, + with headers: [String: String]? = nil, + encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, + timeoutInterval: TimeInterval? = nil, + progressCallback: ((Double) -> Void)? = nil, + completionQueue: DispatchQueue = .main, + completion: @escaping Endpoint.Completion + ) -> Operation { + let url = config.buildURL(endpoint.endpointURLPath) let request = Endpoint.Request(url, uriId: endpoint.uriId, auth: auth, requestData: data, encryptor: encryptor) request.timeoutInterval = timeoutInterval - return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion) + return post( + request: request, + headers: headers, + progressCallback: progressCallback, + completionQueue: completionQueue, + completion: completion + ) } /// Sends signed request with provided authentication. @@ -147,30 +173,42 @@ public class WPNNetworkingService { /// - completionQueue: Queue on wich the completion will be executed. /// Default value is .main /// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter. - /// - Returns: Operation for observation or operation chaining. + /// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own. @discardableResult - public func post>(data: Req, - signedWith auth: PowerAuthAuthentication, - to endpoint: Endpoint, - with headers: [String: String]? = nil, - encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, - timeoutInterval: TimeInterval? = nil, - progressCallback: ((Double) -> Void)? = nil, - completionQueue: DispatchQueue = .main, - completion: @escaping Endpoint.Completion) -> Operation { + public func post>( + data: Req, + signedWith auth: PowerAuthAuthentication, + to endpoint: Endpoint, + with headers: [String: String]? = nil, + encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil, + timeoutInterval: TimeInterval? = nil, + progressCallback: ((Double) -> Void)? = nil, + completionQueue: DispatchQueue = .main, + completion: @escaping Endpoint.Completion + ) -> Operation { + let url = config.buildURL(endpoint.endpointURLPath) let request = Endpoint.Request(url, tokenName: endpoint.tokenName, auth: auth, requestData: data, encryptor: encryptor) request.timeoutInterval = timeoutInterval - return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion) + return post( + request: request, + headers: headers, + progressCallback: progressCallback, + completionQueue: completionQueue, + completion: completion + ) } /// Adds a HTTP post request to the request queue. @discardableResult - func post>(request: Endpoint.Request, - headers: [String: String]?, - progressCallback: ((Double) -> Void)?, - completionQueue: DispatchQueue, - completion: @escaping Endpoint.Completion) -> Operation { + func post>( + request: Endpoint.Request, + headers: [String: String]?, + progressCallback: ((Double) -> Void)?, + completionQueue: DispatchQueue, + completion: @escaping Endpoint.Completion + ) -> Operation { + // Setup default headers request.addHeaders(getDefaultHeaders()) @@ -187,16 +225,22 @@ public class WPNNetworkingService { } } - self.processRequest(request) { error in + self.processRequest(request) { [weak self] error in if let error { completion(nil, error) return } + guard let self, operation.isCancelled == false else { + completion(nil, .init(reason: .canceled)) + return + } + self.httpClient.post(request: request, progressCallback: progressCallback, completion: { [weak self] data, urlResponse, error in - guard let self = self, operation.isCancelled == false else { + guard let self, operation.isCancelled == false else { + completion(nil, .init(reason: .canceled)) return } @@ -260,7 +304,7 @@ public class WPNNetworkingService { op.completionQueue = completionQueue - if (concurencyStrategy == .serialSigned && request.needsSignature) || concurencyStrategy == .serialAll { + if concurrencyStrategy == .serialSigned && request.needsSignature { // Add operation to the "signing" queue. if !powerAuth.executeOperation(onSerialQueue: op) { // Operation wont be added to the queue if there is a missing @@ -289,7 +333,10 @@ public class WPNNetworkingService { } /// All necessary interactions with the PowerAuthSDK like request signing, token refresh and time synchronization. - private func processRequest(_ request: WPNHttpRequest, completion: @escaping (WPNError?) -> Void) { + private func processRequest( + _ request: WPNHttpRequest, + completion: @escaping (WPNError?) -> Void + ) { // global completion queue to ensure that in case of async call to the server // we calculate the signature on the background queue synchronizeTime(completionQueue: .global()) { error in @@ -322,7 +369,10 @@ public class WPNNetworkingService { } /// Calculates a signature for request. The function must be called on background thread. - private func bgCalculateSignature(_ request: WPNHttpRequest, completion: @escaping (WPNError?) -> Void) { + private func bgCalculateSignature( + _ request: WPNHttpRequest, + completion: @escaping (WPNError?) -> Void + ) { do { guard let data = request.requestData else { completion(WPNError(reason: .network_invalidRequestObject))