Skip to content

Commit b9029ef

Browse files
authored
Add support for custom cancellation error (#683)
1 parent 45626d3 commit b9029ef

File tree

8 files changed

+44
-18
lines changed

8 files changed

+44
-18
lines changed

Sources/AsyncHTTPClient/HTTPHandler.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ extension URL {
688688
}
689689

690690
protocol HTTPClientTaskDelegate {
691-
func cancel()
691+
func fail(_ error: Error)
692692
}
693693

694694
extension HTTPClient {
@@ -780,12 +780,18 @@ extension HTTPClient {
780780

781781
/// Cancels the request execution.
782782
public func cancel() {
783+
self.fail(reason: HTTPClientError.cancelled)
784+
}
785+
786+
/// Cancels the request execution with a custom `Error`.
787+
/// - Parameter reason: the error that is used to fail the promise
788+
public func fail(reason error: Error) {
783789
let taskDelegate = self.lock.withLock { () -> HTTPClientTaskDelegate? in
784790
self._isCancelled = true
785791
return self._taskDelegate
786792
}
787793

788-
taskDelegate?.cancel()
794+
taskDelegate?.fail(error)
789795
}
790796

791797
func succeed<Delegate: HTTPClientResponseDelegate>(promise: EventLoopPromise<Response>?,

Sources/AsyncHTTPClient/RequestBag.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ final class RequestBag<Delegate: HTTPClientResponseDelegate> {
394394
}
395395
}
396396

397-
extension RequestBag: HTTPSchedulableRequest {
397+
extension RequestBag: HTTPSchedulableRequest, HTTPClientTaskDelegate {
398398
var tlsConfiguration: TLSConfiguration? {
399399
self.request.tlsConfiguration
400400
}
@@ -511,9 +511,3 @@ extension RequestBag: HTTPExecutableRequest {
511511
}
512512
}
513513
}
514-
515-
extension RequestBag: HTTPClientTaskDelegate {
516-
func cancel() {
517-
self.fail(HTTPClientError.cancelled)
518-
}
519-
}

Tests/AsyncHTTPClientTests/HTTP1ClientChannelHandlerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ class HTTP1ClientChannelHandlerTests: XCTestCase {
327327
XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead)))
328328

329329
// canceling the request
330-
requestBag.cancel()
330+
requestBag.fail(HTTPClientError.cancelled)
331331
XCTAssertThrowsError(try requestBag.task.futureResult.wait()) {
332332
XCTAssertEqual($0 as? HTTPClientError, .cancelled)
333333
}

Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ class HTTP2ClientRequestHandlerTests: XCTestCase {
276276
XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead)))
277277

