Avoid precondition failure in write timeout#803
Merged
Conversation
Motivation: In some cases we can crash because of a precondition failure when the write timeout fires and we aren't in the running state. This can happen for example if the connection is closed whilst the write timer is active. Modifications: Remove the precondition and instead take no action if the timeout fires outside of the running state. Instead we take a new `Action`, `.noAction` when the timer fires. Result: Fewer crashes.
4d896b0 to
90e8b58
Compare
When a request completes we have no use for the idle write timer, we clear the read timer and we should clear the write one too.
rnro
commented
Jan 28, 2025
| context.close(promise: nil) | ||
|
|
||
| case .wait: | ||
| case .wait, .noAction: |
Collaborator
Author
There was a problem hiding this comment.
Do we need this new state or can we just re-use .wait?
Member
There was a problem hiding this comment.
reuse .wait. .wait is a do nothing. can be renamed in a follow up pr.
fabianfett
reviewed
Jan 28, 2025
| context.close(promise: nil) | ||
|
|
||
| case .wait: | ||
| case .wait, .noAction: |
Member
There was a problem hiding this comment.
reuse .wait. .wait is a do nothing. can be renamed in a follow up pr.
| mutating func idleWriteTimeoutTriggered() -> Action { | ||
| guard case .inRequest(var requestStateMachine, let close) = self.state else { | ||
| preconditionFailure("Invalid state: \(self.state)") | ||
| return .noAction |
Member
There was a problem hiding this comment.
Do you mind adding another test to HTTP1ConnectionStateMachineTests that just tests this edge?
| case fireChannelInactive | ||
| case fireChannelError(Error, closeConnection: Bool) | ||
|
|
||
| case noAction |
| XCTAssertEqual(request.events.map(\.kind), [.willExecuteRequest, .requestHeadSent]) | ||
| } | ||
|
|
||
| class SlowHandler: ChannelOutboundHandler { |
Collaborator
Author
There was a problem hiding this comment.
Ah, a relic from a previous test approach.
| request.body = .init(contentLength: nil, stream: streamCallback) | ||
|
|
||
| let delegate = NullResponseDelegate() | ||
| var maybeRequestBag: RequestBag<NullResponseDelegate>? |
Member
There was a problem hiding this comment.
Why not use a ResponseAccumulator here instead?
Collaborator
Author
There was a problem hiding this comment.
I hadn't spotted that, thanks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation:
In some cases we can crash because of a precondition failure when the write timeout fires and we aren't in the running state. This can happen for example if the connection is closed whilst the write timer is active.
Modifications:
Action,.noActionwhen the timer fires.Result:
Fewer crashes.
The supplied tests fails without these changes and passes with either of them.