1- #if !DISABLE_FOUNDATION_NETWORKING
1+ import CallableKit
22import Foundation
33#if canImport(FoundationNetworking)
44@preconcurrency import FoundationNetworking
55#endif
66
7- enum FoundationHTTPStubClientError : Error {
8- case unexpectedState
9- case unexpectedStatusCode( _ code: Int )
10- }
7+ public struct URLSessionStubClient : StubClientProtocol {
8+ public enum UnexpectedError : Error {
9+ case state
10+ case statusCode( _ code: Int )
11+ }
1112
12- struct FoundationHTTPStubResponseError : Error , CustomStringConvertible {
13- var path : String
14- var body : Data
15- var request : URLRequest
16- var response : HTTPURLResponse
17- var description : String {
18- " ResponseError. path= \( path) , status= \( response. statusCode) "
13+ public struct ResponseError : Error , CustomStringConvertible {
14+ public var path : String
15+ public var body : Data
16+ public var request : URLRequest
17+ public var response : HTTPURLResponse
18+ public var description : String {
19+ " ResponseError. path= \( path) , status= \( response. statusCode) "
20+ }
1921 }
20- }
2122
22- final class FoundationHTTPStubClient : StubClientProtocol {
23- private let baseURL : URL
24- private let session : URLSession
25- private let onWillSendRequest : ( @Sendable ( inout URLRequest ) async throws -> Void ) ?
26- private let mapResponseError : ( @Sendable ( FoundationHTTPStubResponseError) throws -> Never ) ?
23+ public var baseURL : URL
24+ public var session : URLSession
25+ public var onWillSendRequest : ( @Sendable ( inout URLRequest ) async throws -> Void ) ?
26+ public var mapResponseError : ( @Sendable ( ResponseError) throws -> Never ) ?
2727
28- init (
28+ public init (
2929 baseURL: URL ,
30+ session: URLSession = . init( configuration: . ephemeral) ,
3031 onWillSendRequest: ( @Sendable ( inout URLRequest ) async throws -> Void ) ? = nil ,
31- mapResponseError: ( @Sendable ( FoundationHTTPStubResponseError ) throws -> Never ) ? = nil
32+ mapResponseError: ( @Sendable ( ResponseError ) throws -> Never ) ? = nil
3233 ) {
3334 self . baseURL = baseURL
34- session = . init ( configuration : . ephemeral )
35+ self . session = session
3536 self . onWillSendRequest = onWillSendRequest
3637 self . mapResponseError = mapResponseError
3738 }
3839
39- func send< Req: Encodable , Res: Decodable > (
40+ public func send< Req: Encodable , Res: Decodable > (
4041 path: String ,
4142 request: Req
4243 ) async throws -> Res {
@@ -48,7 +49,7 @@ final class FoundationHTTPStubClient: StubClientProtocol {
4849 q. addValue ( " \( body. count) " , forHTTPHeaderField: " Content-Length " )
4950 q. httpBody = body
5051
51- if let onWillSendRequest = onWillSendRequest {
52+ if let onWillSendRequest {
5253 try await onWillSendRequest ( & q)
5354 }
5455 let ( data, urlResponse) = try await session. data ( for: q)
@@ -62,25 +63,25 @@ final class FoundationHTTPStubClient: StubClientProtocol {
6263 request: URLRequest
6364 ) throws -> Res {
6465 guard let urlResponse = response as? HTTPURLResponse else {
65- throw FoundationHTTPStubClientError . unexpectedState
66+ throw UnexpectedError . state
6667 }
6768
6869 if 200 ... 299 ~= urlResponse. statusCode {
6970 return try makeDecoder ( ) . decode ( Res . self, from: data)
7071 } else if 400 ... 599 ~= urlResponse. statusCode {
71- let error = FoundationHTTPStubResponseError (
72+ let error = ResponseError (
7273 path: path,
7374 body: data,
7475 request: request,
7576 response: urlResponse
7677 )
77- if let mapResponseError = mapResponseError {
78+ if let mapResponseError {
7879 try mapResponseError ( error)
7980 } else {
8081 throw error
8182 }
8283 } else {
83- throw FoundationHTTPStubClientError . unexpectedStatusCode ( urlResponse. statusCode)
84+ throw UnexpectedError . statusCode ( urlResponse. statusCode)
8485 }
8586 }
8687}
@@ -98,13 +99,9 @@ private func makeEncoder() -> JSONEncoder {
9899}
99100
100101#if canImport(FoundationNetworking)
101- private class TaskBox : @unchecked Sendable {
102- var task : URLSessionTask ?
103- }
104-
105102extension URLSession {
106103 func data( for request: URLRequest ) async throws -> ( Data , URLResponse ) {
107- let taskBox = TaskBox ( )
104+ nonisolated ( unsafe ) var taskBox : URLSessionTask ?
108105 return try await withTaskCancellationHandler ( operation: {
109106 try await withCheckedThrowingContinuation { continuation in
110107 let task = dataTask ( with: request) { data, response, error in
@@ -115,13 +112,12 @@ extension URLSession {
115112 }
116113 return continuation. resume ( returning: ( data, response) )
117114 }
118- taskBox. task = task
115+ taskBox = task
119116 task. resume ( )
120117 }
121118 } , onCancel: {
122- taskBox. task ? . cancel ( )
119+ taskBox? . cancel ( )
123120 } )
124121 }
125122}
126123#endif
127- #endif
0 commit comments