Skip to content

Commit 4b7a68e

Browse files
authored
Fix OOM issue when setting concurrentHTTP1ConnectionsPerHostSoftLimit to Int.max (#763)
### Motivation: When a user wishes to make the connection pool create as many concurrent connections as possible, a natural way to achieve this would be to set `.max` to the `concurrentHTTP1ConnectionsPerHostSoftLimit` property. ```swift HTTPClient.Configuration().connectionPool = .init( idleTimeout: .hours(1), concurrentHTTP1ConnectionsPerHostSoftLimit: .max ) ``` The `concurrentHTTP1ConnectionsPerHostSoftLimit` property is of type `Int`. Setting it to `Int.max` leads to `Int.max` being passed as an argument to `Array`s `.reserveCapacity(_:)` method, causing an OOM issue. Addresses Github Issue #751 ### Modifications: Capped the argument to `self.connections.reserveCapacity(_:)` to 1024 in `HTTPConnectionPool.HTTP1Connections` ### Result: Users can now set the `concurrentHTTP1ConnectionsPerHostSoftLimit` property to `.max` without causing an OOM issue.
1 parent 0bd9111 commit 4b7a68e

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ extension HTTPConnectionPool {
261261

262262
init(maximumConcurrentConnections: Int, generator: Connection.ID.Generator, maximumConnectionUses: Int?) {
263263
self.connections = []
264-
self.connections.reserveCapacity(maximumConcurrentConnections)
264+
self.connections.reserveCapacity(min(maximumConcurrentConnections, 1024))
265265
self.overflowIndex = self.connections.endIndex
266266
self.maximumConcurrentConnections = maximumConcurrentConnections
267267
self.generator = generator

Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift

+18
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,24 @@ final class AsyncAwaitEndToEndTests: XCTestCase {
642642
}
643643
}
644644

645+
func testInsanelyHighConcurrentHTTP1ConnectionLimitDoesNotCrash() async throws {
646+
let bin = HTTPBin(.http1_1(compress: false))
647+
defer { XCTAssertNoThrow(try bin.shutdown()) }
648+
649+
var httpClientConfig = HTTPClient.Configuration()
650+
httpClientConfig.connectionPool = .init(
651+
idleTimeout: .hours(1),
652+
concurrentHTTP1ConnectionsPerHostSoftLimit: Int.max
653+
)
654+
httpClientConfig.timeout = .init(connect: .seconds(10), read: .seconds(100), write: .seconds(100))
655+
656+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: httpClientConfig)
657+
defer { XCTAssertNoThrow(try httpClient.syncShutdown()) }
658+
659+
let request = HTTPClientRequest(url: "http://localhost:\(bin.port)")
660+
_ = try await httpClient.execute(request, deadline: .now() + .seconds(2))
661+
}
662+
645663
func testRedirectChangesHostHeader() {
646664
XCTAsyncTest {
647665
let bin = HTTPBin(.http2(compress: false))

0 commit comments

Comments
 (0)