Skip to content

Commit cc8e7a6

Browse files
authoredNov 10, 2021
basic http2 integration tests (#467)
1 parent d49602f commit cc8e7a6

File tree

4 files changed

+231
-1
lines changed

4 files changed

+231
-1
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
//
15+
// HTTP2ClientTests+XCTest.swift
16+
//
17+
import XCTest
18+
19+
///
20+
/// NOTE: This file was generated by generate_linux_tests.rb
21+
///
22+
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
23+
///
24+
25+
extension HTTP2ClientTests {
26+
static var allTests: [(String, (HTTP2ClientTests) -> () throws -> Void)] {
27+
return [
28+
("testSimpleGet", testSimpleGet),
29+
("testConcurrentRequests", testConcurrentRequests),
30+
("testConcurrentRequestsFromDifferentThreads", testConcurrentRequestsFromDifferentThreads),
31+
("testConcurrentRequestsWorkWithRequiredEventLoop", testConcurrentRequestsWorkWithRequiredEventLoop),
32+
]
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
// TODO: remove @testable once we officially support HTTP/2
16+
@testable import AsyncHTTPClient // Tests that really need @testable go into HTTP2ClientInternalTests.swift
17+
#if canImport(Network)
18+
import Network
19+
#endif
20+
import Logging
21+
import NIOCore
22+
import NIOPosix
23+
import NIOSSL
24+
import XCTest
25+
26+
class HTTP2ClientTests: XCTestCase {
27+
func makeDefaultHTTPClient() -> HTTPClient {
28+
var tlsConfig = TLSConfiguration.makeClientConfiguration()
29+
tlsConfig.certificateVerification = .none
30+
return HTTPClient(
31+
eventLoopGroupProvider: .createNew,
32+
configuration: HTTPClient.Configuration(
33+
tlsConfiguration: tlsConfig,
34+
httpVersion: .automatic
35+
),
36+
backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:))
37+
)
38+
}
39+
40+
func testSimpleGet() {
41+
let bin = HTTPBin(.http2(compress: false))
42+
defer { XCTAssertNoThrow(try bin.shutdown()) }
43+
let client = self.makeDefaultHTTPClient()
44+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
45+
var response: HTTPClient.Response?
46+
XCTAssertNoThrow(response = try client.get(url: "https://localhost:\(bin.port)/get").wait())
47+
48+
XCTAssertEqual(.ok, response?.status)
49+
XCTAssertEqual(response?.version, .http2)
50+
}
51+
52+
func testConcurrentRequests() {
53+
let bin = HTTPBin(.http2(compress: false))
54+
defer { XCTAssertNoThrow(try bin.shutdown()) }
55+
let client = self.makeDefaultHTTPClient()
56+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
57+
let el = client.eventLoopGroup.next()
58+
let requestPromises = (0..<1000).map { _ in
59+
client.get(url: "https://localhost:\(bin.port)/get")
60+
.map { result -> Void in
61+
XCTAssertEqual(result.version, .http2)
62+
}
63+
}
64+
XCTAssertNoThrow(try EventLoopFuture.whenAllComplete(requestPromises, on: el).wait())
65+
}
66+
67+
func testConcurrentRequestsFromDifferentThreads() {
68+
let bin = HTTPBin(.http2(compress: false))
69+
defer { XCTAssertNoThrow(try bin.shutdown()) }
70+
let client = self.makeDefaultHTTPClient()
71+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
72+
let numberOfWorkers = 20
73+
let numberOfRequestsPerWorkers = 20
74+
let allWorkersReady = DispatchSemaphore(value: 0)
75+
let allWorkersGo = DispatchSemaphore(value: 0)
76+
let allDone = DispatchGroup()
77+
78+
let url = "https://localhost:\(bin.port)/get"
79+
80+
var response: HTTPClient.Response?
81+
XCTAssertNoThrow(response = try client.get(url: url).wait())
82+
83+
XCTAssertEqual(.ok, response?.status)
84+
XCTAssertEqual(response?.version, .http2)
85+
86+
for w in 0..<numberOfWorkers {
87+
let q = DispatchQueue(label: "worker \(w)")
88+
q.async(group: allDone) {
89+
func go() {
90+
allWorkersReady.signal() // tell the driver we're ready
91+
allWorkersGo.wait() // wait for the driver to let us go
92+
93+
for _ in 0..<numberOfRequestsPerWorkers {
94+
var response: HTTPClient.Response?
95+
XCTAssertNoThrow(response = try client.get(url: url).wait())
96+
97+
XCTAssertEqual(.ok, response?.status)
98+
XCTAssertEqual(response?.version, .http2)
99+
}
100+
}
101+
go()
102+
}
103+
}
104+
105+
for _ in 0..<numberOfWorkers {
106+
allWorkersReady.wait()
107+
}
108+
// now all workers should be waiting for the go signal
109+
110+
for _ in 0..<numberOfWorkers {
111+
allWorkersGo.signal()
112+
}
113+
// all workers should be running, let's wait for them to finish
114+
allDone.wait()
115+
}
116+
117+
func testConcurrentRequestsWorkWithRequiredEventLoop() {
118+
let numberOfWorkers = 20
119+
let numberOfRequestsPerWorkers = 20
120+
let allWorkersReady = DispatchSemaphore(value: 0)
121+
let allWorkersGo = DispatchSemaphore(value: 0)
122+
let allDone = DispatchGroup()
123+
124+
let localHTTPBin = HTTPBin(.http2(compress: false))
125+
let elg = MultiThreadedEventLoopGroup(numberOfThreads: numberOfWorkers)
126+
var tlsConfig = TLSConfiguration.makeClientConfiguration()
127+
tlsConfig.certificateVerification = .none
128+
let localClient = HTTPClient(
129+
eventLoopGroupProvider: .shared(elg),
130+
configuration: HTTPClient.Configuration(
131+
tlsConfiguration: tlsConfig,
132+
httpVersion: .automatic
133+
),
134+
backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:))
135+
)
136+
defer {
137+
XCTAssertNoThrow(try localClient.syncShutdown())
138+
XCTAssertNoThrow(try localHTTPBin.shutdown())
139+
}
140+
141+
let url = "https://localhost:\(localHTTPBin.port)/get"
142+
143+
var response: HTTPClient.Response?
144+
XCTAssertNoThrow(response = try localClient.get(url: url).wait())
145+
146+
XCTAssertEqual(.ok, response?.status)
147+
XCTAssertEqual(response?.version, .http2)
148+
149+
for w in 0..<numberOfWorkers {
150+
let q = DispatchQueue(label: "worker \(w)")
151+
let el = elg.next()
152+
q.async(group: allDone) {
153+
func go() {
154+
allWorkersReady.signal() // tell the driver we're ready
155+
allWorkersGo.wait() // wait for the driver to let us go
156+
157+
for _ in 0..<numberOfRequestsPerWorkers {
158+
var response: HTTPClient.Response?
159+
let request = try! HTTPClient.Request(url: url)
160+
let requestPromise = localClient
161+
.execute(
162+
request: request,
163+
eventLoop: .delegateAndChannel(on: el)
164+
)
165+
.map { response -> HTTPClient.Response in
166+
XCTAssertTrue(el.inEventLoop)
167+
return response
168+
}
169+
XCTAssertNoThrow(response = try requestPromise.wait())
170+
171+
XCTAssertEqual(.ok, response?.status)
172+
XCTAssertEqual(response?.version, .http2)
173+
}
174+
}
175+
go()
176+
}
177+
}
178+
179+
for _ in 0..<numberOfWorkers {
180+
allWorkersReady.wait()
181+
}
182+
// now all workers should be waiting for the go signal
183+
184+
for _ in 0..<numberOfWorkers {
185+
allWorkersGo.signal()
186+
}
187+
// all workers should be running, let's wait for them to finish
188+
allDone.wait()
189+
}
190+
}

