Skip to content

Commit 135fe72

Browse files
committed
Refactor interceptors
1 parent af3310d commit 135fe72

22 files changed

+882
-481
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//
2+
// MyNewBenchmarkTarget.swift
3+
// Networking
4+
//
5+
// Created by Joe Maghzal on 5/29/25.
6+
//
7+
8+
import Benchmark
9+
import Foundation
10+
import Networking
11+
12+
//@Client
13+
//struct MyClient {
14+
// var command: Session {
15+
// Session()
16+
// .url("https://www.google.com")
17+
// .retryPolicy(.doNotRetry)
18+
// }
19+
//}
20+
21+
@Request
22+
struct BaseRequest {
23+
@Header var language = "en"
24+
@Parameter var data = "12"
25+
var timeout: TimeInterval = 90
26+
var request: some Request {
27+
HTTPRequest(path: "path") {
28+
ContentType(.applicationJson)
29+
Header("Custom", value: "value")
30+
}.timeout(timeout)
31+
.method(.post)
32+
.cachePolicy(.reloadIgnoringCacheData)
33+
.appendingParameter(Parameter("test", value: "benchmark"))
34+
.additionalHeaders {
35+
Header("fjfs", value: "tokr")
36+
Header("Additional", value: "value")
37+
}.appending("v2")
38+
}
39+
}
40+
41+
42+
//let client = MyClient()
43+
44+
let benchmarks : @Sendable () -> Void = {
45+
46+
func defaultCounter() -> Int {
47+
10
48+
}
49+
50+
func dummyCounter(_ count: Int) {
51+
for index in 0 ..< count {
52+
blackHole(index)
53+
}
54+
}
55+
56+
Benchmark("All metrics, full concurrency, async", configuration: .init(metrics: BenchmarkMetric.all)) { benchmark in
57+
var url = URL(string: "https://example.com")!
58+
url.append(path: "v2")
59+
url.append(
60+
queryItems: [
61+
URLQueryItem(name: "test", value: "benchmark"),
62+
URLQueryItem(name: "data", value: "12")
63+
]
64+
)
65+
var urlRequest = URLRequest(url: url)
66+
urlRequest.cachePolicy = .reloadIgnoringCacheData
67+
urlRequest.httpMethod = "POST"
68+
urlRequest.timeoutInterval = 90
69+
urlRequest.setValue("tokr", forHTTPHeaderField: "fjfs")
70+
urlRequest.setValue("value", forHTTPHeaderField: "Additional")
71+
urlRequest.setValue("value", forHTTPHeaderField: "Custom")
72+
urlRequest.setValue("en", forHTTPHeaderField: "language")
73+
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
74+
// do {
75+
// let request = try AnyRequest(BaseRequest())._makeURLRequest()
76+
// }catch {
77+
// print(error)
78+
// }
79+
}
80+
}
81+
//
82+
//struct ResponseB<T: Decodable>: Decodable {
83+
// var message: String?
84+
// var status_code: Int?
85+
// var errors: Errors?
86+
// var data: T?
87+
//}
88+
//
89+
//struct Errors: Decodable {
90+
// var message: [String]?
91+
// var info: [String]?
92+
//}
93+
//
94+
//extension DataTask {
95+
// func ttdecode<T: Decodable>(as type: T.Type) async throws -> T {
96+
// let response = try await response()
97+
// let decoder = await configurations.decoder
98+
// let decoded = try decoder.decode(ResponseB<T>.self, from: response.data)
99+
// if let message = decoded.message {
100+
// throw NSError(domain: message, code: 100)
101+
// }
102+
// if let data = decoded.data {
103+
// return data
104+
// }
105+
// throw NSError(domain: "Emptyy", code: 101)
106+
// }
107+
//}

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/NetworkingClient/ConfigurationValues.swift

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ extension ConfigurationValues {
1616
@Config public internal(set) var redirectionHandler: any RedirectionHandler = DefaultRedirectionHandler()
1717

1818
/// The validator used to validate HTTP response statuses.
19-
@Config public internal(set) var statusValidator: any StatusValidator = DefaultStatusValidator()
19+
@Config public internal(set) var statusValidator: (any StatusValidator)? = DefaultStatusValidator()
2020

2121
/// The retry policy used for request retries.
22-
@Config public internal(set) var retryPolicy: any RetryPolicy = DefaultRetryPolicy()
22+
@Config public internal(set) var retryPolicy: (any RetryInterceptor)? = DefaultRetryInterceptor()
2323

2424
/// The cache handler used for managing response caching.
2525
@Config public internal(set) var cacheHandler: any ResponseCacheHandler = DefaultResponseCacheHandler()
2626

2727
/// The interceptor used to modify or inspect requests before sending.
28-
@Config public internal(set) var interceptor: any RequestInterceptor = DefaultRequestInterceptor()
28+
@Config public internal(set) var interceptor: (any RequestInterceptor)? = nil
2929

3030
/// The authentication handler used to refresh authorization credentials.
31-
@Config public internal(set) var authHandler: (any AuthenticationInterceptor)? = nil
31+
@Config public internal(set) var authInterceptor: AuthInterceptor? = nil
3232

3333
/// The task storage used to track and cancel network tasks.
3434
///
@@ -44,42 +44,6 @@ extension Configurable {
4444
return configuration(\.logsEnabled, enabled)
4545
}
4646

47-
/// Sets the interceptor used to intercept requests before they are executed.
48-
public func interceptor(_ interceptor: some RequestInterceptor) -> Self {
49-
return configuration(\.interceptor, interceptor)
50-
}
51-
52-
/// Sets the interceptor used to intercept requests before they are executed.
53-
public func onRequest(
54-
_ handler: @escaping DefaultRequestInterceptor.Handler
55-
) -> Self {
56-
return interceptor(DefaultRequestInterceptor(handler))
57-
}
58-
59-
/// Sets the retry policy to use when a request fails..
60-
public func retryPolicy(_ retryPolicy: some RetryPolicy) -> Self {
61-
return configuration(\.retryPolicy, retryPolicy)
62-
}
63-
64-
/// Sets the retry policy to use when a request fails..
65-
///
66-
/// - Parameters:
67-
/// - limit: Maximum number of retry attempts.
68-
/// - statuses: A set of response statuses for which retries should be attempted.
69-
/// - handler: An optional custom retry decision handler.
70-
public func retry(
71-
limit: Int,
72-
for statuses: Set<ResponseStatus> = [],
73-
handler: DefaultRetryPolicy.Handler? = nil
74-
) -> Self {
75-
let policy = DefaultRetryPolicy(
76-
maxRetryCount: limit,
77-
retryableStatuses: statuses,
78-
handler: handler
79-
)
80-
return retryPolicy(policy)
81-
}
82-
8347
/// Sets the handler used for managing response caching.
8448
public func cacheHandler(_ handler: some ResponseCacheHandler) -> Self {
8549
return configuration(\.cacheHandler, handler)
@@ -89,32 +53,4 @@ extension Configurable {
8953
public func redirectionHandler(_ handler: some RedirectionHandler) -> Self {
9054
return configuration(\.redirectionHandler, handler)
9155
}
92-
93-
/// Sets the validator used to validate HTTP response statuses.
94-
public func statusValidator(_ validator: some StatusValidator) -> Self {
95-
return configuration(\.statusValidator, validator)
96-
}
97-
98-
/// Sets the validator used to validate HTTP response statuses.
99-
///
100-
/// - Parameters:
101-
/// - statuses: A set of valid response statuses.
102-
/// - handler: An optional closure executed when a status needs validation.
103-
public func validate(
104-
for statuses: Set<ResponseStatus> = ResponseStatus.validStatuses,
105-
_ handler: DefaultStatusValidator.Handler? = nil
106-
) -> Self {
107-
let validator = DefaultStatusValidator(
108-
validStatuses: statuses,
109-
handler
110-
)
111-
return statusValidator(validator)
112-
}
113-
114-
/// Sets the handler used to manage request authorization.
115-
public func authorization(
116-
_ interceptor: some AuthenticationInterceptor
117-
) -> Self {
118-
return configuration(\.authHandler, interceptor)
119-
}
12056
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// AuthenticationInterceptor.swift
3+
// Networking
4+
//
5+
// Created by Joe Maghzal on 3/5/25.
6+
//
7+
8+
import Foundation
9+
import NetworkingCore
10+
11+
public actor AuthInterceptor {
12+
private let provider: any AuthProvider
13+
private var task: Task<Void, any Error>?
14+
15+
public init(provider: any AuthProvider) {
16+
self.provider = provider
17+
}
18+
19+
private func refresh(with session: Session) async throws {
20+
if let task {
21+
try await task.value
22+
}else {
23+
task = Task {
24+
try await provider.refresh(with: session)
25+
}
26+
try await task?.value
27+
}
28+
}
29+
}
30+
31+
// MARK: - RequestInterceptor
32+
extension AuthInterceptor: RequestInterceptor {
33+
public func intercept(
34+
_ task: some NetworkingTask,
35+
request: consuming URLRequest,
36+
for session: Session,
37+
with configurations: ConfigurationValues
38+
) async throws -> URLRequest {
39+
if provider.requiresRefresh() {
40+
try await refresh(with: session)
41+
}
42+
return try provider.credential.modifying(consume request)
43+
}
44+
}
45+
46+
// MARK: - ResponseInterceptor
47+
extension AuthInterceptor: ResponseInterceptor {
48+
public func intercept(
49+
_ task: some NetworkingTask,
50+
for session: Session,
51+
with context: borrowing Context
52+
) async throws -> RequestContinuation {
53+
guard context.status == .unauthorized, context.retryCount == 0 else {
54+
return .continue
55+
}
56+
57+
do {
58+
try await refresh(with: session)
59+
}catch {
60+
throw error
61+
}
62+
return .retry
63+
}
64+
}
65+
66+
extension Configurable {
67+
/// Sets the handler used to manage request authorization.
68+
public func authorization(
69+
_ provider: some AuthProvider
70+
) -> Self {
71+
return configuration(\.authInterceptor, AuthInterceptor(provider: provider))
72+
}
73+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// AuthProvider.swift
3+
// Networking
4+
//
5+
// Created by Joe Maghzal on 31/05/2025.
6+
//
7+
8+
import Foundation
9+
import NetworkingCore
10+
11+
public protocol AuthProvider: Sendable {
12+
associatedtype Credential: RequestModifier
13+
var credential: Self.Credential {get}
14+
15+
func refresh(with session: Session) async throws
16+
func requiresRefresh() -> Bool
17+
}

Sources/NetworkingClient/Handlers/AuthenticationInterceptor.swift

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)