From b933becb47a977bbd8eb546d0fd7efb9d7711f7d Mon Sep 17 00:00:00 2001 From: sbiscigl Date: Wed, 20 Aug 2025 09:36:18 -0400 Subject: [PATCH] bind out network_interface_names_array from connection manager options --- include/aws/crt/http/HttpConnectionManager.h | 15 +++++ source/http/HttpConnectionManager.cpp | 6 ++ tests/CMakeLists.txt | 1 + tests/HttpClientConnectionManagerTest.cpp | 65 ++++++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/include/aws/crt/http/HttpConnectionManager.h b/include/aws/crt/http/HttpConnectionManager.h index ceb0c5eb7..b30024380 100644 --- a/include/aws/crt/http/HttpConnectionManager.h +++ b/include/aws/crt/http/HttpConnectionManager.h @@ -57,6 +57,21 @@ namespace Aws * reference to the connection manager. */ bool EnableBlockingShutdown; + + /** + * THIS IS AN EXPERIMENTAL AND UNSTABLE API + * (Optional) + * An array of network interface names. The manager will distribute the connections across network + * interface names provided in this array. If any interface name is invalid, goes down, or has any + * issues like network access, you will see connection failures. If + * `socket_options.network_interface_name` is also set, an `AWS_ERROR_INVALID_ARGUMENT` error will be + * raised. + * + * This option is only supported on Linux, MacOS, and platforms that have either SO_BINDTODEVICE or + * IP_BOUND_IF. It is not supported on Windows. `AWS_ERROR_PLATFORM_NOT_SUPPORTED` will be raised on + * unsupported platforms. + */ + Vector NetworkInterfaces; }; /** diff --git a/source/http/HttpConnectionManager.cpp b/source/http/HttpConnectionManager.cpp index 4ec879ec8..bfe046a40 100644 --- a/source/http/HttpConnectionManager.cpp +++ b/source/http/HttpConnectionManager.cpp @@ -136,6 +136,12 @@ namespace Aws } managerOptions.host = aws_byte_cursor_from_c_str(connectionOptions.HostName.c_str()); + if (m_options.NetworkInterfaces.size() > 0) + { + managerOptions.network_interface_names_array = m_options.NetworkInterfaces.data(); + managerOptions.num_network_interface_names = m_options.NetworkInterfaces.size(); + } + m_connectionManager = aws_http_connection_manager_new(allocator, &managerOptions); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index df86e33de..493be14c7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -91,6 +91,7 @@ if(NOT BYO_CRYPTO) add_net_test_case(HttpClientConnectionManagerInvalidTlsConnectionOptions) add_net_test_case(HttpClientConnectionWithPendingAcquisitions) add_net_test_case(HttpClientConnectionWithPendingAcquisitionsAndClosedConnections) + add_net_test_case(HttpClientCreateManagerWithNetworkInterfacesList) add_net_test_case(IotConnectionDestruction) add_net_test_case(IotConnectionDestructionWithExecutingCallback) add_net_test_case(IotConnectionDestructionWithinConnectionCallback) diff --git a/tests/HttpClientConnectionManagerTest.cpp b/tests/HttpClientConnectionManagerTest.cpp index ccbd2d8d6..cc9efac62 100644 --- a/tests/HttpClientConnectionManagerTest.cpp +++ b/tests/HttpClientConnectionManagerTest.cpp @@ -447,4 +447,69 @@ AWS_TEST_CASE( HttpClientConnectionWithPendingAcquisitionsAndClosedConnections, s_TestHttpClientConnectionWithPendingAcquisitionsAndClosedConnections) +static int s_TestHttpClientCreateManagerWithNetworkInterfacesList(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + { + Aws::Crt::ApiHandle apiHandle(allocator); + + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + + // Ensure that if PQ TLS ciphers are supported on the current platform, that setting them works when connecting + // to S3. This TlsCipherPreference has post quantum ciphers at the top of it's preference list (that will be + // ignored if S3 doesn't support them) followed by regular TLS ciphers that can be chosen and negotiated by S3. + aws_tls_cipher_pref tls_cipher_pref = AWS_IO_TLS_CIPHER_PREF_PQ_DEFAULT; + + if (aws_tls_is_cipher_pref_supported(tls_cipher_pref)) + { + tlsCtxOptions.SetTlsCipherPreference(tls_cipher_pref); + } + + Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator); + ASSERT_TRUE(tlsContext); + + Aws::Crt::Io::TlsConnectionOptions tlsConnectionOptions = tlsContext.NewConnectionOptions(); + + ByteCursor cursor = ByteCursorFromCString("https://s3.amazonaws.com"); + Io::Uri uri(cursor, allocator); + + auto hostName = uri.GetHostName(); + tlsConnectionOptions.SetServerName(hostName); + + Aws::Crt::Io::SocketOptions socketOptions; + socketOptions.SetConnectTimeoutMs(10000); + + Aws::Crt::Io::EventLoopGroup eventLoopGroup(1, allocator); + ASSERT_TRUE(eventLoopGroup); + + Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator); + ASSERT_TRUE(defaultHostResolver); + + Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); + ASSERT_TRUE(clientBootstrap); + clientBootstrap.EnableBlockingShutdown(); + Http::HttpClientConnectionOptions connectionOptions; + connectionOptions.Bootstrap = &clientBootstrap; + connectionOptions.SocketOptions = socketOptions; + connectionOptions.TlsOptions = tlsConnectionOptions; + connectionOptions.HostName = String((const char *)hostName.ptr, hostName.len); + connectionOptions.Port = 443; + + Http::HttpClientConnectionManagerOptions connectionManagerOptions; + connectionManagerOptions.ConnectionOptions = connectionOptions; + connectionManagerOptions.MaxConnections = 30; + connectionManagerOptions.EnableBlockingShutdown = true; + + Vector networkInterfaces{ByteCursorFromCString("eth0"), ByteCursorFromCString("eth1")}; + connectionManagerOptions.NetworkInterfaces = networkInterfaces; + + auto connectionManager = + Http::HttpClientConnectionManager::NewClientConnectionManager(connectionManagerOptions, allocator); + ASSERT_TRUE(connectionManager); + } + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(HttpClientCreateManagerWithNetworkInterfacesList, s_TestHttpClientCreateManagerWithNetworkInterfacesList) + #endif // !BYO_CRYPTO