Skip to content

Commit 5bee16a

Browse files
authored
Switch over state in HTTPConnectionPool.HTTP2StateMachine.failedToCreateNewConnection (#647)
1 parent fd03ed0 commit 5bee16a

File tree

3 files changed

+61
-13
lines changed

3 files changed

+61
-13
lines changed

‎Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift‎

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -404,25 +404,34 @@ extension HTTPConnectionPool{
404404
}
405405

406406
mutatingfunc failedToCreateNewConnection(_ error:Error, connectionID:Connection.ID)->Action{
407-
// TODO: switch over state https://github.com/swift-server/async-http-client/issues/638
408407
self.failedConsecutiveConnectionAttempts +=1
409408
self.lastConnectFailure = error
410409

411-
guardself.retryConnectionEstablishment else{
412-
guardlet(index, _)=self.connections.failConnection(connectionID)else{
413-
preconditionFailure("A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost.")
410+
switchself.lifecycleState {
411+
case.running:
412+
guardself.retryConnectionEstablishment else{
413+
guardlet(index, _)=self.connections.failConnection(connectionID)else{
414+
preconditionFailure("A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost.")
415+
}
416+
self.connections.removeConnection(at: index)
417+
418+
return.init(
419+
request:self.failAllRequests(reason: error),
420+
connection:.none
421+
)
414422
}
415-
self.connections.removeConnection(at: index)
416423

417-
return.init(
418-
request:self.failAllRequests(reason: error),
419-
connection:.none
420-
)
424+
leteventLoop=self.connections.backoffNextConnectionAttempt(connectionID)
425+
letbackoff=calculateBackoff(failedAttempt:self.failedConsecutiveConnectionAttempts)
426+
return.init(request:.none, connection:.scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop))
427+
case.shuttingDown:
428+
guardlet(index, context)=self.connections.failConnection(connectionID)else{
429+
preconditionFailure("A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost.")
430+
}
431+
returnself.nextActionForFailedConnection(at: index, on: context.eventLoop)
432+
case.shutDown:
433+
preconditionFailure("If the pool is already shutdown, all connections must have been torn down.")
421434
}
422-
423-
leteventLoop=self.connections.backoffNextConnectionAttempt(connectionID)
424-
letbackoff=calculateBackoff(failedAttempt:self.failedConsecutiveConnectionAttempts)
425-
return.init(request:.none, connection:.scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop))
426435
}
427436

428437
mutatingfunc waitingForConnectivity(_ error:Error, connectionID:Connection.ID)->Action{

‎Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests+XCTest.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extension HTTPConnectionPool_HTTP2StateMachineTests{
2727
return[
2828
("testCreatingOfConnection", testCreatingOfConnection),
2929
("testConnectionFailureBackoff", testConnectionFailureBackoff),
30+
("testConnectionFailureWhileShuttingDown", testConnectionFailureWhileShuttingDown),
3031
("testConnectionFailureWithoutRetry", testConnectionFailureWithoutRetry),
3132
("testCancelRequestWorks", testCancelRequestWorks),
3233
("testExecuteOnShuttingDownPool", testExecuteOnShuttingDownPool),

‎Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,44 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase{
194194
XCTAssertEqual(state.connectionCreationBackoffDone(newConnectionID),.none)
195195
}
196196

197+
func testConnectionFailureWhileShuttingDown(){
198+
structSomeError:Error,Equatable{}
199+
letelg=EmbeddedEventLoopGroup(loops:4)
200+
defer{XCTAssertNoThrow(try elg.syncShutdownGracefully())}
201+
202+
varstate=HTTPConnectionPool.HTTP2StateMachine(
203+
idGenerator:.init(),
204+
retryConnectionEstablishment:false,
205+
lifecycleState:.running
206+
)
207+
208+
letmockRequest=MockHTTPRequest(eventLoop: elg.next())
209+
letrequest=HTTPConnectionPool.Request(mockRequest)
210+
211+
letaction= state.executeRequest(request)
212+
XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request)
213+
214+
// 1. connection attempt
215+
guard case .createConnection(let connectionID, on:let connectionEL)= action.connection else{
216+
returnXCTFail("Unexpected connection action: \(action.connection)")
217+
}
218+
XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux
219+
220+
// 2. initialise shutdown
221+
letshutdownAction= state.shutdown()
222+
XCTAssertEqual(shutdownAction.connection,.cleanupConnections(.init(), isShutdown:.no))
223+
guard case .failRequestsAndCancelTimeouts(let requestsToFail,let requestError)= shutdownAction.request else{
224+
returnXCTFail("Unexpected request action: \(action.request)")
225+
}
226+
XCTAssertEqualTypeAndValue(requestError,HTTPClientError.cancelled)
227+
XCTAssertEqualTypeAndValue(requestsToFail,[request])
228+
229+
// 3. connection attempt fails
230+
letfailedConnectAction= state.failedToCreateNewConnection(SomeError(), connectionID: connectionID)
231+
XCTAssertEqual(failedConnectAction.request,.none)
232+
XCTAssertEqual(failedConnectAction.connection,.cleanupConnections(.init(), isShutdown:.yes(unclean:true)))
233+
}
234+
197235
func testConnectionFailureWithoutRetry(){
198236
structSomeError:Error,Equatable{}
199237
letelg=EmbeddedEventLoopGroup(loops:4)

0 commit comments

Comments
(0)