Skip to content

Commit 962e12d

Browse files
committed
add continuation to AsyncRequestBag.init
tests still need to migrate to the new init
1 parent 8acc1f3 commit 962e12d

File tree

3 files changed

+82
-29
lines changed

3 files changed

+82
-29
lines changed

Sources/AsyncHTTPClient/AsyncAwait/AsyncRequestBag.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class AsyncRequestBag {
4141
requestOptions: RequestOptions,
4242
logger: Logger,
4343
connectionDeadline: NIODeadline,
44-
preferredEventLoop: EventLoop
44+
preferredEventLoop: EventLoop,
45+
responseContinuation: UnsafeContinuation<HTTPClientResponse, Error>
4546
) throws {
4647
self.request = request
4748
self.requestOptions = requestOptions
@@ -53,6 +54,8 @@ class AsyncRequestBag {
5354
self.poolKey = validatedRequest.poolKey
5455
self.requestHead = validatedRequest.head
5556
self.requestFramingMetadata = validatedRequest.requestFramingMetadata
57+
58+
self.state.registerContinuation(responseContinuation)
5659
}
5760

5861
func result() async throws -> HTTPClientResponse {

Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift

+63-19
Original file line numberDiff line numberDiff line change
@@ -191,26 +191,70 @@ extension HTTPClientResponse.Body: AsyncSequence {
191191
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
192192
extension HTTPClient {
193193
func execute(_ request: HTTPClientRequest, deadline: NIODeadline, logger: Logger) async throws -> HTTPClientResponse {
194-
let bag = try AsyncRequestBag(
195-
request: request,
196-
requestOptions: .init(idleReadTimeout: nil),
197-
logger: logger,
198-
connectionDeadline: .now() + .seconds(10),
199-
preferredEventLoop: self.eventLoopGroup.next()
200-
)
201-
202-
return try await withTaskCancellationHandler {
203-
bag.cancel()
204-
} operation: { () -> HTTPClientResponse in
205-
// first register the completion
206-
async let result = bag.result()
207-
208-
// second throw it onto the connection pool for execution
209-
self.poolManager.executeRequest(bag)
210-
211-
// third await result
212-
return try await result
194+
actor SwiftCancellationHandlingSucksAsFuck {
195+
196+
enum State {
197+
case initialized
198+
case register(AsyncRequestBag)
199+
case cancelled
200+
}
201+
202+
private var state: State = .initialized
203+
204+
init() {}
205+
206+
func registerRequestBag(_ bag: AsyncRequestBag) {
207+
switch self.state {
208+
case .initialized:
209+
self.state = .register(bag)
210+
case .cancelled:
211+
bag.cancel()
212+
case .register:
213+
preconditionFailure()
214+
}
215+
}
216+
217+
func cancel() {
218+
switch self.state {
219+
case .register(let bag):
220+
self.state = .cancelled
221+
bag.cancel()
222+
case .cancelled:
223+
break
224+
case .initialized:
225+
self.state = .cancelled
226+
}
227+
}
213228
}
229+
230+
let cancelHandler = SwiftCancellationHandlingSucksAsFuck()
231+
232+
//let onCancel: @Sendable () -> () =
233+
234+
return try await withTaskCancellationHandler(operation: { () async throws -> HTTPClientResponse in
235+
try await withUnsafeThrowingContinuation{
236+
(continuation: UnsafeContinuation<HTTPClientResponse, Swift.Error>) -> Void in
237+
238+
let bag = try! AsyncRequestBag(
239+
request: request,
240+
requestOptions: .init(idleReadTimeout: nil),
241+
logger: logger,
242+
connectionDeadline: .now() + .seconds(10),
243+
preferredEventLoop: self.eventLoopGroup.next(),
244+
responseContinuation: continuation
245+
)
246+
247+
_Concurrency.Task {
248+
await cancelHandler.registerRequestBag(bag)
249+
}
250+
251+
self.poolManager.executeRequest(bag)
252+
}
253+
}, onCancel: {
254+
_Concurrency.Task {
255+
await cancelHandler.cancel()
256+
}
257+
})
214258
}
215259
}
216260

Tests/AsyncHTTPClientTests/AsyncRequestTests.swift

+15-9
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ final class AsyncRequestTests: XCTestCase {
2929
request.method = .GET
3030

3131
var maybeRequestBag: AsyncRequestBag?
32-
XCTAssertNoThrow(maybeRequestBag = try AsyncRequestBag(
33-
request: request,
34-
requestOptions: .forTests(),
35-
logger: logger,
36-
connectionDeadline: .distantFuture,
37-
preferredEventLoop: embeddedEventLoop
38-
))
39-
guard let requestBag = maybeRequestBag else { return XCTFail("unexpectedly found nil") }
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)
42+
}
43+
44+
45+
46+
// guard let requestBag = maybeRequestBag else { return XCTFail("unexpectedly found nil") }
4047

41-
async let result = requestBag.result()
4248

4349
Task.detached {
4450
try await Task.sleep(nanoseconds: 5 * 1000 * 1000)

0 commit comments

Comments
 (0)