@@ -21,38 +21,28 @@ import XCTest
21
21
@available ( macOS 12 . 0 , iOS 15 . 0 , watchOS 8 . 0 , tvOS 15 . 0 , * )
22
22
final class AsyncRequestTests: XCTestCase {
23
23
func testCancelAsyncRequest( ) async {
24
- let logger = Logger ( label: " test " )
25
24
let embeddedEventLoop = EmbeddedEventLoop ( )
26
25
defer { XCTAssertNoThrow ( try embeddedEventLoop. syncShutdownGracefully ( ) ) }
27
26
28
27
var request = HTTPClientRequest ( url: " https://localhost/ " )
29
28
request. method = . GET
30
-
31
- var maybeRequestBag : AsyncRequestBag ?
32
- var requestBagPromise = embeddedEventLoop. makePromise ( of: AsyncRequestBag . self)
33
- async let result = withUnsafeContinuation { ( continuation: UnsafeContinuation < HTTPClientResponse , Error > ) in
34
- let requestBag = try AsyncRequestBag (
35
- request: request,
36
- requestOptions: . forTests( ) ,
37
- logger: logger,
38
- connectionDeadline: . distantFuture,
39
- preferredEventLoop: embeddedEventLoop
40
- )
41
- requestBagPromise. succeed ( requestBag)
29
+ var maybePreparedRequest : PreparedRequest ?
30
+ XCTAssertNoThrow ( maybePreparedRequest = try request. prepareForExecution ( ) )
31
+ guard let preparedRequest = maybePreparedRequest else {
32
+ return
42
33
}
43
-
44
-
45
-
46
- // guard let requestBag = maybeRequestBag else { return XCTFail("unexpectedly found nil") }
47
-
34
+ let ( requestBag, responseTask) = AsyncRequestBag . makeWithResultTask (
35
+ request: preparedRequest,
36
+ preferredEventLoop: embeddedEventLoop
37
+ )
48
38
49
39
Task . detached {
50
40
try await Task . sleep ( nanoseconds: 5 * 1000 * 1000 )
51
41
requestBag. cancel ( )
52
42
}
53
43
54
44
do {
55
- _ = try await result
45
+ _ = try await responseTask . result. get ( )
56
46
XCTFail ( " Expected to throw error " )
57
47
} catch {
58
48
XCTAssertEqual ( error as? HTTPClientError , . cancelled)
@@ -62,24 +52,19 @@ final class AsyncRequestTests: XCTestCase {
62
52
func testResponseStreamingWorks( ) async {
63
53
let embeddedEventLoop = EmbeddedEventLoop ( )
64
54
defer { XCTAssertNoThrow ( try embeddedEventLoop. syncShutdownGracefully ( ) ) }
65
- let logger = Logger ( label: " test " )
66
55
67
56
var request = HTTPClientRequest ( url: " https://localhost/ " )
68
57
request. method = . GET
69
-
70
- var maybeRequestBag : AsyncRequestBag ?
71
- XCTAssertNoThrow ( maybeRequestBag = try AsyncRequestBag (
72
- request: request,
73
- requestOptions: . forTests( ) ,
74
- logger: logger,
75
- connectionDeadline: . distantFuture,
76
- preferredEventLoop: embeddedEventLoop
77
- ) )
78
- guard let requestBag = maybeRequestBag else { return XCTFail ( " unexpectedly found nil " ) }
79
58
80
-
81
- async let awaitableResponse = requestBag. result ( )
82
- await Task . yield ( )
59
+ var maybePreparedRequest : PreparedRequest ?
60
+ XCTAssertNoThrow ( maybePreparedRequest = try request. prepareForExecution ( ) )
61
+ guard let preparedRequest = maybePreparedRequest else {
62
+ return
63
+ }
64
+ let ( requestBag, responseTask) = AsyncRequestBag . makeWithResultTask (
65
+ request: preparedRequest,
66
+ preferredEventLoop: embeddedEventLoop
67
+ )
83
68
84
69
let executor = MockRequestExecutor (
85
70
pauseRequestBodyPartStreamAfterASingleWrite: true ,
@@ -94,7 +79,7 @@ final class AsyncRequestTests: XCTestCase {
94
79
requestBag. receiveResponseHead ( responseHead)
95
80
96
81
do {
97
- let response = try await awaitableResponse
82
+ let response = try await responseTask . result . get ( )
98
83
XCTAssertEqual ( response. status, responseHead. status)
99
84
XCTAssertEqual ( response. headers, responseHead. headers)
100
85
XCTAssertEqual ( response. version, responseHead. version)
@@ -133,7 +118,6 @@ final class AsyncRequestTests: XCTestCase {
133
118
func testWriteBackpressureWorks( ) async {
134
119
let embeddedEventLoop = EmbeddedEventLoop ( )
135
120
defer { XCTAssertNoThrow ( try embeddedEventLoop. syncShutdownGracefully ( ) ) }
136
- let logger = Logger ( label: " test " )
137
121
138
122
let streamWriter = AsyncSequenceWriter ( )
139
123
if await streamWriter. hasDemand { XCTFail ( " Did not expect to have a demand at this point " ) }
@@ -142,20 +126,15 @@ final class AsyncRequestTests: XCTestCase {
142
126
request. method = . POST
143
127
request. body = . stream( streamWriter)
144
128
145
- var maybeRequestBag: AsyncRequestBag ?
146
- XCTAssertNoThrow( maybeRequestBag = try AsyncRequestBag (
147
- request: request,
148
- requestOptions: . forTests( ) ,
149
- logger: logger,
150
- connectionDeadline: . distantFuture,
129
+ var maybePreparedRequest: PreparedRequest ?
130
+ XCTAssertNoThrow( maybePreparedRequest = try request. prepareForExecution ( ) )
131
+ guard let preparedRequest = maybePreparedRequest else {
132
+ return
133
+ }
134
+ let ( requestBag, responseTask) = AsyncRequestBag . makeWithResultTask (
135
+ request: preparedRequest,
151
136
preferredEventLoop: embeddedEventLoop
152
- ) )
153
- guard let requestBag = maybeRequestBag else { return XCTFail ( " unexpectedly found nil " ) }
154
-
155
- async let await ableResponse = requestBag. result ( )
156
-
157
- // we need to yield here, to ensure the continuation can be build up
158
- await Task. yield ( )
137
+ )
159
138
160
139
let executor = MockRequestExecutor ( eventLoop: embeddedEventLoop)
161
140
@@ -201,7 +180,7 @@ final class AsyncRequestTests: XCTestCase {
201
180
XCTAssertFalse( executor. signalledDemandForResponseBody)
202
181
requestBag. receiveResponseHead ( responseHead)
203
182
204
- let response = try await awaitableResponse
183
+ let response = try await responseTask . result . get ( )
205
184
XCTAssertEqual( response. status, responseHead. status)
206
185
XCTAssertEqual( response. headers, responseHead. headers)
207
186
XCTAssertEqual( response. version, responseHead. version)
@@ -245,22 +224,21 @@ final class AsyncRequestTests: XCTestCase {
245
224
var request = HTTPClientRequest ( url: " https://localhost: \( httpBin. port) / " )
246
225
request. headers = [ " host " : " localhost: \( httpBin. port) " ]
247
226
248
- let requestBag = try AsyncRequestBag (
249
- request: request,
250
- requestOptions: . forTests( ) ,
251
- logger: Logger ( label: " test " ) ,
252
- connectionDeadline: . distantFuture,
227
+ var maybePreparedRequest : PreparedRequest ?
228
+ XCTAssertNoThrow ( maybePreparedRequest = try request. prepareForExecution ( ) )
229
+ guard let preparedRequest = maybePreparedRequest else {
230
+ return
231
+ }
232
+ let ( requestBag, responseTask) = AsyncRequestBag . makeWithResultTask (
233
+ request: preparedRequest,
253
234
preferredEventLoop: eventLoopGroup. next ( )
254
235
)
255
236
256
- async let awaitableResponse = requestBag. result ( )
257
- await Task . yield ( )
258
-
259
237
http2Connection. executeRequest ( requestBag)
260
238
261
239
XCTAssertEqual ( delegate. hitStreamClosed, 0 )
262
240
263
- let response = try await awaitableResponse
241
+ let response = try await responseTask . result . get ( )
264
242
265
243
XCTAssertEqual ( response. status, . ok)
266
244
XCTAssertEqual ( response. version, . http2)
@@ -306,22 +284,21 @@ final class AsyncRequestTests: XCTestCase {
306
284
request. headers = [ " host " : " localhost: \( httpBin. port) " ]
307
285
request. body = . stream( length: 800 , streamWriter)
308
286
309
- let requestBag = try AsyncRequestBag (
310
- request: request,
311
- requestOptions: . forTests( ) ,
312
- logger: Logger ( label: " test " ) ,
313
- connectionDeadline: . distantFuture,
287
+ var maybePreparedRequest: PreparedRequest ?
288
+ XCTAssertNoThrow( maybePreparedRequest = try request. prepareForExecution ( ) )
289
+ guard let preparedRequest = maybePreparedRequest else {
290
+ return
291
+ }
292
+ let ( requestBag, responseTask) = AsyncRequestBag . makeWithResultTask (
293
+ request: preparedRequest,
314
294
preferredEventLoop: eventLoopGroup. next ( )
315
295
)
316
296
317
- async let await ableResponse = requestBag. result ( )
318
- await Task . yield ( ) // yield is used here to ensure register continuation is executed here.
319
-
320
297
http2Connection. executeRequest ( requestBag)
321
298
322
299
XCTAssertEqual ( delegate. hitStreamClosed, 0 )
323
300
324
- let response = try await awaitableResponse
301
+ let response = try await responseTask . result . get ( )
325
302
326
303
XCTAssertEqual ( response. status, . ok)
327
304
XCTAssertEqual ( response. version, . http2)
@@ -506,3 +483,33 @@ actor AsyncSequenceWriter: AsyncSequence {
506
483
}
507
484
}
508
485
}
486
+
487
+ @available( macOS 12.0 , iOS 15.0 , watchOS 8.0 , tvOS 15.0 , * )
488
+ extension AsyncRequestBag {
489
+ fileprivate static func makeWithResultTask(
490
+ request: PreparedRequest,
491
+ requestOptions: RequestOptions = . forTests( ) ,
492
+ logger: Logger = Logger ( label: " test " ) ,
493
+ connectionDeadline: NIODeadline = . distantFuture,
494
+ preferredEventLoop: EventLoop
495
+ ) - > ( AsyncRequestBag, _Concurrency. Task< HTTPClientResponse, Error> ) {
496
+ let requestBagPromise = preferredEventLoop. makePromise ( of: AsyncRequestBag . self)
497
+ let result = Task {
498
+ try await withUnsafeThrowingContinuation { ( continuation: UnsafeContinuation < HTTPClientResponse , Error > ) in
499
+ let requestBag = AsyncRequestBag (
500
+ request: request,
501
+ requestOptions: requestOptions,
502
+ logger: logger,
503
+ connectionDeadline: connectionDeadline,
504
+ preferredEventLoop: preferredEventLoop,
505
+ responseContinuation: continuation
506
+ )
507
+ requestBagPromise. succeed ( requestBag)
508
+ }
509
+ }
510
+ // the promise can never fail and it is therefore safe to force unwrap
511
+ let requestBag = try ! requestBagPromise. futureResult. wait ( )
512
+
513
+ return ( requestBag, result)
514
+ }
515
+ }
0 commit comments