aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fbench/CMakeLists.txt1
-rw-r--r--fbench/src/httpclient/CMakeLists.txt1
-rw-r--r--fbench/src/httpclient/httpclient.cpp16
-rw-r--r--fbench/src/httpclient/httpclient.h4
-rw-r--r--fbench/src/test/authority/CMakeLists.txt10
-rw-r--r--fbench/src/test/authority/authority_test.cpp89
-rw-r--r--fbench/src/util/CMakeLists.txt3
-rw-r--r--fbench/src/util/authority.cpp42
-rw-r--r--fbench/src/util/authority.h30
-rw-r--r--fnet/src/tests/connect/connect_test.cpp2
-rw-r--r--vespalib/src/vespa/vespalib/net/crypto_engine.h6
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.cpp12
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h2
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h2
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h2
15 files changed, 210 insertions, 12 deletions
diff --git a/fbench/CMakeLists.txt b/fbench/CMakeLists.txt
index 5cc56786227..3da632d98a6 100644
--- a/fbench/CMakeLists.txt
+++ b/fbench/CMakeLists.txt
@@ -15,6 +15,7 @@ vespa_define_module(
TESTS
src/test
+ src/test/authority
)
vespa_install_script(util/resultfilter.pl vespa-fbench-result-filter.pl bin)
diff --git a/fbench/src/httpclient/CMakeLists.txt b/fbench/src/httpclient/CMakeLists.txt
index 5f3333128b3..a28f3666383 100644
--- a/fbench/src/httpclient/CMakeLists.txt
+++ b/fbench/src/httpclient/CMakeLists.txt
@@ -3,5 +3,6 @@ vespa_add_library(fbench_httpclient STATIC
SOURCES
httpclient.cpp
DEPENDS
+ fbench_util
fastos
)
diff --git a/fbench/src/httpclient/httpclient.cpp b/fbench/src/httpclient/httpclient.cpp
index 99134a6e297..9615a6e6df7 100644
--- a/fbench/src/httpclient/httpclient.cpp
+++ b/fbench/src/httpclient/httpclient.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "httpclient.h"
#include <vespa/vespalib/net/socket_spec.h>
+#include <util/authority.h>
#include <cassert>
#include <cstring>
@@ -29,7 +30,8 @@ HTTPClient::HTTPClient(vespalib::CryptoEngine::SP engine, const char *hostname,
_keepAlive(keepAlive),
_headerBenchmarkdataCoverage(headerBenchmarkdataCoverage),
_extraHeaders(extraHeaders),
- _authority(authority),
+ _sni_spec(make_sni_spec(authority, hostname, port, _engine->use_tls_when_client())),
+ _host_header_value(make_host_header_value(_sni_spec, _engine->use_tls_when_client())),
_reuseCount(0),
_bufsize(10240),
_buf(new char[_bufsize]),
@@ -51,11 +53,6 @@ HTTPClient::HTTPClient(vespalib::CryptoEngine::SP engine, const char *hostname,
_dataDone(false),
_reader(NULL)
{
- if (_authority == "") {
- char tmp[1024];
- snprintf(tmp, 1024, "%s:%d", hostname, port);
- _authority = tmp;
- }
}
bool
@@ -70,8 +67,7 @@ HTTPClient::connect_socket()
if (!handle.valid()) {
return false;
}
- _socket = vespalib::SyncCryptoSocket::create_client(*_engine, std::move(handle),
- vespalib::SocketSpec::from_host_port(_hostname, _port));
+ _socket = vespalib::SyncCryptoSocket::create_client(*_engine, std::move(handle), _sni_spec);
return bool(_socket);
}
@@ -153,14 +149,14 @@ HTTPClient::Connect(const char *url, bool usePost, const char *content, int cLen
"Content-Length: %d\r\n"
"%s"
"\r\n",
- url, _authority.c_str(), cLen, headers.c_str());
+ url, _host_header_value.c_str(), cLen, headers.c_str());
} else {
snprintf(req, req_max,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"%s"
"\r\n",
- url, _authority.c_str(), headers.c_str());
+ url, _host_header_value.c_str(), headers.c_str());
}
// try to reuse connection if keep-alive is enabled
diff --git a/fbench/src/httpclient/httpclient.h b/fbench/src/httpclient/httpclient.h
index 9c3ccd437d1..cad01826db7 100644
--- a/fbench/src/httpclient/httpclient.h
+++ b/fbench/src/httpclient/httpclient.h
@@ -6,6 +6,7 @@
#include <vespa/vespalib/net/sync_crypto_socket.h>
#include <vespa/vespalib/net/crypto_engine.h>
#include <vespa/vespalib/net/socket_address.h>
+#include <vespa/vespalib/net/socket_spec.h>
/**
* This class implements a HTTP client that may be used to fetch
@@ -99,7 +100,8 @@ protected:
bool _keepAlive;
bool _headerBenchmarkdataCoverage;
std::string _extraHeaders;
- std::string _authority;
+ vespalib::SocketSpec _sni_spec;
+ std::string _host_header_value;
uint64_t _reuseCount;
size_t _bufsize;
diff --git a/fbench/src/test/authority/CMakeLists.txt b/fbench/src/test/authority/CMakeLists.txt
new file mode 100644
index 00000000000..00f804f43f6
--- /dev/null
+++ b/fbench/src/test/authority/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(fbench_authority_test_app TEST
+ SOURCES
+ authority_test.cpp
+ DEPENDS
+ fbench_util
+ vespalib
+ gtest
+)
+vespa_add_test(NAME fbench_authority_test_app COMMAND fbench_authority_test_app)
diff --git a/fbench/src/test/authority/authority_test.cpp b/fbench/src/test/authority/authority_test.cpp
new file mode 100644
index 00000000000..f5d0afa32bb
--- /dev/null
+++ b/fbench/src/test/authority/authority_test.cpp
@@ -0,0 +1,89 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <util/authority.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using vespalib::SocketSpec;
+
+//-----------------------------------------------------------------------------
+
+TEST(MakeSNISpecTest, host_port_is_parsed_as_expected) {
+ EXPECT_EQ(make_sni_spec("my_host:123", "fallback", 456, false).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("my_host:123", "fallback", 456, true).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("my_host:123", "fallback", 456, false).port(), 123);
+ EXPECT_EQ(make_sni_spec("my_host:123", "fallback", 456, true).port(), 123);
+}
+
+TEST(MakeSNISpecTest, user_info_is_stripped) {
+ EXPECT_EQ(make_sni_spec("myuser:deprecated@my_host:123", "fallback", 456, false).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("myuser:deprecated@my_host:123", "fallback", 456, true).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("myuser:deprecated@my_host:123", "fallback", 456, false).port(), 123);
+ EXPECT_EQ(make_sni_spec("myuser:deprecated@my_host:123", "fallback", 456, true).port(), 123);
+}
+
+TEST(MakeSNISpecTest, port_can_be_skipped) {
+ EXPECT_EQ(make_sni_spec("my_host", "fallback", 456, false).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("my_host", "fallback", 456, true).host(), "my_host");
+ EXPECT_EQ(make_sni_spec("my_host", "fallback", 456, false).port(), 80);
+ EXPECT_EQ(make_sni_spec("my_host", "fallback", 456, true).port(), 443);
+}
+
+TEST(MakeSNISpecTest, quoted_ip_addresses_work_as_expected) {
+ EXPECT_EQ(make_sni_spec("[::1]:123", "fallback", 456, false).host(), "::1");
+ EXPECT_EQ(make_sni_spec("[::1]:123", "fallback", 456, true).host(), "::1");
+ EXPECT_EQ(make_sni_spec("[::1]:123", "fallback", 456, false).port(), 123);
+ EXPECT_EQ(make_sni_spec("[::1]:123", "fallback", 456, true).port(), 123);
+ EXPECT_EQ(make_sni_spec("[::1]", "fallback", 456, false).host(), "::1");
+ EXPECT_EQ(make_sni_spec("[::1]", "fallback", 456, true).host(), "::1");
+ EXPECT_EQ(make_sni_spec("[::1]", "fallback", 456, false).port(), 80);
+ EXPECT_EQ(make_sni_spec("[::1]", "fallback", 456, true).port(), 443);
+}
+
+TEST(MakeSNISpecTest, supplied_host_port_is_used_as_fallback) {
+ EXPECT_EQ(make_sni_spec("", "fallback", 456, false).host(), "fallback");
+ EXPECT_EQ(make_sni_spec("", "fallback", 456, true).host(), "fallback");
+ EXPECT_EQ(make_sni_spec("", "fallback", 456, false).port(), 456);
+ EXPECT_EQ(make_sni_spec("", "fallback", 456, true).port(), 456);
+}
+
+//-----------------------------------------------------------------------------
+
+std::string make_host_header_value(const vespalib::SocketSpec &sni_spec, bool use_https);
+
+TEST(MakeHostHeaderValueTest, host_port_is_formatted_as_expected) {
+ auto my_spec = SocketSpec::from_host_port("myhost", 123);
+ EXPECT_EQ(make_host_header_value(my_spec, false), "myhost:123");
+ EXPECT_EQ(make_host_header_value(my_spec, true), "myhost:123");
+}
+
+TEST(MakeHostHeaderValueTest, inappropriate_spec_gives_empty_host_value) {
+ std::vector<SocketSpec> bad_specs = {
+ SocketSpec::invalid,
+ SocketSpec::from_port(123),
+ SocketSpec::from_name("foo"),
+ SocketSpec::from_path("bar")
+ };
+ for (const auto &spec: bad_specs) {
+ EXPECT_EQ(make_host_header_value(spec, false), "");
+ EXPECT_EQ(make_host_header_value(spec, true), "");
+ }
+}
+
+TEST(MakeHostHeaderValueTest, default_port_is_omitted) {
+ auto spec1 = SocketSpec::from_host_port("myhost", 80);
+ auto spec2 = SocketSpec::from_host_port("myhost", 443);
+ EXPECT_EQ(make_host_header_value(spec1, false), "myhost");
+ EXPECT_EQ(make_host_header_value(spec1, true), "myhost:80");
+ EXPECT_EQ(make_host_header_value(spec2, false), "myhost:443");
+ EXPECT_EQ(make_host_header_value(spec2, true), "myhost");
+}
+
+TEST(MakeHostHeaderValueTest, ipv6_addresses_are_quoted) {
+ auto my_spec = SocketSpec::from_host_port("::1", 123);
+ EXPECT_EQ(make_host_header_value(my_spec, false), "[::1]:123");
+ EXPECT_EQ(make_host_header_value(my_spec, true), "[::1]:123");
+}
+
+//-----------------------------------------------------------------------------
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/fbench/src/util/CMakeLists.txt b/fbench/src/util/CMakeLists.txt
index 47cc46ffc8f..3cdff26ce16 100644
--- a/fbench/src/util/CMakeLists.txt
+++ b/fbench/src/util/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(fbench_util STATIC
SOURCES
+ authority.cpp
+ clientstatus.cpp
filereader.cpp
timer.cpp
- clientstatus.cpp
DEPENDS
)
diff --git a/fbench/src/util/authority.cpp b/fbench/src/util/authority.cpp
new file mode 100644
index 00000000000..6247c72d9b0
--- /dev/null
+++ b/fbench/src/util/authority.cpp
@@ -0,0 +1,42 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "authority.h"
+#include <vespa/vespalib/util/stringfmt.h>
+#include <cassert>
+
+namespace {
+
+int default_port(bool use_https) { return use_https ? 443 : 80; }
+
+}
+
+vespalib::SocketSpec make_sni_spec(const std::string &authority, const char *hostname, int port, bool use_https) {
+ if (authority.empty()) {
+ return vespalib::SocketSpec::from_host_port(hostname, port);
+ }
+ auto split = authority.rfind('@');
+ std::string spec_str = (split == std::string::npos) ? authority : authority.substr(split + 1);
+ auto a = spec_str.rfind(':');
+ auto b = spec_str.rfind(']');
+ bool has_port = (a != std::string::npos) && ((b == std::string::npos) || (a > b));
+ if (has_port) {
+ spec_str = "tcp/" + spec_str;
+ } else {
+ spec_str = vespalib::make_string("tcp/%s:%d", spec_str.c_str(), default_port(use_https));
+ }
+ // use SocketSpec parser to ensure ipv6 addresses are dequoted
+ return vespalib::SocketSpec(spec_str);
+}
+
+std::string make_host_header_value(const vespalib::SocketSpec &sni_spec, bool use_https) {
+ if (sni_spec.host().empty()) {
+ return "";
+ }
+ if (sni_spec.port() == default_port(use_https)) {
+ return sni_spec.host();
+ }
+ // use SocketSpec formatter to ensure ipv6 addresses are quoted
+ std::string spec_str = sni_spec.spec();
+ assert(spec_str.find("tcp/") == 0);
+ return spec_str.substr(4);
+}
diff --git a/fbench/src/util/authority.h b/fbench/src/util/authority.h
new file mode 100644
index 00000000000..49dab4a29fd
--- /dev/null
+++ b/fbench/src/util/authority.h
@@ -0,0 +1,30 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/net/socket_spec.h>
+
+/**
+ * Assemble an SNI (Server Name Indication) spec that will be used
+ * when handshaking over TLS. The authority will be used if
+ * non-empty. Hostname/port will be used as fall-back. Note that the
+ * SNI spec will also be used to generate the Host header used in
+ * subsequent HTTP requests.
+ *
+ * @return sni spec
+ * @param authority user-provided authority
+ * @param hostname name of the host we are connecting to
+ * @param port which port we are connecting to
+ * @param use_https are we using https? (TLS)
+ **/
+vespalib::SocketSpec make_sni_spec(const std::string &authority, const char *hostname, int port, bool use_https);
+
+/**
+ * Use an SNI spec to generate a matching Host header to be used in
+ * HTTP requests. Note that default port numbers will be omitted.
+ *
+ * @return host header value
+ * @param sni_spec SNI spec
+ * @param use_https are we using https? (TLS)
+ **/
+std::string make_host_header_value(const vespalib::SocketSpec &sni_spec, bool use_https);
diff --git a/fnet/src/tests/connect/connect_test.cpp b/fnet/src/tests/connect/connect_test.cpp
index d94b6759077..62000efb682 100644
--- a/fnet/src/tests/connect/connect_test.cpp
+++ b/fnet/src/tests/connect/connect_test.cpp
@@ -65,6 +65,8 @@ struct BlockingCryptoEngine : public CryptoEngine {
Gate handshake_work_enter;
Gate handshake_work_exit;
Gate handshake_socket_deleted;
+ bool use_tls_when_client() const override { return false; }
+ bool always_use_tls_when_server() const override { return false; }
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &) override {
return std::make_unique<BlockingCryptoSocket>(std::move(socket),
handshake_work_enter, handshake_work_exit, handshake_socket_deleted);
diff --git a/vespalib/src/vespa/vespalib/net/crypto_engine.h b/vespalib/src/vespa/vespalib/net/crypto_engine.h
index 4deacf9a6c7..71511b8a552 100644
--- a/vespalib/src/vespa/vespalib/net/crypto_engine.h
+++ b/vespalib/src/vespa/vespalib/net/crypto_engine.h
@@ -19,6 +19,8 @@ class SocketSpec;
**/
struct CryptoEngine {
using SP = std::shared_ptr<CryptoEngine>;
+ virtual bool use_tls_when_client() const = 0;
+ virtual bool always_use_tls_when_server() const = 0;
virtual CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) = 0;
virtual CryptoSocket::UP create_server_crypto_socket(SocketHandle socket) = 0;
virtual ~CryptoEngine();
@@ -29,6 +31,8 @@ struct CryptoEngine {
* Crypto engine without encryption.
**/
struct NullCryptoEngine : public CryptoEngine {
+ bool use_tls_when_client() const override { return false; }
+ bool always_use_tls_when_server() const override { return false; }
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
CryptoSocket::UP create_server_crypto_socket(SocketHandle socket) override;
};
@@ -39,6 +43,8 @@ struct NullCryptoEngine : public CryptoEngine {
* from TLS.
**/
struct XorCryptoEngine : public CryptoEngine {
+ bool use_tls_when_client() const override { return false; }
+ bool always_use_tls_when_server() const override { return false; }
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
CryptoSocket::UP create_server_crypto_socket(SocketHandle socket) override;
};
diff --git a/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.cpp
index c425ab75ce8..bdb2402adbc 100644
--- a/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.cpp
@@ -99,6 +99,18 @@ CryptoSocket::UP AutoReloadingTlsCryptoEngine::create_server_crypto_socket(Socke
return acquire_current_engine()->create_server_crypto_socket(std::move(socket));
}
+bool
+AutoReloadingTlsCryptoEngine::use_tls_when_client() const
+{
+ return acquire_current_engine()->use_tls_when_client();
+}
+
+bool
+AutoReloadingTlsCryptoEngine::always_use_tls_when_server() const
+{
+ return acquire_current_engine()->always_use_tls_when_server();
+}
+
std::unique_ptr<TlsCryptoSocket>
AutoReloadingTlsCryptoEngine::create_tls_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) {
return acquire_current_engine()->create_tls_client_crypto_socket(std::move(socket), spec);
diff --git a/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h
index e268cbc8f1a..1b80b782daf 100644
--- a/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h
+++ b/vespalib/src/vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h
@@ -47,6 +47,8 @@ public:
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
CryptoSocket::UP create_server_crypto_socket(SocketHandle socket) override;
+ bool use_tls_when_client() const override;
+ bool always_use_tls_when_server() const override;
std::unique_ptr<TlsCryptoSocket> create_tls_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
std::unique_ptr<TlsCryptoSocket> create_tls_server_crypto_socket(SocketHandle socket) override;
};
diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h
index 147a770bc8f..ece7d094c54 100644
--- a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h
+++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h
@@ -28,6 +28,8 @@ public:
: _null_engine(std::make_shared<NullCryptoEngine>()),
_tls_engine(std::move(tls_engine)),
_use_tls_when_client(use_tls_when_client) {}
+ bool use_tls_when_client() const override { return _use_tls_when_client; }
+ bool always_use_tls_when_server() const override { return false; }
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
CryptoSocket::UP create_server_crypto_socket(SocketHandle socket) override;
};
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h
index 5e760cf5585..444a817b357 100644
--- a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h
+++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h
@@ -27,6 +27,8 @@ public:
net::tls::AuthorizationMode authz_mode = net::tls::AuthorizationMode::Enforce);
std::unique_ptr<TlsCryptoSocket> create_tls_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override;
std::unique_ptr<TlsCryptoSocket> create_tls_server_crypto_socket(SocketHandle socket) override;
+ bool use_tls_when_client() const override { return true; }
+ bool always_use_tls_when_server() const override { return true; }
CryptoSocket::UP create_client_crypto_socket(SocketHandle socket, const SocketSpec &spec) override {
return create_tls_client_crypto_socket(std::move(socket), spec);
}