Skip to content

Commit b479afe

Browse files
authored
Fix bug in migration from HTTP1 to HTTP2 and back to HTTP1 (#486)
1 parent 2fe3f42 commit b479afe

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,21 @@ extension HTTPConnectionPool {
532532

533533
mutating func migrateToHTTP2() -> HTTP1ToHTTP2MigrationContext {
534534
var migrationContext = HTTP1ToHTTP2MigrationContext()
535-
self.connections.removeAll { connection in
536-
switch connection.migrateToHTTP2(&migrationContext) {
535+
let initialOverflowIndex = self.overflowIndex
536+
537+
self.connections = self.connections.enumerated().compactMap { index, connectionState in
538+
switch connectionState.migrateToHTTP2(&migrationContext) {
537539
case .removeConnection:
538-
return true
540+
// If the connection has an index smaller than the previous overflow index,
541+
// we deal with a general purpose connection.
542+
// For this reason we need to decrement the overflow index.
543+
if index < initialOverflowIndex {
544+
self.overflowIndex = self.connections.index(before: self.overflowIndex)
545+
}
546+
return nil
547+
539548
case .keepConnection:
540-
return false
549+
return connectionState
541550
}
542551
}
543552
return migrationContext
@@ -654,6 +663,9 @@ extension HTTPConnectionPool {
654663
self.connections = self.connections.enumerated().compactMap { index, connectionState in
655664
switch connectionState.cleanup(&cleanupContext) {
656665
case .removeConnection:
666+
// If the connection has an index smaller than the previous overflow index,
667+
// we deal with a general purpose connection.
668+
// For this reason we need to decrement the overflow index.
657669
if index < initialOverflowIndex {
658670
self.overflowIndex = self.connections.index(before: self.overflowIndex)
659671
}

Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1ConnectionsTest+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extension HTTPConnectionPool_HTTP1ConnectionsTests {
4141
("testMigrationFromHTTP2WithAlreadyLeasedHTTP1Connection", testMigrationFromHTTP2WithAlreadyLeasedHTTP1Connection),
4242
("testMigrationFromHTTP2WithMoreStartingConnectionsThanMaximumAllowedConccurentConnections", testMigrationFromHTTP2WithMoreStartingConnectionsThanMaximumAllowedConccurentConnections),
4343
("testMigrationFromHTTP2StartsEnoghOverflowConnectionsForRequiredEventLoopRequests", testMigrationFromHTTP2StartsEnoghOverflowConnectionsForRequiredEventLoopRequests),
44+
("testMigrationFromHTTP1ToHTTP2AndBackToHTTP1", testMigrationFromHTTP1ToHTTP2AndBackToHTTP1),
4445
]
4546
}
4647
}

Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1ConnectionsTest.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,4 +563,43 @@ class HTTPConnectionPool_HTTP1ConnectionsTests: XCTestCase {
563563
XCTAssertTrue(context.eventLoop === el)
564564
}
565565
}
566+
567+
func testMigrationFromHTTP1ToHTTP2AndBackToHTTP1() throws {
568+
let elg = EmbeddedEventLoopGroup(loops: 2)
569+
defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) }
570+
let el1 = elg.next()
571+
let el2 = elg.next()
572+
573+
let generator = HTTPConnectionPool.Connection.ID.Generator()
574+
var connections = HTTPConnectionPool.HTTP1Connections(maximumConcurrentConnections: 8, generator: generator)
575+
576+
let connID1 = connections.createNewConnection(on: el1)
577+
578+
let context = connections.migrateToHTTP2()
579+
XCTAssertEqual(context, .init(
580+
backingOff: [],
581+
starting: [(connID1, el1)],
582+
close: []
583+
))
584+
585+
let connID2 = generator.next()
586+
587+
connections.migrateFromHTTP2(
588+
starting: [(connID2, el2)],
589+
backingOff: []
590+
)
591+
592+
let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID2, eventLoop: el2)
593+
let (_, idleContext) = connections.newHTTP1ConnectionEstablished(conn2)
594+
XCTAssertEqual(idleContext.use, .generalPurpose)
595+
XCTAssertEqual(idleContext.eventLoop.id, el2.id)
596+
}
597+
}
598+
599+
extension HTTPConnectionPool.HTTP1Connections.HTTP1ToHTTP2MigrationContext: Equatable {
600+
public static func == (lhs: Self, rhs: Self) -> Bool {
601+
return lhs.close == rhs.close &&
602+
lhs.starting.elementsEqual(rhs.starting, by: { $0.0 == $1.0 && $0.1 === $1.1 }) &&
603+
lhs.backingOff.elementsEqual(rhs.backingOff, by: { $0.0 == $1.0 && $0.1 === $1.1 })
604+
}
566605
}

0 commit comments

Comments
 (0)