Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: making upstream ALPN accesible. #18884

Merged
merged 8 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
5 changes: 5 additions & 0 deletions envoy/ssl/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ class ConnectionInfo {
* connection.
**/
virtual const std::string& tlsVersion() const PURE;

/**
* @return std::string the protocol negotiated via ALPN.
**/
virtual const std::string& alpn() const PURE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly surprised this doesn't already exist!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's nextProtocol in one of the other classes, but it's not accessible where we need it, and I think it makes more sense here.

};

using ConnectionInfoConstSharedPtr = std::shared_ptr<const ConnectionInfo>;
Expand Down
1 change: 1 addition & 0 deletions source/common/network/socket_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter {
void setConnectionID(uint64_t id) override { connection_id_ = id; }
Ssl::ConnectionInfoConstSharedPtr sslConnection() const override { return ssl_info_; }
void setSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) override {
ASSERT(!ssl_info_);
ssl_info_ = ssl_connection_info;
}

Expand Down
7 changes: 3 additions & 4 deletions source/common/quic/envoy_quic_client_session.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@ EnvoyQuicClientSession::EnvoyQuicClientSession(
uint32_t send_buffer_limit, EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory,
QuicStatNames& quic_stat_names, Stats::Scope& scope)
: QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher,
send_buffer_limit),
send_buffer_limit,
std::make_shared<QuicSslConnectionInfo>(*this)),
quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id,
crypto_config.get(), push_promise_index),
crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory),
quic_stat_names_(quic_stat_names), scope_(scope) {
quic_ssl_info_ = std::make_shared<QuicSslConnectionInfo>(*this);
}
quic_stat_names_(quic_stat_names), scope_(scope) {}

EnvoyQuicClientSession::~EnvoyQuicClientSession() {
ASSERT(!connection()->connected());
Expand Down
4 changes: 2 additions & 2 deletions source/common/quic/envoy_quic_server_session.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ EnvoyQuicServerSession::EnvoyQuicServerSession(
: quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper,
crypto_config, compressed_certs_cache),
QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher,
send_buffer_limit),
send_buffer_limit,
std::make_shared<QuicSslConnectionInfo>(*this)),
quic_connection_(std::move(connection)), quic_stat_names_(quic_stat_names),
listener_scope_(listener_scope), crypto_server_stream_factory_(crypto_server_stream_factory) {
quic_ssl_info_ = std::make_shared<QuicSslConnectionInfo>(*this);
}

