Skip to content
Open
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
09dff69
Update options to support CONNECT tunneling
asingh-g May 27, 2025
4a60b04
Update bootstrap to resolve encapsulation URIs
asingh-g May 27, 2025
914540f
Create bootstrapper for Encapsulation Envoy
asingh-g May 27, 2025
83fbb68
uncomment function to spawn threads
asingh-g May 27, 2025
662a01d
Bootstrap and split encap envoy into its own process
asingh-g May 29, 2025
cedc22b
Add missing configs to BUILD
asingh-g May 29, 2025
1abb196
Add missing dependency for loading bootstrap
asingh-g Jun 2, 2025
8eb8e7e
Fix incorrect initialisation of TLS context
asingh-g Jun 2, 2025
efc871a
Fix transport socket bug for H1/H2
asingh-g Jun 2, 2025
b8cb51b
Add tunnel concurrency options
asingh-g Jun 6, 2025
a8b16b4
Fix bug in tunnel concurrency options
asingh-g Jun 11, 2025
fb9aafe
FIx warnings
asingh-g Jun 20, 2025
8a1e8be
Fix tunnel protocol H3 check
asingh-g Jun 23, 2025
a53fc56
Add unit tests for tunneling
asingh-g Jun 24, 2025
7b39942
Fix missing header declaration
asingh-g Jun 24, 2025
6d2cc5b
Fix missing option in mock
asingh-g Jun 24, 2025
55033d2
Create initial templates for tunneling integration tests
asingh-g Jun 25, 2025
3bcdaf2
Update test to add cluster libs
asingh-g Jun 25, 2025
a6501c6
Fix missing return value
asingh-g Jun 25, 2025
ca2a4a4
Update Port finding function to be cleaner
asingh-g Jun 25, 2025
5eeb351
Improve logic for killing envoy subprocess
asingh-g Jun 25, 2025
6d06a2c
Remove stdouts
asingh-g Jun 25, 2025
9ff1a39
Fix GetAvailablePort for IPV6
asingh-g Jun 27, 2025
b0c7105
Complete H1/2 CONNECT tunneling integration tests
asingh-g Jun 27, 2025
3f35e43
Update tunneling integration tests to run serially and prevent freezing
asingh-g Jun 27, 2025
ffb7326
Simplify tunnel protocol flags
asingh-g Jun 27, 2025
eb8d821
Implement H3 CONNECT integration test
asingh-g Jun 28, 2025
a83e45f
Complete H3 over H2 CONNECT-UDP integration test
asingh-g Jun 28, 2025
d7f9fcc
Fix formatting for PR
asingh-g Jun 28, 2025
07afce6
Update README
asingh-g Jun 28, 2025
07f4ecb
update Envoy to commit b253ff8 (#1352)
fei-deng May 29, 2025
aeb7649
update envoy to 8f16edb (#1355)
fei-deng May 29, 2025
4a4accc
update envoy commit, sha, coverage report fixes (#1356)
fei-deng May 29, 2025
9caad8e
Update Envoy to 0c00c89 (May 30, 2025). (#1357)
mum4k Jun 2, 2025
d349572
Re-enable TSAN in CI with a fix. (#1359)
mum4k Jun 3, 2025
0d70c7d
Bump ossf/scorecard-action from 2.4.1 to 2.4.2 (#1358)
dependabot[bot] Jun 3, 2025
9fd2b75
Bump github/codeql-action from 3.28.17 to 3.28.18 (#1345)
dependabot[bot] Jun 3, 2025
f67a6c7
Bump setuptools from 70.3.0 to 78.1.1 in /tools/base (#1347)
dependabot[bot] Jun 3, 2025
f5bd855
Envoy update f6b019e (Jun 6, 2025) (#1362)
jiajunye Jun 16, 2025
aa092d0
Removing an obsolete pin on setuptools. (#1360)
mum4k Jun 17, 2025
f55df68
ci: Migrate to github/RBE (+ cleanup/fix CI) (#1251)
phlax Jun 18, 2025
3ac2fb2
Update Envoy to 9ceb376 (Jun 19, 2025) (#1372)
eric846 Jun 21, 2025
d940e70
Update Envoy to 25037e7 (Jun 23, 2025) (#1374)
eric846 Jun 23, 2025
69f9c5c
Update envoy to bf9eb6e (Jun 26th 2025). (#1377)
mum4k Jun 27, 2025
ab78459
Bump urllib3 from 2.4.0 to 2.5.0 in /tools/base (#1373)
dependabot[bot] Jun 27, 2025
823f168
Re-enable the ability to run tests using RBE. (#1378)
mum4k Jun 27, 2025
13fa343
Bump requests from 2.32.3 to 2.32.4 in /tools/base (#1363)
dependabot[bot] Jun 28, 2025
0c84e49
Bump github/codeql-action from 3.28.18 to 3.29.0 (#1365)
dependabot[bot] Jun 28, 2025
3e47e5c
Fix CI failures with sterror and README
asingh-g Jun 30, 2025
3554394
Fix some CI formatting failures
asingh-g Jun 30, 2025
be4d54f
Fix test failures for when process is terminated early
asingh-g Jul 1, 2025
4a7b8da
Update Envoy to 9ceb376 (Jun 19, 2025) (#1372)
eric846 Jun 21, 2025
0d630ce
Update Envoy to 9ceb376 (Jun 19, 2025) (#1372)
eric846 Jun 21, 2025
796728c
Update Envoy to 25037e7 (Jun 23, 2025) (#1374)
eric846 Jun 23, 2025
87f61fe
Fix gcc catch exception
asingh-g Jul 2, 2025
f74184d
Merge branch 'main' into nighthawk-connect-support
asingh-g Jul 2, 2025
dcd5916
fix bazelrc
asingh-g Jul 2, 2025
ce45aef
fix clang-format
asingh-g Jul 2, 2025
65ae425
fix numbering for tunneling options
asingh-g Aug 20, 2025
e434c56
Remove some tunnel options
asingh-g Aug 20, 2025
902a44b
Fix bootstrap changes
asingh-g Sep 3, 2025
e08d276
Fix bazelrc newline removal
asingh-g Sep 3, 2025
c1f358a
update documentation and update flag for socket
asingh-g Sep 3, 2025
5b94ec1
fix documentation bug for tunnel-uri
asingh-g Sep 3, 2025
f2bf28f
update options test to test more permutations for tunneling
asingh-g Sep 3, 2025
6213d55
add test for port finding utility
asingh-g Sep 8, 2025
49d63b7
Merge branch 'main' into nighthawk-connect-support
asingh-g Sep 15, 2025
6dbdb8b
Fix HTTP3 protocol options and add process test for encapsulation
asingh-g Sep 15, 2025
47d662f
fix bootstrap test with outdated flag
asingh-g Sep 15, 2025
d442198
fix format
asingh-g Sep 16, 2025
f86842b
update README with doc update tool
asingh-g Sep 16, 2025
e12ced9
Kick CI
asingh-g Sep 22, 2025
9e70cd0
update tsan timeouts to be longer
asingh-g Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ bazel-bin/nighthawk_client [--user-defined-plugin-config <string>] ...
|experimental_fortio_pedantic|csv
|prometheus>] [-v <trace|debug|info|warn
|error|critical>] [--concurrency <string>]
[--tunnel-tls-context <string>]
[--tunnel-uri <string>] [--tunnel-protocol
<http1|http2|http3>]
[--http3-protocol-options <string>] [-p
<http1|http2|http3>] [--h2] [--timeout
<uint32_t>] [--duration <uint32_t>]
Expand Down Expand Up @@ -397,6 +400,24 @@ Nighthawk process. Note that increasing this results in an effective
load multiplier combined with the configured --rps and --connections
values. Default: 1.

--tunnel-tls-context <string>
Upstream TlS context configuration in json. Required to encapsulate in
HTTP3 Example (json):
{common_tls_context:{tls_params:{cipher_suites:["-ALL:ECDHE-RSA-AES128
-SHA"]}}}

--tunnel-uri <string>
The address of the proxy. Possible values: [http1, http2, http3]. The
default protocol is 'http1'

--tunnel-protocol <http1|http2|http3>
The protocol for setting up tunnel encapsulation. Possible values:
[http1, http2, http3]. The default protocol is 'http1' Combinations
not supported currently are protocol = HTTP3 and tunnel_protocol =
HTTP1. and protocol = HTTP3 and tunnel_protocol = HTTP3. When protocol
is set to HTTP3 and tunneling is enabled, the CONNECT-UDP method is
used Otherwise, the HTTP CONNECT method is used

--http3-protocol-options <string>
HTTP3 protocol options (envoy::config::core::v3::Http3ProtocolOptions)
in json. If specified, Nighthawk uses these HTTP3 protocol options
Expand Down
23 changes: 22 additions & 1 deletion api/client/options.proto
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ message Protocol {

// TODO(oschaaf): Ultimately this will be a load test specification. The fact that it
// can arrive via CLI is just a concrete detail. Change this to reflect that.
// Next unused number is 114.
// Next unused number is 120.
message CommandLineOptions {
// The target requests-per-second rate. Default: 5.
google.protobuf.UInt32Value requests_per_second = 1
Expand All @@ -163,6 +163,25 @@ message CommandLineOptions {
Protocol protocol = 107;
}

// Options for routing requests via a proxy. if set, requests
// are encapsulated and forwarded to a terminating proxy running at
// tunnel_uri.
// When the oneof_protocol field is set to H1 or H2, an HTTP CONNECT
// tunnel is established with the proxy
// When the oneof_protocol field is set to H3, a CONNECT-UDP
// upgrade is used instead
message TunnelOptions {
// URI to the proxy.
string tunnel_uri = 1;
// the top level protocol.
Protocol tunnel_protocol = 2;
// TLS context for the proxy.
// TLS configuration is required for HTTP/3 tunnels.
envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext tunnel_tls_context = 3;
}

TunnelOptions tunnel_options = 114;

// Allows user to set specific HTTP3 protocol options.
// Only valid when protocol is set to HTTP3.
// Is exclusive with any other command line option that would modify the
Expand All @@ -173,6 +192,8 @@ message CommandLineOptions {
// Nighthawk leverage all vCPUs that have affinity to the Nighthawk process. Note that
// increasing this results in an effective load multiplier combined with the configured
// --rps and --connections values. Default: 1.
// When tunneling is enabled using tunnel_options, the tunnel has the same
// concurrency.
google.protobuf.StringValue concurrency =
6; // [(validate.rules).string = {pattern: "^([0-9]*|auto)$"}];
// Verbosity of the output. Possible values: [trace, debug, info, warn,
Expand Down
7 changes: 7 additions & 0 deletions include/nighthawk/client/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class Options {
virtual const absl::optional<envoy::config::core::v3::Http3ProtocolOptions>&
http3ProtocolOptions() const PURE;

// HTTP CONNECT/CONNECT-UDP Tunneling related options.
virtual Envoy::Http::Protocol tunnelProtocol() const PURE;
virtual std::string tunnelUri() const PURE;
virtual uint32_t encapPort() const PURE;
virtual const absl::optional<envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext>
tunnelTlsContext() const PURE;

virtual std::string concurrency() const PURE;
virtual nighthawk::client::Verbosity::VerbosityOptions verbosity() const PURE;
virtual nighthawk::client::OutputFormat::OutputFormatOptions outputFormat() const PURE;
Expand Down
10 changes: 10 additions & 0 deletions source/client/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ envoy_cc_library(
"//include/nighthawk/client:options_lib",
"//source/common:nighthawk_common_lib",
"@envoy//source/common/common:statusor_lib_with_external_headers",
"@envoy//source/common/formatter:formatter_extension_lib",
"@envoy//source/extensions/filters/network/tcp_proxy:config",
"@envoy//source/extensions/filters/udp/udp_proxy:config",
"@envoy//source/extensions/filters/udp/udp_proxy:udp_proxy_filter_lib",
"@envoy//source/extensions/filters/udp/udp_proxy/session_filters/http_capsule:config",
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/udp/udp_proxy/session/dynamic_forward_proxy/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/udp/udp_proxy/session/http_capsule/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/udp/udp_proxy/v3:pkg_cc_proto",
],
)

Expand Down Expand Up @@ -135,6 +144,7 @@ envoy_cc_library(
"@envoy//source/common/tracing:tracer_lib_with_external_headers",
"@envoy//source/common/upstream:cluster_manager_lib_with_external_headers",
"@envoy//source/exe:all_extensions_lib_with_external_headers",
"@envoy//source/exe:main_common_lib_with_external_headers",
"@envoy//source/exe:platform_header_lib_with_external_headers",
"@envoy//source/exe:platform_impl_lib",
"@envoy//source/exe:process_wide_lib_with_external_headers",
Expand Down
96 changes: 96 additions & 0 deletions source/client/options_impl.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "source/client/options_impl.h"

#include <cerrno>
#include <cstdint>
#include <exception>

#include "external/envoy/source/common/protobuf/message_validator_impl.h"
#include "external/envoy/source/common/protobuf/protobuf.h"
#include "external/envoy/source/common/protobuf/utility.h"
Expand All @@ -12,6 +16,7 @@
#include "source/common/version_info.h"

#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/types/optional.h"
#include "fmt/ranges.h"
Expand Down Expand Up @@ -85,6 +90,35 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv) {
"{quic_protocol_options:{max_concurrent_streams:1}}",
false, "", "string", cmd);

std::vector<std::string> tunnel_protocols = {"http1", "http2", "http3"};
TCLAP::ValuesConstraint<std::string> tunnel_protocols_allowed(tunnel_protocols);
TCLAP::ValueArg<std::string> tunnel_protocol(
"", "tunnel-protocol",
fmt::format(
"The protocol for setting up tunnel encapsulation. Possible values: [http1, http2, "
"http3]. The default protocol is '{}' "
"Combinations not supported currently are protocol = HTTP3 and tunnel_protocol = HTTP1."
" and protocol = HTTP3 and tunnel_protocol = HTTP3."
" When protocol is set to HTTP3 and tunneling is enabled, the CONNECT-UDP method is used"
" Otherwise, the HTTP CONNECT method is used",
absl::AsciiStrToLower(
nighthawk::client::Protocol_ProtocolOptions_Name(tunnel_protocol_))),
false, "", &tunnel_protocols_allowed, cmd);
TCLAP::ValueArg<std::string> tunnel_uri(
"", "tunnel-uri",
fmt::format(
"The address of the proxy. Possible values: [http1, http2, "
"http3]. The default protocol is '{}' ",
absl::AsciiStrToLower(nighthawk::client::Protocol_ProtocolOptions_Name(protocol_))),
false, "", "string", cmd);
TCLAP::ValueArg<std::string> tunnel_tls_context(
"", "tunnel-tls-context",
"Upstream TlS context configuration in json."
" Required to encapsulate in HTTP3"
" Example (json): "
" {common_tls_context:{tls_params:{cipher_suites:[\"-ALL:ECDHE-RSA-AES128-SHA\"]}}}",
false, "", "string", cmd);

TCLAP::ValueArg<std::string> concurrency(
"", "concurrency",
fmt::format(
Expand Down Expand Up @@ -677,6 +711,46 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv) {
}
}

if (tunnel_protocol.isSet()) {
std::string upper_cased = tunnel_protocol.getValue();
absl::AsciiStrToUpper(&upper_cased);
RELEASE_ASSERT(
nighthawk::client::Protocol::ProtocolOptions_Parse(upper_cased, &tunnel_protocol_),
"Failed to parse tunnel protocol");
if (!tunnel_uri.isSet()) {
throw MalformedArgvException("--tunnel-protocol requires --tunnel-uri");
}
tunnel_uri_ = tunnel_uri.getValue();
encap_port_ = Utility::GetAvailablePort(/*udp=*/protocol_ == Protocol::HTTP3, address_family_);

} else if (tunnel_uri.isSet() || tunnel_tls_context.isSet()) {
throw MalformedArgvException("tunnel flags require --tunnel-protocol");
}

if (!tunnel_tls_context.getValue().empty()) {
try {
tunnel_tls_context_.emplace(
envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext());
Envoy::MessageUtil::loadFromJson(tunnel_tls_context.getValue(), tunnel_tls_context_.value(),
Envoy::ProtobufMessage::getStrictValidationVisitor());
} catch (const Envoy::EnvoyException& e) {
throw MalformedArgvException(e.what());
}
} else if (tunnel_protocol_ == Protocol::HTTP3) {
throw MalformedArgvException("--tunnel-tls-context is required to use --tunnel-protocol http3");
}

if (tunnel_protocol.isSet()) {
if (tunnel_protocol_ == Protocol::HTTP3 && protocol_ == Protocol::HTTP3) {
throw MalformedArgvException(
"--protocol HTTP3 over --tunnel-protocol HTTP3 is not supported");
}
if (tunnel_protocol_ == Protocol::HTTP1 && protocol_ == Protocol::HTTP3) {
throw MalformedArgvException(
"--protocol HTTP3 over --tunnel-protocol HTTP1 is not supported");
}
}

validate();
}

Expand All @@ -690,6 +764,16 @@ Envoy::Http::Protocol OptionsImpl::protocol() const {
}
}

Envoy::Http::Protocol OptionsImpl::tunnelProtocol() const {
if (tunnel_protocol_ == Protocol::HTTP2) {
return Envoy::Http::Protocol::Http2;
} else if (tunnel_protocol_ == Protocol::HTTP3) {
return Envoy::Http::Protocol::Http3;
} else {
return Envoy::Http::Protocol::Http11;
}
}

void OptionsImpl::parsePredicates(const TCLAP::MultiArg<std::string>& arg,
TerminationPredicateMap& predicates) {
if (arg.isSet()) {
Expand Down Expand Up @@ -753,6 +837,18 @@ OptionsImpl::OptionsImpl(const nighthawk::client::CommandLineOptions& options) {
burst_size_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(options, burst_size, burst_size_);
address_family_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(options, address_family, address_family_);

if (options.has_tunnel_options()) {
tunnel_protocol_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(options.tunnel_options(), tunnel_protocol,
tunnel_protocol_);
tunnel_uri_ = options.tunnel_options().tunnel_uri();

// we must find an available port for the encap listener
encap_port_ =
Utility::GetAvailablePort(/*is_udp=*/protocol_ == Protocol::HTTP3, address_family_);

tunnel_tls_context_->MergeFrom(options.tunnel_options().tunnel_tls_context());
}

if (options.has_request_options()) {
const auto& request_options = options.request_options();
for (const auto& header : request_options.request_headers()) {
Expand Down
17 changes: 17 additions & 0 deletions source/client/options_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ class OptionsImpl : public Options, public Envoy::Logger::Loggable<Envoy::Logger
absl::optional<std::string> uri() const override { return uri_; }

Envoy::Http::Protocol protocol() const override;

Envoy::Http::Protocol tunnelProtocol() const override;
std::string tunnelUri() const override { return tunnel_uri_; }
uint32_t encapPort() const override { return encap_port_; }
virtual const absl::optional<envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext>
tunnelTlsContext() const override {
return tunnel_tls_context_;
}

const absl::optional<envoy::config::core::v3::Http3ProtocolOptions>&
http3ProtocolOptions() const override {
return http3_protocol_options_;
Expand Down Expand Up @@ -138,6 +147,14 @@ class OptionsImpl : public Options, public Envoy::Logger::Loggable<Envoy::Logger
absl::optional<envoy::config::core::v3::Http3ProtocolOptions> http3_protocol_options_;

std::string concurrency_;

// Tunnel related options.
nighthawk::client::Protocol::ProtocolOptions tunnel_protocol_{nighthawk::client::Protocol::HTTP1};
std::string tunnel_uri_;
uint32_t encap_port_{0};
absl::optional<envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext>
tunnel_tls_context_;

nighthawk::client::Verbosity::VerbosityOptions verbosity_{nighthawk::client::Verbosity::WARN};
nighthawk::client::OutputFormat::OutputFormatOptions output_format_{
nighthawk::client::OutputFormat::JSON};
Expand Down
Loading
Loading