Skip to content

Commit 3960678

Browse files
authored
Don’t call didReceiveError twice if deadline is exceeded and request is canceled aftewards (#609)
1 parent 0527bbb commit 3960678

File tree

3 files changed

+55
-7
lines changed

3 files changed

+55
-7
lines changed

Sources/AsyncHTTPClient/RequestBag+StateMachine.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,13 +607,18 @@ extension RequestBag.StateMachine {
607607
// An error occurred after the request has finished. Ignore...
608608
return .none
609609
case .deadlineExceededWhileQueued:
610-
// if we just get a `HTTPClientError.cancelled` we can use the original cancellation reason
611-
// to give a more descriptive error to the user.
612-
if (error as? HTTPClientError) == .cancelled {
613-
return .failTask(HTTPClientError.deadlineExceeded, nil, nil)
614-
}
615-
// otherwise we already had an intermediate connection error which we should present to the user instead
616-
return .failTask(error, nil, nil)
610+
let realError: Error = {
611+
if (error as? HTTPClientError) == .cancelled {
612+
/// if we just get a `HTTPClientError.cancelled` we can use the original cancellation reason
613+
/// to give a more descriptive error to the user.
614+
return HTTPClientError.deadlineExceeded
615+
} else {
616+
/// otherwise we already had an intermediate connection error which we should present to the user instead
617+
return error
618+
}
619+
}()
620+
self.state = .finished(error: realError)
621+
return .failTask(realError, nil, nil)
617622
case .finished(.some(_)):
618623
// this might happen, if the stream consumer has failed... let's just drop the data
619624
return .none

Tests/AsyncHTTPClientTests/RequestBagTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extension RequestBagTests {
2929
("testTaskIsFailedIfWritingFails", testTaskIsFailedIfWritingFails),
3030
("testCancelFailsTaskBeforeRequestIsSent", testCancelFailsTaskBeforeRequestIsSent),
3131
("testDeadlineExceededFailsTaskEvenIfRaceBetweenCancelingSchedulerAndRequestStart", testDeadlineExceededFailsTaskEvenIfRaceBetweenCancelingSchedulerAndRequestStart),
32+
("testCancelHasNoEffectAfterDeadlineExceededFailsTask", testCancelHasNoEffectAfterDeadlineExceededFailsTask),
3233
("testCancelFailsTaskAfterRequestIsSent", testCancelFailsTaskAfterRequestIsSent),
3334
("testCancelFailsTaskWhenTaskIsQueued", testCancelFailsTaskWhenTaskIsQueued),
3435
("testFailsTaskWhenTaskIsWaitingForMoreFromServer", testFailsTaskWhenTaskIsWaitingForMoreFromServer),

Tests/AsyncHTTPClientTests/RequestBagTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,48 @@ final class RequestBagTests: XCTestCase {
266266
}
267267
}
268268

269+
func testCancelHasNoEffectAfterDeadlineExceededFailsTask() {
270+
struct MyError: Error, Equatable {}
271+
let embeddedEventLoop = EmbeddedEventLoop()
272+
defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) }
273+
let logger = Logger(label: "test")
274+
275+
var maybeRequest: HTTPClient.Request?
276+
XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org"))
277+
guard let request = maybeRequest else { return XCTFail("Expected to have a request") }
278+
279+
let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop)
280+
var maybeRequestBag: RequestBag<UploadCountingDelegate>?
281+
XCTAssertNoThrow(maybeRequestBag = try RequestBag(
282+
request: request,
283+
eventLoopPreference: .delegate(on: embeddedEventLoop),
284+
task: .init(eventLoop: embeddedEventLoop, logger: logger),
285+
redirectHandler: nil,
286+
connectionDeadline: .now() + .seconds(30),
287+
requestOptions: .forTests(),
288+
delegate: delegate
289+
))
290+
guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") }
291+
XCTAssert(bag.eventLoop === embeddedEventLoop)
292+
293+
let queuer = MockTaskQueuer()
294+
bag.requestWasQueued(queuer)
295+
296+
XCTAssertEqual(queuer.hitCancelCount, 0)
297+
bag.deadlineExceeded()
298+
XCTAssertEqual(queuer.hitCancelCount, 1)
299+
XCTAssertEqual(delegate.hitDidReceiveError, 0)
300+
bag.fail(MyError())
301+
XCTAssertEqual(delegate.hitDidReceiveError, 1)
302+
303+
bag.cancel()
304+
XCTAssertEqual(delegate.hitDidReceiveError, 1)
305+
306+
XCTAssertThrowsError(try bag.task.futureResult.wait()) {
307+
XCTAssertEqualTypeAndValue($0, MyError())
308+
}
309+
}
310+
269311
func testCancelFailsTaskAfterRequestIsSent() {
270312
let embeddedEventLoop = EmbeddedEventLoop()
271313
defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) }

0 commit comments

Comments
 (0)