EnvoyQuicServerSession::~EnvoyQuicServerSession() {
Expand Down
7 changes: 5 additions & 2 deletions source/common/quic/quic_filter_manager_connection_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ namespace Quic {

QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl(
QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id,
Event::Dispatcher& dispatcher, uint32_t send_buffer_limit)
Event::Dispatcher& dispatcher, uint32_t send_buffer_limit,
std::shared_ptr<QuicSslConnectionInfo>&& info)
// Using this for purpose other than logging is not safe. Because QUIC connection id can be
// 18 bytes, so there might be collision when it's hashed to 8 bytes.
: Network::ConnectionImplBase(dispatcher, /*id=*/connection_id.Hash()),
network_connection_(&connection),
network_connection_(&connection), quic_ssl_info_(std::move(info)),
filter_manager_(
std::make_unique<Network::FilterManagerImpl>(*this, *connection.connectionSocket())),
stream_info_(dispatcher.timeSource(),
Expand All @@ -23,6 +24,8 @@ QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl(
send_buffer_limit / 2, send_buffer_limit, [this]() { onSendBufferLowWatermark(); },
[this]() { onSendBufferHighWatermark(); }, ENVOY_LOGGER()) {
stream_info_.protocol(Http::Protocol::Http3);
network_connection_->connectionSocket()->connectionInfoProvider().setSslConnection(
Ssl::ConnectionInfoConstSharedPtr(quic_ssl_info_));
}

void QuicFilterManagerConnectionImpl::addWriteFilter(Network::WriteFilterSharedPtr filter) {
Expand Down
3 changes: 2 additions & 1 deletion source/common/quic/quic_filter_manager_connection_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase,
public:
QuicFilterManagerConnectionImpl(QuicNetworkConnection& connection,
const quic::QuicConnectionId& connection_id,
Event::Dispatcher& dispatcher, uint32_t send_buffer_limit);
Event::Dispatcher& dispatcher, uint32_t send_buffer_limit,
std::shared_ptr<QuicSslConnectionInfo>&& info);
// Network::FilterManager
// Overridden to delegate calls to filter_manager_.
void addWriteFilter(Network::WriteFilterSharedPtr filter) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ const std::string& ConnectionInfoImplBase::tlsVersion() const {
return cached_tls_version_;
}

const std::string& ConnectionInfoImplBase::alpn() const {
if (alpn_.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are hard-coding "h3" in server session right now. Would this cause inconsistency between the ALPN returned via Ssl::ConnectionInfo and the one returned via ConnectionSocket::requestedApplicationProtocols()? requestedApplicationProtocols() is only used in server side, so I guess the upstream connection's connection socket doesn't have ALPN set there, either will it be used. But the changes in this PR applies to downstream as well, hence HCM or the filters can use both the new interface and requestedApplicationProtocols() to get the downstream ALPN and they can be different. Maybe overriding this implementation in QuicSslConnectionInfo according to perspective?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't we hard-coding h3 as the thing to negotiate? I think returning what boring returns is correct here.

const unsigned char* proto;
unsigned int proto_len;
SSL_get0_alpn_selected(ssl(), &proto, &proto_len);
if (proto != nullptr) {
alpn_ = std::string(reinterpret_cast<const char*>(proto), proto_len);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL. C chars are so annoying.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry quick question: this code must exist somewhere else, right? Like in TLS inspector or somewhere else? Should we factor it out into some utility function?

/wait-any

}
}
return alpn_;
}

const std::string& ConnectionInfoImplBase::serialNumberPeerCertificate() const {
if (!cached_serial_number_peer_certificate_.empty()) {
return cached_serial_number_peer_certificate_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo {
uint16_t ciphersuiteId() const override;
std::string ciphersuiteString() const override;
const std::string& tlsVersion() const override;
const std::string& alpn() const override;

virtual SSL* ssl() const PURE;

Expand All @@ -56,6 +57,7 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo {
mutable std::vector<std::string> cached_dns_san_local_certificate_;
mutable std::string cached_session_id_;
mutable std::string cached_tls_version_;
mutable std::string alpn_;
};

} // namespace Tls
Expand Down
2 changes: 1 addition & 1 deletion test/common/quic/test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterMana
uint32_t send_buffer_limit)
: quic::QuicSpdySession(connection, /*visitor=*/nullptr, config, supported_versions),
QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher,
send_buffer_limit),
send_buffer_limit, {nullptr}),
crypto_stream_(std::make_unique<TestQuicCryptoStream>(this)) {}

void Initialize() override {
Expand Down
1 change: 1 addition & 0 deletions test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ envoy_cc_test(
"//source/extensions/filters/http/buffer:config",
"//test/integration/filters:encoder_decoder_buffer_filter_lib",
"//test/integration/filters:random_pause_filter_lib",
"//test/integration/filters:stream_info_to_headers_filter_lib",
"//test/test_common:utility_lib",
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto",
Expand Down
15 changes: 15 additions & 0 deletions test/integration/filters/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -635,3 +635,18 @@ envoy_cc_test_library(
"//test/extensions/filters/http/common:empty_http_filter_config_lib",
],
)

envoy_cc_test_library(
name = "stream_info_to_headers_filter_lib",
srcs = [
"stream_info_to_headers_filter.cc",
],
deps = [
":common_lib",
"//envoy/http:filter_interface",
"//envoy/registry",
"//envoy/server:filter_config_interface",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"//test/extensions/filters/http/common:empty_http_filter_config_lib",
],
)
36 changes: 36 additions & 0 deletions test/integration/filters/stream_info_to_headers_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "envoy/registry/registry.h"
#include "envoy/server/filter_config.h"

#include "source/extensions/filters/http/common/pass_through_filter.h"

#include "test/extensions/filters/http/common/empty_http_filter_config.h"
#include "test/integration/filters/common.h"

#include "gtest/gtest.h"

namespace Envoy {

// A filter that sticks stream info into headers for integration testing.
class StreamInfoToHeadersFilter : public Http::PassThroughFilter {
alyssawilk marked this conversation as resolved.
Show resolved Hide resolved
public:
constexpr static char name[] = "stream-info-to-headers-filter";

Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override {
return Http::FilterHeadersStatus::Continue;
}

Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool) override {
if (decoder_callbacks_->streamInfo().upstreamSslConnection()) {
headers.addCopy(Http::LowerCaseString("alpn"),
decoder_callbacks_->streamInfo().upstreamSslConnection()->alpn());
}
return Http::FilterHeadersStatus::Continue;
}
};

constexpr char StreamInfoToHeadersFilter::name[];
static Registry::RegisterFactory<SimpleFilterConfig<StreamInfoToHeadersFilter>,
Server::Configuration::NamedHttpFilterConfigFactory>
register_;

} // namespace Envoy
9 changes: 9 additions & 0 deletions test/integration/multiplexed_upstream_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ TEST_P(MultiplexedUpstreamIntegrationTest, TestSchemeAndXFP) {

// Ensure Envoy handles streaming requests and responses simultaneously.
void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) {
config_helper_.prependFilter(fmt::format(R"EOF(
name: stream-info-to-headers-filter
alyssawilk marked this conversation as resolved.
Show resolved Hide resolved
typed_config:
"@type": type.googleapis.com/google.protobuf.Empty)EOF"));

initialize();
codec_client_ = makeHttpConnection(lookupPort("http"));

Expand Down Expand Up @@ -143,6 +148,10 @@ void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes)
upstream_request_->encodeTrailers(Http::TestResponseTrailerMapImpl{{"trailer", "bar"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
std::string expected_alpn = upstreamProtocol() == Http::CodecType::HTTP2 ? "h2" : "h3";
ASSERT_FALSE(response->headers().get(Http::LowerCaseString("alpn")).empty());
ASSERT_EQ(response->headers().get(Http::LowerCaseString("alpn"))[0]->value().getStringView(),
expected_alpn);
}

TEST_P(MultiplexedUpstreamIntegrationTest, BidirectionalStreaming) { bidirectionalStreaming(1024); }
Expand Down
1 change: 1 addition & 0 deletions test/mocks/ssl/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class MockConnectionInfo : public ConnectionInfo {
MOCK_METHOD(uint16_t, ciphersuiteId, (), (const));
MOCK_METHOD(std::string, ciphersuiteString, (), (const));
MOCK_METHOD(const std::string&, tlsVersion, (), (const));
MOCK_METHOD(const std::string&, alpn, (), (const));
};

class MockClientContext : public ClientContext {
Expand Down