‎Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Foundation
1717
import Logging
1818
import NIOConcurrencyHelpers
1919
import NIOCore
20+
import NIOHPACK
2021
import NIOHTTP1
2122
import NIOHTTP2
2223
import NIOHTTPCompression
@@ -484,7 +485,11 @@ internal final class HTTPBin<RequestHandler: ChannelInboundHandler> where
484485
// Successful upgrade to HTTP/2. Let the user configure the pipeline.
485486
let http2Handler = NIOHTTP2Handler(
486487
mode: .server,
487-
initialSettings: NIOHTTP2.nioDefaultSettings
488+
initialSettings: [
489+
// TODO: make max concurrent streams configurable
490+
HTTP2Setting(parameter: .maxConcurrentStreams, value: 10),
491+
HTTP2Setting(parameter: .maxHeaderListSize, value: HPACKDecoder.defaultMaxHeaderListSize),
492+
]
488493
)
489494
let multiplexer = HTTP2StreamMultiplexer(
490495
mode: .server,

‎Tests/LinuxMain.swift

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import XCTest
3131
testCase(HTTP1ConnectionTests.allTests),
3232
testCase(HTTP1ProxyConnectHandlerTests.allTests),
3333
testCase(HTTP2ClientRequestHandlerTests.allTests),
34+
testCase(HTTP2ClientTests.allTests),
3435
testCase(HTTP2ConnectionTests.allTests),
3536
testCase(HTTP2IdleHandlerTests.allTests),
3637
testCase(HTTPClientCookieTests.allTests),

0 commit comments

Comments
 (0)
Please sign in to comment.