Skip to content

Commit de4d1ad

Browse files
authored
add a parallel test case (#140)
Motivation: It's important to also have a test case where async-http-client is actually used to do multiple parallel requests on multiple workers. Modification: Add a test where 5 workers are doing 100 requests each. Result: Better test coverage.
1 parent 7aeaf6f commit de4d1ad

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extension HTTPClientTests {
6161
("testDecompressionLimit", testDecompressionLimit),
6262
("testLoopDetectionRedirectLimit", testLoopDetectionRedirectLimit),
6363
("testCountRedirectLimit", testCountRedirectLimit),
64+
("testMultipleConcurrentRequests", testMultipleConcurrentRequests),
6465
("testWorksWith500Error", testWorksWith500Error),
6566
("testWorksWithHTTP10Response", testWorksWithHTTP10Response),
6667
("testWorksWhenServerClosesConnectionAfterReceivingRequest", testWorksWhenServerClosesConnectionAfterReceivingRequest),

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+60
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,66 @@ class HTTPClientTests: XCTestCase {
673673
}
674674
}
675675

676+
func testMultipleConcurrentRequests() throws {
677+
let numberOfRequestsPerThread = 100
678+
let numberOfParallelWorkers = 5
679+
680+
final class HTTPServer: ChannelInboundHandler {
681+
typealias InboundIn = HTTPServerRequestPart
682+
typealias OutboundOut = HTTPServerResponsePart
683+
684+
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
685+
if case .end = self.unwrapInboundIn(data) {
686+
let responseHead = HTTPServerResponsePart.head(.init(version: .init(major: 1, minor: 1),
687+
status: .ok))
688+
context.write(self.wrapOutboundOut(responseHead), promise: nil)
689+
context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
690+
}
691+
}
692+
}
693+
694+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 2)
695+
defer {
696+
XCTAssertNoThrow(try group.syncShutdownGracefully())
697+
}
698+
699+
var server: Channel?
700+
XCTAssertNoThrow(server = try ServerBootstrap(group: group)
701+
.serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1)
702+
.serverChannelOption(ChannelOptions.backlog, value: .init(numberOfParallelWorkers))
703+
.childChannelInitializer { channel in
704+
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: false,
705+
withServerUpgrade: nil,
706+
withErrorHandling: false).flatMap {
707+
channel.pipeline.addHandler(HTTPServer())
708+
}
709+
}
710+
.bind(to: .init(ipAddress: "127.0.0.1", port: 0))
711+
.wait())
712+
defer {
713+
XCTAssertNoThrow(try server?.close().wait())
714+
}
715+
716+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(group))
717+
defer {
718+
XCTAssertNoThrow(try httpClient.syncShutdown())
719+
}
720+
721+
let g = DispatchGroup()
722+
for workerID in 0..<numberOfParallelWorkers {
723+
DispatchQueue(label: "\(#file):\(#line):worker-\(workerID)").async(group: g) {
724+
func makeRequest() {
725+
let url = "http://127.0.0.1:\(server?.localAddress?.port ?? -1)/hello"
726+
XCTAssertNoThrow(try httpClient.get(url: url).wait())
727+
}
728+
for _ in 0..<numberOfRequestsPerThread {
729+
makeRequest()
730+
}
731+
}
732+
}
733+
g.wait()
734+
}
735+
676736
func testWorksWith500Error() {
677737
let web = NIOHTTP1TestServer(group: self.group)
678738
defer {

0 commit comments

Comments
 (0)