278278
// canceling the request
279-
requestBag.cancel()
279+
requestBag.fail(HTTPClientError.cancelled)
280280
XCTAssertThrowsError(try requestBag.task.futureResult.wait()) {
281281
XCTAssertEqual($0 as? HTTPClientError, .cancelled)
282282
}

Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ extension HTTPClientTests {
5252
("testStreaming", testStreaming),
5353
("testFileDownload", testFileDownload),
5454
("testFileDownloadError", testFileDownloadError),
55+
("testFileDownloadCustomError", testFileDownloadCustomError),
5556
("testRemoteClose", testRemoteClose),
5657
("testReadTimeout", testReadTimeout),
5758
("testConnectTimeout", testConnectTimeout),

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,31 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass {
569569
XCTAssertEqual(0, progress.receivedBytes)
570570
}
571571

572+
func testFileDownloadCustomError() throws {
573+
let request = try Request(url: self.defaultHTTPBinURLPrefix + "get")
574+
struct CustomError: Equatable, Error {}
575+
576+
try TemporaryFileHelpers.withTemporaryFilePath { path in
577+
let delegate = try FileDownloadDelegate(path: path, reportHead: { task, head in
578+
XCTAssertEqual(head.status, .ok)
579+
task.fail(reason: CustomError())
580+
}, reportProgress: { _, _ in
581+
XCTFail("should never be called")
582+
})
583+
XCTAssertThrowsError(
584+
try self.defaultClient.execute(
585+
request: request,
586+
delegate: delegate
587+
)
588+
.wait()
589+
) { error in
590+
XCTAssertEqualTypeAndValue(error, CustomError())
591+
}
592+
593+
XCTAssertFalse(TemporaryFileHelpers.fileExists(path: path))
594+
}
595+
}
596+
572597
func testRemoteClose() {
573598
XCTAssertThrowsError(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "close").wait()) {
574599
XCTAssertEqual($0 as? HTTPClientError, .remoteConnectionClosed)

Tests/AsyncHTTPClientTests/HTTPConnectionPoolTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ class HTTPConnectionPoolTests: XCTestCase {
334334

335335
pool.executeRequest(requestBag)
336336
XCTAssertNoThrow(try eventLoop.scheduleTask(in: .seconds(1)) {}.futureResult.wait())
337-
requestBag.cancel()
337+
requestBag.fail(HTTPClientError.cancelled)
338338

339339
XCTAssertThrowsError(try requestBag.task.futureResult.wait()) {
340340
XCTAssertEqual($0 as? HTTPClientError, .cancelled)

Tests/AsyncHTTPClientTests/RequestBagTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ final class RequestBagTests: XCTestCase {
220220
XCTAssert(bag.eventLoop === embeddedEventLoop)
221221

222222
let executor = MockRequestExecutor(eventLoop: embeddedEventLoop)
223-
bag.cancel()
223+
bag.fail(HTTPClientError.cancelled)
224224

225225
bag.willExecuteRequest(executor)
226226
XCTAssertTrue(executor.isCancelled, "The request bag, should call cancel immediately on the executor")
@@ -301,7 +301,7 @@ final class RequestBagTests: XCTestCase {
301301
bag.fail(MyError())
302302
XCTAssertEqual(delegate.hitDidReceiveError, 1)
303303

304-
bag.cancel()
304+
bag.fail(HTTPClientError.cancelled)
305305
XCTAssertEqual(delegate.hitDidReceiveError, 1)
306306

307307
XCTAssertThrowsError(try bag.task.futureResult.wait()) {
@@ -342,7 +342,7 @@ final class RequestBagTests: XCTestCase {
342342
XCTAssertEqual(delegate.hitDidSendRequestHead, 1)
343343
XCTAssertEqual(delegate.hitDidSendRequest, 1)
344344

345-
bag.cancel()
345+
bag.fail(HTTPClientError.cancelled)
346346
XCTAssertTrue(executor.isCancelled, "The request bag, should call cancel immediately on the executor")
347347

348348
XCTAssertThrowsError(try bag.task.futureResult.wait()) {
@@ -376,7 +376,7 @@ final class RequestBagTests: XCTestCase {
376376
bag.requestWasQueued(queuer)
377377

378378
XCTAssertEqual(queuer.hitCancelCount, 0)
379-
bag.cancel()
379+
bag.fail(HTTPClientError.cancelled)
380380
XCTAssertEqual(queuer.hitCancelCount, 1)
381381

382382
XCTAssertThrowsError(try bag.task.futureResult.wait()) {
@@ -445,9 +445,9 @@ final class RequestBagTests: XCTestCase {
445445
let executor = MockRequestExecutor(eventLoop: embeddedEventLoop)
446446
executor.runRequest(bag)
447447

448-
// This simulates a race between the user cancelling the task (which invokes `RequestBag.cancel`) and the
448+
// This simulates a race between the user cancelling the task (which invokes `RequestBag.fail(_:)`) and the
449449
// call to `resumeRequestBodyStream` (which comes from the `Channel` event loop and so may have to hop.
450-
bag.cancel()
450+
bag.fail(HTTPClientError.cancelled)
451451
bag.resumeRequestBodyStream()
452452

453453
XCTAssertEqual(executor.isCancelled, true)

0 commit comments

Comments
 (0)