mirror of
https://github.com/swift-server/async-http-client.git
synced 2026-06-02 07:37:34 +00:00
Crash fix: HTTP2 can handle requests are cancelled (#555)
Co-authored-by: George Barnett <gbarnett@apple.com>
This commit is contained in:
co-authored by
George Barnett
parent
5ead318ad2
commit
5844a6b4ee
@@ -108,16 +108,24 @@ struct HTTPRequestStateMachine {
|
||||
}
|
||||
|
||||
mutating func startRequest(head: HTTPRequestHead, metadata: RequestFramingMetadata) -> Action {
|
||||
guard case .initialized = self.state else {
|
||||
preconditionFailure("`start()` must be called first, and exactly once. Invalid state: \(self.state)")
|
||||
}
|
||||
switch self.state {
|
||||
case .initialized:
|
||||
guard self.isChannelWritable else {
|
||||
self.state = .waitForChannelToBecomeWritable(head, metadata)
|
||||
return .wait
|
||||
}
|
||||
return self.startSendingRequest(head: head, metadata: metadata)
|
||||
|
||||
guard self.isChannelWritable else {
|
||||
self.state = .waitForChannelToBecomeWritable(head, metadata)
|
||||
case .failed:
|
||||
// The request state machine is marked as failed before the request is started, if
|
||||
// the request was cancelled before hitting the channel handler. Before `startRequest`
|
||||
// is called on the state machine, `willExecuteRequest` is called on
|
||||
// `HTTPExecutableRequest`, which might loopback to state machines cancel method.
|
||||
return .wait
|
||||
}
|
||||
|
||||
return self.startSendingRequest(head: head, metadata: metadata)
|
||||
case .running, .finished, .waitForChannelToBecomeWritable, .modifying:
|
||||
preconditionFailure("`startRequest()` must be called first, and exactly once. Invalid state: \(self.state)")
|
||||
}
|
||||
}
|
||||
|
||||
mutating func writabilityChanged(writable: Bool) -> Action {
|
||||
@@ -381,6 +389,10 @@ struct HTTPRequestStateMachine {
|
||||
case .initialized, .waitForChannelToBecomeWritable:
|
||||
let error = HTTPClientError.cancelled
|
||||
self.state = .failed(error)
|
||||
// Okay, this has different semantics for HTTP/1 and HTTP/2. In HTTP/1 we don't want to
|
||||
// close the connection, if we haven't sent anything yet, to reuse the connection for
|
||||
// another request. In HTTP/2 we must close the channel to ensure it is released from
|
||||
// HTTP/2 multiplexer.
|
||||
return .failRequest(error, .none)
|
||||
|
||||
case .running:
|
||||
|
||||
Reference in New Issue
Block a user