diff --git a/Tests/AsyncHTTPClientTests/HTTP2ClientTests+XCTest.swift b/Tests/AsyncHTTPClientTests/HTTP2ClientTests+XCTest.swift
new file mode 100644
index 000000000..ffe9c14a1
--- /dev/null
+++ b/Tests/AsyncHTTPClientTests/HTTP2ClientTests+XCTest.swift
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the AsyncHTTPClient open source project
+//
+// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+//
+// HTTP2ClientTests+XCTest.swift
+//
+import XCTest
+
+///
+/// NOTE: This file was generated by generate_linux_tests.rb
+///
+/// Do NOT edit this file directly as it will be regenerated automatically when needed.
+///
+
+extension HTTP2ClientTests {
+    static var allTests: [(String, (HTTP2ClientTests) -> () throws -> Void)] {
+        return [
+            ("testSimpleGet", testSimpleGet),
+            ("testConcurrentRequests", testConcurrentRequests),
+            ("testConcurrentRequestsFromDifferentThreads", testConcurrentRequestsFromDifferentThreads),
+            ("testConcurrentRequestsWorkWithRequiredEventLoop", testConcurrentRequestsWorkWithRequiredEventLoop),
+        ]
+    }
+}
diff --git a/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift b/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift
new file mode 100644
index 000000000..bef964577
--- /dev/null
+++ b/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift
@@ -0,0 +1,190 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the AsyncHTTPClient open source project
+//
+// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+// TODO: remove @testable once we officially support HTTP/2
+@testable import AsyncHTTPClient // Tests that really need @testable go into HTTP2ClientInternalTests.swift
+#if canImport(Network)
+    import Network
+#endif
+import Logging
+import NIOCore
+import NIOPosix
+import NIOSSL
+import XCTest
+
+class HTTP2ClientTests: XCTestCase {
+    func makeDefaultHTTPClient() -> HTTPClient {
+        var tlsConfig = TLSConfiguration.makeClientConfiguration()
+        tlsConfig.certificateVerification = .none
+        return HTTPClient(
+            eventLoopGroupProvider: .createNew,
+            configuration: HTTPClient.Configuration(
+                tlsConfiguration: tlsConfig,
+                httpVersion: .automatic
+            ),
+            backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:))
+        )
+    }
+
+    func testSimpleGet() {
+        let bin = HTTPBin(.http2(compress: false))
+        defer { XCTAssertNoThrow(try bin.shutdown()) }
+        let client = self.makeDefaultHTTPClient()
+        defer { XCTAssertNoThrow(try client.syncShutdown()) }
+        var response: HTTPClient.Response?
+        XCTAssertNoThrow(response = try client.get(url: "https://localhost:\(bin.port)/get").wait())
+
+        XCTAssertEqual(.ok, response?.status)
+        XCTAssertEqual(response?.version, .http2)
+    }
+
+    func testConcurrentRequests() {
+        let bin = HTTPBin(.http2(compress: false))
+        defer { XCTAssertNoThrow(try bin.shutdown()) }
+        let client = self.makeDefaultHTTPClient()
+        defer { XCTAssertNoThrow(try client.syncShutdown()) }
+        let el = client.eventLoopGroup.next()
+        let requestPromises = (0..<1000).map { _ in
+            client.get(url: "https://localhost:\(bin.port)/get")
+                .map { result -> Void in
+                    XCTAssertEqual(result.version, .http2)
+                }
+        }
+        XCTAssertNoThrow(try EventLoopFuture.whenAllComplete(requestPromises, on: el).wait())
+    }
+
+    func testConcurrentRequestsFromDifferentThreads() {
+        let bin = HTTPBin(.http2(compress: false))
+        defer { XCTAssertNoThrow(try bin.shutdown()) }
+        let client = self.makeDefaultHTTPClient()
+        defer { XCTAssertNoThrow(try client.syncShutdown()) }
+        let numberOfWorkers = 20
+        let numberOfRequestsPerWorkers = 20
+        let allWorkersReady = DispatchSemaphore(value: 0)
+        let allWorkersGo = DispatchSemaphore(value: 0)
+        let allDone = DispatchGroup()
+
+        let url = "https://localhost:\(bin.port)/get"
+
+        var response: HTTPClient.Response?
+        XCTAssertNoThrow(response = try client.get(url: url).wait())
+
+        XCTAssertEqual(.ok, response?.status)
+        XCTAssertEqual(response?.version, .http2)
+
+        for w in 0..<numberOfWorkers {
+            let q = DispatchQueue(label: "worker \(w)")
+            q.async(group: allDone) {
+                func go() {
+                    allWorkersReady.signal() // tell the driver we're ready
+                    allWorkersGo.wait() // wait for the driver to let us go
+
+                    for _ in 0..<numberOfRequestsPerWorkers {
+                        var response: HTTPClient.Response?
+                        XCTAssertNoThrow(response = try client.get(url: url).wait())
+
+                        XCTAssertEqual(.ok, response?.status)
+                        XCTAssertEqual(response?.version, .http2)
+                    }
+                }
+                go()
+            }
+        }
+
+        for _ in 0..<numberOfWorkers {
+            allWorkersReady.wait()
+        }
+        // now all workers should be waiting for the go signal
+
+        for _ in 0..<numberOfWorkers {
+            allWorkersGo.signal()
+        }
+        // all workers should be running, let's wait for them to finish
+        allDone.wait()
+    }
+
+    func testConcurrentRequestsWorkWithRequiredEventLoop() {
+        let numberOfWorkers = 20
+        let numberOfRequestsPerWorkers = 20
+        let allWorkersReady = DispatchSemaphore(value: 0)
+        let allWorkersGo = DispatchSemaphore(value: 0)
+        let allDone = DispatchGroup()
+
+        let localHTTPBin = HTTPBin(.http2(compress: false))
+        let elg = MultiThreadedEventLoopGroup(numberOfThreads: numberOfWorkers)
+        var tlsConfig = TLSConfiguration.makeClientConfiguration()
+        tlsConfig.certificateVerification = .none
+        let localClient = HTTPClient(
+            eventLoopGroupProvider: .shared(elg),
+            configuration: HTTPClient.Configuration(
+                tlsConfiguration: tlsConfig,
+                httpVersion: .automatic
+            ),
+            backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:))
+        )
+        defer {
+            XCTAssertNoThrow(try localClient.syncShutdown())
+            XCTAssertNoThrow(try localHTTPBin.shutdown())
+        }
+
+        let url = "https://localhost:\(localHTTPBin.port)/get"
+
+        var response: HTTPClient.Response?
+        XCTAssertNoThrow(response = try localClient.get(url: url).wait())
+
+        XCTAssertEqual(.ok, response?.status)
+        XCTAssertEqual(response?.version, .http2)
+
+        for w in 0..<numberOfWorkers {
+            let q = DispatchQueue(label: "worker \(w)")
+            let el = elg.next()
+            q.async(group: allDone) {
+                func go() {
+                    allWorkersReady.signal() // tell the driver we're ready
+                    allWorkersGo.wait() // wait for the driver to let us go
+
+                    for _ in 0..<numberOfRequestsPerWorkers {
+                        var response: HTTPClient.Response?
+                        let request = try! HTTPClient.Request(url: url)
+                        let requestPromise = localClient
+                            .execute(
+                                request: request,
+                                eventLoop: .delegateAndChannel(on: el)
+                            )
+                            .map { response -> HTTPClient.Response in
+                                XCTAssertTrue(el.inEventLoop)
+                                return response
+                            }
+                        XCTAssertNoThrow(response = try requestPromise.wait())
+
+                        XCTAssertEqual(.ok, response?.status)
+                        XCTAssertEqual(response?.version, .http2)
+                    }
+                }
+                go()
+            }
+        }
+
+        for _ in 0..<numberOfWorkers {
+            allWorkersReady.wait()
+        }
+        // now all workers should be waiting for the go signal
+
+        for _ in 0..<numberOfWorkers {
+            allWorkersGo.signal()
+        }
+        // all workers should be running, let's wait for them to finish
+        allDone.wait()
+    }
+}
diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift b/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift
index d54c640bc..15baf0cc1 100644
--- a/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift
+++ b/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift
@@ -17,6 +17,7 @@ import Foundation
 import Logging
 import NIOConcurrencyHelpers
 import NIOCore
