Skip to content

Commit d7aa733

Browse files
committed
add http2 to http1 migration test
1 parent 75ede30 commit d7aa733

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extension HTTPConnectionPool_HTTP2StateMachineTests {
4040
("testMigrationFromHTTP1ToHTTP2WhileShuttingDown", testMigrationFromHTTP1ToHTTP2WhileShuttingDown),
4141
("testMigrationFromHTTP1ToHTTP2WithAlreadyStartedHTTP1Connections", testMigrationFromHTTP1ToHTTP2WithAlreadyStartedHTTP1Connections),
4242
("testHTTP2toHTTP1Migration", testHTTP2toHTTP1Migration),
43+
("testHTTP2toHTTP1MigrationDuringShutdown", testHTTP2toHTTP1MigrationDuringShutdown),
4344
("testConnectionIsImmediatelyCreatedAfterBackoffTimerFires", testConnectionIsImmediatelyCreatedAfterBackoffTimerFires),
4445
("testMaxConcurrentStreamsIsRespected", testMaxConcurrentStreamsIsRespected),
4546
("testEventsAfterConnectionIsClosed", testEventsAfterConnectionIsClosed),

Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift

+73-2
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase {
687687
guard case .createConnection(let conn1ID, let eventLoop) = action.connection else {
688688
return XCTFail("Unexpected connection action \(action.connection)")
689689
}
690-
690+
691691
XCTAssertTrue(eventLoop === el1)
692692
XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop))
693693
XCTAssertNoThrow(try connections.createConnection(conn1ID, on: el1))
@@ -698,7 +698,6 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase {
698698
guard case .failRequestsAndCancelTimeouts(let requestsToCancel, let error) = shutdownAction.request else {
699699
return XCTFail("unexpected shutdown action \(shutdownAction)")
700700
}
701-
702701
XCTAssertEqualTypeAndValue(error, HTTPClientError.cancelled)
703702

704703
for request in requestsToCancel {
@@ -916,6 +915,78 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase {
916915
XCTAssertNoThrow(try connections.closeConnection(http2Conn))
917916
}
918917

918+
func testHTTP2toHTTP1MigrationDuringShutdown() {
919+
let elg = EmbeddedEventLoopGroup(loops: 2)
920+
let el1 = elg.next()
921+
let el2 = elg.next()
922+
var connections = MockConnectionPool()
923+
var queuer = MockRequestQueuer()
924+
var state = HTTPConnectionPool.StateMachine(idGenerator: .init(), maximumConcurrentHTTP1Connections: 8)
925+
926+
// create http2 connection
927+
let mockRequest = MockHTTPRequest(eventLoop: el1)
928+
let request1 = HTTPConnectionPool.Request(mockRequest)
929+
let action1 = state.executeRequest(request1)
930+
guard case .createConnection(let http2ConnID, let http2EventLoop) = action1.connection else {
931+
return XCTFail("Unexpected connection action \(action1.connection)")
932+
}
933+
XCTAssertTrue(http2EventLoop === el1)
934+
XCTAssertEqual(action1.request, .scheduleRequestTimeout(for: request1, on: mockRequest.eventLoop))
935+
XCTAssertNoThrow(try connections.createConnection(http2ConnID, on: el1))
936+
XCTAssertNoThrow(try queuer.queue(mockRequest, id: request1.id))
937+
let http2Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http2ConnID, eventLoop: el1)
938+
XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(http2ConnID, maxConcurrentStreams: 10))
939+
let migrationAction1 = state.newHTTP2ConnectionCreated(http2Conn, maxConcurrentStreams: 10)
940+
guard case .executeRequestsAndCancelTimeouts(let requests, http2Conn) = migrationAction1.request else {
941+
return XCTFail("unexpected request action \(migrationAction1.request)")
942+
}
943+
XCTAssertEqual(migrationAction1.connection, .migration(createConnections: [], closeConnections: [], scheduleTimeout: nil))
944+
XCTAssertEqual(requests.count, 1)
945+
for request in requests {
946+
XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request()))
947+
XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn))
948+
}
949+
950+
// a request with new required event loop should create a new connection
951+
let mockRequestWithRequiredEventLoop = MockHTTPRequest(eventLoop: el2, requiresEventLoopForChannel: true)
952+
let requestWithRequiredEventLoop = HTTPConnectionPool.Request(mockRequestWithRequiredEventLoop)
953+
let action2 = state.executeRequest(requestWithRequiredEventLoop)
954+
guard case .createConnection(let http1ConnId, let http1EventLoop) = action2.connection else {
955+
return XCTFail("Unexpected connection action \(action2.connection)")
956+
}
957+
XCTAssertTrue(http1EventLoop === el2)
958+
XCTAssertEqual(action2.request, .scheduleRequestTimeout(for: requestWithRequiredEventLoop, on: mockRequestWithRequiredEventLoop.eventLoop))
959+
XCTAssertNoThrow(try connections.createConnection(http1ConnId, on: el2))
960+
XCTAssertNoThrow(try queuer.queue(mockRequestWithRequiredEventLoop, id: requestWithRequiredEventLoop.id))
961+
962+
/// we now no longer want anything of it
963+
let shutdownAction = state.shutdown()
964+
guard case .failRequestsAndCancelTimeouts(let requestsToCancel, let error) = shutdownAction.request else {
965+
return XCTFail("unexpected shutdown action \(shutdownAction)")
966+
}
967+
XCTAssertEqualTypeAndValue(error, HTTPClientError.cancelled)
968+
969+
for request in requestsToCancel {
970+
XCTAssertNoThrow(try queuer.cancel(request.id))
971+
}
972+
XCTAssertTrue(queuer.isEmpty)
973+
974+
// if we established a new http/1 connection we should migrate back to http/1,
975+
// close the connection and shutdown the pool
976+
let http1Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http1ConnId, eventLoop: el2)
977+
XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP1(http1ConnId))
978+
let migrationAction2 = state.newHTTP1ConnectionCreated(http1Conn)
979+
XCTAssertEqual(migrationAction2.request, .none)
980+
XCTAssertEqual(migrationAction2.connection, .migration(createConnections: [], closeConnections: [http1Conn], scheduleTimeout: nil))
981+
982+
// in http/1 state, we should close idle http2 connections
983+
XCTAssertNoThrow(try connections.finishExecution(http2Conn.id))
984+
let releaseAction = state.http2ConnectionStreamClosed(http2Conn.id)
985+
XCTAssertEqual(releaseAction.connection, .closeConnection(http2Conn, isShutdown: .yes(unclean: true)))
986+
XCTAssertEqual(releaseAction.request, .none)
987+
XCTAssertNoThrow(try connections.closeConnection(http2Conn))
988+
}
989+
919990
func testConnectionIsImmediatelyCreatedAfterBackoffTimerFires() {
920991
let elg = EmbeddedEventLoopGroup(loops: 2)
921992
let el1 = elg.next()

0 commit comments

Comments
 (0)