Skip to content

Commit 45225b9

Browse files
committed
add testHTTP1ToHTTP2MigrationAndShutdownIfFirstConnectionIsHTTP1
1 parent 23c3ee1 commit 45225b9

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ extension HTTPConnectionPool {
2424

2525
typealias Action = HTTPConnectionPool.StateMachine.Action
2626

27-
private var connections: HTTP1Connections
27+
private(set) var connections: HTTP1Connections
2828
private var failedConsecutiveConnectionAttempts: Int = 0
2929
/// the error from the last connection creation
3030
private var lastConnectFailure: Error?
3131

32-
private var requests: RequestQueue
32+
private(set) var requests: RequestQueue
3333
private var state: State = .running
3434

3535
init(idGenerator: Connection.ID.Generator, maximumConcurrentConnections: Int) {
@@ -41,7 +41,7 @@ extension HTTPConnectionPool {
4141
self.requests = RequestQueue()
4242
}
4343

44-
// MARK: - Events -
44+
// MARK: - Events
4545

4646
mutating func executeRequest(_ request: Request) -> Action {
4747
switch self.state {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ extension HTTPConnectionPool {
369369
)
370370
}
371371

372-
mutating func http1ConnectionClose(_ connectionID: Connection.ID) -> Action {
372+
mutating func http1ConnectionClosed(_ connectionID: Connection.ID) -> Action {
373373
guard let index = self.http1Connections?.failConnection(connectionID)?.0 else {
374374
return .none
375375
}
@@ -433,8 +433,8 @@ extension HTTPConnectionPool {
433433

434434
// If there aren't any more connections, everything is shutdown
435435
let isShutdown: StateMachine.ConnectionAction.IsShutdown
436-
let unclean = !(cleanupContext.cancel.isEmpty && waitingRequests.isEmpty)
437-
if self.connections.isEmpty {
436+
let unclean = !(cleanupContext.cancel.isEmpty && waitingRequests.isEmpty && self.http1Connections == nil)
437+
if self.connections.isEmpty && self.http1Connections == nil {
438438
isShutdown = .yes(unclean: unclean)
439439
self.state = .shutDown
440440
} else {

Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,69 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase {
261261
XCTAssertEqual(closeAction.connection, .cleanupConnections(.init(), isShutdown: .yes(unclean: true)))
262262
XCTAssertEqual(closeAction.request, .none)
263263
}
264+
265+
func testHTTP1ToHTTP2MigrationAndShutdownIfFirstConnectionIsHTTP1() {
266+
let elg = EmbeddedEventLoopGroup(loops: 4)
267+
let el1 = elg.next()
268+
269+
let idGenerator = HTTPConnectionPool.Connection.ID.Generator()
270+
var http1State = HTTPConnectionPool.HTTP1StateMachine(idGenerator: idGenerator, maximumConcurrentConnections: 8)
271+
272+
let mockRequest1 = MockHTTPRequest(eventLoop: el1)
273+
let request1 = HTTPConnectionPool.Request(mockRequest1)
274+
let mockRequest2 = MockHTTPRequest(eventLoop: el1)
275+
let request2 = HTTPConnectionPool.Request(mockRequest2)
276+
277+
let executeAction1 = http1State.executeRequest(request1)
278+
XCTAssertEqual(executeAction1.request, .scheduleRequestTimeout(for: request1, on: el1))
279+
guard case .createConnection(let conn1ID, _) = executeAction1.connection else {
280+
return XCTFail("unexpected connection action \(executeAction1.connection)")
281+
}
282+
let executeAction2 = http1State.executeRequest(request2)
283+
XCTAssertEqual(executeAction2.request, .scheduleRequestTimeout(for: request2, on: el1))
284+
guard case .createConnection(let conn2ID, _) = executeAction2.connection else {
285+
return XCTFail("unexpected connection action \(executeAction2.connection)")
286+
}
287+
288+
// first connection is a HTTP1 connection
289+
let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1)
290+
let conn1Action = http1State.newHTTP1ConnectionEstablished(conn1)
291+
XCTAssertEqual(conn1Action.connection, .none)
292+
XCTAssertEqual(conn1Action.request, .executeRequest(request1, conn1, cancelTimeout: true))
293+
294+
295+
// second connection is a HTTP2 connection and we need to migrate
296+
let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el1)
297+
var http2State = HTTPConnectionPool.HTTP2StateMaschine(idGenerator: idGenerator)
298+
299+
let migrationAction = http2State.migrateConnectionsFromHTTP1(
300+
connections: http1State.connections,
301+
requests: http1State.requests
302+
)
303+
XCTAssertEqual(migrationAction, .none)
304+
305+
let http2ConnectAction = http2State.newHTTP2ConnectionEstablished(conn2, maxConcurrentStreams: 100)
306+
XCTAssertEqual(http2ConnectAction.connection, .none)
307+
guard case .executeRequestsAndCancelTimeouts([request2], conn2) = http2ConnectAction.request else {
308+
return XCTFail("Unexpected request action \(http2ConnectAction.request)")
309+
}
310+
311+
// second request is done first
312+
let closeAction = http2State.http2ConnectionStreamClosed(conn2ID)
313+
XCTAssertEqual(closeAction.request, .none)
314+
XCTAssertEqual(closeAction.connection, .scheduleTimeoutTimer(conn2ID, on: el1))
315+
316+
317+
let shutdownAction = http2State.shutdown()
318+
XCTAssertEqual(shutdownAction.request, .none)
319+
XCTAssertEqual(shutdownAction.connection, .cleanupConnections(.init(
320+
close: [conn2],
321+
cancel: [],
322+
connectBackoff: []
323+
), isShutdown: .no))
324+
325+
let releaseAction = http2State.http1ConnectionReleased(conn1ID)
326+
XCTAssertEqual(releaseAction.request, .none)
327+
XCTAssertEqual(releaseAction.connection, .closeConnection(conn1, isShutdown: .yes(unclean: true)))
328+
}
264329
}

0 commit comments

Comments
 (0)