+import NIOHPACK
 import NIOHTTP1
 import NIOHTTP2
 import NIOHTTPCompression
@@ -484,7 +485,11 @@ internal final class HTTPBin<RequestHandler: ChannelInboundHandler> where
                     // Successful upgrade to HTTP/2. Let the user configure the pipeline.
                     let http2Handler = NIOHTTP2Handler(
                         mode: .server,
-                        initialSettings: NIOHTTP2.nioDefaultSettings
+                        initialSettings: [
+                            // TODO: make max concurrent streams configurable
+                            HTTP2Setting(parameter: .maxConcurrentStreams, value: 10),
+                            HTTP2Setting(parameter: .maxHeaderListSize, value: HPACKDecoder.defaultMaxHeaderListSize),
+                        ]
                     )
                     let multiplexer = HTTP2StreamMultiplexer(
                         mode: .server,
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
index 1adb04801..ddc4dad39 100644
--- a/Tests/LinuxMain.swift
+++ b/Tests/LinuxMain.swift
@@ -31,6 +31,7 @@ import XCTest
         testCase(HTTP1ConnectionTests.allTests),
         testCase(HTTP1ProxyConnectHandlerTests.allTests),
         testCase(HTTP2ClientRequestHandlerTests.allTests),
+        testCase(HTTP2ClientTests.allTests),
         testCase(HTTP2ConnectionTests.allTests),
         testCase(HTTP2IdleHandlerTests.allTests),
         testCase(HTTPClientCookieTests.allTests),