-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
quiche: use max header size configured in HCM #15912
Changes from 42 commits
bb02ae6
09fc660
6ed3c94
186a69d
66dbde0
d5cbd00
cf08311
97a3033
e00fd2b
5682791
96c587e
725d93e
354a322
2b94626
77cb937
4e9e56a
88398b8
5c11723
39e3f9a
4c2b490
97292b2
fdc6189
280fc25
3c02978
c31225b
e1fd5d7
2dd1ae5
60015bf
55da362
71eb14c
4c45c9f
8da2e26
852a392
ec6a58c
cf32e9d
dd14732
8730535
f423074
87cd212
6c48a12
59322e6
669ef78
0396a8f
fecd6ba
75e85ce
dbac8ea
1b9a3b2
836a6e8
a8297cb
885a7b9
7953bc8
95561c4
6eefb38
96aad88
f929456
6c38879
7b29d70
b6e3ace
09aca06
5d77b16
5d4469a
42c08af
1677acc
0651906
55fd7b3
874a4b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,14 +34,17 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, | |
connection_->addConnectionCallbacks(*this); | ||
connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)}); | ||
|
||
// In general, codecs are handed new not-yet-connected connections, but in the | ||
// case of ALPN, the codec may be handed an already connected connection. | ||
if (!connection_->connecting()) { | ||
ASSERT(connection_->state() == Network::Connection::State::Open); | ||
connected_ = true; | ||
} else { | ||
ENVOY_CONN_LOG(debug, "connecting", *connection_); | ||
connection_->connect(); | ||
// Do not start handshake for H3 connection till it is initialized. | ||
if (type_ != Type::HTTP3) { | ||
// In general, codecs are handed new not-yet-connected connections, but in the | ||
// case of ALPN, the codec may be handed an already connected connection. | ||
if (!connection_->connecting()) { | ||
ASSERT(connection_->state() == Network::Connection::State::Open); | ||
connected_ = true; | ||
} else { | ||
ENVOY_CONN_LOG(debug, "connecting", *connection_); | ||
connection_->connect(); | ||
} | ||
} | ||
|
||
if (idle_timeout_) { | ||
|
@@ -185,10 +188,21 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne | |
} | ||
case Type::HTTP3: { | ||
#ifdef ENVOY_ENABLE_QUIC | ||
auto& quic_session = dynamic_cast<Quic::EnvoyQuicClientSession&>(*connection_); | ||
codec_ = std::make_unique<Quic::QuicHttpClientConnectionImpl>( | ||
dynamic_cast<Quic::EnvoyQuicClientSession&>(*connection_), *this, | ||
host->cluster().http3CodecStats(), host->cluster().http3Options(), | ||
quic_session, *this, host->cluster().http3CodecStats(), host->cluster().http3Options(), | ||
Http::DEFAULT_MAX_REQUEST_HEADERS_KB); | ||
// Initialize the session after max request header size is changed in above http client | ||
// connection creation. | ||
quic_session.Initialize(); | ||
Comment on lines
+197
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we call quic_session.Initialize in QuicHttpClientConnectionImpl's constructor? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we could. But explicitly call Initialize() is more consistent with QUICHE code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, quic_session has already been created before this function is called, so the initialize is already far away from the constructor. Your call. |
||
// The other two codecs have already connected in base class. | ||
if (!connection_->connecting()) { | ||
ASSERT(connection_->state() == Network::Connection::State::Open); | ||
connected_ = true; | ||
} else { | ||
ENVOY_CONN_LOG(debug, "connecting", *connection_); | ||
connection_->connect(); | ||
} | ||
break; | ||
#else | ||
// Should be blocked by configuration checking at an earlier point. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ ActiveQuicListener::ActiveQuicListener( | |
kernel_worker_routing_(kernel_worker_routing) { | ||
// This flag fix a QUICHE issue which may crash Envoy during connection close. | ||
SetQuicReloadableFlag(quic_single_ack_in_packet2, true); | ||
SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is going to be permanent right? Let's comment what it does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this will be permanent to align with nghttp2. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you also comment on why the default needs to be true but we need to turn it off here? If the default(in quic_protocol_flags_list.h) needs to be true, could we turn it off once at envoy startup? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default doesn't needs to be true. It is true just because the upstream QUICHE set so. Envoy can definitely start with this flag being false. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Feel free to address this as a follow up)
In that case, how about change the default to false too.
Please make it so. |
||
|
||
if (Runtime::LoaderSingleton::getExisting()) { | ||
enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
#include "common/http/utility.h" | ||
#include "common/quic/envoy_quic_server_connection.h" | ||
#include "common/quic/envoy_quic_server_session.h" | ||
#include "common/quic/envoy_quic_utils.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
@@ -48,8 +49,8 @@ void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_i | |
|
||
std::unique_ptr<quic::QuicSession> EnvoyQuicDispatcher::CreateQuicSession( | ||
quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address, | ||
const quic::QuicSocketAddress& peer_address, absl::string_view /*alpn*/, | ||
const quic::ParsedQuicVersion& version, absl::string_view /*sni*/) { | ||
const quic::QuicSocketAddress& peer_address, absl::string_view alpn, | ||
const quic::ParsedQuicVersion& version, absl::string_view sni) { | ||
quic::QuicConfig quic_config = config(); | ||
// TODO(danzh) setup flow control window via config. | ||
quic_config.SetInitialStreamFlowControlWindowToSend( | ||
|
@@ -63,6 +64,17 @@ std::unique_ptr<quic::QuicSession> EnvoyQuicDispatcher::CreateQuicSession( | |
quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this, | ||
session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_, | ||
listener_config_.perConnectionBufferLimitBytes(), listener_config_); | ||
|
||
const Network::FilterChain* filter_chain = | ||
getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, | ||
peer_address, std::string(sni), alpn); | ||
ASSERT(filter_chain); | ||
if (filter_chain != nullptr) { | ||
const bool has_filter_initialized = | ||
listener_config_.filterChainFactory().createNetworkFilterChain( | ||
*quic_session, filter_chain->networkFilterFactories()); | ||
ASSERT(has_filter_initialized); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you comment about the validity of this ASSERT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. As long as HCM is configured, the ASSERT should pass here. |
||
} | ||
quic_session->Initialize(); | ||
// Filter chain can't be retrieved here as self address is unknown at this | ||
// point. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,14 +14,11 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( | |
std::unique_ptr<EnvoyQuicConnection> connection, quic::QuicSession::Visitor* visitor, | ||
quic::QuicCryptoServerStream::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, | ||
quic::QuicCompressedCertsCache* compressed_certs_cache, Event::Dispatcher& dispatcher, | ||
uint32_t send_buffer_limit, Network::ListenerConfig& listener_config) | ||
uint32_t send_buffer_limit, Network::ListenerConfig& /*listener_config*/) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like the listener_config argument should be removed; the call to createNetworkFilterChain was moved to the parent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed |
||
: quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, | ||
crypto_config, compressed_certs_cache), | ||
QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), | ||
quic_connection_(std::move(connection)), listener_config_(listener_config) { | ||
// HTTP/3 header limits should be configurable, but for now hard-code to Envoy defaults. | ||
set_max_inbound_header_list_size(Http::DEFAULT_MAX_REQUEST_HEADERS_KB * 1000); | ||
} | ||
quic_connection_(std::move(connection)) {} | ||
|
||
EnvoyQuicServerSession::~EnvoyQuicServerSession() { | ||
ASSERT(!quic_connection_->connected()); | ||
|
@@ -89,6 +86,7 @@ void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseF | |
|
||
void EnvoyQuicServerSession::Initialize() { | ||
quic::QuicServerSessionBase::Initialize(); | ||
initialized_ = true; | ||
quic_connection_->setEnvoyConnection(*this); | ||
} | ||
|
||
|
@@ -109,7 +107,6 @@ void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel lev | |
if (level != quic::ENCRYPTION_FORWARD_SECURE) { | ||
return; | ||
} | ||
maybeCreateNetworkFilters(); | ||
// This is only reached once, when handshake is done. | ||
raiseConnectionEvent(Network::ConnectionEvent::Connected); | ||
} | ||
|
@@ -118,22 +115,9 @@ bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); } | |
|
||
void EnvoyQuicServerSession::OnTlsHandshakeComplete() { | ||
quic::QuicServerSessionBase::OnTlsHandshakeComplete(); | ||
maybeCreateNetworkFilters(); | ||
raiseConnectionEvent(Network::ConnectionEvent::Connected); | ||
} | ||
|
||
void EnvoyQuicServerSession::maybeCreateNetworkFilters() { | ||
auto proof_source_details = | ||
dynamic_cast<const EnvoyQuicProofSourceDetails*>(GetCryptoStream()->ProofSourceDetails()); | ||
ASSERT(proof_source_details != nullptr, | ||
"ProofSource didn't provide ProofSource::Details. No filter chain will be installed."); | ||
|
||
const bool has_filter_initialized = | ||
listener_config_.filterChainFactory().createNetworkFilterChain( | ||
*this, proof_source_details->filterChain().networkFilterFactories()); | ||
ASSERT(has_filter_initialized); | ||
} | ||
|
||
size_t EnvoyQuicServerSession::WriteHeadersOnHeadersStream( | ||
quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, | ||
const spdy::SpdyStreamPrecedence& precedence, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
#include "common/quic/quic_filter_manager_connection_impl.h" | ||
|
||
#include <initializer_list> | ||
#include <memory> | ||
|
||
namespace Envoy { | ||
|
@@ -65,6 +66,11 @@ void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { | |
// Already detached from quic connection. | ||
return; | ||
} | ||
if (!initialized_) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get here? I thought quicConnection would return null above if not initialized. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! We need to distinguish between already closed and not initialized. In latter case, we need to retain close type and close in OnCanWrite(). |
||
// Delay close till the first OnCanWrite() call. | ||
delayed_close_state_ = DelayedCloseState::CloseAfterFlush; | ||
return; | ||
} | ||
const bool delayed_close_timeout_configured = delayed_close_timeout_.count() > 0; | ||
if (hasDataToWrite() && type != Network::ConnectionCloseType::NoFlush) { | ||
if (delayed_close_timeout_configured) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,16 +73,16 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, | |
} | ||
Ssl::ConnectionInfoConstSharedPtr ssl() const override; | ||
Network::Connection::State state() const override { | ||
if (quic_connection_ != nullptr && quic_connection_->connected()) { | ||
if (!initialized_ || (quic_connection_ != nullptr && quic_connection_->connected())) { | ||
return Network::Connection::State::Open; | ||
} | ||
return Network::Connection::State::Closed; | ||
} | ||
bool connecting() const override { | ||
if (quic_connection_ != nullptr && !quic_connection_->IsHandshakeComplete()) { | ||
return true; | ||
if (initialized_ && (quic_connection_ == nullptr || quic_connection_->IsHandshakeComplete())) { | ||
return false; | ||
} | ||
return false; | ||
return true; | ||
} | ||
void write(Buffer::Instance& /*data*/, bool /*end_stream*/) override { | ||
// All writes should be handled by Quic internally. | ||
|
@@ -143,6 +143,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, | |
absl::optional<std::reference_wrapper<Http::Http3::CodecStats>> codec_stats_; | ||
absl::optional<std::reference_wrapper<const envoy::config::core::v3::Http3ProtocolOptions>> | ||
http3_options_; | ||
// If false, do not call into quic_connection_. | ||
bool initialized_{false}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it makes sense to have a helper for quic_connection() which returned nullptr if not initialized? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactory QuicFilterManagerConnection and EnvoyQuicConnection so that EnvoyQuicConnection doesn't grant access to QuicConnection interface. And added quic_connection() to the subclasses of QuicFilterManagerConnection which returns nullptrl if not initialized. |
||
|
||
private: | ||
friend class Envoy::TestPauseFilterForQuic; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to not special case HTTP/3 here.
How about we add an initialize() or connect() function for the codec client, and do all the work there consistently for H1-H3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting a stand alone interface to be called after construction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I prefer that to H1 and H2 auto-connecting and H3 not doing so. Happy to tag Snow or Matt if you want a second opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with making connect() explicit:
@mattklein123 WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure SGTM!