From 97ae09941ca09ccb20d8f8102a57f1112298e48c Mon Sep 17 00:00:00 2001 From: Thomas Rademaker Date: Wed, 11 Feb 2026 14:25:09 -0500 Subject: [PATCH] use networkingKit --- Package.resolved | 11 +- Package.swift | 13 +- Sources/CoreATProtocol/Models/ATError.swift | 2 + Sources/CoreATProtocol/Networking.swift | 11 +- .../Encoding/JSONParameterEncoder.swift | 29 -- .../Encoding/ParameterEncoding.swift | 37 --- .../Encoding/URLParameterEncoder.swift | 130 -------- .../Networking/Extensions/CharacterSet.swift | 21 -- .../Networking/Extensions/Encodable.swift | 7 - .../Networking/Services/EndpointType.swift | 9 - .../Networking/Services/HTTPMethod.swift | 7 - .../Networking/Services/HTTPTask.swift | 7 - .../Networking/Services/NetworkRouter.swift | 279 ------------------ .../Services/NetworkingProtocol.swift | 8 - .../Networking/Services/StatusCode.swift | 76 ----- 15 files changed, 26 insertions(+), 621 deletions(-) delete mode 100644 Sources/CoreATProtocol/Networking/Encoding/JSONParameterEncoder.swift delete mode 100644 Sources/CoreATProtocol/Networking/Encoding/ParameterEncoding.swift delete mode 100644 Sources/CoreATProtocol/Networking/Encoding/URLParameterEncoder.swift delete mode 100644 Sources/CoreATProtocol/Networking/Extensions/CharacterSet.swift delete mode 100644 Sources/CoreATProtocol/Networking/Extensions/Encodable.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/EndpointType.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/HTTPMethod.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/HTTPTask.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/NetworkRouter.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/NetworkingProtocol.swift delete mode 100644 Sources/CoreATProtocol/Networking/Services/StatusCode.swift diff --git a/Package.resolved b/Package.resolved index 1a3af64..dd0f740 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "46681c90ffb61eca5269d3e2ab8743c6f802287641f8bccf7c47227aa7a6a97a", + "originHash" : "1d85a62ed3cf11cb30641c7b4268569eb131f9e444c996901e1a6d31971ec3f3", "pins" : [ { "identity" : "jwt-kit", @@ -10,6 +10,15 @@ "version" : "5.3.0" } }, + { + "identity" : "networkingkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SparrowTek/NetworkingKit.git", + "state" : { + "branch" : "main", + "revision" : "f713ba71c03ee007183622e735f7739655da34eb" + } + }, { "identity" : "oauthenticator", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index edb8f92..74d3dfa 100644 --- a/Package.swift +++ b/Package.swift @@ -5,11 +5,12 @@ import PackageDescription let package = Package( name: "CoreATProtocol", platforms: [ - .iOS(.v17), - .watchOS(.v11), - .tvOS(.v17), - .macOS(.v14), - .macCatalyst(.v17), + .iOS(.v26), + .macOS(.v26), + .macCatalyst(.v26), + .tvOS(.v26), + .watchOS(.v26), + .visionOS(.v26), ], products: [ .library( @@ -24,12 +25,14 @@ let package = Package( .package(url: "https://github.com/radmakr/OAuthenticator.git", branch: "CoreAtProtocol"), // .package(path: "../OAuthenticator"), .package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0"), + .package(url: "https://github.com/SparrowTek/NetworkingKit.git", branch: "main"), ], targets: [ .target( name: "CoreATProtocol", dependencies: [ "OAuthenticator", + "NetworkingKit", .product(name: "JWTKit", package: "jwt-kit"), ], swiftSettings: [ diff --git a/Sources/CoreATProtocol/Models/ATError.swift b/Sources/CoreATProtocol/Models/ATError.swift index 9c2ab37..3eb0b20 100644 --- a/Sources/CoreATProtocol/Models/ATError.swift +++ b/Sources/CoreATProtocol/Models/ATError.swift @@ -5,6 +5,8 @@ // Created by Thomas Rademaker on 10/8/25. // +import NetworkingKit + public enum AtError: Error { case message(ErrorMessage) case network(NetworkError) diff --git a/Sources/CoreATProtocol/Networking.swift b/Sources/CoreATProtocol/Networking.swift index 3d2d17f..126f693 100644 --- a/Sources/CoreATProtocol/Networking.swift +++ b/Sources/CoreATProtocol/Networking.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkingKit extension JSONDecoder { public static var atDecoder: JSONDecoder { @@ -41,22 +42,22 @@ func shouldPerformRequest(lastFetched: Double, timeLimit: Int = 3600) -> Bool { return differenceInMinutes >= timeLimit } -@APActor +@NetworkingKitActor public class APRouterDelegate: NetworkRouterDelegate { private var refreshTask: Task? public func intercept(_ request: inout URLRequest) async { - if APEnvironment.current.dpopPrivateKey != nil { + if await APEnvironment.current.dpopPrivateKey != nil { return } - if let accessToken = APEnvironment.current.accessToken { + if let accessToken = await APEnvironment.current.accessToken { request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") } } public func shouldRetry(error: Error, attempts: Int) async throws -> Bool { func refreshViaOAuth() async throws -> Bool { - guard let handler = APEnvironment.current.tokenRefreshHandler else { + guard let handler = await APEnvironment.current.tokenRefreshHandler else { return false } @@ -74,7 +75,7 @@ public class APRouterDelegate: NetworkRouterDelegate { if attempts == 1, case .network(let networkError) = error as? AtError, - case .statusCode(let statusCode, _) = networkError, + case .statusCode(let statusCode, _, _) = networkError, let statusCode = statusCode?.rawValue, statusCode == 401 || statusCode == 403 { return try await refreshViaOAuth() diff --git a/Sources/CoreATProtocol/Networking/Encoding/JSONParameterEncoder.swift b/Sources/CoreATProtocol/Networking/Encoding/JSONParameterEncoder.swift deleted file mode 100644 index 100a280..0000000 --- a/Sources/CoreATProtocol/Networking/Encoding/JSONParameterEncoder.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -struct JSONParameterEncoder: ParameterEncoder { - func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws { - do { - let jsonAsData = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) - encode(urlRequest: &urlRequest, with: jsonAsData) - } catch { - throw NetworkError.encodingFailed - } - } - - func encode(urlRequest: inout URLRequest, with encodable: Encodable) throws { - do { - let data = try encodable.toJSONData() - encode(urlRequest: &urlRequest, with: data) - } catch { - throw NetworkError.encodingFailed - } - } - - func encode(urlRequest: inout URLRequest, with data: Data) { - urlRequest.httpBody = data - - if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { - urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") - } - } -} diff --git a/Sources/CoreATProtocol/Networking/Encoding/ParameterEncoding.swift b/Sources/CoreATProtocol/Networking/Encoding/ParameterEncoding.swift deleted file mode 100644 index 57b0ded..0000000 --- a/Sources/CoreATProtocol/Networking/Encoding/ParameterEncoding.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -public typealias Parameters = [String : Any] - -protocol ParameterEncoder { - func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws -} - -@APActor -public enum ParameterEncoding: Sendable { - - case urlEncoding(parameters: Parameters) - case jsonEncoding(parameters: Parameters) - case jsonDataEncoding(data: Data) - case jsonEncodableEncoding(encodable: Encodable) - case urlAndJsonEncoding(urlParameters: Parameters, bodyParameters: Parameters) - - func encode(urlRequest: inout URLRequest) throws { - do { - switch self { - case .urlEncoding(let parameters): - try URLParameterEncoder().encode(urlRequest: &urlRequest, with: parameters) - case .jsonEncoding(let parameters): - try JSONParameterEncoder().encode(urlRequest: &urlRequest, with: parameters) - case .jsonDataEncoding(let data): - JSONParameterEncoder().encode(urlRequest: &urlRequest, with: data) - case .jsonEncodableEncoding(let encodable): - try JSONParameterEncoder().encode(urlRequest: &urlRequest, with: encodable) - case .urlAndJsonEncoding(let urlParameters, let bodyParameters): - try URLParameterEncoder().encode(urlRequest: &urlRequest, with: urlParameters) - try JSONParameterEncoder().encode(urlRequest: &urlRequest, with: bodyParameters) - } - } catch { - throw NetworkError.encodingFailed - } - } -} diff --git a/Sources/CoreATProtocol/Networking/Encoding/URLParameterEncoder.swift b/Sources/CoreATProtocol/Networking/Encoding/URLParameterEncoder.swift deleted file mode 100644 index ebae7a8..0000000 --- a/Sources/CoreATProtocol/Networking/Encoding/URLParameterEncoder.swift +++ /dev/null @@ -1,130 +0,0 @@ -import Foundation - -struct URLParameterEncoder: ParameterEncoder { - /// Configures how `Array` parameters are encoded. - enum ArrayEncoding { - /// An empty set of square brackets is appended to the key for every value. This is the default behavior. - case brackets - /// No brackets are appended. The key is encoded as is. - case noBrackets - /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior. - case indexInBrackets - - func encode(key: String, atIndex index: Int) -> String { - switch self { - case .brackets: - return "\(key)[]" - case .noBrackets: - return key - case .indexInBrackets: - return "\(key)[\(index)]" - } - } - } - - /// Configures how `Bool` parameters are encoded. - enum BoolEncoding { - /// Encode `true` as `1` and `false` as `0`. This is the default behavior. - case numeric - /// Encode `true` and `false` as string literals. - case literal - - func encode(value: Bool) -> String { - switch self { - case .numeric: - return value ? "1" : "0" - case .literal: - return value ? "true" : "false" - } - } - } - - /// The encoding to use for `Array` parameters. - let arrayEncoding: ArrayEncoding - - /// The encoding to use for `Bool` parameters. - let boolEncoding: BoolEncoding - - /// The character set tp use for escaping - let characterSet: CharacterSet - - init(arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric, characterSet: CharacterSet = .apURLQueryAllowed) { - self.arrayEncoding = arrayEncoding - self.boolEncoding = boolEncoding - self.characterSet = characterSet - } - - func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws { - - guard let url = urlRequest.url else { throw NetworkError.missingURL } - - if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { - let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) - urlComponents.percentEncodedQuery = percentEncodedQuery - urlRequest.url = urlComponents.url - } - - if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { - urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") - } - } - - private func query(_ parameters: [String: Any]) -> String { - var components: [(String, String)] = [] - - for key in parameters.keys.sorted(by: <) { - let value = parameters[key]! - components += queryComponents(fromKey: key, value: value) - } - return components.map { "\($0)=\($1)" }.joined(separator: "&") - } - - /// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively. - /// - /// - Parameters: - /// - key: Key of the query component. - /// - value: Value of the query component. - /// - /// - Returns: The percent-escaped, URL encoded query string components. - func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { - var components: [(String, String)] = [] - switch value { - case let dictionary as [String: Any]: - for (nestedKey, value) in dictionary { - components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) - } - case let array as [Any]: - for (index, value) in array.enumerated() { - components += queryComponents(fromKey: arrayEncoding.encode(key: key, atIndex: index), value: value) - } - case let number as NSNumber: - if number.isBool { - components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue)))) - } else { - components.append((escape(key), escape("\(number)"))) - } - case let bool as Bool: - components.append((escape(key), escape(boolEncoding.encode(value: bool)))) - default: - components.append((escape(key), escape("\(value)"))) - } - return components - } - - /// Creates a percent-escaped string following RFC 3986 for a query string key or value. - /// - /// - Parameter string: `String` to be percent-escaped. - /// - /// - Returns: The percent-escaped `String`. - func escape(_ string: String) -> String { - string.addingPercentEncoding(withAllowedCharacters: characterSet) ?? string - } -} - -extension NSNumber { - fileprivate var isBool: Bool { - // Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of - // swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22). - String(cString: objCType) == "c" - } -} diff --git a/Sources/CoreATProtocol/Networking/Extensions/CharacterSet.swift b/Sources/CoreATProtocol/Networking/Extensions/CharacterSet.swift deleted file mode 100644 index dc80c43..0000000 --- a/Sources/CoreATProtocol/Networking/Extensions/CharacterSet.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation - -extension CharacterSet { - /// Creates a CharacterSet from RFC 3986 allowed characters. - /// - /// RFC 3986 states that the following characters are "reserved" characters. - /// - /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" - /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" - /// - /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow - /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" - /// should be percent-escaped in the query string. - static let apURLQueryAllowed: CharacterSet = { - let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 - let subDelimitersToEncode = "!$&'()*+,;=" - let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") - - return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters) - }() -} diff --git a/Sources/CoreATProtocol/Networking/Extensions/Encodable.swift b/Sources/CoreATProtocol/Networking/Extensions/Encodable.swift deleted file mode 100644 index 7701ce1..0000000 --- a/Sources/CoreATProtocol/Networking/Extensions/Encodable.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -extension Encodable { - func toJSONData() throws -> Data { - try JSONEncoder().encode(self) - } -} diff --git a/Sources/CoreATProtocol/Networking/Services/EndpointType.swift b/Sources/CoreATProtocol/Networking/Services/EndpointType.swift deleted file mode 100644 index 231b73c..0000000 --- a/Sources/CoreATProtocol/Networking/Services/EndpointType.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public protocol EndpointType: Sendable { - var baseURL: URL { get async } - var path: String { get } - var httpMethod: HTTPMethod { get } - var task: HTTPTask { get async } - var headers: HTTPHeaders? { get async } -} diff --git a/Sources/CoreATProtocol/Networking/Services/HTTPMethod.swift b/Sources/CoreATProtocol/Networking/Services/HTTPMethod.swift deleted file mode 100644 index 61bcae9..0000000 --- a/Sources/CoreATProtocol/Networking/Services/HTTPMethod.swift +++ /dev/null @@ -1,7 +0,0 @@ -public enum HTTPMethod : String { - case get = "GET" - case post = "POST" - case put = "PUT" - case patch = "PATCH" - case delete = "DELETE" -} diff --git a/Sources/CoreATProtocol/Networking/Services/HTTPTask.swift b/Sources/CoreATProtocol/Networking/Services/HTTPTask.swift deleted file mode 100644 index 97ef4c7..0000000 --- a/Sources/CoreATProtocol/Networking/Services/HTTPTask.swift +++ /dev/null @@ -1,7 +0,0 @@ -public enum HTTPTask: Sendable { - case request - - case requestParameters(encoding: ParameterEncoding) - - // case download, upload...etc -} diff --git a/Sources/CoreATProtocol/Networking/Services/NetworkRouter.swift b/Sources/CoreATProtocol/Networking/Services/NetworkRouter.swift deleted file mode 100644 index 8f0bec0..0000000 --- a/Sources/CoreATProtocol/Networking/Services/NetworkRouter.swift +++ /dev/null @@ -1,279 +0,0 @@ -import Foundation -import JWTKit -import OAuthenticator -#if canImport(CryptoKit) -import CryptoKit -#else -import Crypto -#endif - -@APActor -public protocol NetworkRouterDelegate: AnyObject { - func intercept(_ request: inout URLRequest) async - func shouldRetry(error: Error, attempts: Int) async throws -> Bool -} - -/// Describes the implementation details of a NetworkRouter -/// -/// ``NetworkRouter`` is the only implementation of this protocol available to the end user, but they can create their own -/// implementations that can be used for testing for instance. -@APActor -public protocol NetworkRouterProtocol: AnyObject { - associatedtype Endpoint: EndpointType - var delegate: NetworkRouterDelegate? { get set } - func execute(_ route: Endpoint, attempts: Int) async throws -> T -} - -public enum NetworkError : Error, Sendable { - case encodingFailed - case missingURL - case statusCode(_ statusCode: StatusCode?, data: Data) - case noStatusCode - case noData - case tokenRefresh -} - -public typealias HTTPHeaders = [String:String] - -/// The NetworkRouter is a generic class that has an ``EndpointType`` and it conforms to ``NetworkRouterProtocol` -@APActor -public class NetworkRouter: NetworkRouterProtocol { - - public weak var delegate: NetworkRouterDelegate? - let networking: Networking - let urlSessionTaskDelegate: URLSessionTaskDelegate? - var decoder: JSONDecoder - private let dpopActor = DPoPRequestActor() - - public init(networking: Networking? = nil, urlSessionDelegate: URLSessionDelegate? = nil, urlSessionTaskDelegate: URLSessionTaskDelegate? = nil, decoder: JSONDecoder? = nil) { - if let networking = networking { - self.networking = networking - } else { - self.networking = URLSession(configuration: URLSessionConfiguration.default, delegate: urlSessionDelegate, delegateQueue: nil) - } - - self.urlSessionTaskDelegate = urlSessionTaskDelegate - - if let decoder = decoder { - self.decoder = decoder - } else { - self.decoder = JSONDecoder() - self.decoder.keyDecodingStrategy = .convertFromSnakeCase - } - } - - /// This generic method will take a route and return the desired type via a network call - /// This method is async and it can throw errors - /// - Returns: The generic type is returned - public func execute(_ route: Endpoint, attempts: Int = 1) async throws -> T { - guard var request = try? await buildRequest(from: route) else { throw NetworkError.encodingFailed } - await delegate?.intercept(&request) - - let (data, response) = try await executeRequest(request) - guard let httpResponse = response as? HTTPURLResponse else { throw NetworkError.noStatusCode } - switch httpResponse.statusCode { - case 200...299: - return try decoder.decode(T.self, from: data) - default: - let statusCode = StatusCode(rawValue: httpResponse.statusCode) - let statusNetworkError = AtError.network(NetworkError.statusCode(statusCode, data: data)) - guard let delegate else { throw statusNetworkError } - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - - let errorToThrow: AtError - if let errorMessage = try? decoder.decode(ErrorMessage.self, from: data) { - errorToThrow = AtError.message(errorMessage) - } else { - errorToThrow = statusNetworkError - } - - guard try await delegate.shouldRetry(error: errorToThrow, attempts: attempts) else { throw errorToThrow } - return try await execute(route, attempts: attempts + 1) - } - } - - private func executeRequest(_ request: URLRequest) async throws -> (Data, URLResponse) { - if let accessToken = APEnvironment.current.accessToken, - let privateKey = APEnvironment.current.dpopPrivateKey, - let keys = APEnvironment.current.dpopKeys { - return try await dpopResponse( - for: request, - accessToken: accessToken, - privateKey: privateKey, - keys: keys - ) - } - - return try await networking.data(for: request, delegate: urlSessionTaskDelegate) - } - - private func dpopResponse( - for request: URLRequest, - accessToken: String, - privateKey: ES256PrivateKey, - keys: JWTKeyCollection - ) async throws -> (Data, URLResponse) { - let tokenHash = hashToken(accessToken) - let jwtGenerator: DPoPSigner.JWTGenerator = { params in - try await self.generateDPoPJWT( - params: params, - tokenHash: tokenHash, - privateKey: privateKey, - keys: keys - ) - } - - let responseProvider: URLResponseProvider = { request in - try await self.networking.data(for: request, delegate: nil) - } - - return try await dpopActor.response( - request: request, - jwtGenerator: jwtGenerator, - token: accessToken, - tokenHash: tokenHash, - provider: responseProvider - ) - } - - private func generateDPoPJWT( - params: DPoPSigner.JWTParameters, - tokenHash: String, - privateKey: ES256PrivateKey, - keys: JWTKeyCollection - ) async throws -> String { - let htu = stripQueryAndFragment(from: params.requestEndpoint) - let payload = DPoPRequestPayload( - htm: params.httpMethod, - htu: htu, - iat: .init(value: .now), - jti: .init(value: UUID().uuidString), - nonce: params.nonce, - ath: tokenHash - ) - - var header = JWTHeader() - header.typ = "dpop+jwt" - header.alg = "ES256" - - if let keyParams = privateKey.parameters { - let xBase64URL = keyParams.x - .replacingOccurrences(of: "+", with: "-") - .replacingOccurrences(of: "/", with: "_") - .replacingOccurrences(of: "=", with: "") - let yBase64URL = keyParams.y - .replacingOccurrences(of: "+", with: "-") - .replacingOccurrences(of: "/", with: "_") - .replacingOccurrences(of: "=", with: "") - - header.jwk = [ - "kty": .string("EC"), - "crv": .string("P-256"), - "x": .string(xBase64URL), - "y": .string(yBase64URL) - ] - } - - return try await keys.sign(payload, header: header) - } - - private func stripQueryAndFragment(from url: String) -> String { - let fragmentIndex = url.firstIndex(of: "#").map { url.distance(from: url.startIndex, to: $0) } ?? -1 - let queryIndex = url.firstIndex(of: "?").map { url.distance(from: url.startIndex, to: $0) } ?? -1 - - let end: Int - if fragmentIndex == -1 { - end = queryIndex - } else if queryIndex == -1 { - end = fragmentIndex - } else { - end = min(fragmentIndex, queryIndex) - } - - return end == -1 ? url : String(url.prefix(end)) - } - - private func hashToken(_ token: String) -> String { - let digest = SHA256.hash(data: Data(token.utf8)) - return Data(digest).base64URLEncodedString() - } - - func buildRequest(from route: Endpoint) async throws -> URLRequest { - - var request = await URLRequest(url: route.baseURL.appendingPathComponent(route.path), - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: 10.0) - - request.httpMethod = route.httpMethod.rawValue - do { - switch await route.task { - case .request: - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - await addAdditionalHeaders(route.headers, request: &request) - case .requestParameters(let parameterEncoding): - await addAdditionalHeaders(route.headers, request: &request) - try configureParameters(parameterEncoding: parameterEncoding, request: &request) - } - return request - } catch { - throw error - } - } - - private func configureParameters(parameterEncoding: ParameterEncoding, request: inout URLRequest) throws { - try parameterEncoding.encode(urlRequest: &request) - } - - private func addAdditionalHeaders(_ additionalHeaders: HTTPHeaders?, request: inout URLRequest) { - guard let headers = additionalHeaders else { return } - for (key, value) in headers { - request.setValue(value, forHTTPHeaderField: key) - } - } -} - -private struct DPoPRequestPayload: JWTPayload { - let htm: String - let htu: String - let iat: IssuedAtClaim - let jti: IDClaim - let nonce: String? - let ath: String? - - func verify(using key: some JWTAlgorithm) throws { - // No additional verification needed for DPoP - } -} - -private actor DPoPRequestActor { - private let signer = DPoPSigner() - - func response( - request: URLRequest, - jwtGenerator: DPoPSigner.JWTGenerator, - token: String, - tokenHash: String, - provider: URLResponseProvider - ) async throws -> (Data, URLResponse) { - try await signer.response( - isolation: self, - for: request, - using: jwtGenerator, - token: token, - tokenHash: tokenHash, - issuingServer: nil, - provider: provider - ) - } -} - -private extension Data { - func base64URLEncodedString() -> String { - base64EncodedString() - .replacingOccurrences(of: "+", with: "-") - .replacingOccurrences(of: "/", with: "_") - .replacingOccurrences(of: "=", with: "") - } -} diff --git a/Sources/CoreATProtocol/Networking/Services/NetworkingProtocol.swift b/Sources/CoreATProtocol/Networking/Services/NetworkingProtocol.swift deleted file mode 100644 index 2e16a78..0000000 --- a/Sources/CoreATProtocol/Networking/Services/NetworkingProtocol.swift +++ /dev/null @@ -1,8 +0,0 @@ -@preconcurrency import Foundation - -@APActor -public protocol Networking: Sendable { - func data(for request: URLRequest, delegate: URLSessionTaskDelegate?) async throws -> (Data, URLResponse) -} - -extension URLSession: Networking { } diff --git a/Sources/CoreATProtocol/Networking/Services/StatusCode.swift b/Sources/CoreATProtocol/Networking/Services/StatusCode.swift deleted file mode 100644 index d252960..0000000 --- a/Sources/CoreATProtocol/Networking/Services/StatusCode.swift +++ /dev/null @@ -1,76 +0,0 @@ -import Foundation - -public enum StatusCode: Int, Sendable { - // 1xx - case continueCode = 100 - case switchingProtocols = 101 - case processing = 102 - case earlyHints = 103 - - // 2xx - case ok = 200 - case created = 201 - case accepted = 202 - case nonAuthoritativeInformation = 203 - case noContent = 204 - case resetContent = 205 - case partialContent = 206 - case mutliStatus = 207 - case alreadyReported = 208 - case IMUsed = 226 - - // 3xx - case multipleChoices = 300 - case movedPermanently = 301 - case found = 302 - case seeOthers = 303 - case notModified = 304 - case useProxy = 305 - case switchProxy = 306 - case temporaryRedirect = 307 - case permanentRedirect = 308 - - // 4xx - case badRequest = 400 - case unauthorized = 401 - case paymentRequired = 402 - case forbidden = 403 - case notFound = 404 - case methodNotAllowed = 405 - case notAcceptable = 406 - case proxyAuthenticationRequired = 407 - case requestTimeout = 408 - case conflict = 409 - case gone = 410 - case lengthRequired = 411 - case preconditionFailed = 412 - case payloadTooLarge = 413 - case uriTooLong = 414 - case unsupportedMediaType = 415 - case rangeNotSatisfiable = 416 - case expectationFailed = 417 - case imATeapot = 418 - case misdirectedRequest = 421 - case unprocessableEntity = 422 - case locked = 423 - case failedDependency = 424 - case tooEarly = 425 - case upgradeRequire = 426 - case preconditionRequire = 428 - case tooManyRequests = 429 - case requestHeaderFieldsTooLarge = 431 - case unavailableForLegalResons = 451 - - // 5xx - case internalServerError = 500 - case notImplemented = 501 - case badGateway = 502 - case serviceUnavailable = 503 - case gatewayTimeout = 504 - case httpVersionNotSupported = 505 - case variantAlsoNegatiates = 506 - case insufficientStorage = 507 - case loopDetected = 508 - case notExtended = 510 - case networkAuthenticationRequired = 511 -} -- 2.51.2