From fede418d58ad0e117bb6be89c67a1761bb05573f Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 15 Jul 2024 16:55:15 +0100 Subject: [PATCH 1/2] Add alloc tests for alternative stream creation API --- .../test_01_resources/shared.swift | 1 + ...ponse_no_promise_on_channel_creation.swift | 158 ++++++++++++++++++ Sources/NIOHTTP2/HTTP2StreamMultiplexer.swift | 17 ++ docker/docker-compose.2204.510.yaml | 4 + docker/docker-compose.2204.58.yaml | 4 + docker/docker-compose.2204.59.yaml | 4 + docker/docker-compose.2204.main.yaml | 32 ++-- 7 files changed, 206 insertions(+), 14 deletions(-) create mode 100644 IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift diff --git a/IntegrationTests/tests_01_allocation_counters/test_01_resources/shared.swift b/IntegrationTests/tests_01_allocation_counters/test_01_resources/shared.swift index 40887f37..d4838700 100644 --- a/IntegrationTests/tests_01_allocation_counters/test_01_resources/shared.swift +++ b/IntegrationTests/tests_01_allocation_counters/test_01_resources/shared.swift @@ -19,6 +19,7 @@ import NIOHTTP2 /// Test use only. Allows abstracting over the two multiplexer implementations to write common testing code internal protocol MultiplexerChannelCreator { func createStreamChannel(promise: EventLoopPromise?, _ streamStateInitializer: @escaping NIOHTTP2Handler.StreamInitializer) + func createStreamChannel(_ initializer: @escaping NIOChannelInitializer) -> EventLoopFuture } extension HTTP2StreamMultiplexer: MultiplexerChannelCreator { } diff --git a/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift b/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift new file mode 100644 index 00000000..6ea4724e --- /dev/null +++ b/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2019-2023 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import NIOCore +import NIOEmbedded +import NIOHPACK +import NIOHTTP1 +import NIOHTTP2 + +final class ServerHandler: ChannelInboundHandler { + typealias InboundIn = HTTP2Frame.FramePayload + typealias OutboundOut = HTTP2Frame.FramePayload + + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + let payload = self.unwrapInboundIn(data) + switch payload { + case .headers(let headers) where headers.endStream: + break + case .data(let data) where data.endStream: + break + default: + // Ignore this frame + return + } + + // We got END_STREAM. Let's send a response. + let headers = HPACKHeaders([(":status", "200")]) + let responseFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: true)) + context.writeAndFlush(self.wrapOutboundOut(responseFramePayload), promise: nil) + } +} + + +final class ClientHandler: ChannelInboundHandler { + typealias InboundIn = HTTP2Frame.FramePayload + typealias OutboundOut = HTTP2Frame.FramePayload + + let dataBlockCount: Int + let dataBlockLengthBytes: Int + + init(dataBlockCount: Int, dataBlockLengthBytes: Int) { + self.dataBlockCount = dataBlockCount + self.dataBlockLengthBytes = dataBlockLengthBytes + } + + func channelActive(context: ChannelHandlerContext) { + // Send a request. + let headers = HPACKHeaders([(":path", "/"), + (":authority", "localhost"), + (":method", "GET"), + (":scheme", "https")]) + + if self.dataBlockCount > 0 { + let requestFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: false)) + context.write(self.wrapOutboundOut(requestFramePayload), promise: nil) + + let buffer = ByteBuffer(repeating: 0, count: self.dataBlockLengthBytes) + + for _ in 0 ..< self.dataBlockCount-1 { + context.write(self.wrapOutboundOut(HTTP2Frame.FramePayload.data(.init(data: .byteBuffer(buffer), endStream: false))), promise: nil) + } + context.writeAndFlush(self.wrapOutboundOut(HTTP2Frame.FramePayload.data(.init(data: .byteBuffer(buffer), endStream: true))), promise: nil) + } else { + let requestFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: true)) + context.writeAndFlush(self.wrapOutboundOut(requestFramePayload), promise: nil) + } + } +} + +func run(identifier: String) { + testRun(identifier: identifier) { clientChannel in + return try! clientChannel.configureHTTP2Pipeline(mode: .client) { channel in + return channel.eventLoop.makeSucceededVoidFuture() + }.wait() + } serverPipelineConfigurator: { serverChannel in + _ = try! serverChannel.configureHTTP2Pipeline(mode: .server) { channel in + return channel.pipeline.addHandler(ServerHandler()) + }.wait() + } + + testRun(identifier: identifier + "_many", dataBlockCount: 100, dataBlockLengthBytes: 1000) { clientChannel in + return try! clientChannel.configureHTTP2Pipeline(mode: .client) { channel in + return channel.eventLoop.makeSucceededVoidFuture() + }.wait() + } serverPipelineConfigurator: { serverChannel in + _ = try! serverChannel.configureHTTP2Pipeline(mode: .server) { channel in + return channel.pipeline.addHandler(ServerHandler()) + }.wait() + } + + // + // MARK: - Inline HTTP2 multiplexer tests + testRun(identifier: identifier + "_inline") { clientChannel in + return try! clientChannel.configureHTTP2Pipeline(mode: .client, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in + return channel.eventLoop.makeSucceededVoidFuture() + }.wait() + } serverPipelineConfigurator: { serverChannel in + _ = try! serverChannel.configureHTTP2Pipeline(mode: .server, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in + return channel.pipeline.addHandler(ServerHandler()) + }.wait() + } + + testRun(identifier: identifier + "_many_inline", dataBlockCount: 100, dataBlockLengthBytes: 1000) { clientChannel in + return try! clientChannel.configureHTTP2Pipeline(mode: .client, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in + return channel.eventLoop.makeSucceededVoidFuture() + }.wait() + } serverPipelineConfigurator: { serverChannel in + _ = try! serverChannel.configureHTTP2Pipeline(mode: .server, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in + return channel.pipeline.addHandler(ServerHandler()) + }.wait() + } +} + +private func testRun(identifier: String, dataBlockCount: Int = 0, dataBlockLengthBytes: Int = 0, clientPipelineConfigurator: (Channel) throws -> MultiplexerChannelCreator, serverPipelineConfigurator: (Channel) throws -> ()) { + let loop = EmbeddedEventLoop() + + measure(identifier: identifier) { + var sumOfStreamIDs = 0 + + for _ in 0..<1000 { + let clientChannel = EmbeddedChannel(loop: loop) + let serverChannel = EmbeddedChannel(loop: loop) + + let clientMultiplexer = try! clientPipelineConfigurator(clientChannel) + try! serverPipelineConfigurator(serverChannel) + try! clientChannel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5678)).wait() + try! serverChannel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5678)).wait() + + let channelFuture = clientMultiplexer.createStreamChannel { channel in + return channel.pipeline.addHandler(ClientHandler(dataBlockCount: dataBlockCount, dataBlockLengthBytes: dataBlockLengthBytes)) + } + clientChannel.embeddedEventLoop.run() + let child = try! channelFuture.wait() + let streamID = try! Int(child.getOption(HTTP2StreamChannelOptions.streamID).wait()) + + sumOfStreamIDs += streamID + try! interactInMemory(clientChannel, serverChannel) + try! child.closeFuture.wait() + + try! clientChannel.close().wait() + try! serverChannel.close().wait() + } + + return sumOfStreamIDs + } +} + diff --git a/Sources/NIOHTTP2/HTTP2StreamMultiplexer.swift b/Sources/NIOHTTP2/HTTP2StreamMultiplexer.swift index ef24442a..2827ffa6 100644 --- a/Sources/NIOHTTP2/HTTP2StreamMultiplexer.swift +++ b/Sources/NIOHTTP2/HTTP2StreamMultiplexer.swift @@ -260,6 +260,23 @@ extension HTTP2StreamMultiplexer { } } + /// Create a new `Channel` for a new stream initiated by this peer. + /// + /// This method is intended for situations where the NIO application is initiating the stream. For clients, + /// this is for all request streams. For servers, this is for pushed streams. + /// + /// > Note: Resources for the stream will be freed after it has been closed. + /// + /// - Parameters: + /// - streamStateInitializer: A callback that will be invoked to allow you to configure the + /// `ChannelPipeline` for the newly created channel. + /// - Returns: A future for the initialized `Channel`. + public func createStreamChannel(_ initializer: @escaping NIOChannelInitializer) -> EventLoopFuture { + let promise = self.channel.eventLoop.makePromise(of: Channel.self) + self.createStreamChannel(promise: promise, initializer) + return promise.futureResult + } + /// Create a new `Channel` for a new stream initiated by this peer. /// /// This method is intended for situations where the NIO application is initiating the stream. For clients, diff --git a/docker/docker-compose.2204.510.yaml b/docker/docker-compose.2204.510.yaml index a4d3ec2b..c70e6109 100644 --- a/docker/docker-compose.2204.510.yaml +++ b/docker/docker-compose.2204.510.yaml @@ -38,6 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 diff --git a/docker/docker-compose.2204.58.yaml b/docker/docker-compose.2204.58.yaml index 0dedb051..d6781f80 100644 --- a/docker/docker-compose.2204.58.yaml +++ b/docker/docker-compose.2204.58.yaml @@ -38,6 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 diff --git a/docker/docker-compose.2204.59.yaml b/docker/docker-compose.2204.59.yaml index 5562f641..a6086879 100644 --- a/docker/docker-compose.2204.59.yaml +++ b/docker/docker-compose.2204.59.yaml @@ -38,6 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 diff --git a/docker/docker-compose.2204.main.yaml b/docker/docker-compose.2204.main.yaml index 174c9752..00df2b62 100644 --- a/docker/docker-compose.2204.main.yaml +++ b/docker/docker-compose.2204.main.yaml @@ -27,25 +27,29 @@ services: test: image: swift-nio-http2:22.04-main environment: - - MAX_ALLOCS_ALLOWED_1k_requests_inline_interleaved=35150 - - MAX_ALLOCS_ALLOWED_1k_requests_inline_noninterleaved=34100 - - MAX_ALLOCS_ALLOWED_1k_requests_interleaved=41150 - - MAX_ALLOCS_ALLOWED_1k_requests_noninterleaved=40100 - - MAX_ALLOCS_ALLOWED_client_server_h1_request_response=288050 - - MAX_ALLOCS_ALLOWED_client_server_h1_request_response_inline=273050 - - MAX_ALLOCS_ALLOWED_client_server_request_response=257050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=248050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1202050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=893050 - - MAX_ALLOCS_ALLOWED_create_client_stream_channel=39050 - - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=39050 + - MAX_ALLOCS_ALLOWED_1k_requests_inline_interleaved=33150 + - MAX_ALLOCS_ALLOWED_1k_requests_inline_noninterleaved=32100 + - MAX_ALLOCS_ALLOWED_1k_requests_interleaved=39150 + - MAX_ALLOCS_ALLOWED_1k_requests_noninterleaved=38100 + - MAX_ALLOCS_ALLOWED_client_server_h1_request_response=284050 + - MAX_ALLOCS_ALLOWED_client_server_h1_request_response_inline=269050 + - MAX_ALLOCS_ALLOWED_client_server_request_response=253050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 + - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_long_string=300050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_short_string=200050 - MAX_ALLOCS_ALLOWED_hpack_decoding=5050 - - MAX_ALLOCS_ALLOWED_stream_teardown_100_concurrent=292650 - - MAX_ALLOCS_ALLOWED_stream_teardown_100_concurrent_inline=291750 + - MAX_ALLOCS_ALLOWED_stream_teardown_100_concurrent=282650 + - MAX_ALLOCS_ALLOWED_stream_teardown_100_concurrent_inline=281750 - IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error shell: From 0071ce7bec253ecd19a1c9bced25554621458448 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Tue, 16 Jul 2024 11:07:57 +0100 Subject: [PATCH 2/2] Turn tests into variants instead of duplicating them --- ...ponse_no_promise_on_channel_creation.swift | 158 ------------------ .../test_create_client_stream_channel.swift | 45 ++++- docker/docker-compose.2204.510.yaml | 6 +- docker/docker-compose.2204.58.yaml | 6 +- docker/docker-compose.2204.59.yaml | 6 +- docker/docker-compose.2204.main.yaml | 6 +- 6 files changed, 45 insertions(+), 182 deletions(-) delete mode 100644 IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift diff --git a/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift b/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift deleted file mode 100644 index 6ea4724e..00000000 --- a/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_client_server_request_response_no_promise_on_channel_creation.swift +++ /dev/null @@ -1,158 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftNIO open source project -// -// Copyright (c) 2019-2023 Apple Inc. and the SwiftNIO project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of SwiftNIO project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHPACK -import NIOHTTP1 -import NIOHTTP2 - -final class ServerHandler: ChannelInboundHandler { - typealias InboundIn = HTTP2Frame.FramePayload - typealias OutboundOut = HTTP2Frame.FramePayload - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let payload = self.unwrapInboundIn(data) - switch payload { - case .headers(let headers) where headers.endStream: - break - case .data(let data) where data.endStream: - break - default: - // Ignore this frame - return - } - - // We got END_STREAM. Let's send a response. - let headers = HPACKHeaders([(":status", "200")]) - let responseFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: true)) - context.writeAndFlush(self.wrapOutboundOut(responseFramePayload), promise: nil) - } -} - - -final class ClientHandler: ChannelInboundHandler { - typealias InboundIn = HTTP2Frame.FramePayload - typealias OutboundOut = HTTP2Frame.FramePayload - - let dataBlockCount: Int - let dataBlockLengthBytes: Int - - init(dataBlockCount: Int, dataBlockLengthBytes: Int) { - self.dataBlockCount = dataBlockCount - self.dataBlockLengthBytes = dataBlockLengthBytes - } - - func channelActive(context: ChannelHandlerContext) { - // Send a request. - let headers = HPACKHeaders([(":path", "/"), - (":authority", "localhost"), - (":method", "GET"), - (":scheme", "https")]) - - if self.dataBlockCount > 0 { - let requestFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: false)) - context.write(self.wrapOutboundOut(requestFramePayload), promise: nil) - - let buffer = ByteBuffer(repeating: 0, count: self.dataBlockLengthBytes) - - for _ in 0 ..< self.dataBlockCount-1 { - context.write(self.wrapOutboundOut(HTTP2Frame.FramePayload.data(.init(data: .byteBuffer(buffer), endStream: false))), promise: nil) - } - context.writeAndFlush(self.wrapOutboundOut(HTTP2Frame.FramePayload.data(.init(data: .byteBuffer(buffer), endStream: true))), promise: nil) - } else { - let requestFramePayload = HTTP2Frame.FramePayload.headers(.init(headers: headers, endStream: true)) - context.writeAndFlush(self.wrapOutboundOut(requestFramePayload), promise: nil) - } - } -} - -func run(identifier: String) { - testRun(identifier: identifier) { clientChannel in - return try! clientChannel.configureHTTP2Pipeline(mode: .client) { channel in - return channel.eventLoop.makeSucceededVoidFuture() - }.wait() - } serverPipelineConfigurator: { serverChannel in - _ = try! serverChannel.configureHTTP2Pipeline(mode: .server) { channel in - return channel.pipeline.addHandler(ServerHandler()) - }.wait() - } - - testRun(identifier: identifier + "_many", dataBlockCount: 100, dataBlockLengthBytes: 1000) { clientChannel in - return try! clientChannel.configureHTTP2Pipeline(mode: .client) { channel in - return channel.eventLoop.makeSucceededVoidFuture() - }.wait() - } serverPipelineConfigurator: { serverChannel in - _ = try! serverChannel.configureHTTP2Pipeline(mode: .server) { channel in - return channel.pipeline.addHandler(ServerHandler()) - }.wait() - } - - // - // MARK: - Inline HTTP2 multiplexer tests - testRun(identifier: identifier + "_inline") { clientChannel in - return try! clientChannel.configureHTTP2Pipeline(mode: .client, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in - return channel.eventLoop.makeSucceededVoidFuture() - }.wait() - } serverPipelineConfigurator: { serverChannel in - _ = try! serverChannel.configureHTTP2Pipeline(mode: .server, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in - return channel.pipeline.addHandler(ServerHandler()) - }.wait() - } - - testRun(identifier: identifier + "_many_inline", dataBlockCount: 100, dataBlockLengthBytes: 1000) { clientChannel in - return try! clientChannel.configureHTTP2Pipeline(mode: .client, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in - return channel.eventLoop.makeSucceededVoidFuture() - }.wait() - } serverPipelineConfigurator: { serverChannel in - _ = try! serverChannel.configureHTTP2Pipeline(mode: .server, connectionConfiguration: .init(), streamConfiguration: .init()) { channel in - return channel.pipeline.addHandler(ServerHandler()) - }.wait() - } -} - -private func testRun(identifier: String, dataBlockCount: Int = 0, dataBlockLengthBytes: Int = 0, clientPipelineConfigurator: (Channel) throws -> MultiplexerChannelCreator, serverPipelineConfigurator: (Channel) throws -> ()) { - let loop = EmbeddedEventLoop() - - measure(identifier: identifier) { - var sumOfStreamIDs = 0 - - for _ in 0..<1000 { - let clientChannel = EmbeddedChannel(loop: loop) - let serverChannel = EmbeddedChannel(loop: loop) - - let clientMultiplexer = try! clientPipelineConfigurator(clientChannel) - try! serverPipelineConfigurator(serverChannel) - try! clientChannel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5678)).wait() - try! serverChannel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5678)).wait() - - let channelFuture = clientMultiplexer.createStreamChannel { channel in - return channel.pipeline.addHandler(ClientHandler(dataBlockCount: dataBlockCount, dataBlockLengthBytes: dataBlockLengthBytes)) - } - clientChannel.embeddedEventLoop.run() - let child = try! channelFuture.wait() - let streamID = try! Int(child.getOption(HTTP2StreamChannelOptions.streamID).wait()) - - sumOfStreamIDs += streamID - try! interactInMemory(clientChannel, serverChannel) - try! child.closeFuture.wait() - - try! clientChannel.close().wait() - try! serverChannel.close().wait() - } - - return sumOfStreamIDs - } -} - diff --git a/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_create_client_stream_channel.swift b/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_create_client_stream_channel.swift index 6a7c38c6..8a304ebb 100644 --- a/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_create_client_stream_channel.swift +++ b/IntegrationTests/tests_01_allocation_counters/test_01_resources/test_create_client_stream_channel.swift @@ -20,7 +20,17 @@ import NIOHTTP2 func run(identifier: String) { - testRun(identifier: identifier) { channel in + testRun(identifier: identifier, usePromiseBasedAPI: true) { channel in + let http2Handler = NIOHTTP2Handler(mode: .client) + let multiplexer = HTTP2StreamMultiplexer(mode: .client, channel: channel, inboundStreamInitializer: nil) + try! channel.pipeline.addHandlers([ + http2Handler, + multiplexer + ]).wait() + return multiplexer + } + + testRun(identifier: identifier + "_no_promise_based_API", usePromiseBasedAPI: false) { channel in let http2Handler = NIOHTTP2Handler(mode: .client) let multiplexer = HTTP2StreamMultiplexer(mode: .client, channel: channel, inboundStreamInitializer: nil) try! channel.pipeline.addHandlers([ @@ -30,9 +40,16 @@ func run(identifier: String) { return multiplexer } - // // MARK: - Inline HTTP2 multiplexer tests - testRun(identifier: identifier + "_inline") { channel in + testRun(identifier: identifier + "_inline", usePromiseBasedAPI: true) { channel in + let http2Handler = NIOHTTP2Handler(mode: .client, eventLoop: channel.eventLoop) { channel in + return channel.eventLoop.makeSucceededVoidFuture() + } + try! channel.pipeline.addHandler(http2Handler).wait() + return try! http2Handler.multiplexer.wait() + } + + testRun(identifier: identifier + "_inline_no_promise_based_API", usePromiseBasedAPI: false) { channel in let http2Handler = NIOHTTP2Handler(mode: .client, eventLoop: channel.eventLoop) { channel in return channel.eventLoop.makeSucceededVoidFuture() } @@ -41,7 +58,11 @@ func run(identifier: String) { } } -private func testRun(identifier: String, pipelineConfigurator: (Channel) throws -> MultiplexerChannelCreator) { +private func testRun( + identifier: String, + usePromiseBasedAPI: Bool, + pipelineConfigurator: (Channel) throws -> MultiplexerChannelCreator +) { let channel = EmbeddedChannel(handler: nil) let multiplexer = try! pipelineConfigurator(channel) try! channel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5678)).wait() @@ -50,12 +71,20 @@ private func testRun(identifier: String, pipelineConfigurator: (Channel) throws var streams = 0 for _ in 0..<1000 { - let promise = channel.eventLoop.makePromise(of: Channel.self) - multiplexer.createStreamChannel(promise: promise) { channel in - return channel.eventLoop.makeSucceededFuture(()) + let channelFuture: EventLoopFuture + if usePromiseBasedAPI { + let promise = channel.eventLoop.makePromise(of: Channel.self) + multiplexer.createStreamChannel(promise: promise) { channel in + return channel.eventLoop.makeSucceededFuture(()) + } + channelFuture = promise.futureResult + } else { + channelFuture = multiplexer.createStreamChannel { channel in + return channel.eventLoop.makeSucceededFuture(()) + } } channel.embeddedEventLoop.run() - let child = try! promise.futureResult.wait() + let child = try! channelFuture.wait() streams += 1 let closeFuture = child.close() diff --git a/docker/docker-compose.2204.510.yaml b/docker/docker-compose.2204.510.yaml index c70e6109..c47517a7 100644 --- a/docker/docker-compose.2204.510.yaml +++ b/docker/docker-compose.2204.510.yaml @@ -38,12 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline_no_promise_based_API=44050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_no_promise_based_API=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_long_string=300050 diff --git a/docker/docker-compose.2204.58.yaml b/docker/docker-compose.2204.58.yaml index d6781f80..dbcf55a4 100644 --- a/docker/docker-compose.2204.58.yaml +++ b/docker/docker-compose.2204.58.yaml @@ -38,12 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline_no_promise_based_API=44050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_no_promise_based_API=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_long_string=300050 diff --git a/docker/docker-compose.2204.59.yaml b/docker/docker-compose.2204.59.yaml index a6086879..7ec7acae 100644 --- a/docker/docker-compose.2204.59.yaml +++ b/docker/docker-compose.2204.59.yaml @@ -38,12 +38,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline_no_promise_based_API=44050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_no_promise_based_API=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_long_string=300050 diff --git a/docker/docker-compose.2204.main.yaml b/docker/docker-compose.2204.main.yaml index 00df2b62..448325d0 100644 --- a/docker/docker-compose.2204.main.yaml +++ b/docker/docker-compose.2204.main.yaml @@ -37,12 +37,10 @@ services: - MAX_ALLOCS_ALLOWED_client_server_request_response_inline=244050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many=1198050 - MAX_ALLOCS_ALLOWED_client_server_request_response_many_inline=889050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation=253050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_inline=251050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many=1198050 - - MAX_ALLOCS_ALLOWED_client_server_request_response_no_promise_on_channel_creation_many_inline=896050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel=37050 - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline=37050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_inline_no_promise_based_API=44050 + - MAX_ALLOCS_ALLOWED_create_client_stream_channel_no_promise_based_API=37050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace=200050 - MAX_ALLOCS_ALLOWED_get_100000_headers_canonical_form_trimming_whitespace_from_long_string=300050