diff --git a/docs/root/configuration/http/http_conn_man/stats.rst b/docs/root/configuration/http/http_conn_man/stats.rst index 3a4d7a568c82f..a12c2adcc2afa 100644 --- a/docs/root/configuration/http/http_conn_man/stats.rst +++ b/docs/root/configuration/http/http_conn_man/stats.rst @@ -196,11 +196,6 @@ On the upstream side all http3 statistics are rooted at *cluster..http3.* rx_reset, Counter, Total number of reset stream frames received by Envoy tx_reset, Counter, Total number of reset stream frames transmitted by Envoy metadata_not_supported_error, Counter, Total number of metadata dropped during HTTP/3 encoding - quic_version_43, Counter, Total number of quic connections that use transport version 43. This is expected to be removed when this version is deprecated. - quic_version_46, Counter, Total number of quic connections that use transport version 46. This is expected to be removed when this version is deprecated. - quic_version_50, Counter, Total number of quic connections that use transport version 50. This is expected to be removed when this version is deprecated. - quic_version_51, Counter, Total number of quic connections that use transport version 51. This is expected to be removed when this version is deprecated. - quic_version_h3_29, Counter, Total number of quic connections that use transport version h3-29. This is expected to be removed when this version is deprecated. quic_version_rfc_v1, Counter, Total number of quic connections that use transport version rfc-v1. diff --git a/source/common/http/http3/codec_stats.h b/source/common/http/http3/codec_stats.h index 47b69965896e3..fc8355ed1b317 100644 --- a/source/common/http/http3/codec_stats.h +++ b/source/common/http/http3/codec_stats.h @@ -18,11 +18,6 @@ namespace Http3 { COUNTER(rx_reset) \ COUNTER(tx_reset) \ COUNTER(metadata_not_supported_error) \ - COUNTER(quic_version_43) \ - COUNTER(quic_version_46) \ - COUNTER(quic_version_50) \ - COUNTER(quic_version_51) \ - COUNTER(quic_version_h3_29) \ COUNTER(quic_version_rfc_v1) /** diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 840224c2a4829..e0081de6f29b4 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -49,7 +49,7 @@ ActiveQuicListener::ActiveQuicListener( listen_socket, *this, listener_config.udpListenerConfig()->config().downstream_socket_config()), &listener_config), - dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()), + dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedHttp3Versions()), kernel_worker_routing_(kernel_worker_routing), packets_to_read_to_connection_count_ratio_(packets_to_read_to_connection_count_ratio), crypto_server_stream_factory_(crypto_server_stream_factory) { diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 2e796745a4ca4..00d86543436ff 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -64,18 +64,17 @@ createQuicNetworkConnection(Http::PersistentQuicInfo& info, Event::Dispatcher& d if (config == nullptr) { return nullptr; // no secrets available yet. } - + quic::ParsedQuicVersionVector quic_versions = quic::CurrentSupportedHttp3Versions(); + ASSERT(!quic_versions.empty()); auto connection = std::make_unique( quic::QuicUtils::CreateRandomConnectionId(), server_addr, info_impl->conn_helper_, - info_impl->alarm_factory_, quic::ParsedQuicVersionVector{info_impl->supported_versions_[0]}, - local_addr, dispatcher, nullptr); + info_impl->alarm_factory_, quic_versions, local_addr, dispatcher, nullptr); - ASSERT(!info_impl->supported_versions_.empty()); // QUICHE client session always use the 1st version to start handshake. auto ret = std::make_unique( - info_impl->quic_config_, info_impl->supported_versions_, std::move(connection), - info_impl->server_id_, std::move(config), &info_impl->push_promise_index_, dispatcher, - info_impl->buffer_limit_, info_impl->crypto_stream_factory_, quic_stat_names, scope); + info_impl->quic_config_, quic_versions, std::move(connection), info_impl->server_id_, + std::move(config), &info_impl->push_promise_index_, dispatcher, info_impl->buffer_limit_, + info_impl->crypto_stream_factory_, quic_stat_names, scope); return ret; } diff --git a/source/common/quic/client_connection_factory_impl.h b/source/common/quic/client_connection_factory_impl.h index 5267c67b8fd0f..acc1d0cb244c2 100644 --- a/source/common/quic/client_connection_factory_impl.h +++ b/source/common/quic/client_connection_factory_impl.h @@ -40,7 +40,6 @@ struct PersistentQuicInfoImpl : public Http::PersistentQuicInfo { Envoy::Ssl::ClientContextSharedPtr client_context_; // If client context changes, client config will be updated as well. std::shared_ptr client_config_; - const quic::ParsedQuicVersionVector supported_versions_{quic::CurrentSupportedVersions()}; quic::QuicConfig quic_config_; // The cluster buffer limits. const uint32_t buffer_limit_; diff --git a/source/common/quic/codec_impl.cc b/source/common/quic/codec_impl.cc index 546ea7ee0c193..0afcf65b99055 100644 --- a/source/common/quic/codec_impl.cc +++ b/source/common/quic/codec_impl.cc @@ -51,19 +51,11 @@ void QuicHttpServerConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWate } void QuicHttpServerConnectionImpl::shutdownNotice() { - if (quic::VersionUsesHttp3(quic_server_session_.transport_version())) { - quic_server_session_.SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "Server shutdown"); - } else { - ENVOY_CONN_LOG(debug, "Shutdown notice is not propagated to QUIC.", quic_server_session_); - } + quic_server_session_.SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "Server shutdown"); } void QuicHttpServerConnectionImpl::goAway() { - if (quic::VersionUsesHttp3(quic_server_session_.transport_version())) { - quic_server_session_.SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "server shutdown imminent"); - } else { - quic_server_session_.SendGoAway(quic::QUIC_PEER_GOING_AWAY, "server shutdown imminent"); - } + quic_server_session_.SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "server shutdown imminent"); } QuicHttpClientConnectionImpl::QuicHttpClientConnectionImpl( diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 18221eb9bb355..e22515ed303b8 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -51,9 +51,6 @@ void EnvoyQuicClientConnection::processPacket( Network::Address::InstanceConstSharedPtr local_address, Network::Address::InstanceConstSharedPtr peer_address, Buffer::InstancePtr buffer, MonotonicTime receive_time) { - if (!connected()) { - return; - } quic::QuicTime timestamp = quic::QuicTime::Zero() + quic::QuicTime::Delta::FromMicroseconds( diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 2bf290113682f..805afe0c1dceb 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -48,26 +48,10 @@ void EnvoyQuicClientSession::Initialize() { } void EnvoyQuicClientSession::OnCanWrite() { - if (quic::VersionUsesHttp3(transport_version())) { - quic::QuicSpdyClientSession::OnCanWrite(); - } else { - // This will cause header stream flushing. It is the only place to discount bytes buffered in - // header stream from connection watermark buffer during writing. - SendBufferMonitor::ScopedWatermarkBufferUpdater updater(headers_stream(), this); - quic::QuicSpdyClientSession::OnCanWrite(); - } + quic::QuicSpdyClientSession::OnCanWrite(); maybeApplyDelayClosePolicy(); } -void EnvoyQuicClientSession::OnGoAway(const quic::QuicGoAwayFrame& frame) { - ENVOY_CONN_LOG(debug, "GOAWAY received with error {}: {}", *this, - quic::QuicErrorCodeToString(frame.error_code), frame.reason_phrase); - quic::QuicSpdyClientSession::OnGoAway(frame); - if (http_connection_callbacks_ != nullptr) { - http_connection_callbacks_->onGoAway(quicErrorCodeToEnvoyErrorCode(frame.error_code)); - } -} - void EnvoyQuicClientSession::OnHttp3GoAway(uint64_t stream_id) { ENVOY_CONN_LOG(debug, "HTTP/3 GOAWAY received", *this); quic::QuicSpdyClientSession::OnHttp3GoAway(stream_id); @@ -128,25 +112,10 @@ quic::QuicConnection* EnvoyQuicClientSession::quicConnection() { } void EnvoyQuicClientSession::OnTlsHandshakeComplete() { + quic::QuicSpdyClientSession::OnTlsHandshakeComplete(); raiseConnectionEvent(Network::ConnectionEvent::Connected); } -size_t EnvoyQuicClientSession::WriteHeadersOnHeadersStream( - quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - const spdy::SpdyStreamPrecedence& precedence, - quic::QuicReferenceCountedPointer ack_listener) { - ASSERT(!quic::VersionUsesHttp3(transport_version())); - // gQUIC headers are sent on a dedicated stream. Only count the bytes sent against - // connection level watermark buffer. Do not count them into stream level - // watermark buffer, because it is impossible to identify which byte belongs - // to which stream when the buffered bytes are drained in headers stream. - // This updater may be in the scope of another one in OnCanWrite(), in such - // case, this one doesn't update the watermark. - SendBufferMonitor::ScopedWatermarkBufferUpdater updater(headers_stream(), this); - return quic::QuicSpdyClientSession::WriteHeadersOnHeadersStream(id, std::move(headers), fin, - precedence, ack_listener); -} - std::unique_ptr EnvoyQuicClientSession::CreateQuicCryptoStream() { return crypto_stream_factory_.createEnvoyQuicCryptoClientStream( server_id(), this, crypto_config()->proof_verifier()->CreateDefaultContext(), crypto_config(), diff --git a/source/common/quic/envoy_quic_client_session.h b/source/common/quic/envoy_quic_client_session.h index 7239e51850a41..def847ea0eb23 100644 --- a/source/common/quic/envoy_quic_client_session.h +++ b/source/common/quic/envoy_quic_client_session.h @@ -64,13 +64,8 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, quic::ConnectionCloseSource source) override; void Initialize() override; void OnCanWrite() override; - void OnGoAway(const quic::QuicGoAwayFrame& frame) override; void OnHttp3GoAway(uint64_t stream_id) override; void OnTlsHandshakeComplete() override; - size_t WriteHeadersOnHeadersStream( - quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - const spdy::SpdyStreamPrecedence& precedence, - quic::QuicReferenceCountedPointer ack_listener) override; void MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicRstStreamErrorCode error, quic::QuicStreamOffset bytes_written) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; diff --git a/source/common/quic/envoy_quic_dispatcher.h b/source/common/quic/envoy_quic_dispatcher.h index 0864a909f42ff..b429e908d11be 100644 --- a/source/common/quic/envoy_quic_dispatcher.h +++ b/source/common/quic/envoy_quic_dispatcher.h @@ -25,21 +25,16 @@ namespace Envoy { namespace Quic { -// Envoy specific provider of server connection id and decision maker of -// accepting new connection or not. +// Dummy implementation only used by Google Quic. class EnvoyQuicCryptoServerStreamHelper : public quic::QuicCryptoServerStreamBase::Helper { public: - ~EnvoyQuicCryptoServerStreamHelper() override = default; - // quic::QuicCryptoServerStream::Helper bool CanAcceptClientHello(const quic::CryptoHandshakeMessage& /*message*/, const quic::QuicSocketAddress& /*client_address*/, const quic::QuicSocketAddress& /*peer_address*/, const quic::QuicSocketAddress& /*self_address*/, std::string* /*error_details*/) const override { - // TODO(danzh): decide to accept or not based on information from given handshake message, i.e. - // user agent and SNI. - return true; + NOT_REACHED_GCOVR_EXCL_LINE; } }; diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index c9ee45eb12000..67d9e0ce3ce55 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -28,8 +28,24 @@ EnvoyQuicProofSource::GetCertChain(const quic::QuicSocketAddress& server_address const std::string& chain_str = cert_config.certificateChain(); std::stringstream pem_stream(chain_str); std::vector chain = quic::CertificateView::LoadPemFromStream(&pem_stream); - return quic::QuicReferenceCountedPointer( + + quic::QuicReferenceCountedPointer cert_chain( new quic::ProofSource::Chain(chain)); + std::string error_details; + bssl::UniquePtr cert = parseDERCertificate(cert_chain->certs[0], &error_details); + if (cert == nullptr) { + ENVOY_LOG(warn, absl::StrCat("Invalid leaf cert: ", error_details)); + return nullptr; + } + + bssl::UniquePtr pub_key(X509_get_pubkey(cert.get())); + int sign_alg = deduceSignatureAlgorithmFromPublicKey(pub_key.get(), &error_details); + if (sign_alg == 0) { + ENVOY_LOG(warn, absl::StrCat("Failed to deduce signature algorithm from public key: ", + error_details)); + return nullptr; + } + return cert_chain; } void EnvoyQuicProofSource::signPayload( @@ -82,7 +98,7 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre ENVOY_LOG(trace, "Getting cert chain for {}", hostname); // TODO(danzh) modify QUICHE to make quic session or ALPN accessible to avoid hard-coded ALPN. Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket( - listen_socket_.ioHandle(), server_address, client_address, hostname, "h3-29"); + listen_socket_.ioHandle(), server_address, client_address, hostname, "h3"); const Network::FilterChain* filter_chain = filter_chain_manager_.findFilterChain(*connection_socket); diff --git a/source/common/quic/envoy_quic_proof_source_base.cc b/source/common/quic/envoy_quic_proof_source_base.cc index f6adb247d95ed..39c14e29854c0 100644 --- a/source/common/quic/envoy_quic_proof_source_base.cc +++ b/source/common/quic/envoy_quic_proof_source_base.cc @@ -16,59 +16,15 @@ namespace Envoy { namespace Quic { -void EnvoyQuicProofSourceBase::GetProof(const quic::QuicSocketAddress& server_address, - const quic::QuicSocketAddress& client_address, - const std::string& hostname, - const std::string& server_config, +void EnvoyQuicProofSourceBase::GetProof(const quic::QuicSocketAddress& /*server_address*/, + const quic::QuicSocketAddress& /*client_address*/, + const std::string& /*hostname*/, + const std::string& /*server_config*/, quic::QuicTransportVersion /*transport_version*/, - absl::string_view chlo_hash, - std::unique_ptr callback) { - quic::QuicReferenceCountedPointer chain = - GetCertChain(server_address, client_address, hostname); - - if (chain == nullptr || chain->certs.empty()) { - quic::QuicCryptoProof proof; - callback->Run(/*ok=*/false, nullptr, proof, nullptr); - return; - } - size_t payload_size = sizeof(quic::kProofSignatureLabel) + sizeof(uint32_t) + chlo_hash.size() + - server_config.size(); - auto payload = std::make_unique(payload_size); - quic::QuicDataWriter payload_writer(payload_size, payload.get(), - quiche::Endianness::HOST_BYTE_ORDER); - bool success = - payload_writer.WriteBytes(quic::kProofSignatureLabel, sizeof(quic::kProofSignatureLabel)) && - payload_writer.WriteUInt32(chlo_hash.size()) && payload_writer.WriteStringPiece(chlo_hash) && - payload_writer.WriteStringPiece(server_config); - if (!success) { - quic::QuicCryptoProof proof; - callback->Run(/*ok=*/false, nullptr, proof, nullptr); - return; - } - - std::string error_details; - bssl::UniquePtr cert = parseDERCertificate(chain->certs[0], &error_details); - if (cert == nullptr) { - ENVOY_LOG(warn, absl::StrCat("Invalid leaf cert: ", error_details)); - quic::QuicCryptoProof proof; - callback->Run(/*ok=*/false, nullptr, proof, nullptr); - return; - } - - bssl::UniquePtr pub_key(X509_get_pubkey(cert.get())); - int sign_alg = deduceSignatureAlgorithmFromPublicKey(pub_key.get(), &error_details); - if (sign_alg == 0) { - ENVOY_LOG(warn, absl::StrCat("Failed to deduce signature algorithm from public key: ", - error_details)); - quic::QuicCryptoProof proof; - callback->Run(/*ok=*/false, nullptr, proof, nullptr); - return; - } - - auto signature_callback = std::make_unique(std::move(callback), chain); - - signPayload(server_address, client_address, hostname, sign_alg, - absl::string_view(payload.get(), payload_size), std::move(signature_callback)); + absl::string_view /*chlo_hash*/, + std::unique_ptr /*callback*/) { + // Only reachable in Google QUIC which is not supported by Envoy. + NOT_REACHED_GCOVR_EXCL_LINE; } void EnvoyQuicProofSourceBase::ComputeTlsSignature( diff --git a/source/common/quic/envoy_quic_proof_source_base.h b/source/common/quic/envoy_quic_proof_source_base.h index f963edec26e5a..5b263a69d5a30 100644 --- a/source/common/quic/envoy_quic_proof_source_base.h +++ b/source/common/quic/envoy_quic_proof_source_base.h @@ -33,8 +33,6 @@ class EnvoyQuicProofSourceDetails : public quic::ProofSource::Details { public: explicit EnvoyQuicProofSourceDetails(const Network::FilterChain& filter_chain) : filter_chain_(filter_chain) {} - EnvoyQuicProofSourceDetails(const EnvoyQuicProofSourceDetails& other) - : filter_chain_(other.filter_chain_) {} const Network::FilterChain& filterChain() const { return filter_chain_; } @@ -50,8 +48,6 @@ class EnvoyQuicProofSourceBase : public quic::ProofSource, ~EnvoyQuicProofSourceBase() override = default; // quic::ProofSource - // Returns a certs chain and its fake SCT "Fake timestamp" and TLS signature wrapped - // in QuicCryptoProof. void GetProof(const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address, const std::string& hostname, const std::string& server_config, quic::QuicTransportVersion /*transport_version*/, @@ -72,33 +68,6 @@ class EnvoyQuicProofSourceBase : public quic::ProofSource, const std::string& hostname, uint16_t signature_algorithm, absl::string_view in, std::unique_ptr callback) PURE; - -private: - // Used by GetProof() to get signature. - class SignatureCallback : public quic::ProofSource::SignatureCallback { - public: - // TODO(danzh) Pass in Details to retain the certs chain, and quic::ProofSource::Callback to be - // triggered in Run(). - SignatureCallback(std::unique_ptr callback, - quic::QuicReferenceCountedPointer chain) - : callback_(std::move(callback)), chain_(chain) {} - - // quic::ProofSource::SignatureCallback - void Run(bool ok, std::string signature, std::unique_ptr
details) override { - quic::QuicCryptoProof proof; - if (!ok) { - callback_->Run(false, chain_, proof, nullptr); - return; - } - proof.signature = signature; - proof.leaf_cert_scts = "Fake timestamp"; - callback_->Run(true, chain_, proof, std::move(details)); - } - - private: - std::unique_ptr callback_; - quic::QuicReferenceCountedPointer chain_; - }; }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_proof_verifier.cc b/source/common/quic/envoy_quic_proof_verifier.cc index 2a4b80c76e1aa..4a2c3245180dd 100644 --- a/source/common/quic/envoy_quic_proof_verifier.cc +++ b/source/common/quic/envoy_quic_proof_verifier.cc @@ -28,6 +28,13 @@ quic::QuicAsyncStatus EnvoyQuicProofVerifier::VerifyCertChain( sk_X509_push(intermediates.get(), cert.release()); } } + std::unique_ptr cert_view = + quic::CertificateView::ParseSingleCertificate(certs[0]); + ASSERT(cert_view != nullptr); + int sign_alg = deduceSignatureAlgorithmFromPublicKey(cert_view->public_key(), error_details); + if (sign_alg == 0) { + return quic::QUIC_FAILURE; + } // We down cast rather than add verifyCertChain to Envoy::Ssl::Context because // verifyCertChain uses a bunch of SSL-specific structs which we want to keep // out of the interface definition. @@ -37,9 +44,6 @@ quic::QuicAsyncStatus EnvoyQuicProofVerifier::VerifyCertChain( return quic::QUIC_FAILURE; } - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(certs[0]); - ASSERT(cert_view != nullptr); for (const absl::string_view& config_san : cert_view->subject_alt_name_domains()) { if (Extensions::TransportSockets::Tls::DefaultCertValidator::dnsNameMatch(hostname, config_san)) { diff --git a/source/common/quic/envoy_quic_proof_verifier_base.cc b/source/common/quic/envoy_quic_proof_verifier_base.cc index 7f791b0c3f103..0dca3b23c3143 100644 --- a/source/common/quic/envoy_quic_proof_verifier_base.cc +++ b/source/common/quic/envoy_quic_proof_verifier_base.cc @@ -11,59 +11,14 @@ namespace Envoy { namespace Quic { quic::QuicAsyncStatus EnvoyQuicProofVerifierBase::VerifyProof( - const std::string& hostname, const uint16_t port, const std::string& server_config, - quic::QuicTransportVersion /*quic_version*/, absl::string_view chlo_hash, - const std::vector& certs, const std::string& cert_sct, - const std::string& signature, const quic::ProofVerifyContext* context, - std::string* error_details, std::unique_ptr* details, - std::unique_ptr callback) { - if (certs.empty()) { - *error_details = "Received empty cert chain."; - return quic::QUIC_FAILURE; - } - if (!verifySignature(server_config, chlo_hash, certs[0], signature, error_details)) { - return quic::QUIC_FAILURE; - } - - return VerifyCertChain(hostname, port, certs, "", cert_sct, context, error_details, details, - nullptr, std::move(callback)); -} - -bool EnvoyQuicProofVerifierBase::verifySignature(const std::string& server_config, - absl::string_view chlo_hash, - const std::string& cert, - const std::string& signature, - std::string* error_details) { - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(cert); - if (cert_view == nullptr) { - *error_details = "Invalid leaf cert."; - return false; - } - int sign_alg = deduceSignatureAlgorithmFromPublicKey(cert_view->public_key(), error_details); - if (sign_alg == 0) { - return false; - } - - size_t payload_size = sizeof(quic::kProofSignatureLabel) + sizeof(uint32_t) + chlo_hash.size() + - server_config.size(); - auto payload = std::make_unique(payload_size); - quic::QuicDataWriter payload_writer(payload_size, payload.get(), - quiche::Endianness::HOST_BYTE_ORDER); - bool success = - payload_writer.WriteBytes(quic::kProofSignatureLabel, sizeof(quic::kProofSignatureLabel)) && - payload_writer.WriteUInt32(chlo_hash.size()) && payload_writer.WriteStringPiece(chlo_hash) && - payload_writer.WriteStringPiece(server_config); - if (!success) { - *error_details = "QuicPacketWriter error."; - return false; - } - bool valid = cert_view->VerifySignature(absl::string_view(payload.get(), payload_size), signature, - sign_alg); - if (!valid) { - *error_details = "Signature is not valid."; - } - return valid; + const std::string& /*hostname*/, const uint16_t /*port*/, const std::string& /*server_config*/, + quic::QuicTransportVersion /*quic_version*/, absl::string_view /*chlo_hash*/, + const std::vector& /*certs*/, const std::string& /*cert_sct*/, + const std::string& /*signature*/, const quic::ProofVerifyContext* /*context*/, + std::string* /*error_details*/, std::unique_ptr* /*details*/, + std::unique_ptr /*callback*/) { + // Only reachable in Google QUIC which is not supported by Envoy. + NOT_REACHED_GCOVR_EXCL_LINE; } } // namespace Quic diff --git a/source/common/quic/envoy_quic_proof_verifier_base.h b/source/common/quic/envoy_quic_proof_verifier_base.h index e3835b917ddae..45bdd2a4161b7 100644 --- a/source/common/quic/envoy_quic_proof_verifier_base.h +++ b/source/common/quic/envoy_quic_proof_verifier_base.h @@ -27,8 +27,6 @@ class EnvoyQuicProofVerifierBase : public quic::ProofVerifier, ~EnvoyQuicProofVerifierBase() override = default; // quic::ProofVerifier - // Return success if the certs chain is valid and signature of { - // server_config + chlo_hash} is valid. Otherwise failure. quic::QuicAsyncStatus VerifyProof(const std::string& hostname, const uint16_t port, const std::string& server_config, quic::QuicTransportVersion /*quic_version*/, absl::string_view chlo_hash, @@ -38,11 +36,6 @@ class EnvoyQuicProofVerifierBase : public quic::ProofVerifier, std::unique_ptr callback) override; std::unique_ptr CreateDefaultContext() override { return nullptr; } - -protected: - virtual bool verifySignature(const std::string& server_config, absl::string_view chlo_hash, - const std::string& cert, const std::string& signature, - std::string* error_details); }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index ca373f22547a1..15d1d28745dd3 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -98,26 +98,12 @@ void EnvoyQuicServerSession::Initialize() { } void EnvoyQuicServerSession::OnCanWrite() { - if (quic::VersionUsesHttp3(transport_version())) { - quic::QuicServerSessionBase::OnCanWrite(); - } else { - SendBufferMonitor::ScopedWatermarkBufferUpdater updater(headers_stream(), this); - quic::QuicServerSessionBase::OnCanWrite(); - } + quic::QuicServerSessionBase::OnCanWrite(); // Do not update delay close state according to connection level packet egress because that is // equivalent to TCP transport layer egress. But only do so if the session gets chance to write. maybeApplyDelayClosePolicy(); } -void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel level) { - quic::QuicServerSessionBase::SetDefaultEncryptionLevel(level); - if (level != quic::ENCRYPTION_FORWARD_SECURE) { - return; - } - // This is only reached once, when handshake is done. - raiseConnectionEvent(Network::ConnectionEvent::Connected); -} - bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); } const quic::QuicConnection* EnvoyQuicServerSession::quicConnection() const { @@ -133,22 +119,6 @@ void EnvoyQuicServerSession::OnTlsHandshakeComplete() { raiseConnectionEvent(Network::ConnectionEvent::Connected); } -size_t EnvoyQuicServerSession::WriteHeadersOnHeadersStream( - quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - const spdy::SpdyStreamPrecedence& precedence, - quic::QuicReferenceCountedPointer ack_listener) { - ASSERT(!quic::VersionUsesHttp3(transport_version())); - // gQUIC headers are sent on a dedicated stream. Only count the bytes sent against - // connection level watermark buffer. Do not count them into stream level - // watermark buffer, because it is impossible to identify which byte belongs - // to which stream when the buffered bytes are drained in headers stream. - // This updater may be in the scope of another one in OnCanWrite(), in such - // case, this one doesn't update the watermark. - SendBufferMonitor::ScopedWatermarkBufferUpdater updater(headers_stream(), this); - return quic::QuicServerSessionBase::WriteHeadersOnHeadersStream(id, std::move(headers), fin, - precedence, ack_listener); -} - void EnvoyQuicServerSession::MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicRstStreamErrorCode error, quic::QuicStreamOffset bytes_written) { diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index 9e6bedb7aac21..a3a804023aef9 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -70,12 +70,6 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, void MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicRstStreamErrorCode error, quic::QuicStreamOffset bytes_written) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; - // quic::QuicSpdySession - void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override; - size_t WriteHeadersOnHeadersStream( - quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - const spdy::SpdyStreamPrecedence& precedence, - quic::QuicReferenceCountedPointer ack_listener) override; void setHeadersWithUnderscoreAction( envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index aa100414b93be..86ac524b5e660 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -72,14 +72,6 @@ void EnvoyQuicServerStream::encode100ContinueHeaders(const Http::ResponseHeaderM void EnvoyQuicServerStream::encodeHeaders(const Http::ResponseHeaderMap& headers, bool end_stream) { ENVOY_STREAM_LOG(debug, "encodeHeaders (end_stream={}) {}.", *this, end_stream, headers); - // QUICHE guarantees to take all the headers. This could cause infinite data to - // be buffered on headers stream in Google QUIC implementation because - // headers stream doesn't have upper bound for its send buffer. But in IETF - // QUIC implementation this is safe as headers are sent on data stream which - // is bounded by max concurrent streams limited. - // Same vulnerability exists in crypto stream which can infinitely buffer data - // if handshake implementation goes wrong. - // TODO(#8826) Modify QUICHE to have an upper bound for header stream send buffer. // This is counting not serialized bytes in the send buffer. local_end_stream_ = end_stream; SendBufferMonitor::ScopedWatermarkBufferUpdater updater(this, this); @@ -276,8 +268,7 @@ void EnvoyQuicServerStream::OnStreamReset(const quic::QuicRstStreamFrame& frame) ENVOY_STREAM_LOG(debug, "received RESET_STREAM with reset code={}", *this, frame.error_code); stats_.rx_reset_.inc(); bool end_stream_decoded_and_encoded = read_side_closed() && local_end_stream_; - // This closes read side in both Google Quic and IETF Quic, but doesn't close write side in IETF - // Quic. + // This closes read side in IETF Quic, but doesn't close write side. quic::QuicSpdyServerStreamBase::OnStreamReset(frame); ASSERT(read_side_closed()); if (write_side_closed() && !end_stream_decoded_and_encoded) { diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 769f8cf086497..0a42e22cbbe73 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -125,15 +125,6 @@ Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCod } } -Http::GoAwayErrorCode quicErrorCodeToEnvoyErrorCode(quic::QuicErrorCode error) noexcept { - switch (error) { - case quic::QUIC_NO_ERROR: - return Http::GoAwayErrorCode::NoError; - default: - return Http::GoAwayErrorCode::Other; - } -} - Network::ConnectionSocketPtr createConnectionSocket(Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 434fd939572c1..673150cb85197 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -136,10 +136,6 @@ Http::StreamResetReason quicErrorCodeToEnvoyLocalResetReason(quic::QuicErrorCode // Called when underlying QUIC connection is closed by peer. Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCode error); -// Called when a GOAWAY frame is received. -ABSL_MUST_USE_RESULT -Http::GoAwayErrorCode quicErrorCodeToEnvoyErrorCode(quic::QuicErrorCode error) noexcept; - // Create a connection socket instance and apply given socket options to the // socket. IP_PKTINFO and SO_RXQ_OVFL is always set if supported. Network::ConnectionSocketPtr diff --git a/source/common/quic/platform/quiche_flags_impl.cc b/source/common/quic/platform/quiche_flags_impl.cc index a251af757b085..af607a83adc49 100644 --- a/source/common/quic/platform/quiche_flags_impl.cc +++ b/source/common/quic/platform/quiche_flags_impl.cc @@ -32,6 +32,8 @@ absl::flat_hash_map makeFlagMap() { QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_true, true) #undef QUIC_FLAG + // Disable IETF draft 29 implementation. Envoy only supports RFC-v1. + FLAGS_quic_reloadable_flag_quic_disable_version_draft_29->setValue(true); #define QUIC_PROTOCOL_FLAG(type, flag, ...) flags.emplace(FLAGS_##flag->name(), FLAGS_##flag); #include "quiche/quic/core/quic_protocol_flags_list.h" diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index 1532078e2e50d..a7adce01ea084 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -175,27 +175,11 @@ void QuicFilterManagerConnectionImpl::onConnectionCloseEvent( // The connection was closed before it could be used. Stats are not recorded. return; } - switch (version.transport_version) { - case quic::QUIC_VERSION_43: - codec_stats_->quic_version_43_.inc(); - return; - case quic::QUIC_VERSION_46: - codec_stats_->quic_version_46_.inc(); - return; - case quic::QUIC_VERSION_50: - codec_stats_->quic_version_50_.inc(); - return; - case quic::QUIC_VERSION_51: - codec_stats_->quic_version_51_.inc(); - return; - case quic::QUIC_VERSION_IETF_DRAFT_29: - codec_stats_->quic_version_h3_29_.inc(); - return; - case quic::QUIC_VERSION_IETF_RFC_V1: + if (version.transport_version == quic::QUIC_VERSION_IETF_RFC_V1) { codec_stats_->quic_version_rfc_v1_.inc(); - return; - default: - return; + } else { + ENVOY_BUG(false, fmt::format("Unexpected QUIC version {}", + quic::QuicVersionToString(version.transport_version))); } } diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index e1a96ea42b90a..3441f7fc47d9e 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -41,8 +41,6 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, QuicFilterManagerConnectionImpl(QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit); - ~QuicFilterManagerConnectionImpl() override = default; - // Network::FilterManager // Overridden to delegate calls to filter_manager_. void addWriteFilter(Network::WriteFilterSharedPtr filter) override; diff --git a/source/common/quic/send_buffer_monitor.cc b/source/common/quic/send_buffer_monitor.cc index 5a9426db96702..b949f0a897821 100644 --- a/source/common/quic/send_buffer_monitor.cc +++ b/source/common/quic/send_buffer_monitor.cc @@ -7,20 +7,11 @@ SendBufferMonitor::ScopedWatermarkBufferUpdater::ScopedWatermarkBufferUpdater( quic::QuicStream* quic_stream, SendBufferMonitor* send_buffer_monitor) : quic_stream_(quic_stream), old_buffered_bytes_(quic_stream_->BufferedDataBytes()), send_buffer_monitor_(send_buffer_monitor) { - if (!send_buffer_monitor_->is_doing_watermark_accounting_) { - send_buffer_monitor_->is_doing_watermark_accounting_ = true; - count_bytes_ = true; - } else { - ASSERT(static_cast(quic_stream) != static_cast(send_buffer_monitor)); - count_bytes_ = false; - } + ASSERT(!send_buffer_monitor_->is_doing_watermark_accounting_); + send_buffer_monitor_->is_doing_watermark_accounting_ = true; } SendBufferMonitor::ScopedWatermarkBufferUpdater::~ScopedWatermarkBufferUpdater() { - if (!count_bytes_) { - ASSERT(static_cast(quic_stream_) != static_cast(send_buffer_monitor_)); - return; - } // If quic_stream_ is done writing, regards all buffered bytes, if there is any, as drained. uint64_t new_buffered_bytes = quic_stream_->write_side_closed() ? 0u : quic_stream_->BufferedDataBytes(); diff --git a/source/common/quic/send_buffer_monitor.h b/source/common/quic/send_buffer_monitor.h index ea9d99db41faf..db2211bee0397 100644 --- a/source/common/quic/send_buffer_monitor.h +++ b/source/common/quic/send_buffer_monitor.h @@ -32,7 +32,6 @@ class SendBufferMonitor { ~ScopedWatermarkBufferUpdater(); private: - bool count_bytes_{false}; quic::QuicStream* quic_stream_{nullptr}; uint64_t old_buffered_bytes_{0}; SendBufferMonitor* send_buffer_monitor_{nullptr}; diff --git a/test/common/http/conn_pool_grid_test.cc b/test/common/http/conn_pool_grid_test.cc index 89affdb9a4d04..908e82bf1282b 100644 --- a/test/common/http/conn_pool_grid_test.cc +++ b/test/common/http/conn_pool_grid_test.cc @@ -125,7 +125,7 @@ class ConnectivityGridTestBase : public Event::TestUsingSimulatedTime, public te void addHttp3AlternateProtocol() { AlternateProtocolsCacheImpl::Origin origin("https", "hostname", 9000); const std::vector protocols = { - {"h3-29", "", origin.port_, simTime().monotonicTime() + Seconds(5)}}; + {"h3", "", origin.port_, simTime().monotonicTime() + Seconds(5)}}; alternate_protocols_->setAlternatives(origin, protocols); } diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index 13da84735c546..3637e0d9315ec 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -73,21 +73,14 @@ class ActiveQuicListenerFactoryPeer { } }; -class ActiveQuicListenerTest : public QuicMultiVersionTest { +class ActiveQuicListenerTest : public testing::TestWithParam { protected: ActiveQuicListenerTest() - : version_(GetParam().first), api_(Api::createApiForTest(simulated_time_system_)), + : version_(GetParam()), api_(Api::createApiForTest(simulated_time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), clock_(*dispatcher_), local_address_(Network::Test::getCanonicalLoopbackAddress(version_)), - connection_handler_(*dispatcher_, absl::nullopt), quic_version_([]() { - if (GetParam().second == QuicVersionType::GquicQuicCrypto) { - return quic::CurrentSupportedVersionsWithQuicCrypto(); - } - bool use_http3 = GetParam().second == QuicVersionType::Iquic; - SetQuicReloadableFlag(quic_disable_version_draft_29, !use_http3); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !use_http3); - return quic::CurrentSupportedVersions(); - }()[0]), + connection_handler_(*dispatcher_, absl::nullopt), + quic_version_(quic::CurrentSupportedHttp3Versions()[0]), quic_stat_names_(listener_config_.listenerScope().symbolTable()) {} template @@ -209,10 +202,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { void sendCHLO(quic::QuicConnectionId connection_id) { client_sockets_.push_back(std::make_unique(Network::Socket::Type::Datagram, local_address_, nullptr)); - Buffer::OwnedImpl payload = generateChloPacketToSend( - quic_version_, quic_config_, ActiveQuicListenerPeer::cryptoConfig(*quic_listener_), - connection_id, clock_, envoyIpAddressToQuicSocketAddress(local_address_->ip()), - envoyIpAddressToQuicSocketAddress(local_address_->ip()), "test.example.org"); + Buffer::OwnedImpl payload = + generateChloPacketToSend(quic_version_, quic_config_, connection_id); Buffer::RawSliceVector slice = payload.getRawSlices(); ASSERT_EQ(1u, slice.size()); // Send a full CHLO to finish 0-RTT handshake. @@ -332,7 +323,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { }; INSTANTIATE_TEST_SUITE_P(ActiveQuicListenerTests, ActiveQuicListenerTest, - testing::ValuesIn(generateTestParam()), testParamsToString); + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); TEST_P(ActiveQuicListenerTest, ReceiveCHLO) { initialize(); @@ -355,22 +347,14 @@ TEST_P(ActiveQuicListenerTest, ReceiveCHLO) { EXPECT_EQ(quic::kMinimumFlowControlSendWindow, const_cast(session) ->config() ->GetInitialSessionFlowControlWindowToSend()); - // IETF Quic supports low flow control limit. But Google Quic only supports flow control window no - // smaller than 16kB. - if (GetParam().second == QuicVersionType::Iquic) { - EXPECT_EQ(stream_window_size_, const_cast(session) - ->config() - ->GetInitialMaxStreamDataBytesIncomingBidirectionalToSend()); - } else { - EXPECT_EQ(quic::kMinimumFlowControlSendWindow, const_cast(session) - ->config() - ->GetInitialStreamFlowControlWindowToSend()); - } + EXPECT_EQ(stream_window_size_, const_cast(session) + ->config() + ->GetInitialMaxStreamDataBytesIncomingBidirectionalToSend()); readFromClientSockets(); } TEST_P(ActiveQuicListenerTest, ConfigureReasonableInitialFlowControlWindow) { - // These initial flow control windows should be accepted by both Google QUIC and IETF QUIC. + // These initial flow control windows should be accepted by QUIC. connection_window_size_ = 64 * 1024; stream_window_size_ = 32 * 1024; initialize(); @@ -464,7 +448,8 @@ class ActiveQuicListenerEmptyFlagConfigTest : public ActiveQuicListenerTest { INSTANTIATE_TEST_SUITE_P(ActiveQuicListenerEmptyFlagConfigTests, ActiveQuicListenerEmptyFlagConfigTest, - testing::ValuesIn(generateTestParam()), testParamsToString); + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); // Quic listener should be enabled by default, if not enabled explicitly in config. TEST_P(ActiveQuicListenerEmptyFlagConfigTest, ReceiveFullQuicCHLO) { diff --git a/test/common/quic/envoy_quic_client_session_test.cc b/test/common/quic/envoy_quic_client_session_test.cc index 76bb3ebb73bb5..84554a41bd9da 100644 --- a/test/common/quic/envoy_quic_client_session_test.cc +++ b/test/common/quic/envoy_quic_client_session_test.cc @@ -64,16 +64,13 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { using EnvoyQuicClientConnection::connectionStats; }; -class EnvoyQuicClientSessionTest : public testing::TestWithParam { +class EnvoyQuicClientSessionTest : public testing::Test { public: EnvoyQuicClientSessionTest() : api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), quic_version_([]() { - SetQuicReloadableFlag(quic_disable_version_draft_29, !GetParam()); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !GetParam()); - return quic::ParsedVersionOfIndex(quic::CurrentSupportedVersions(), 0); - }()), + alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), + quic_version_({quic::CurrentSupportedHttp3Versions()[0]}), peer_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), 12345)), self_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), @@ -163,10 +160,7 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParam { QuicHttpClientConnectionImpl http_connection_; }; -INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionTests, EnvoyQuicClientSessionTest, - testing::ValuesIn({true, false})); - -TEST_P(EnvoyQuicClientSessionTest, NewStream) { +TEST_F(EnvoyQuicClientSessionTest, NewStream) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -183,7 +177,7 @@ TEST_P(EnvoyQuicClientSessionTest, NewStream) { stream.OnStreamHeaderList(/*fin=*/true, headers.uncompressed_header_bytes(), headers); } -TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { +TEST_F(EnvoyQuicClientSessionTest, PacketLimits) { // We always allow for reading packets, even if there's no stream. EXPECT_EQ(0, envoy_quic_session_.GetNumActiveStreams()); EXPECT_EQ(16, envoy_quic_session_.numPacketsExpectedPerEventLoop()); @@ -222,7 +216,7 @@ TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); } -TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { +TEST_F(EnvoyQuicClientSessionTest, OnResetFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -240,12 +234,12 @@ TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { ->value()); } -TEST_P(EnvoyQuicClientSessionTest, SendResetFrame) { +TEST_F(EnvoyQuicClientSessionTest, SendResetFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); - // G-QUIC or IETF bi-directional stream. + // IETF bi-directional stream. quic::QuicStreamId stream_id = stream.id(); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalReset, _)); EXPECT_CALL(*quic_connection_, SendControlFrame(_)); @@ -257,20 +251,15 @@ TEST_P(EnvoyQuicClientSessionTest, SendResetFrame) { ->value()); } -TEST_P(EnvoyQuicClientSessionTest, OnGoAwayFrame) { +TEST_F(EnvoyQuicClientSessionTest, OnGoAwayFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EXPECT_CALL(http_connection_callbacks_, onGoAway(Http::GoAwayErrorCode::NoError)); - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - envoy_quic_session_.OnHttp3GoAway(4u); - } else { - quic::QuicGoAwayFrame goaway; - quic_connection_->OnGoAwayFrame(goaway); - } + envoy_quic_session_.OnHttp3GoAway(4u); } -TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { +TEST_F(EnvoyQuicClientSessionTest, ConnectionClose) { std::string error_details("dummy details"); quic::QuicErrorCode error(quic::QUIC_INVALID_FRAME_DATA); quic::QuicConnectionCloseFrame frame(quic_version_[0].transport_version, error, @@ -288,7 +277,7 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { ->value()); } -TEST_P(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { +TEST_F(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -304,96 +293,27 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { ->value()); } -class EnvoyQuicClientSessionAllQuicVersionTest - : public testing::TestWithParam { -public: - EnvoyQuicClientSessionAllQuicVersionTest() - : api_(Api::createApiForTest(time_system_)), - dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), - peer_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), - 12345)), - self_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), - 54321)), - quic_connection_(new TestEnvoyQuicClientConnection( - quic::test::TestConnectionId(), connection_helper_, alarm_factory_, writer_, - quic::test::SupportedVersions(GetParam()), *dispatcher_, - createConnectionSocket(peer_addr_, self_addr_, nullptr))), - crypto_config_(std::make_shared( - quic::test::crypto_test_utils::ProofVerifierForTesting())), - quic_stat_names_(store_.symbolTable()), - envoy_quic_session_( - quic_config_, quic::test::SupportedVersions(GetParam()), - std::unique_ptr(quic_connection_), - quic::QuicServerId("example.com", 443, false), crypto_config_, nullptr, *dispatcher_, - /*send_buffer_limit*/ 1024 * 1024, crypto_stream_factory_, quic_stat_names_, store_), - stats_({ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(store_, "http3."), - POOL_GAUGE_PREFIX(store_, "http3."))}), - http_connection_(envoy_quic_session_, http_connection_callbacks_, stats_, http3_options_, - 64 * 1024, 100) { - EXPECT_EQ(time_system_.systemTime(), envoy_quic_session_.streamInfo().startTime()); - EXPECT_EQ(EMPTY_STRING, envoy_quic_session_.nextProtocol()); - EXPECT_EQ(Http::Protocol::Http3, http_connection_.protocol()); - - time_system_.advanceTimeWait(std::chrono::milliseconds(1)); - ON_CALL(writer_, WritePacket(_, _, _, _, _)) - .WillByDefault(testing::Return(quic::WriteResult(quic::WRITE_STATUS_OK, 1))); - } - - void SetUp() override { - envoy_quic_session_.Initialize(); - setQuicConfigWithDefaultValues(envoy_quic_session_.config()); - envoy_quic_session_.OnConfigNegotiated(); - envoy_quic_session_.addConnectionCallbacks(network_connection_callbacks_); - envoy_quic_session_.setConnectionStats( - {read_total_, read_current_, write_total_, write_current_, nullptr, nullptr}); - EXPECT_EQ(&read_total_, &quic_connection_->connectionStats().read_total_); - } - - void TearDown() override { - if (quic_connection_->connected()) { - EXPECT_CALL(*quic_connection_, - SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); - EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); - envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); - } - } - -protected: - Event::SimulatedTimeSystemHelper time_system_; - Api::ApiPtr api_; - Event::DispatcherPtr dispatcher_; - EnvoyQuicConnectionHelper connection_helper_; - EnvoyQuicAlarmFactory alarm_factory_; - testing::NiceMock writer_; - Network::Address::InstanceConstSharedPtr peer_addr_; - Network::Address::InstanceConstSharedPtr self_addr_; - TestEnvoyQuicClientConnection* quic_connection_; - quic::QuicConfig quic_config_; - std::shared_ptr crypto_config_; - TestQuicCryptoClientStreamFactory crypto_stream_factory_; - Stats::IsolatedStoreImpl store_; - QuicStatNames quic_stat_names_; - EnvoyQuicClientSession envoy_quic_session_; - Network::MockConnectionCallbacks network_connection_callbacks_; - Http::MockServerConnectionCallbacks http_connection_callbacks_; - testing::StrictMock read_total_; - testing::StrictMock read_current_; - testing::StrictMock write_total_; - testing::StrictMock write_current_; - Http::Http3::CodecStats stats_; - envoy::config::core::v3::Http3ProtocolOptions http3_options_; - QuicHttpClientConnectionImpl http_connection_; -}; - -INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionAllQuicVersionTests, - EnvoyQuicClientSessionAllQuicVersionTest, - testing::ValuesIn(quic::AllSupportedVersions())); +TEST_F(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { + Http::MockResponseDecoder response_decoder; + Http::MockStreamCallbacks stream_callbacks; + EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); + EXPECT_CALL(*quic_connection_, + SendConnectionClosePacket(quic::QUIC_HANDSHAKE_FAILED, _, "fake handshake time out")); + EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); + EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::ConnectionFailure, _)); + envoy_quic_session_.OnStreamError(quic::QUIC_HANDSHAKE_FAILED, "fake handshake time out"); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + EXPECT_TRUE(stream.write_side_closed() && stream.reading_stopped()); + EXPECT_EQ(1U, + TestUtility::findCounter( + store_, "http3.upstream.tx.quic_connection_close_error_code_QUIC_HANDSHAKE_FAILED") + ->value()); +} -TEST_P(EnvoyQuicClientSessionAllQuicVersionTest, ConnectionClosePopulatesQuicVersionStats) { +TEST_F(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { std::string error_details("dummy details"); quic::QuicErrorCode error(quic::QUIC_INVALID_FRAME_DATA); - quic::QuicConnectionCloseFrame frame(GetParam().transport_version, error, + quic::QuicConnectionCloseFrame frame(quic_version_[0].transport_version, error, quic::NO_IETF_QUIC_ERROR, error_details, /* transport_close_frame_type = */ 0); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); @@ -401,32 +321,7 @@ TEST_P(EnvoyQuicClientSessionAllQuicVersionTest, ConnectionClosePopulatesQuicVer EXPECT_EQ(absl::StrCat(quic::QuicErrorCodeToString(error), " with details: ", error_details), envoy_quic_session_.transportFailureReason()); EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); - std::string quic_version_stat_name; - switch (GetParam().transport_version) { - case quic::QUIC_VERSION_43: - quic_version_stat_name = "43"; - break; - case quic::QUIC_VERSION_46: - quic_version_stat_name = "46"; - break; - case quic::QUIC_VERSION_50: - quic_version_stat_name = "50"; - break; - case quic::QUIC_VERSION_51: - quic_version_stat_name = "51"; - break; - case quic::QUIC_VERSION_IETF_DRAFT_29: - quic_version_stat_name = "h3_29"; - break; - case quic::QUIC_VERSION_IETF_RFC_V1: - quic_version_stat_name = "rfc_v1"; - break; - default: - break; - } - EXPECT_EQ(1U, TestUtility::findCounter( - store_, absl::StrCat("http3.quic_version_", quic_version_stat_name)) - ->value()); + EXPECT_EQ(1U, TestUtility::findCounter(store_, "http3.quic_version_rfc_v1")->value()); } } // namespace Quic diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index 5de4695bb4794..a1e0adb8b37ef 100644 --- a/test/common/quic/envoy_quic_client_stream_test.cc +++ b/test/common/quic/envoy_quic_client_stream_test.cc @@ -24,16 +24,13 @@ class MockDelegate : public PacketsToReadDelegate { MOCK_METHOD(size_t, numPacketsExpectedPerEventLoop, ()); }; -class EnvoyQuicClientStreamTest : public testing::TestWithParam { +class EnvoyQuicClientStreamTest : public testing::Test { public: EnvoyQuicClientStreamTest() : api_(Api::createApiForTest()), dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), quic_version_([]() { - SetQuicReloadableFlag(quic_disable_version_draft_29, !GetParam()); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !GetParam()); - return quic::CurrentSupportedVersions()[0]; - }()), + alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), + quic_version_(quic::CurrentSupportedHttp3Versions()[0]), peer_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), 12345)), self_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), @@ -46,9 +43,8 @@ class EnvoyQuicClientStreamTest : public testing::TestWithParam { std::unique_ptr(quic_connection_), *dispatcher_, quic_config_.GetInitialStreamFlowControlWindowToSend() * 2, crypto_stream_factory_), - stream_id_(quic::VersionUsesHttp3(quic_version_.transport_version) ? 4u : 5u), - stats_({ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(scope_, "http3."), - POOL_GAUGE_PREFIX(scope_, "http3."))}), + stream_id_(4u), stats_({ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(scope_, "http3."), + POOL_GAUGE_PREFIX(scope_, "http3."))}), quic_stream_(new EnvoyQuicClientStream(stream_id_, &quic_session_, quic::BIDIRECTIONAL, stats_, http3_options_)), request_headers_{{":authority", host_}, {":method", "POST"}, {":path", "/"}}, @@ -77,19 +73,8 @@ class EnvoyQuicClientStreamTest : public testing::TestWithParam { setQuicConfigWithDefaultValues(quic_session_.config()); quic_session_.OnConfigNegotiated(); quic_connection_->setUpConnectionSocket(delegate_); - response_headers_.OnHeaderBlockStart(); - response_headers_.OnHeader(":status", "200"); - response_headers_.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, - /*compressed_header_bytes=*/0); spdy_response_headers_[":status"] = "200"; - trailers_.OnHeaderBlockStart(); - trailers_.OnHeader("key1", "value1"); - if (!quic::VersionUsesHttp3(quic_version_.transport_version)) { - // ":final-offset" is required and stripped off by quic. - trailers_.OnHeader(":final-offset", absl::StrCat("", response_body_.length())); - } - trailers_.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); spdy_trailers_["key1"] = "value1"; } @@ -101,13 +86,6 @@ class EnvoyQuicClientStreamTest : public testing::TestWithParam { } } - std::string bodyToStreamPayload(const std::string& body) { - if (!quic::VersionUsesHttp3(quic_version_.transport_version)) { - return body; - } - return bodyToHttp3StreamPayload(body); - } - size_t receiveResponse(const std::string& payload, bool fin, size_t offset = 0) { EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([](const Http::ResponseHeaderMapPtr& headers, bool) { @@ -119,21 +97,12 @@ class EnvoyQuicClientStreamTest : public testing::TestWithParam { EXPECT_EQ(payload, buffer.toString()); EXPECT_EQ(fin, finished_reading); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), - bodyToStreamPayload(payload)); - quic::QuicStreamFrame frame(stream_id_, fin, offset, data); - quic_stream_->OnStreamFrame(frame); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - return offset + data.length(); - } - quic_stream_->OnStreamHeaderList(/*fin=*/false, response_headers_.uncompressed_header_bytes(), - response_headers_); - - quic::QuicStreamFrame frame(stream_id_, fin, offset, payload); + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), + bodyToHttp3StreamPayload(payload)); + quic::QuicStreamFrame frame(stream_id_, fin, offset, data); quic_stream_->OnStreamFrame(frame); EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - return offset + payload.length(); + return offset + data.length(); } protected: @@ -160,39 +129,28 @@ class EnvoyQuicClientStreamTest : public testing::TestWithParam { std::string host_{"www.abc.com"}; Http::TestRequestHeaderMapImpl request_headers_; Http::TestRequestTrailerMapImpl request_trailers_; - quic::QuicHeaderList response_headers_; spdy::SpdyHeaderBlock spdy_response_headers_; - quic::QuicHeaderList trailers_; spdy::SpdyHeaderBlock spdy_trailers_; Buffer::OwnedImpl request_body_{"Hello world"}; std::string response_body_{"OK\n"}; }; -INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientStreamTests, EnvoyQuicClientStreamTest, - testing::ValuesIn({true, false})); - -TEST_P(EnvoyQuicClientStreamTest, GetRequestAndHeaderOnlyResponse) { +TEST_F(EnvoyQuicClientStreamTest, GetRequestAndHeaderOnlyResponse) { const auto result = quic_stream_->encodeHeaders(request_headers_, /*end_stream=*/true); EXPECT_TRUE(result.ok()); - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/!quic::VersionUsesHttp3( - quic_version_.transport_version))) + EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([](const Http::ResponseHeaderMapPtr& headers, bool) { EXPECT_EQ("200", headers->getStatusValue()); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); - quic::QuicStreamFrame frame(stream_id_, true, 0, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/true, response_headers_.uncompressed_header_bytes(), - response_headers_); - } + EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); + quic::QuicStreamFrame frame(stream_id_, true, 0, payload); + quic_stream_->OnStreamFrame(frame); EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); } -TEST_P(EnvoyQuicClientStreamTest, PostRequestAndResponse) { +TEST_F(EnvoyQuicClientStreamTest, PostRequestAndResponse) { EXPECT_EQ(absl::nullopt, quic_stream_->http1StreamEncoderOptions()); const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); @@ -207,25 +165,19 @@ TEST_P(EnvoyQuicClientStreamTest, PostRequestAndResponse) { EXPECT_EQ("value1", headers->get(key1)[0]->value().getStringView()); EXPECT_TRUE(headers->get(key2).empty()); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string more_response_body{"bbb"}; - EXPECT_CALL(stream_decoder_, decodeData(_, _)) - .WillOnce(Invoke([&](Buffer::Instance& buffer, bool finished_reading) { - EXPECT_EQ(more_response_body, buffer.toString()); - EXPECT_EQ(false, finished_reading); - })); - std::string payload = absl::StrCat(bodyToStreamPayload(more_response_body), - spdyHeaderToHttp3StreamPayload(spdy_trailers_)); - quic::QuicStreamFrame frame(stream_id_, true, offset, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList( - /*fin=*/!quic::VersionUsesHttp3(quic_version_.transport_version), - trailers_.uncompressed_header_bytes(), trailers_); - } + std::string more_response_body{"bbb"}; + EXPECT_CALL(stream_decoder_, decodeData(_, _)) + .WillOnce(Invoke([&](Buffer::Instance& buffer, bool finished_reading) { + EXPECT_EQ(more_response_body, buffer.toString()); + EXPECT_EQ(false, finished_reading); + })); + std::string payload = absl::StrCat(bodyToHttp3StreamPayload(more_response_body), + spdyHeaderToHttp3StreamPayload(spdy_trailers_)); + quic::QuicStreamFrame frame(stream_id_, true, offset, payload); + quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, PostRequestAnd100Continue) { +TEST_F(EnvoyQuicClientStreamTest, PostRequestAnd100Continue) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); @@ -245,94 +197,33 @@ TEST_P(EnvoyQuicClientStreamTest, PostRequestAnd100Continue) { // Receive several 10x headers, only the first 100 Continue header should be // delivered. for (const std::string& status : {"100", "103", "100"}) { - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - spdy::SpdyHeaderBlock continue_header; - continue_header[":status"] = status; - continue_header["i"] = absl::StrCat("", i++); - std::string data = spdyHeaderToHttp3StreamPayload(continue_header); - quic::QuicStreamFrame frame(stream_id_, false, offset, data); - quic_stream_->OnStreamFrame(frame); - offset += data.length(); - } else { - quic::QuicHeaderList continue_header; - continue_header.OnHeaderBlockStart(); - continue_header.OnHeader(":status", status); - continue_header.OnHeader("i", absl::StrCat("", i++)); - continue_header.OnHeaderBlockEnd(0, 0); - quic_stream_->OnStreamHeaderList(/*fin=*/false, continue_header.uncompressed_header_bytes(), - continue_header); - } + spdy::SpdyHeaderBlock continue_header; + continue_header[":status"] = status; + continue_header["i"] = absl::StrCat("", i++); + std::string data = spdyHeaderToHttp3StreamPayload(continue_header); + quic::QuicStreamFrame frame(stream_id_, false, offset, data); + quic_stream_->OnStreamFrame(frame); + offset += data.length(); } receiveResponse(response_body_, true, offset); } -TEST_P(EnvoyQuicClientStreamTest, ResetUpon101SwitchProtocol) { +TEST_F(EnvoyQuicClientStreamTest, ResetUpon101SwitchProtocol) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::ProtocolError, _)); // Receive several 10x headers, only the first 100 Continue header should be // delivered. - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - spdy::SpdyHeaderBlock continue_header; - continue_header[":status"] = "101"; - std::string data = spdyHeaderToHttp3StreamPayload(continue_header); - quic::QuicStreamFrame frame(stream_id_, false, 0u, data); - quic_stream_->OnStreamFrame(frame); - } else { - quic::QuicHeaderList continue_header; - continue_header.OnHeaderBlockStart(); - continue_header.OnHeader(":status", "101"); - continue_header.OnHeaderBlockEnd(0, 0); - quic_stream_->OnStreamHeaderList(/*fin=*/false, continue_header.uncompressed_header_bytes(), - continue_header); - } -} - -TEST_P(EnvoyQuicClientStreamTest, OutOfOrderTrailers) { - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); - return; - } - const auto result = quic_stream_->encodeHeaders(request_headers_, true); - EXPECT_TRUE(result.ok()); - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke([](const Http::ResponseHeaderMapPtr& headers, bool) { - EXPECT_EQ("200", headers->getStatusValue()); - })); - quic_stream_->OnStreamHeaderList(/*fin=*/false, response_headers_.uncompressed_header_bytes(), - response_headers_); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - - // Trailer should be delivered to HCM later after body arrives. - quic_stream_->OnStreamHeaderList(/*fin=*/true, trailers_.uncompressed_header_bytes(), trailers_); - - quic::QuicStreamFrame frame(stream_id_, false, 0, response_body_); - EXPECT_CALL(stream_decoder_, decodeData(_, _)) - .Times(testing::AtMost(2)) - .WillOnce(Invoke([this](Buffer::Instance& buffer, bool finished_reading) { - EXPECT_EQ(response_body_, buffer.toString()); - EXPECT_FALSE(finished_reading); - })) - // Depends on QUIC version, there may be an empty STREAM_FRAME with FIN. But - // since there is trailers, finished_reading should always be false. - .WillOnce(Invoke([](Buffer::Instance& buffer, bool finished_reading) { - EXPECT_FALSE(finished_reading); - EXPECT_EQ(0, buffer.length()); - })); - - EXPECT_CALL(stream_decoder_, decodeTrailers_(_)) - .WillOnce(Invoke([](const Http::ResponseTrailerMapPtr& headers) { - Http::LowerCaseString key1("key1"); - Http::LowerCaseString key2(":final-offset"); - EXPECT_EQ("value1", headers->get(key1)[0]->value().getStringView()); - EXPECT_TRUE(headers->get(key2).empty()); - })); + spdy::SpdyHeaderBlock continue_header; + continue_header[":status"] = "101"; + std::string data = spdyHeaderToHttp3StreamPayload(continue_header); + quic::QuicStreamFrame frame(stream_id_, false, 0u, data); quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, WatermarkSendBuffer) { +TEST_F(EnvoyQuicClientStreamTest, WatermarkSendBuffer) { // Bump connection flow control window large enough not to cause connection // level flow control blocked. quic::QuicWindowUpdateFrame window_update( @@ -388,14 +279,8 @@ TEST_P(EnvoyQuicClientStreamTest, WatermarkSendBuffer) { } // Tests that headers and trailers buffered in send buffer contribute towards buffer watermark -// limits. Only IETF QUIC writes them on data stream, gQUIC writes them on dedicated headers stream -// and only contributes to connection watermark buffer. -TEST_P(EnvoyQuicClientStreamTest, HeadersContributeToWatermarkIquic) { - if (!quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); - return; - } - +// limits. +TEST_F(EnvoyQuicClientStreamTest, HeadersContributeToWatermark) { // Bump connection flow control window large enough not to cause connection level flow control // blocked quic::QuicWindowUpdateFrame window_update( @@ -464,48 +349,39 @@ TEST_P(EnvoyQuicClientStreamTest, HeadersContributeToWatermarkIquic) { EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); } -TEST_P(EnvoyQuicClientStreamTest, ResetStream) { - EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); - quic_stream_->resetStream(Http::StreamResetReason::LocalReset); +TEST_F(EnvoyQuicClientStreamTest, ResetStream) { + EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::ConnectionFailure, _)); + quic_stream_->resetStream(Http::StreamResetReason::ConnectionFailure); EXPECT_TRUE(quic_stream_->rst_sent()); } -TEST_P(EnvoyQuicClientStreamTest, ReceiveResetStream) { +TEST_F(EnvoyQuicClientStreamTest, ReceiveResetStream) { EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::RemoteReset, _)); quic_stream_->OnStreamReset(quic::QuicRstStreamFrame( quic::kInvalidControlFrameId, quic_stream_->id(), quic::QUIC_STREAM_NO_ERROR, 0)); EXPECT_TRUE(quic_stream_->rst_received()); } -TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingHeader) { +TEST_F(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingHeader) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); quic_stream_->encodeData(request_body_, true); - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/!quic::VersionUsesHttp3( - quic_version_.transport_version))) + EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([this](const Http::ResponseHeaderMapPtr&, bool) { quic_connection_->CloseConnection( quic::QUIC_NO_ERROR, "Closed in decodeHeaders", quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - // onResetStream() callback should be triggered because end_stream is - // not decoded with header. - EXPECT_CALL(stream_callbacks_, - onResetStream(Http::StreamResetReason::ConnectionTermination, _)); - std::string data = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); - quic::QuicStreamFrame frame(stream_id_, true, 0, data); - quic_stream_->OnStreamFrame(frame); - } else { - // onResetStream() callback shouldn't be triggered because end_stream is - // already decoded. - quic_stream_->OnStreamHeaderList(/*fin=*/true, response_headers_.uncompressed_header_bytes(), - response_headers_); - } + // onResetStream() callback should be triggered because end_stream is + // not decoded with header. + EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::ConnectionTermination, _)); + std::string data = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); + quic::QuicStreamFrame frame(stream_id_, true, 0, data); + quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithEndStream) { +TEST_F(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithEndStream) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); quic_stream_->encodeData(request_body_, true); @@ -518,21 +394,13 @@ TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithEndStream quic::QUIC_NO_ERROR, "Closed in decodeDdata", quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), - bodyToStreamPayload(response_body_)); - quic::QuicStreamFrame frame(stream_id_, true, 0, data); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/false, response_headers_.uncompressed_header_bytes(), - response_headers_); - - quic::QuicStreamFrame frame(stream_id_, true, 0, response_body_); - quic_stream_->OnStreamFrame(frame); - } + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), + bodyToHttp3StreamPayload(response_body_)); + quic::QuicStreamFrame frame(stream_id_, true, 0, data); + quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithTrailer) { +TEST_F(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithTrailer) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); quic_stream_->encodeData(request_body_, true); @@ -547,25 +415,14 @@ TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingDataWithTrailer) EXPECT_TRUE(quic_stream_->read_side_closed()); })); EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::ConnectionTermination, _)); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), - bodyToStreamPayload(response_body_), - spdyHeaderToHttp3StreamPayload(spdy_trailers_)); - quic::QuicStreamFrame frame(stream_id_, true, 0, data); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/false, response_headers_.uncompressed_header_bytes(), - response_headers_); - - quic::QuicStreamFrame frame(stream_id_, /*fin=*/false, 0, response_body_); - quic_stream_->OnStreamFrame(frame); - quic_stream_->OnStreamHeaderList( - /*fin=*/!quic::VersionUsesHttp3(quic_version_.transport_version), - trailers_.uncompressed_header_bytes(), trailers_); - } + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), + bodyToHttp3StreamPayload(response_body_), + spdyHeaderToHttp3StreamPayload(spdy_trailers_)); + quic::QuicStreamFrame frame(stream_id_, true, 0, data); + quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingTrailer) { +TEST_F(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingTrailer) { const auto result = quic_stream_->encodeHeaders(request_headers_, true); EXPECT_TRUE(result.ok()); @@ -577,18 +434,12 @@ TEST_P(EnvoyQuicClientStreamTest, CloseConnectionDuringDecodingTrailer) { quic::QUIC_NO_ERROR, "Closed in decodeTrailers", quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers_); - quic::QuicStreamFrame frame(stream_id_, true, offset, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList( - /*fin=*/!quic::VersionUsesHttp3(quic_version_.transport_version), - trailers_.uncompressed_header_bytes(), trailers_); - } + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers_); + quic::QuicStreamFrame frame(stream_id_, true, offset, payload); + quic_stream_->OnStreamFrame(frame); } -TEST_P(EnvoyQuicClientStreamTest, MetadataNotSupported) { +TEST_F(EnvoyQuicClientStreamTest, MetadataNotSupported) { Http::MetadataMap metadata_map = {{"key", "value"}}; Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); Http::MetadataMapVector metadata_map_vector; @@ -599,25 +450,20 @@ TEST_P(EnvoyQuicClientStreamTest, MetadataNotSupported) { } // Tests that posted stream block callback won't cause use-after-free crash. -TEST_P(EnvoyQuicClientStreamTest, ReadDisabledBeforeClose) { +TEST_F(EnvoyQuicClientStreamTest, ReadDisabledBeforeClose) { const auto result = quic_stream_->encodeHeaders(request_headers_, /*end_stream=*/true); EXPECT_TRUE(result.ok()); - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/!quic::VersionUsesHttp3( - quic_version_.transport_version))) + EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([this](const Http::ResponseHeaderMapPtr& headers, bool) { EXPECT_EQ("200", headers->getStatusValue()); quic_stream_->readDisable(true); })); - if (quic_version_.UsesHttp3()) { - EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); - quic::QuicStreamFrame frame(stream_id_, true, 0, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/true, response_headers_.uncompressed_header_bytes(), - response_headers_); - } + EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_response_headers_); + quic::QuicStreamFrame frame(stream_id_, true, 0, payload); + quic_stream_->OnStreamFrame(frame); + // Reset to close the stream. EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); quic_stream_->resetStream(Http::StreamResetReason::LocalReset); @@ -626,5 +472,25 @@ TEST_P(EnvoyQuicClientStreamTest, ReadDisabledBeforeClose) { dispatcher_->run(Event::Dispatcher::RunType::NonBlock); } +TEST_F(EnvoyQuicClientStreamTest, MaxIncomingHeadersCount) { + quic_session_.setMaxIncomingHeadersCount(100); + const auto result = quic_stream_->encodeHeaders(request_headers_, false); + EXPECT_TRUE(result.ok()); + quic_stream_->encodeData(request_body_, true); + + // Receive more response headers than allowed. Such response headers shouldn't be delivered to + // stream decoder. + EXPECT_CALL(stream_decoder_, decodeHeaders_(_, _)).Times(0u); + EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); + for (size_t i = 0; i < 101; ++i) { + spdy_response_headers_[absl::StrCat("key", i)] = absl::StrCat("value", i); + } + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_response_headers_), + bodyToHttp3StreamPayload(response_body_), + spdyHeaderToHttp3StreamPayload(spdy_trailers_)); + quic::QuicStreamFrame frame(stream_id_, true, 0, data); + quic_stream_->OnStreamFrame(frame); +} + } // namespace Quic } // namespace Envoy diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index 8260ef7b6e833..ffe9ea95ec22d 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -48,11 +48,11 @@ namespace { const size_t kNumSessionsToCreatePerLoopForTests = 16; } -class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, +class EnvoyQuicDispatcherTest : public testing::TestWithParam, protected Logger::Loggable { public: EnvoyQuicDispatcherTest() - : version_(GetParam().first), api_(Api::createApiForTest(time_system_)), + : version_(GetParam()), api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), listen_socket_(std::make_unique>>( @@ -61,15 +61,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, crypto_config_(quic::QuicCryptoServerConfig::TESTING, quic::QuicRandom::GetInstance(), std::unique_ptr(proof_source_), quic::KeyExchangeSource::Default()), - version_manager_([]() { - if (GetParam().second == QuicVersionType::GquicQuicCrypto) { - return quic::CurrentSupportedVersionsWithQuicCrypto(); - } - bool use_http3 = GetParam().second == QuicVersionType::Iquic; - SetQuicReloadableFlag(quic_disable_version_draft_29, !use_http3); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !use_http3); - return quic::CurrentSupportedVersions(); - }()), + version_manager_(quic::CurrentSupportedHttp3Versions()), quic_version_(version_manager_.GetSupportedVersions()[0]), listener_stats_({ALL_LISTENER_STATS(POOL_COUNTER(listener_config_.listenerScope()), POOL_GAUGE(listener_config_.listenerScope()), @@ -114,10 +106,8 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, void processValidChloPacket(const quic::QuicSocketAddress& peer_addr) { // Create a Quic Crypto or TLS1.3 CHLO packet. EnvoyQuicClock clock(*dispatcher_); - Buffer::OwnedImpl payload = generateChloPacketToSend( - quic_version_, quic_config_, crypto_config_, connection_id_, clock, - envoyIpAddressToQuicSocketAddress(listen_socket_->addressProvider().localAddress()->ip()), - peer_addr, "test.example.org"); + Buffer::OwnedImpl payload = + generateChloPacketToSend(quic_version_, quic_config_, connection_id_); Buffer::RawSliceVector slice = payload.getRawSlices(); ASSERT(slice.size() == 1); auto encrypted_packet = std::make_unique( @@ -198,17 +188,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, EXPECT_CALL(listener_config_, filterChainManager()).WillOnce(ReturnRef(filter_chain_manager)); EXPECT_CALL(filter_chain_manager, findFilterChain(_)) .WillOnce(Invoke([this](const Network::ConnectionSocket& socket) { - switch (GetParam().second) { - case QuicVersionType::GquicQuicCrypto: - EXPECT_EQ("", socket.requestedApplicationProtocols()[0]); - break; - case QuicVersionType::GquicTls: - EXPECT_EQ("h3-T051", socket.requestedApplicationProtocols()[0]); - break; - case QuicVersionType::Iquic: - EXPECT_EQ("h3", socket.requestedApplicationProtocols()[0]); - break; - } + EXPECT_EQ("h3", socket.requestedApplicationProtocols()[0]); EXPECT_EQ("test.example.org", socket.requestedServerName()); return &proof_source_->filterChain(); })); @@ -230,10 +210,6 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, EXPECT_CALL(*read_filter, onNewConnection()) // Stop iteration to avoid calling getRead/WriteBuffer(). .WillOnce(Return(Network::FilterStatus::StopIteration)); - if (!quicVersionUsesTls()) { - // The test utility can't generate 0-RTT packet for Quic TLS handshake yet. - EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); - } processValidChloPacketAndCheckStatus(should_buffer); EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::LocalClose)); @@ -241,8 +217,6 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, envoy_quic_dispatcher_.Shutdown(); } - bool quicVersionUsesTls() { return quic_version_.UsesTls(); } - protected: Network::Address::IpVersion version_; Event::SimulatedTimeSystemHelper time_system_; @@ -266,7 +240,8 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, }; INSTANTIATE_TEST_SUITE_P(EnvoyQuicDispatcherTests, EnvoyQuicDispatcherTest, - testing::ValuesIn(generateTestParam()), testParamsToString); + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); TEST_P(EnvoyQuicDispatcherTest, CreateNewConnectionUponCHLO) { processValidChloPacketAndInitializeFilters(false); @@ -310,10 +285,6 @@ TEST_P(EnvoyQuicDispatcherTest, CloseConnectionDuringFilterInstallation) { // Stop iteration to avoid calling getRead/WriteBuffer(). .WillOnce(Return(Network::FilterStatus::StopIteration)); - if (!quicVersionUsesTls()) { - EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); - } - EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::LocalClose)); quic::QuicSocketAddress peer_addr(version_ == Network::Address::IpVersion::v4 ? quic::QuicIpAddress::Loopback4() diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index efcd16337872f..1d355b77019a4 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -12,6 +12,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "quiche/quic/core/crypto/certificate_view.h" #include "quiche/quic/test_tools/test_certificates.h" using testing::Invoke; @@ -22,13 +23,9 @@ namespace Envoy { namespace Quic { -class TestGetProofCallback : public quic::ProofSource::Callback { +class SignatureVerifier { public: - TestGetProofCallback(bool& called, bool should_succeed, const std::string& server_config, - quic::QuicTransportVersion& version, absl::string_view chlo_hash, - Network::FilterChain& filter_chain) - : called_(called), should_succeed_(should_succeed), server_config_(server_config), - version_(version), chlo_hash_(chlo_hash), expected_filter_chain_(filter_chain) { + SignatureVerifier() { ON_CALL(client_context_config_, cipherSuites) .WillByDefault(ReturnRef( Extensions::TransportSockets::Tls::ClientContextConfigImpl::DEFAULT_CIPHER_SUITES)); @@ -75,34 +72,29 @@ class TestGetProofCallback : public quic::ProofSource::Callback { verifier_ = std::make_unique(std::move(context)); } - // quic::ProofSource::Callback - void Run(bool ok, const quic::QuicReferenceCountedPointer& chain, - const quic::QuicCryptoProof& proof, - std::unique_ptr details) override { - called_ = true; - if (!should_succeed_) { - EXPECT_FALSE(ok); - return; - }; - EXPECT_TRUE(ok); - EXPECT_EQ(2, chain->certs.size()); + void + verifyCertsAndSignature(const quic::QuicReferenceCountedPointer& chain, + const std::string& payload, const std::string& signature) { + const std::string& leaf = chain->certs[0]; + std::unique_ptr cert_view = + quic::CertificateView::ParseSingleCertificate(leaf); + ASSERT_NE(cert_view, nullptr); + std::string error_details; + int sign_alg = deduceSignatureAlgorithmFromPublicKey(cert_view->public_key(), &error_details); + EXPECT_NE(sign_alg, 0); + EXPECT_TRUE(cert_view->VerifySignature(payload, signature, sign_alg)); + std::string error; EXPECT_EQ(quic::QUIC_SUCCESS, - verifier_->VerifyProof("www.example.org", 54321, server_config_, version_, chlo_hash_, - chain->certs, proof.leaf_cert_scts, proof.signature, nullptr, - &error, nullptr, nullptr)) + verifier_->VerifyCertChain("www.example.org", 54321, chain->certs, + /*ocsp_response=*/"", /*cert_sct=*/"Fake SCT", + /*context=*/nullptr, &error, + /*details=*/nullptr, /*out_alert=*/nullptr, + /*callback=*/nullptr)) << error; - EXPECT_EQ(&expected_filter_chain_, - &static_cast(details.get())->filterChain()); } private: - bool& called_; - bool should_succeed_; - const std::string& server_config_; - const quic::QuicTransportVersion& version_; - absl::string_view chlo_hash_; - Network::FilterChain& expected_filter_chain_; NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; @@ -112,18 +104,29 @@ class TestGetProofCallback : public quic::ProofSource::Callback { class TestSignatureCallback : public quic::ProofSource::SignatureCallback { public: - TestSignatureCallback(bool expect_success) : expect_success_(expect_success) {} - ~TestSignatureCallback() override { EXPECT_TRUE(run_called_); } + TestSignatureCallback(bool expect_success, Network::FilterChain& filter_chain, + std::string& signature) + : expect_success_(expect_success), signature_(signature), + expected_filter_chain_(filter_chain) {} + ~TestSignatureCallback() override { EXPECT_TRUE(called_); } // quic::ProofSource::SignatureCallback - void Run(bool ok, std::string, std::unique_ptr) override { + void Run(bool ok, std::string signature, + std::unique_ptr details) override { + called_ = true; EXPECT_EQ(expect_success_, ok); - run_called_ = true; + if (ok) { + signature_ = signature; + EXPECT_EQ(&expected_filter_chain_, + &static_cast(details.get())->filterChain()); + } } private: bool expect_success_; - bool run_called_{false}; + bool called_; + std::string& signature_; + Network::FilterChain& expected_filter_chain_; }; class EnvoyQuicProofSourceTest : public ::testing::Test { @@ -152,7 +155,7 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { EXPECT_EQ(*quicAddressToEnvoyAddressInstance(client_address_), *connection_socket.addressProvider().remoteAddress()); EXPECT_EQ("quic", connection_socket.detectedTransportProtocol()); - EXPECT_EQ("h3-29", connection_socket.requestedApplicationProtocols()[0]); + EXPECT_EQ("h3", connection_socket.requestedApplicationProtocols()[0]); return &filter_chain_; })); EXPECT_CALL(filter_chain_, transportSocketFactory()) @@ -168,15 +171,6 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { } } - void testGetProof(bool expect_success) { - bool called = false; - auto callback = std::make_unique(called, expect_success, server_config_, - version_, chlo_hash_, filter_chain_); - proof_source_.GetProof(server_address_, client_address_, hostname_, server_config_, version_, - chlo_hash_, std::move(callback)); - EXPECT_TRUE(called); - } - protected: std::string hostname_{"www.fake.com"}; quic::QuicSocketAddress server_address_; @@ -197,17 +191,32 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { EnvoyQuicProofSource proof_source_; }; -TEST_F(EnvoyQuicProofSourceTest, TestGetProof) { +TEST_F(EnvoyQuicProofSourceTest, TestGetCerChainAndSignatureAndVerify) { expectCertChainAndPrivateKey(expected_certs_, true); - testGetProof(true); + quic::QuicReferenceCountedPointer chain = + proof_source_.GetCertChain(server_address_, client_address_, hostname_); + EXPECT_EQ(2, chain->certs.size()); + + std::string error_details; + bssl::UniquePtr cert = parseDERCertificate(chain->certs[0], &error_details); + EXPECT_NE(cert, nullptr); + bssl::UniquePtr pub_key(X509_get_pubkey(cert.get())); + int sign_alg = deduceSignatureAlgorithmFromPublicKey(pub_key.get(), &error_details); + EXPECT_EQ(sign_alg, SSL_SIGN_RSA_PSS_RSAE_SHA256); + std::string signature; + proof_source_.ComputeTlsSignature( + server_address_, client_address_, hostname_, SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", + std::make_unique(true, filter_chain_, signature)); + SignatureVerifier verifier; + verifier.verifyCertsAndSignature(chain, "payload", signature); } -TEST_F(EnvoyQuicProofSourceTest, GetProofFailBadConfig) { +TEST_F(EnvoyQuicProofSourceTest, GetCertChainFailBadConfig) { // No filter chain. EXPECT_CALL(listen_socket_, ioHandle()).Times(3); EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) .WillOnce(Invoke([&](const Network::ConnectionSocket&) { return nullptr; })); - testGetProof(false); + EXPECT_EQ(nullptr, proof_source_.GetCertChain(server_address_, client_address_, hostname_)); // Cert not ready. EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) @@ -215,24 +224,9 @@ TEST_F(EnvoyQuicProofSourceTest, GetProofFailBadConfig) { EXPECT_CALL(filter_chain_, transportSocketFactory()) .WillOnce(ReturnRef(*transport_socket_factory_)); EXPECT_CALL(*mock_context_config_, isReady()).WillOnce(Return(false)); - testGetProof(false); + EXPECT_EQ(nullptr, proof_source_.GetCertChain(server_address_, client_address_, hostname_)); // No certs in config. - EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) - .WillOnce(Invoke([&](const Network::ConnectionSocket&) { return &filter_chain_; })); - EXPECT_CALL(filter_chain_, transportSocketFactory()) - .WillOnce(ReturnRef(*transport_socket_factory_)); - EXPECT_CALL(*mock_context_config_, isReady()).WillOnce(Return(true)); - std::vector> tls_cert_configs{}; - EXPECT_CALL(*mock_context_config_, tlsCertificates()).WillOnce(Return(tls_cert_configs)); - testGetProof(false); -} - -TEST_F(EnvoyQuicProofSourceTest, GetProofFailNoCertConfig) { - bool called = false; - auto callback = std::make_unique(called, false, server_config_, version_, - chlo_hash_, filter_chain_); - EXPECT_CALL(listen_socket_, ioHandle()).Times(1u); EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) .WillRepeatedly(Invoke([&](const Network::ConnectionSocket& connection_socket) { EXPECT_EQ(*quicAddressToEnvoyAddressInstance(server_address_), @@ -240,29 +234,26 @@ TEST_F(EnvoyQuicProofSourceTest, GetProofFailNoCertConfig) { EXPECT_EQ(*quicAddressToEnvoyAddressInstance(client_address_), *connection_socket.addressProvider().remoteAddress()); EXPECT_EQ("quic", connection_socket.detectedTransportProtocol()); - EXPECT_EQ("h3-29", connection_socket.requestedApplicationProtocols()[0]); + EXPECT_EQ("h3", connection_socket.requestedApplicationProtocols()[0]); return &filter_chain_; })); EXPECT_CALL(filter_chain_, transportSocketFactory()) - .WillRepeatedly(ReturnRef(*transport_socket_factory_)); + .WillOnce(ReturnRef(*transport_socket_factory_)); EXPECT_CALL(*mock_context_config_, isReady()).WillOnce(Return(true)); - EXPECT_CALL(*mock_context_config_, tlsCertificates()) - .WillRepeatedly( - Return(std::vector>{})); - proof_source_.GetProof(server_address_, client_address_, hostname_, server_config_, version_, - chlo_hash_, std::move(callback)); - EXPECT_TRUE(called); + std::vector> tls_cert_configs{}; + EXPECT_CALL(*mock_context_config_, tlsCertificates()).WillOnce(Return(tls_cert_configs)); + EXPECT_EQ(nullptr, proof_source_.GetCertChain(server_address_, client_address_, hostname_)); } -TEST_F(EnvoyQuicProofSourceTest, GetProofFailInvalidCert) { +TEST_F(EnvoyQuicProofSourceTest, GetCertChainFailInvalidCert) { std::string invalid_cert{R"(-----BEGIN CERTIFICATE----- invalid certificate -----END CERTIFICATE-----)"}; expectCertChainAndPrivateKey(invalid_cert, false); - testGetProof(false); + EXPECT_EQ(nullptr, proof_source_.GetCertChain(server_address_, client_address_, hostname_)); } -TEST_F(EnvoyQuicProofSourceTest, GetProofFailInvalidPublicKeyInCert) { +TEST_F(EnvoyQuicProofSourceTest, GetCertChainFailInvalidPublicKeyInCert) { // This is a valid cert with RSA public key. But we don't support RSA key with // length < 1024. std::string cert_with_rsa_1024{R"(-----BEGIN CERTIFICATE----- @@ -284,7 +275,18 @@ x96rVeUbRJ/qU4//nNM/XQa9vIAIcTZ0jFhmb0c3R4rmoqqC3vkSDwtaE5yuS5T4 GUy+n0vQNB0cXGzgcGI= -----END CERTIFICATE-----)"}; expectCertChainAndPrivateKey(cert_with_rsa_1024, false); - testGetProof(false); + EXPECT_EQ(nullptr, proof_source_.GetCertChain(server_address_, client_address_, hostname_)); +} + +TEST_F(EnvoyQuicProofSourceTest, ComputeSignatureFailNoFilterChain) { + EXPECT_CALL(listen_socket_, ioHandle()); + EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) + .WillOnce(Invoke([&](const Network::ConnectionSocket&) { return nullptr; })); + + std::string signature; + proof_source_.ComputeTlsSignature( + server_address_, client_address_, hostname_, SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", + std::make_unique(false, filter_chain_, signature)); } TEST_F(EnvoyQuicProofSourceTest, UnexpectedPrivateKey) { @@ -315,9 +317,10 @@ HO6j1yxTIGU6w8++AQJACdFPnRidOaj5oJmcZq0s6WGTYfegjTOKgi5KQzO0FTwG qGm130brdD+1U1EJnEFmleLZ/W6mEi3MxcKpWOpTqQ== -----END RSA PRIVATE KEY-----)"); EXPECT_CALL(tls_cert_config, privateKey()).WillOnce(ReturnRef(rsa_pkey_1024_len)); - proof_source_.ComputeTlsSignature(server_address_, client_address_, hostname_, - SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", - std::make_unique(false)); + std::string signature; + proof_source_.ComputeTlsSignature( + server_address_, client_address_, hostname_, SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", + std::make_unique(false, filter_chain_, signature)); } TEST_F(EnvoyQuicProofSourceTest, InvalidPrivateKey) { @@ -334,9 +337,10 @@ TEST_F(EnvoyQuicProofSourceTest, InvalidPrivateKey) { EXPECT_CALL(*mock_context_config_, isReady()).WillOnce(Return(true)); std::string invalid_pkey("abcdefg"); EXPECT_CALL(tls_cert_config, privateKey()).WillOnce(ReturnRef(invalid_pkey)); - proof_source_.ComputeTlsSignature(server_address_, client_address_, hostname_, - SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", - std::make_unique(false)); + std::string signature; + proof_source_.ComputeTlsSignature( + server_address_, client_address_, hostname_, SSL_SIGN_RSA_PSS_RSAE_SHA256, "payload", + std::make_unique(false, filter_chain_, signature)); } } // namespace Quic diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index 442e9b867f961..21b7be3136a7e 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -149,8 +149,6 @@ TEST_F(EnvoyQuicProofVerifierTest, VerifyCertChainFailureLeafCertWithGarbage) { TEST_F(EnvoyQuicProofVerifierTest, VerifyCertChainFailureInvalidHost) { configCertVerificationDetails(true); - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(leaf_cert_); const std::string ocsp_response; const std::string cert_sct; std::string error_details; @@ -161,47 +159,8 @@ TEST_F(EnvoyQuicProofVerifierTest, VerifyCertChainFailureInvalidHost) { EXPECT_EQ("Leaf certificate doesn't match hostname: unknown.org", error_details); } -TEST_F(EnvoyQuicProofVerifierTest, VerifyProofFailureEmptyCertChain) { - configCertVerificationDetails(true); - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(leaf_cert_); - quic::QuicTransportVersion version{quic::QUIC_VERSION_UNSUPPORTED}; - absl::string_view chlo_hash{"aaaaa"}; - std::string server_config{"Server Config"}; - const std::string ocsp_response; - const std::string cert_sct; - std::string error_details; - const std::vector certs; - EXPECT_EQ(quic::QUIC_FAILURE, - verifier_->VerifyProof(std::string(cert_view->subject_alt_name_domains()[0]), 54321, - server_config, version, chlo_hash, certs, cert_sct, "signature", - nullptr, &error_details, nullptr, nullptr)); - EXPECT_EQ("Received empty cert chain.", error_details); -} - -TEST_F(EnvoyQuicProofVerifierTest, VerifyProofFailureInvalidLeafCert) { +TEST_F(EnvoyQuicProofVerifierTest, VerifyCertChainFailureUnsupportedECKey) { configCertVerificationDetails(true); - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(leaf_cert_); - quic::QuicTransportVersion version{quic::QUIC_VERSION_UNSUPPORTED}; - absl::string_view chlo_hash{"aaaaa"}; - std::string server_config{"Server Config"}; - const std::string ocsp_response; - const std::string cert_sct; - std::string error_details; - const std::vector certs{"invalid leaf cert"}; - EXPECT_EQ(quic::QUIC_FAILURE, - verifier_->VerifyProof(std::string(cert_view->subject_alt_name_domains()[0]), 54321, - server_config, version, chlo_hash, certs, cert_sct, "signature", - nullptr, &error_details, nullptr, nullptr)); - EXPECT_EQ("Invalid leaf cert.", error_details); -} - -TEST_F(EnvoyQuicProofVerifierTest, VerifyProofFailureUnsupportedECKey) { - configCertVerificationDetails(true); - quic::QuicTransportVersion version{quic::QUIC_VERSION_UNSUPPORTED}; - absl::string_view chlo_hash{"aaaaa"}; - std::string server_config{"Server Config"}; const std::string ocsp_response; const std::string cert_sct; std::string error_details; @@ -228,28 +187,10 @@ VdGXMAjeXhnOnPvmDi5hUz/uvI+Pg6cNmUoCRwSCnK/DazhA quic::CertificateView::ParseSingleCertificate(chain[0]); ASSERT(cert_view); EXPECT_EQ(quic::QUIC_FAILURE, - verifier_->VerifyProof("www.google.com", 54321, server_config, version, chlo_hash, - chain, cert_sct, "signature", nullptr, &error_details, nullptr, - nullptr)); + verifier_->VerifyCertChain("www.google.com", 54321, chain, ocsp_response, cert_sct, + nullptr, &error_details, nullptr, nullptr, nullptr)); EXPECT_EQ("Invalid leaf cert, only P-256 ECDSA certificates are supported", error_details); } -TEST_F(EnvoyQuicProofVerifierTest, VerifyProofFailureInvalidSignature) { - configCertVerificationDetails(true); - std::unique_ptr cert_view = - quic::CertificateView::ParseSingleCertificate(leaf_cert_); - quic::QuicTransportVersion version{quic::QUIC_VERSION_UNSUPPORTED}; - absl::string_view chlo_hash{"aaaaa"}; - std::string server_config{"Server Config"}; - const std::string ocsp_response; - const std::string cert_sct; - std::string error_details; - EXPECT_EQ(quic::QUIC_FAILURE, - verifier_->VerifyProof(std::string(cert_view->subject_alt_name_domains()[0]), 54321, - server_config, version, chlo_hash, {leaf_cert_}, cert_sct, - "signature", nullptr, &error_details, nullptr, nullptr)); - EXPECT_EQ("Signature is not valid.", error_details); -} - } // namespace Quic } // namespace Envoy diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index 349afd7ff9737..8d4818d1753bb 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -146,16 +146,13 @@ class EnvoyQuicTestCryptoServerStreamFactory : public EnvoyQuicCryptoServerStrea } }; -class EnvoyQuicServerSessionTest : public testing::TestWithParam { +class EnvoyQuicServerSessionTest : public testing::Test { public: EnvoyQuicServerSessionTest() : api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), quic_version_([]() { - SetQuicReloadableFlag(quic_disable_version_draft_29, !GetParam()); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !GetParam()); - return quic::ParsedVersionOfIndex(quic::CurrentSupportedVersions(), 0); - }()), + alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), + quic_version_({quic::CurrentSupportedHttp3Versions()[0]}), quic_stat_names_(listener_config_.listenerScope().symbolTable()), quic_connection_(new MockEnvoyQuicServerConnection( connection_helper_, alarm_factory_, writer_, quic_version_, *listener_config_.socket_)), @@ -234,8 +231,7 @@ class EnvoyQuicServerSessionTest : public testing::TestWithParam { encoder.getStream().addCallbacks(stream_callbacks); return request_decoder; })); - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; + quic::QuicStreamId stream_id = 4u; return envoy_quic_session_.GetOrCreateStream(stream_id); } @@ -280,12 +276,8 @@ class EnvoyQuicServerSessionTest : public testing::TestWithParam { envoy::config::core::v3::Http3ProtocolOptions http3_options_; }; -INSTANTIATE_TEST_SUITE_P(EnvoyQuicServerSessionTests, EnvoyQuicServerSessionTest, - testing::ValuesIn({true, false})); - -TEST_P(EnvoyQuicServerSessionTest, NewStreamBeforeInitializingFilter) { - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; +TEST_F(EnvoyQuicServerSessionTest, NewStreamBeforeInitializingFilter) { + quic::QuicStreamId stream_id = 4u; EXPECT_ENVOY_BUG(envoy_quic_session_.GetOrCreateStream(stream_id), fmt::format("attempts to create stream", envoy_quic_session_.id(), stream_id)); EXPECT_CALL(*quic_connection_, @@ -296,14 +288,13 @@ TEST_P(EnvoyQuicServerSessionTest, NewStreamBeforeInitializingFilter) { envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); } -TEST_P(EnvoyQuicServerSessionTest, NewStream) { +TEST_F(EnvoyQuicServerSessionTest, NewStream) { installReadFilter(); Http::MockRequestDecoder request_decoder; EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(testing::ReturnRef(request_decoder)); - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; + quic::QuicStreamId stream_id = 4u; auto stream = reinterpret_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); @@ -328,93 +319,40 @@ TEST_P(EnvoyQuicServerSessionTest, NewStream) { stream->OnStreamHeaderList(/*fin=*/true, headers.uncompressed_header_bytes(), headers); } -TEST_P(EnvoyQuicServerSessionTest, InvalidIncomingStreamId) { +TEST_F(EnvoyQuicServerSessionTest, InvalidIncomingStreamId) { installReadFilter(); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; - // IETF stream 5 and G-Quic stream 2 are server initiated. - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 5u : 2u; + // IETF stream 5 is server initiated. + quic::QuicStreamId stream_id = 5u; std::string data("aaaa"); quic::QuicStreamFrame stream_frame(stream_id, false, 0, data); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)).Times(0); - EXPECT_CALL(*quic_connection_, - SendConnectionClosePacket((quic::VersionUsesHttp3(quic_version_[0].transport_version) - ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION - : quic::QUIC_INVALID_STREAM_ID), - _, "Data for nonexistent stream")); + EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_HTTP_STREAM_WRONG_DIRECTION, + _, "Data for nonexistent stream")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); envoy_quic_session_.OnStreamFrame(stream_frame); } -TEST_P(EnvoyQuicServerSessionTest, NoNewStreamForInvalidIncomingStream) { +TEST_F(EnvoyQuicServerSessionTest, NoNewStreamForInvalidIncomingStream) { installReadFilter(); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; - // IETF stream 5 and G-Quic stream 2 are server initiated. - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 5u : 2u; + // IETF stream 5 is server initiated. + quic::QuicStreamId stream_id = 5u; EXPECT_CALL(http_connection_callbacks_, newStream(_, false)).Times(0); - EXPECT_CALL(*quic_connection_, - SendConnectionClosePacket(quic::VersionUsesHttp3(quic_version_[0].transport_version) - ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION - : quic::QUIC_INVALID_STREAM_ID, - _, "Data for nonexistent stream")); + EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_HTTP_STREAM_WRONG_DIRECTION, + _, "Data for nonexistent stream")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); // Stream creation on closed connection should fail. EXPECT_EQ(nullptr, envoy_quic_session_.GetOrCreateStream(stream_id)); } -TEST_P(EnvoyQuicServerSessionTest, OnResetFrameGoogleQuic) { +TEST_F(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { installReadFilter(); - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - return; - } - Http::MockRequestDecoder request_decoder; - Http::MockStreamCallbacks stream_callbacks; - quic::QuicStream* stream1 = createNewStream(request_decoder, stream_callbacks); - quic::QuicRstStreamFrame rst1(/*control_frame_id=*/1u, stream1->id(), - quic::QUIC_ERROR_PROCESSING_STREAM, /*bytes_written=*/0u); - EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::RemoteReset, _)); - EXPECT_CALL(*quic_connection_, SendControlFrame(_)) - .WillOnce(Invoke([stream_id = stream1->id()](const quic::QuicFrame& frame) { - EXPECT_EQ(stream_id, frame.rst_stream_frame->stream_id); - EXPECT_EQ(quic::QUIC_RST_ACKNOWLEDGEMENT, frame.rst_stream_frame->error_code); - return false; - })); - envoy_quic_session_.OnRstStream(rst1); - - EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) - .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, - bool) -> Http::RequestDecoder& { - encoder.getStream().addCallbacks(stream_callbacks); - return request_decoder; - })); - quic::QuicStream* stream2 = envoy_quic_session_.GetOrCreateStream(stream1->id() + 4u); - quic::QuicRstStreamFrame rst2(/*control_frame_id=*/1u, stream2->id(), quic::QUIC_REFUSED_STREAM, - /*bytes_written=*/0u); - EXPECT_CALL(stream_callbacks, - onResetStream(Http::StreamResetReason::RemoteRefusedStreamReset, _)); - - envoy_quic_session_.OnRstStream(rst2); - EXPECT_EQ(1U, TestUtility::findCounter( - static_cast(listener_config_.listenerScope()), - "http3.downstream.rx.quic_reset_stream_error_code_QUIC_REFUSED_STREAM") - ->value()); - EXPECT_EQ(1U, TestUtility::findCounter( - static_cast(listener_config_.listenerScope()), - "http3.downstream.rx.quic_reset_stream_error_code_QUIC_ERROR_PROCESSING_STREAM") - ->value()); -} - -TEST_P(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { - installReadFilter(); - if (!quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - return; - } Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; auto stream1 = @@ -480,7 +418,7 @@ TEST_P(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { ->value()); } -TEST_P(EnvoyQuicServerSessionTest, ConnectionClose) { +TEST_F(EnvoyQuicServerSessionTest, ConnectionClose) { installReadFilter(); std::string error_details("dummy details"); @@ -495,7 +433,7 @@ TEST_P(EnvoyQuicServerSessionTest, ConnectionClose) { EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); } -TEST_P(EnvoyQuicServerSessionTest, ConnectionCloseWithActiveStream) { +TEST_F(EnvoyQuicServerSessionTest, ConnectionCloseWithActiveStream) { installReadFilter(); Http::MockRequestDecoder request_decoder; @@ -510,7 +448,24 @@ TEST_P(EnvoyQuicServerSessionTest, ConnectionCloseWithActiveStream) { EXPECT_TRUE(stream->write_side_closed() && stream->reading_stopped()); } -TEST_P(EnvoyQuicServerSessionTest, NoFlushWithDataToWrite) { +TEST_F(EnvoyQuicServerSessionTest, RemoteConnectionCloseWithActiveStream) { + installReadFilter(); + + Http::MockRequestDecoder request_decoder; + Http::MockStreamCallbacks stream_callbacks; + quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); + EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); + EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::ConnectionFailure, _)); + quic::QuicConnectionCloseFrame frame(quic_version_[0].transport_version, + quic::QUIC_HANDSHAKE_TIMEOUT, quic::NO_IETF_QUIC_ERROR, + "dummy details", + /* transport_close_frame_type = */ 0); + quic_connection_->OnConnectionCloseFrame(frame); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + EXPECT_TRUE(stream->write_side_closed() && stream->reading_stopped()); +} + +TEST_F(EnvoyQuicServerSessionTest, NoFlushWithDataToWrite) { installReadFilter(); Http::MockRequestDecoder request_decoder; @@ -528,7 +483,7 @@ TEST_P(EnvoyQuicServerSessionTest, NoFlushWithDataToWrite) { EXPECT_TRUE(stream->write_side_closed() && stream->reading_stopped()); } -TEST_P(EnvoyQuicServerSessionTest, FlushCloseWithDataToWrite) { +TEST_F(EnvoyQuicServerSessionTest, FlushCloseWithDataToWrite) { installReadFilter(); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; @@ -551,7 +506,7 @@ TEST_P(EnvoyQuicServerSessionTest, FlushCloseWithDataToWrite) { // Tests that a write event after flush close should update the delay close // timer. -TEST_P(EnvoyQuicServerSessionTest, WriteUpdatesDelayCloseTimer) { +TEST_F(EnvoyQuicServerSessionTest, WriteUpdatesDelayCloseTimer) { installReadFilter(); // Drive congestion control manually. auto send_algorithm = new testing::NiceMock; @@ -636,7 +591,7 @@ TEST_P(EnvoyQuicServerSessionTest, WriteUpdatesDelayCloseTimer) { // Tests that if delay close timeout is not configured, flush close will not act // based on timeout. -TEST_P(EnvoyQuicServerSessionTest, FlushCloseNoTimeout) { +TEST_F(EnvoyQuicServerSessionTest, FlushCloseNoTimeout) { installReadFilter(); // Switch to a encryption forward secure crypto stream. quic::test::QuicServerSessionBasePeer::SetCryptoStream(&envoy_quic_session_, nullptr); @@ -723,7 +678,7 @@ TEST_P(EnvoyQuicServerSessionTest, FlushCloseNoTimeout) { envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); } -TEST_P(EnvoyQuicServerSessionTest, FlushCloseWithTimeout) { +TEST_F(EnvoyQuicServerSessionTest, FlushCloseWithTimeout) { installReadFilter(); envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; @@ -754,7 +709,7 @@ TEST_P(EnvoyQuicServerSessionTest, FlushCloseWithTimeout) { EXPECT_FALSE(quic_connection_->connected()); } -TEST_P(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithTimeout) { +TEST_F(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithTimeout) { installReadFilter(); envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; @@ -787,7 +742,7 @@ TEST_P(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithTimeout) { EXPECT_FALSE(quic_connection_->connected()); } -TEST_P(EnvoyQuicServerSessionTest, FlusWriteTransitToFlushWriteWithDelay) { +TEST_F(EnvoyQuicServerSessionTest, FlusWriteTransitToFlushWriteWithDelay) { installReadFilter(); envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; @@ -824,7 +779,7 @@ TEST_P(EnvoyQuicServerSessionTest, FlusWriteTransitToFlushWriteWithDelay) { EXPECT_FALSE(quic_connection_->connected()); } -TEST_P(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithNoPendingData) { +TEST_F(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithNoPendingData) { installReadFilter(); envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); // This close should be delayed as configured. @@ -847,31 +802,23 @@ TEST_P(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithNoPendingData) { EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); } -TEST_P(EnvoyQuicServerSessionTest, ShutdownNotice) { +TEST_F(EnvoyQuicServerSessionTest, ShutdownNotice) { installReadFilter(); testing::NiceMock debug_visitor; envoy_quic_session_.set_debug_visitor(&debug_visitor); - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); - } else { - // This is a no-op for pre-HTTP3 versions of QUIC. - } + EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); http_connection_->shutdownNotice(); } -TEST_P(EnvoyQuicServerSessionTest, GoAway) { +TEST_F(EnvoyQuicServerSessionTest, GoAway) { installReadFilter(); testing::NiceMock debug_visitor; envoy_quic_session_.set_debug_visitor(&debug_visitor); - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); - } else { - EXPECT_CALL(*quic_connection_, SendControlFrame(_)); - } + EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); http_connection_->goAway(); } -TEST_P(EnvoyQuicServerSessionTest, ConnectedAfterHandshake) { +TEST_F(EnvoyQuicServerSessionTest, ConnectedAfterHandshake) { installReadFilter(); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::Connected)); if (!quic_version_[0].UsesTls()) { @@ -886,13 +833,13 @@ TEST_P(EnvoyQuicServerSessionTest, ConnectedAfterHandshake) { EXPECT_FALSE(quic_connection_->connectionSocket()->ioHandle().isOpen()); } -TEST_P(EnvoyQuicServerSessionTest, NetworkConnectionInterface) { +TEST_F(EnvoyQuicServerSessionTest, NetworkConnectionInterface) { installReadFilter(); EXPECT_EQ(dispatcher_.get(), &envoy_quic_session_.dispatcher()); EXPECT_TRUE(envoy_quic_session_.readEnabled()); } -TEST_P(EnvoyQuicServerSessionTest, SendBufferWatermark) { +TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { // Switch to a encryption forward secure crypto stream. quic::test::QuicServerSessionBasePeer::SetCryptoStream(&envoy_quic_session_, nullptr); quic::test::QuicServerSessionBasePeer::SetCryptoStream( @@ -926,8 +873,7 @@ TEST_P(EnvoyQuicServerSessionTest, SendBufferWatermark) { encoder.getStream().addCallbacks(stream_callbacks); return request_decoder; })); - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; + quic::QuicStreamId stream_id = 4u; auto stream1 = dynamic_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); @@ -1077,224 +1023,5 @@ TEST_P(EnvoyQuicServerSessionTest, SendBufferWatermark) { EXPECT_TRUE(stream2->write_side_closed()); } -TEST_P(EnvoyQuicServerSessionTest, HeadersContributeToWatermarkGquic) { - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - installReadFilter(); - return; - } - // Switch to a encryption forward secure crypto stream. - quic::test::QuicServerSessionBasePeer::SetCryptoStream(&envoy_quic_session_, nullptr); - quic::test::QuicServerSessionBasePeer::SetCryptoStream( - &envoy_quic_session_, - new TestQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_, - &envoy_quic_session_, &crypto_stream_helper_)); - quic_connection_->SetDefaultEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); - quic_connection_->SetEncrypter( - quic::ENCRYPTION_FORWARD_SECURE, - std::make_unique(quic::Perspective::IS_SERVER)); - // Drive congestion control manually. - auto send_algorithm = new testing::NiceMock; - quic::test::QuicConnectionPeer::SetSendAlgorithm(quic_connection_, send_algorithm); - EXPECT_CALL(*send_algorithm, PacingRate(_)).WillRepeatedly(Return(quic::QuicBandwidth::Zero())); - EXPECT_CALL(*send_algorithm, BandwidthEstimate()) - .WillRepeatedly(Return(quic::QuicBandwidth::Zero())); - EXPECT_CALL(*quic_connection_, SendControlFrame(_)).Times(AnyNumber()); - - // Bump connection flow control window large enough not to interfere - // stream writing. - envoy_quic_session_.flow_controller()->UpdateSendWindowOffset( - 10 * quic::kDefaultFlowControlSendWindow); - installReadFilter(); - Http::MockRequestDecoder request_decoder; - Http::MockStreamCallbacks stream_callbacks; - EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) - .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, - bool) -> Http::RequestDecoder& { - encoder.getStream().addCallbacks(stream_callbacks); - return request_decoder; - })); - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; - auto stream1 = - dynamic_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); - - // Receive a GET request on created stream. - quic::QuicHeaderList request_headers; - request_headers.OnHeaderBlockStart(); - std::string host("www.abc.com"); - request_headers.OnHeader(":authority", host); - request_headers.OnHeader(":method", "GET"); - request_headers.OnHeader(":path", "/"); - request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); - // Request headers should be propagated to decoder. - EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { - EXPECT_EQ(host, decoded_headers->getHostValue()); - EXPECT_EQ("/", decoded_headers->getPathValue()); - EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); - })); - stream1->OnStreamHeaderList(/*fin=*/true, request_headers.uncompressed_header_bytes(), - request_headers); - - Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; - // Make connection congestion control blocked so headers are buffered. - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); - stream1->encodeHeaders(response_headers, false); - // Buffer a response slightly smaller than connection level watermark, but - // with the previously buffered headers, this write should reach high - // watermark. - std::string response(24 * 1024 - 1, 'a'); - Buffer::OwnedImpl buffer(response); - // Triggered twice, once by stream, the other time by connection. - EXPECT_CALL(stream_callbacks, onAboveWriteBufferHighWatermark()).Times(2); - EXPECT_CALL(network_connection_callbacks_, onAboveWriteBufferHighWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionAboveWriteBufferHighWatermark(); })); - stream1->encodeData(buffer, false); - EXPECT_FALSE(envoy_quic_session_.IsConnectionFlowControlBlocked()); - - // Write the buffered data out till stream is flow control blocked. Both - // stream and connection level buffers should drop below watermark. - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); - EXPECT_CALL(*send_algorithm, GetCongestionWindow()).WillRepeatedly(Return(quic::kDefaultTCPMSS)); - EXPECT_CALL(network_connection_callbacks_, onBelowWriteBufferLowWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionBelowWriteBufferLowWatermark(); })); - EXPECT_CALL(stream_callbacks, onBelowWriteBufferLowWatermark()).Times(2); - envoy_quic_session_.OnCanWrite(); - EXPECT_TRUE(stream1->IsFlowControlBlocked()); - - // Buffer more response because of flow control. The buffered bytes become just below connection - // level high watermark. - std::string response1(16 * 1024 - 20, 'a'); - Buffer::OwnedImpl buffer1(response1); - EXPECT_CALL(stream_callbacks, onAboveWriteBufferHighWatermark()); - stream1->encodeData(buffer1, false); - - // Make connection congestion control blocked again. - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); - // Buffering the trailers will cause connection to reach high watermark. - EXPECT_CALL(network_connection_callbacks_, onAboveWriteBufferHighWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionAboveWriteBufferHighWatermark(); })); - Http::TestResponseTrailerMapImpl response_trailers{{"trailer-key", "trailer-value"}}; - stream1->encodeTrailers(response_trailers); - - EXPECT_CALL(network_connection_callbacks_, onBelowWriteBufferLowWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionBelowWriteBufferLowWatermark(); })); - EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalReset, _)); - stream1->resetStream(Http::StreamResetReason::LocalReset); - - EXPECT_EQ(1U, TestUtility::findCounter( - static_cast(listener_config_.listenerScope()), - "http3.downstream.tx.quic_reset_stream_error_code_QUIC_STREAM_CANCELLED") - ->value()); -} - -TEST_P(EnvoyQuicServerSessionTest, OnCanWriteUpdateWatermarkGquic) { - if (quic::VersionUsesHttp3(quic_version_[0].transport_version)) { - installReadFilter(); - return; - } - // Switch to a encryption forward secure crypto stream. - quic::test::QuicServerSessionBasePeer::SetCryptoStream(&envoy_quic_session_, nullptr); - quic::test::QuicServerSessionBasePeer::SetCryptoStream( - &envoy_quic_session_, - new TestQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_, - &envoy_quic_session_, &crypto_stream_helper_)); - quic_connection_->SetDefaultEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); - quic_connection_->SetEncrypter( - quic::ENCRYPTION_FORWARD_SECURE, - std::make_unique(quic::Perspective::IS_SERVER)); - // Drive congestion control manually. - auto send_algorithm = new testing::NiceMock; - quic::test::QuicConnectionPeer::SetSendAlgorithm(quic_connection_, send_algorithm); - EXPECT_CALL(*send_algorithm, PacingRate(_)).WillRepeatedly(Return(quic::QuicBandwidth::Zero())); - EXPECT_CALL(*send_algorithm, BandwidthEstimate()) - .WillRepeatedly(Return(quic::QuicBandwidth::Zero())); - EXPECT_CALL(*quic_connection_, SendControlFrame(_)).Times(AnyNumber()); - - // Bump connection flow control window large enough not to interfere - // stream writing. - envoy_quic_session_.flow_controller()->UpdateSendWindowOffset( - 10 * quic::kDefaultFlowControlSendWindow); - installReadFilter(); - Http::MockRequestDecoder request_decoder; - Http::MockStreamCallbacks stream_callbacks; - EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) - .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, - bool) -> Http::RequestDecoder& { - encoder.getStream().addCallbacks(stream_callbacks); - return request_decoder; - })); - quic::QuicStreamId stream_id = - quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; - auto stream1 = - dynamic_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); - - // Receive a GET request on created stream. - quic::QuicHeaderList request_headers; - request_headers.OnHeaderBlockStart(); - std::string host("www.abc.com"); - request_headers.OnHeader(":authority", host); - request_headers.OnHeader(":method", "GET"); - request_headers.OnHeader(":path", "/"); - request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); - // Request headers should be propagated to decoder. - EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { - EXPECT_EQ(host, decoded_headers->getHostValue()); - EXPECT_EQ("/", decoded_headers->getPathValue()); - EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); - })); - stream1->OnStreamHeaderList(/*fin=*/true, request_headers.uncompressed_header_bytes(), - request_headers); - - Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; - stream1->encodeHeaders(response_headers, false); - // Make connection congestion control blocked. - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); - // Buffer a response slightly smaller than connection level watermark, but - // with the previously buffered headers, this write should reach high - // watermark. - std::string response(24 * 1024 - 1, 'a'); - Buffer::OwnedImpl buffer(response); - // Triggered twice, once by stream, the other time by connection. - EXPECT_CALL(stream_callbacks, onAboveWriteBufferHighWatermark()).Times(2); - EXPECT_CALL(network_connection_callbacks_, onAboveWriteBufferHighWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionAboveWriteBufferHighWatermark(); })); - stream1->encodeData(buffer, false); - EXPECT_FALSE(envoy_quic_session_.IsConnectionFlowControlBlocked()); - - // Write the buffered data out till stream is flow control blocked. Both - // stream and connection level buffers should drop below watermark. - bool congestion_control_blocked{false}; - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(!congestion_control_blocked)); - EXPECT_CALL(*send_algorithm, GetCongestionWindow()).WillRepeatedly(Return(quic::kDefaultTCPMSS)); - EXPECT_CALL(network_connection_callbacks_, onBelowWriteBufferLowWatermark) - .WillOnce(Invoke( - [this]() { http_connection_->onUnderlyingConnectionBelowWriteBufferLowWatermark(); })); - // Write trailers when buffered bytes drops below low watermark. - EXPECT_CALL(stream_callbacks, onBelowWriteBufferLowWatermark()) - .Times(2) - .WillOnce(Return()) - .WillOnce(Invoke([stream1, send_algorithm]() { - // Block this trailer so that it gets buffered in headers stream. - // Verify that the buffered bytes are counted into watermark only once. - EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); - - // The trailer is large enough so that if its data is counted into watermark once the - // watermark won't go across the high watermark, if it is counted twice, it would go beyond - // high watermark. Note that the trailers are compressed by Hpack by ~38%. - Http::TestResponseTrailerMapImpl response_trailers{ - {"long-trailer1", std::string(16 * 1024, 'a')}}; - stream1->encodeTrailers(response_trailers); - })); - envoy_quic_session_.OnCanWrite(); - EXPECT_TRUE(stream1->IsFlowControlBlocked()); -} - } // namespace Quic } // namespace Envoy diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index 53ffe144a0e3a..4ae1e4dae81a5 100644 --- a/test/common/quic/envoy_quic_server_stream_test.cc +++ b/test/common/quic/envoy_quic_server_stream_test.cc @@ -39,16 +39,13 @@ using testing::Invoke; namespace Envoy { namespace Quic { -class EnvoyQuicServerStreamTest : public testing::TestWithParam { +class EnvoyQuicServerStreamTest : public testing::Test { public: EnvoyQuicServerStreamTest() : api_(Api::createApiForTest()), dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), quic_version_([]() { - SetQuicReloadableFlag(quic_disable_version_draft_29, !GetParam()); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !GetParam()); - return quic::CurrentSupportedVersions()[0]; - }()), + alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), + quic_version_(quic::CurrentSupportedHttp3Versions()[0]), listener_stats_({ALL_LISTENER_STATS(POOL_COUNTER(listener_config_.listenerScope()), POOL_GAUGE(listener_config_.listenerScope()), POOL_HISTOGRAM(listener_config_.listenerScope()))}), @@ -56,7 +53,7 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { quic::ParsedQuicVersionVector{quic_version_}, *listener_config_.socket_), quic_session_(quic_config_, {quic_version_}, &quic_connection_, *dispatcher_, quic_config_.GetInitialStreamFlowControlWindowToSend() * 2), - stream_id_(VersionUsesHttp3(quic_version_.transport_version) ? 4u : 5u), + stream_id_(4u), stats_( {ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(listener_config_.listenerScope(), "http3."), POOL_GAUGE_PREFIX(listener_config_.listenerScope(), "http3."))}), @@ -87,12 +84,6 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { quic_session_.Initialize(); setQuicConfigWithDefaultValues(quic_session_.config()); quic_session_.OnConfigNegotiated(); - request_headers_.OnHeaderBlockStart(); - request_headers_.OnHeader(":authority", host_); - request_headers_.OnHeader(":method", "POST"); - request_headers_.OnHeader(":path", "/"); - request_headers_.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, - /*compressed_header_bytes=*/0); spdy_request_headers_[":authority"] = host_; spdy_request_headers_[":method"] = "POST"; spdy_request_headers_[":path"] = "/"; @@ -109,13 +100,6 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { } } - std::string bodyToStreamPayload(const std::string& body) { - if (!quic::VersionUsesHttp3(quic_version_.transport_version)) { - return body; - } - return bodyToHttp3StreamPayload(body); - } - size_t receiveRequest(const std::string& payload, bool fin, size_t decoder_buffer_high_watermark) { EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) @@ -133,38 +117,19 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { quic_stream_->readDisable(true); } })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_request_headers_), - bodyToStreamPayload(payload)); - quic::QuicStreamFrame frame(stream_id_, fin, 0, data); - quic_stream_->OnStreamFrame(frame); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - return data.length(); - } - quic_stream_->OnStreamHeaderList(/*fin=*/false, request_headers_.uncompressed_header_bytes(), - request_headers_); - - quic::QuicStreamFrame frame(stream_id_, fin, 0, payload); + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_request_headers_), + bodyToHttp3StreamPayload(payload)); + quic::QuicStreamFrame frame(stream_id_, fin, 0, data); quic_stream_->OnStreamFrame(frame); EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - return payload.length(); + return data.length(); } void receiveTrailers(size_t offset) { - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - spdy_trailers_["key1"] = "value1"; - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers_); - quic::QuicStreamFrame frame(stream_id_, true, offset, payload); - quic_stream_->OnStreamFrame(frame); - } else { - trailers_.OnHeaderBlockStart(); - trailers_.OnHeader("key1", "value1"); - // ":final-offset" is required and stripped off by quic. - trailers_.OnHeader(":final-offset", absl::StrCat("", offset)); - trailers_.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); - quic_stream_->OnStreamHeaderList(/*fin=*/true, trailers_.uncompressed_header_bytes(), - trailers_); - } + spdy_trailers_["key1"] = "value1"; + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers_); + quic::QuicStreamFrame frame(stream_id_, true, offset, payload); + quic_stream_->OnStreamFrame(frame); } protected: @@ -185,33 +150,16 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { EnvoyQuicServerStream* quic_stream_; Http::MockRequestDecoder stream_decoder_; Http::MockStreamCallbacks stream_callbacks_; - quic::QuicHeaderList request_headers_; spdy::SpdyHeaderBlock spdy_request_headers_; Http::TestResponseHeaderMapImpl response_headers_; Http::TestResponseTrailerMapImpl response_trailers_; - quic::QuicHeaderList trailers_; spdy::SpdyHeaderBlock spdy_trailers_; std::string host_{"www.abc.com"}; std::string request_body_{"Hello world"}; }; -INSTANTIATE_TEST_SUITE_P(EnvoyQuicServerStreamTests, EnvoyQuicServerStreamTest, - testing::ValuesIn({true, false})); - -TEST_P(EnvoyQuicServerStreamTest, GetRequestAndResponse) { - quic::QuicHeaderList request_headers; - request_headers.OnHeaderBlockStart(); - request_headers.OnHeader(":authority", host_); - request_headers.OnHeader(":method", "GET"); - request_headers.OnHeader(":path", "/"); - // QUICHE stack doesn't coalesce Cookie headers for header compression optimization. - request_headers.OnHeader("cookie", "a=b"); - request_headers.OnHeader("cookie", "c=d"); - request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, - /*compressed_header_bytes=*/0); - - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/!quic::VersionUsesHttp3( - quic_version_.transport_version))) +TEST_F(EnvoyQuicServerStreamTest, GetRequestAndResponse) { + EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { EXPECT_EQ(host_, headers->getHostValue()); EXPECT_EQ("/", headers->getPathValue()); @@ -224,33 +172,28 @@ TEST_P(EnvoyQuicServerStreamTest, GetRequestAndResponse) { headers->get(Http::Headers::get().Cookie)[0]->value().getStringView()); } })); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); - spdy::SpdyHeaderBlock spdy_headers; - spdy_headers[":authority"] = host_; - spdy_headers[":method"] = "GET"; - spdy_headers[":path"] = "/"; - spdy_headers.AppendValueOrAddHeader("cookie", "a=b"); - spdy_headers.AppendValueOrAddHeader("cookie", "c=d"); - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_headers); - quic::QuicStreamFrame frame(stream_id_, true, 0, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/true, request_headers.uncompressed_header_bytes(), - request_headers); - } + EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); + spdy::SpdyHeaderBlock spdy_headers; + spdy_headers[":authority"] = host_; + spdy_headers[":method"] = "GET"; + spdy_headers[":path"] = "/"; + spdy_headers.AppendValueOrAddHeader("cookie", "a=b"); + spdy_headers.AppendValueOrAddHeader("cookie", "c=d"); + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_headers); + quic::QuicStreamFrame frame(stream_id_, true, 0, payload); + quic_stream_->OnStreamFrame(frame); EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/true); } -TEST_P(EnvoyQuicServerStreamTest, PostRequestAndResponse) { +TEST_F(EnvoyQuicServerStreamTest, PostRequestAndResponse) { EXPECT_EQ(absl::nullopt, quic_stream_->http1StreamEncoderOptions()); receiveRequest(request_body_, true, request_body_.size() * 2); quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/false); quic_stream_->encodeTrailers(response_trailers_); } -TEST_P(EnvoyQuicServerStreamTest, DecodeHeadersBodyAndTrailers) { +TEST_F(EnvoyQuicServerStreamTest, DecodeHeadersBodyAndTrailers) { size_t offset = receiveRequest(request_body_, false, request_body_.size() * 2); EXPECT_CALL(stream_decoder_, decodeTrailers_(_)) @@ -264,76 +207,36 @@ TEST_P(EnvoyQuicServerStreamTest, DecodeHeadersBodyAndTrailers) { EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); } -TEST_P(EnvoyQuicServerStreamTest, OutOfOrderTrailers) { - EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - return; - } - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { - EXPECT_EQ(host_, headers->getHostValue()); - EXPECT_EQ("/", headers->getPathValue()); - EXPECT_EQ(Http::Headers::get().MethodValues.Post, headers->getMethodValue()); - })); - quic_stream_->OnStreamHeaderList(/*fin=*/false, request_headers_.uncompressed_header_bytes(), - request_headers_); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - - // Trailer should be delivered to HCM later after body arrives. - receiveTrailers(request_body_.length()); - - quic::QuicStreamFrame frame(stream_id_, false, 0, request_body_); - EXPECT_CALL(stream_decoder_, decodeData(_, _)) - .WillOnce(Invoke([this](Buffer::Instance& buffer, bool finished_reading) { - EXPECT_EQ(request_body_, buffer.toString()); - EXPECT_FALSE(finished_reading); - })); - - EXPECT_CALL(stream_decoder_, decodeTrailers_(_)) - .WillOnce(Invoke([](const Http::RequestTrailerMapPtr& headers) { - Http::LowerCaseString key1("key1"); - Http::LowerCaseString key2(":final-offset"); - EXPECT_EQ("value1", headers->get(key1)[0]->value().getStringView()); - EXPECT_TRUE(headers->get(key2).empty()); - })); - quic_stream_->OnStreamFrame(frame); -} - -TEST_P(EnvoyQuicServerStreamTest, ResetStreamByHCM) { +TEST_F(EnvoyQuicServerStreamTest, ResetStreamByHCM) { receiveRequest(request_body_, false, request_body_.size() * 2); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); - } + EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); EXPECT_CALL(quic_session_, MaybeSendRstStreamFrame(_, _, _)); - EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); - quic_stream_->resetStream(Http::StreamResetReason::LocalReset); + EXPECT_CALL(stream_callbacks_, + onResetStream(Http::StreamResetReason::LocalRefusedStreamReset, _)); + quic_stream_->resetStream(Http::StreamResetReason::LocalRefusedStreamReset); EXPECT_TRUE(quic_stream_->rst_sent()); } -TEST_P(EnvoyQuicServerStreamTest, EarlyResponseWithStopSending) { +TEST_F(EnvoyQuicServerStreamTest, EarlyResponseWithStopSending) { receiveRequest(request_body_, false, request_body_.size() * 2); // Write response headers with FIN before finish receiving request. quic_stream_->encodeHeaders(response_headers_, true); // Resetting the stream now means stop reading and sending QUIC_STREAM_NO_ERROR or STOP_SENDING. - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); - } else { - EXPECT_CALL(quic_session_, MaybeSendRstStreamFrame(_, _, _)); - } + EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); quic_stream_->resetStream(Http::StreamResetReason::LocalReset); EXPECT_TRUE(quic_stream_->reading_stopped()); EXPECT_EQ(quic::QUIC_STREAM_NO_ERROR, quic_stream_->stream_error()); } -TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponLargePost) { +TEST_F(EnvoyQuicServerStreamTest, ReadDisableUponLargePost) { std::string large_request(1024, 'a'); // Sending such large request will cause read to be disabled. size_t payload_offset = receiveRequest(large_request, false, 512); EXPECT_FALSE(quic_stream_->HasBytesToRead()); // Disable reading one more time. quic_stream_->readDisable(true); - std::string second_part_request = bodyToStreamPayload("bbb"); + std::string second_part_request = bodyToHttp3StreamPayload("bbb"); // Receiving more data in the same event loop will push the receiving pipe line. EXPECT_CALL(stream_decoder_, decodeData(_, _)) .WillOnce(Invoke([](Buffer::Instance& buffer, bool finished_reading) { @@ -350,7 +253,7 @@ TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponLargePost) { dispatcher_->run(Event::Dispatcher::RunType::NonBlock); // This data frame should also be buffered. - std::string last_part_request = bodyToStreamPayload("ccc"); + std::string last_part_request = bodyToHttp3StreamPayload("ccc"); quic::QuicStreamFrame frame2(stream_id_, false, payload_offset, last_part_request); quic_stream_->OnStreamFrame(frame2); payload_offset += last_part_request.length(); @@ -375,18 +278,10 @@ TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponLargePost) { } // Tests that readDisable() doesn't cause re-entry of OnBodyAvailable(). -TEST_P(EnvoyQuicServerStreamTest, ReadDisableAndReEnableImmediately) { - EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { - EXPECT_EQ(host_, headers->getHostValue()); - EXPECT_EQ("/", headers->getPathValue()); - EXPECT_EQ(Http::Headers::get().MethodValues.Post, headers->getMethodValue()); - })); - quic_stream_->OnStreamHeaderList(/*fin=*/false, request_headers_.uncompressed_header_bytes(), - request_headers_); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - +TEST_F(EnvoyQuicServerStreamTest, ReadDisableAndReEnableImmediately) { std::string payload(1024, 'a'); + size_t offset = receiveRequest(payload, false, 2048); + EXPECT_CALL(stream_decoder_, decodeData(_, _)) .WillOnce(Invoke([&](Buffer::Instance& buffer, bool finished_reading) { EXPECT_EQ(payload, buffer.toString()); @@ -395,14 +290,15 @@ TEST_P(EnvoyQuicServerStreamTest, ReadDisableAndReEnableImmediately) { // Re-enable reading should not trigger another decodeData. quic_stream_->readDisable(false); })); - std::string data = bodyToStreamPayload(payload); - quic::QuicStreamFrame frame(stream_id_, false, 0, data); + std::string data = bodyToHttp3StreamPayload(payload); + quic::QuicStreamFrame frame(stream_id_, false, offset, data); quic_stream_->OnStreamFrame(frame); + offset += data.length(); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); // The stream shouldn't be blocked in the next event loop. - std::string last_part_request = bodyToStreamPayload("bbb"); - quic::QuicStreamFrame frame2(stream_id_, true, data.length(), last_part_request); + std::string last_part_request = bodyToHttp3StreamPayload("bbb"); + quic::QuicStreamFrame frame2(stream_id_, true, offset, last_part_request); EXPECT_CALL(stream_decoder_, decodeData(_, _)) .WillOnce(Invoke([&](Buffer::Instance& buffer, bool finished_reading) { EXPECT_EQ("bbb", buffer.toString()); @@ -415,43 +311,29 @@ TEST_P(EnvoyQuicServerStreamTest, ReadDisableAndReEnableImmediately) { EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); } -TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponHeaders) { +TEST_F(EnvoyQuicServerStreamTest, ReadDisableUponHeaders) { std::string payload(1024, 'a'); EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke( [this](const Http::RequestHeaderMapPtr&, bool) { quic_stream_->readDisable(true); })); EXPECT_CALL(stream_decoder_, decodeData(_, _)); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_request_headers_), - bodyToStreamPayload(payload)); - quic::QuicStreamFrame frame(stream_id_, false, 0, data); - quic_stream_->OnStreamFrame(frame); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - } else { - quic_stream_->OnStreamHeaderList(/*fin=*/false, request_headers_.uncompressed_header_bytes(), - request_headers_); - EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); - - quic::QuicStreamFrame frame(stream_id_, false, 0, payload); - quic_stream_->OnStreamFrame(frame); - } + std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_request_headers_), + bodyToHttp3StreamPayload(payload)); + quic::QuicStreamFrame frame(stream_id_, false, 0, data); + quic_stream_->OnStreamFrame(frame); + EXPECT_TRUE(quic_stream_->FinishedReadingHeaders()); // Stream should be blocked in the next event loop. dispatcher_->run(Event::Dispatcher::RunType::NonBlock); // Receiving more date shouldn't trigger decoding. EXPECT_CALL(stream_decoder_, decodeData(_, _)).Times(0); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - std::string data = bodyToStreamPayload(payload); - quic::QuicStreamFrame frame(stream_id_, false, 0, data); - quic_stream_->OnStreamFrame(frame); - } else { - quic::QuicStreamFrame frame(stream_id_, false, 0, payload); - quic_stream_->OnStreamFrame(frame); - } + data = bodyToHttp3StreamPayload(payload); + quic::QuicStreamFrame frame2(stream_id_, false, 0, data); + quic_stream_->OnStreamFrame(frame2); EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); } -TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponTrailers) { +TEST_F(EnvoyQuicServerStreamTest, ReadDisableUponTrailers) { size_t payload_offset = receiveRequest(request_body_, false, request_body_.length() * 2); EXPECT_FALSE(quic_stream_->HasBytesToRead()); @@ -466,7 +348,7 @@ TEST_P(EnvoyQuicServerStreamTest, ReadDisableUponTrailers) { // Tests that the stream with a send buffer whose high limit is 16k and low // limit is 8k sends over 32kB response. -TEST_P(EnvoyQuicServerStreamTest, WatermarkSendBuffer) { +TEST_F(EnvoyQuicServerStreamTest, WatermarkSendBuffer) { receiveRequest(request_body_, true, request_body_.size() * 2); // Bump connection flow control window large enough not to cause connection @@ -523,12 +405,7 @@ TEST_P(EnvoyQuicServerStreamTest, WatermarkSendBuffer) { EXPECT_TRUE(quic_stream_->write_side_closed()); } -TEST_P(EnvoyQuicServerStreamTest, HeadersContributeToWatermarkIquic) { - if (!quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(stream_callbacks_, onResetStream(_, _)); - return; - } - +TEST_F(EnvoyQuicServerStreamTest, HeadersContributeToWatermarkIquic) { receiveRequest(request_body_, true, request_body_.size() * 2); // Bump connection flow control window large enough not to cause connection level flow control @@ -596,78 +473,51 @@ TEST_P(EnvoyQuicServerStreamTest, HeadersContributeToWatermarkIquic) { quic_stream_->encodeTrailers(response_trailers_); } -TEST_P(EnvoyQuicServerStreamTest, RequestHeaderTooLarge) { +TEST_F(EnvoyQuicServerStreamTest, RequestHeaderTooLarge) { // Bump stream flow control window to allow request headers larger than 16K. quic::QuicWindowUpdateFrame window_update1(quic::kInvalidControlFrameId, quic_stream_->id(), 32 * 1024); quic_stream_->OnWindowUpdateFrame(window_update1); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); - } + EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); EXPECT_CALL(quic_session_, MaybeSendRstStreamFrame(_, _, _)); EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - spdy::SpdyHeaderBlock spdy_headers; - spdy_headers[":authority"] = host_; - spdy_headers[":method"] = "POST"; - spdy_headers[":path"] = "/"; - // This header exceeds max header size limit and should cause stream reset. - spdy_headers["long_header"] = std::string(16 * 1024 + 1, 'a'); - std::string payload = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_headers), - bodyToStreamPayload(request_body_)); - quic::QuicStreamFrame frame(stream_id_, false, 0, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic::QuicHeaderList request_headers; - request_headers.set_max_header_list_size(16 * 1024); - request_headers.OnHeaderBlockStart(); - request_headers.OnHeader(":authority", host_); - request_headers.OnHeader(":method", "POST"); - request_headers.OnHeader(":path", "/"); - request_headers.OnHeader("long_header", std::string(16 * 1024 + 1, 'a')); - request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, - /*compressed_header_bytes=*/0); - quic_stream_->OnStreamHeaderList(/*fin=*/false, request_headers.uncompressed_header_bytes(), - request_headers); - } + spdy::SpdyHeaderBlock spdy_headers; + spdy_headers[":authority"] = host_; + spdy_headers[":method"] = "POST"; + spdy_headers[":path"] = "/"; + // This header exceeds max header size limit and should cause stream reset. + spdy_headers["long_header"] = std::string(16 * 1024 + 1, 'a'); + std::string payload = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_headers), + bodyToHttp3StreamPayload(request_body_)); + quic::QuicStreamFrame frame(stream_id_, false, 0, payload); + quic_stream_->OnStreamFrame(frame); + EXPECT_TRUE(quic_stream_->rst_sent()); } -TEST_P(EnvoyQuicServerStreamTest, RequestTrailerTooLarge) { +TEST_F(EnvoyQuicServerStreamTest, RequestTrailerTooLarge) { // Bump stream flow control window to allow request headers larger than 16K. quic::QuicWindowUpdateFrame window_update1(quic::kInvalidControlFrameId, quic_stream_->id(), 20 * 1024); size_t offset = receiveRequest(request_body_, false, request_body_.size() * 2); quic_stream_->OnWindowUpdateFrame(window_update1); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); - } + EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); EXPECT_CALL(quic_session_, MaybeSendRstStreamFrame(_, _, _)); EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); - if (quic::VersionUsesHttp3(quic_version_.transport_version)) { - spdy::SpdyHeaderBlock spdy_trailers; - // This header exceeds max header size limit and should cause stream reset. - spdy_trailers["long_header"] = std::string(16 * 1024 + 1, 'a'); - std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers); - quic::QuicStreamFrame frame(stream_id_, false, offset, payload); - quic_stream_->OnStreamFrame(frame); - } else { - quic::QuicHeaderList spdy_trailers; - spdy_trailers.set_max_header_list_size(16 * 1024); - spdy_trailers.OnHeaderBlockStart(); - spdy_trailers.OnHeader("long_header", std::string(16 * 1024 + 1, 'a')); - spdy_trailers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, - /*compressed_header_bytes=*/0); - quic_stream_->OnStreamHeaderList(/*fin=*/true, spdy_trailers.uncompressed_header_bytes(), - spdy_trailers); - } + spdy::SpdyHeaderBlock spdy_trailers; + // This header exceeds max header size limit and should cause stream reset. + spdy_trailers["long_header"] = std::string(16 * 1024 + 1, 'a'); + std::string payload = spdyHeaderToHttp3StreamPayload(spdy_trailers); + quic::QuicStreamFrame frame(stream_id_, false, offset, payload); + quic_stream_->OnStreamFrame(frame); + EXPECT_TRUE(quic_stream_->rst_sent()); } // Tests that closing connection is QUICHE write call stack doesn't mess up // watermark buffer accounting. -TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseDuringEncoding) { +TEST_F(EnvoyQuicServerStreamTest, ConnectionCloseDuringEncoding) { receiveRequest(request_body_, true, request_body_.size() * 2); quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/false); std::string response(16 * 1024 + 1, 'a'); @@ -708,7 +558,7 @@ TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseDuringEncoding) { // Tests that after end_stream is encoded, closing connection shouldn't call // onResetStream() callbacks. -TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseAfterEndStreamEncoded) { +TEST_F(EnvoyQuicServerStreamTest, ConnectionCloseAfterEndStreamEncoded) { receiveRequest(request_body_, true, request_body_.size() * 2); EXPECT_CALL(quic_connection_, SendConnectionClosePacket(_, quic::NO_IETF_QUIC_ERROR, "Closed in WriteHeaders")); @@ -725,7 +575,7 @@ TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseAfterEndStreamEncoded) { quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/true); } -TEST_P(EnvoyQuicServerStreamTest, MetadataNotSupported) { +TEST_F(EnvoyQuicServerStreamTest, MetadataNotSupported) { Http::MetadataMap metadata_map = {{"key", "value"}}; Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); Http::MetadataMapVector metadata_map_vector; diff --git a/test/common/quic/envoy_quic_utils_test.cc b/test/common/quic/envoy_quic_utils_test.cc index 20a1dfe989690..cc2f49559f9a9 100644 --- a/test/common/quic/envoy_quic_utils_test.cc +++ b/test/common/quic/envoy_quic_utils_test.cc @@ -43,6 +43,7 @@ TEST(EnvoyQuicUtilsTest, ConversionBetweenQuicAddressAndEnvoyAddress) { EXPECT_EQ(quic_addr.ToString(), envoy_addr->asStringView()); EXPECT_EQ(quic_addr, envoyIpAddressToQuicSocketAddress(envoy_addr->ip())); } + EXPECT_FALSE(envoyIpAddressToQuicSocketAddress(nullptr).IsInitialized()); } class MockHeaderValidator : public HeaderValidator { diff --git a/test/common/quic/test_proof_verifier.h b/test/common/quic/test_proof_verifier.h index 3a0596a87b310..8546f92fd37ad 100644 --- a/test/common/quic/test_proof_verifier.h +++ b/test/common/quic/test_proof_verifier.h @@ -5,7 +5,7 @@ namespace Envoy { namespace Quic { -// A test quic::ProofVerifier which always approves the certs and signature. +// A test quic::ProofVerifier which always approves the certs. class TestProofVerifier : public EnvoyQuicProofVerifierBase { public: // quic::ProofVerifier @@ -18,14 +18,6 @@ class TestProofVerifier : public EnvoyQuicProofVerifierBase { std::unique_ptr /*callback*/) override { return quic::QUIC_SUCCESS; } - -protected: - // EnvoyQuicProofVerifierBase - bool verifySignature(const std::string& /*server_config*/, absl::string_view /*chlo_hash*/, - const std::string& /*cert*/, const std::string& /*signature*/, - std::string* /*error_details*/) override { - return true; - } }; } // namespace Quic diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 192f0d4465c65..7c499fd4c32dc 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -210,44 +210,12 @@ class MockEnvoyQuicClientSession : public EnvoyQuicClientSession { QuicStatNames quic_stat_names_{stats_store_.symbolTable()}; }; -Buffer::OwnedImpl -generateChloPacketToSend(quic::ParsedQuicVersion quic_version, quic::QuicConfig& quic_config, - quic::QuicCryptoServerConfig& crypto_config, - quic::QuicConnectionId connection_id, quic::QuicClock& clock, - const quic::QuicSocketAddress& server_address, - const quic::QuicSocketAddress& client_address, std::string sni) { - if (quic_version.UsesTls()) { - std::unique_ptr packet = - std::move(quic::test::GetFirstFlightOfPackets(quic_version, quic_config, connection_id)[0]); - return Buffer::OwnedImpl(packet->data(), packet->length()); - } - quic::CryptoHandshakeMessage chlo = quic::test::crypto_test_utils::GenerateDefaultInchoateCHLO( - &clock, quic_version.transport_version, &crypto_config); - chlo.SetVector(quic::kCOPT, quic::QuicTagVector{quic::kREJ}); - chlo.SetStringPiece(quic::kSNI, sni); - quic::CryptoHandshakeMessage full_chlo; - quic::QuicReferenceCountedPointer signed_config( - new quic::QuicSignedServerConfig); - quic::QuicCompressedCertsCache cache( - quic::QuicCompressedCertsCache::kQuicCompressedCertsCacheSize); - quic::test::crypto_test_utils::GenerateFullCHLO(chlo, &crypto_config, server_address, - client_address, quic_version.transport_version, - &clock, signed_config, &cache, &full_chlo); - // Overwrite version label to the version passed in. - full_chlo.SetVersion(quic::kVER, quic_version); - quic::QuicConfig quic_config_tmp; - quic_config_tmp.ToHandshakeMessage(&full_chlo, quic_version.transport_version); - - std::string packet_content(full_chlo.GetSerialized().AsStringPiece()); - quic::ParsedQuicVersionVector supported_versions{quic_version}; - auto encrypted_packet = - std::unique_ptr(quic::test::ConstructEncryptedPacket( - connection_id, quic::EmptyQuicConnectionId(), - /*version_flag=*/true, /*reset_flag*/ false, - /*packet_number=*/1, packet_content, quic::CONNECTION_ID_PRESENT, - quic::CONNECTION_ID_ABSENT, quic::PACKET_4BYTE_PACKET_NUMBER, &supported_versions)); - - return Buffer::OwnedImpl(encrypted_packet->data(), encrypted_packet->length()); +Buffer::OwnedImpl generateChloPacketToSend(quic::ParsedQuicVersion quic_version, + quic::QuicConfig& quic_config, + quic::QuicConnectionId connection_id) { + std::unique_ptr packet = + std::move(quic::test::GetFirstFlightOfPackets(quic_version, quic_config, connection_id)[0]); + return Buffer::OwnedImpl(packet->data(), packet->length()); } void setQuicConfigWithDefaultValues(quic::QuicConfig* config) { @@ -265,12 +233,6 @@ void setQuicConfigWithDefaultValues(quic::QuicConfig* config) { config, quic::kMinimumFlowControlSendWindow); } -enum class QuicVersionType { - GquicQuicCrypto, - GquicTls, - Iquic, -}; - std::string spdyHeaderToHttp3StreamPayload(const spdy::SpdyHeaderBlock& header) { quic::test::NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; quic::test::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; @@ -293,33 +255,25 @@ std::string bodyToHttp3StreamPayload(const std::string& body) { } // A test suite with variation of ip version and a knob to turn on/off IETF QUIC implementation. -class QuicMultiVersionTest - : public testing::TestWithParam> {}; +class QuicMultiVersionTest : public testing::TestWithParam< + std::pair> { +}; -std::vector> generateTestParam() { - std::vector> param; +std::vector> generateTestParam() { + std::vector> param; for (auto ip_version : TestEnvironment::getIpVersionsForTest()) { - param.emplace_back(ip_version, QuicVersionType::GquicQuicCrypto); - param.emplace_back(ip_version, QuicVersionType::GquicTls); - param.emplace_back(ip_version, QuicVersionType::Iquic); + for (const auto& quic_version : quic::CurrentSupportedHttp3Versions()) { + param.emplace_back(ip_version, quic_version); + } } - return param; } std::string testParamsToString( - const ::testing::TestParamInfo>& + const ::testing::TestParamInfo>& params) { std::string ip_version = params.param.first == Network::Address::IpVersion::v4 ? "IPv4" : "IPv6"; - switch (params.param.second) { - case QuicVersionType::GquicQuicCrypto: - return absl::StrCat(ip_version, "_UseGQuicWithQuicCrypto"); - case QuicVersionType::GquicTls: - return absl::StrCat(ip_version, "_UseGQuicWithTLS"); - case QuicVersionType::Iquic: - return absl::StrCat(ip_version, "_UseHttp3"); - } - NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + return absl::StrCat(ip_version, quic::QuicVersionToString(params.param.second.transport_version)); } } // namespace Quic diff --git a/test/extensions/filters/http/alternate_protocols_cache/filter_integration_test.cc b/test/extensions/filters/http/alternate_protocols_cache/filter_integration_test.cc index 344da0f9bf0b5..74660d6a2c6b2 100644 --- a/test/extensions/filters/http/alternate_protocols_cache/filter_integration_test.cc +++ b/test/extensions/filters/http/alternate_protocols_cache/filter_integration_test.cc @@ -60,7 +60,7 @@ TEST_P(FilterIntegrationTest, AltSvc) { {":method", "POST"}, {":path", "/test/long/url"}, {":scheme", "http"}, {":authority", "host"}, {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; int port = fake_upstreams_[0]->localAddress()->ip()->port(); - std::string alt_svc = absl::StrCat("h3-29=\":", port, "\"; ma=86400"); + std::string alt_svc = absl::StrCat("h3=\":", port, "\"; ma=86400"); Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}, {"alt-svc", alt_svc}}; // First request should go out over HTTP/2. The response includes an Alt-Svc header. diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index a32fda3ddbee4..3199ebcd21599 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -63,21 +63,14 @@ class CodecClientCallbacksForTest : public Http::CodecClientCallbacks { }; // A test that sets up its own client connection with customized quic version and connection ID. -class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVersionTest { +class QuicHttpIntegrationTest : public HttpIntegrationTest, + public testing::TestWithParam { public: QuicHttpIntegrationTest() - : HttpIntegrationTest(Http::CodecType::HTTP3, GetParam().first, + : HttpIntegrationTest(Http::CodecType::HTTP3, GetParam(), ConfigHelper::quicHttpProxyConfig()), - supported_versions_([]() { - if (GetParam().second == QuicVersionType::GquicQuicCrypto) { - return quic::CurrentSupportedVersionsWithQuicCrypto(); - } - bool use_http3 = GetParam().second == QuicVersionType::Iquic; - SetQuicReloadableFlag(quic_disable_version_draft_29, !use_http3); - SetQuicReloadableFlag(quic_disable_version_rfcv1, !use_http3); - return quic::CurrentSupportedVersions(); - }()), - conn_helper_(*dispatcher_), alarm_factory_(*dispatcher_, *conn_helper_.GetClock()) {} + supported_versions_(quic::CurrentSupportedHttp3Versions()), conn_helper_(*dispatcher_), + alarm_factory_(*dispatcher_, *conn_helper_.GetClock()) {} ~QuicHttpIntegrationTest() override { cleanupUpstreamAndDownstream(); @@ -172,13 +165,13 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers } constexpr auto timeout_first = std::chrono::seconds(15); constexpr auto timeout_subsequent = std::chrono::milliseconds(10); - if (GetParam().first == Network::Address::IpVersion::v4) { + if (GetParam() == Network::Address::IpVersion::v4) { test_server_->waitForCounterEq("listener.127.0.0.1_0.downstream_cx_total", 8u, timeout_first); } else { test_server_->waitForCounterEq("listener.[__1]_0.downstream_cx_total", 8u, timeout_first); } for (size_t i = 0; i < concurrency_; ++i) { - if (GetParam().first == Network::Address::IpVersion::v4) { + if (GetParam() == Network::Address::IpVersion::v4) { test_server_->waitForGaugeEq( fmt::format("listener.127.0.0.1_0.worker_{}.downstream_cx_active", i), 1u, timeout_subsequent); @@ -230,7 +223,8 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers }; INSTANTIATE_TEST_SUITE_P(QuicHttpIntegrationTests, QuicHttpIntegrationTest, - testing::ValuesIn(generateTestParam()), testParamsToString); + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); TEST_P(QuicHttpIntegrationTest, GetRequestAndEmptyResponse) { testRouterHeaderOnlyRequestAndResponse(); @@ -264,7 +258,7 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { ->EarlyDataAccepted()); // Close the second connection. codec_client_->close(); - if (GetParam().first == Network::Address::IpVersion::v4) { + if (GetParam() == Network::Address::IpVersion::v4) { test_server_->waitForCounterEq( "listener.127.0.0.1_0.http3.downstream.rx.quic_connection_close_error_" "code_QUIC_NO_ERROR", @@ -274,13 +268,8 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { "error_code_QUIC_NO_ERROR", 2u); } - if (GetParam().second == QuicVersionType::GquicQuicCrypto) { - test_server_->waitForCounterEq("http3.quic_version_50", 2u); - } else if (GetParam().second == QuicVersionType::GquicTls) { - test_server_->waitForCounterEq("http3.quic_version_51", 2u); - } else { - test_server_->waitForCounterEq("http3.quic_version_rfc_v1", 2u); - } + + test_server_->waitForCounterEq("http3.quic_version_rfc_v1", 2u); } // Ensure multiple quic connections work, regardless of platform BPF support @@ -340,6 +329,22 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { EXPECT_TRUE(upstream_request_->complete()); EXPECT_EQ(1024u * 2, upstream_request_->bodyLength()); + + // Switch to a socket with bad socket options. + auto option = std::make_shared(); + EXPECT_CALL(*option, setOption(_, _)) + .WillRepeatedly( + Invoke([](Network::Socket&, envoy::config::core::v3::SocketOption::SocketState state) { + if (state == envoy::config::core::v3::SocketOption::STATE_LISTENING) { + return false; + } + return true; + })); + auto options = std::make_shared(); + options->push_back(option); + quic_connection_->switchConnectionSocket( + createConnectionSocket(server_addr_, local_addr, options)); + EXPECT_TRUE(codec_client_->disconnected()); cleanupUpstreamAndDownstream(); } @@ -352,13 +357,9 @@ TEST_P(QuicHttpIntegrationTest, CertVerificationFailure) { initialize(); codec_client_ = makeRawHttpConnection(makeClientConnection((lookupPort("http"))), absl::nullopt); EXPECT_FALSE(codec_client_->connected()); - std::string failure_reason = - GetParam().second == QuicVersionType::GquicQuicCrypto - ? "QUIC_PROOF_INVALID with details: Proof invalid: X509_verify_cert: certificate " - "verification error at depth 0: ok" - : "QUIC_TLS_CERTIFICATE_UNKNOWN with details: TLS handshake failure " - "(ENCRYPTION_HANDSHAKE) 46: " - "certificate unknown"; + std::string failure_reason = "QUIC_TLS_CERTIFICATE_UNKNOWN with details: TLS handshake failure " + "(ENCRYPTION_HANDSHAKE) 46: " + "certificate unknown"; EXPECT_EQ(failure_reason, codec_client_->connection()->transportFailureReason()); } @@ -390,12 +391,10 @@ TEST_P(QuicHttpIntegrationTest, Reset101SwitchProtocolResponse) { EXPECT_FALSE(response->complete()); // Verify stream error counters are correctly incremented. - std::string counter_scope = GetParam().first == Network::Address::IpVersion::v4 + std::string counter_scope = GetParam() == Network::Address::IpVersion::v4 ? "listener.127.0.0.1_0.http3.downstream.rx." : "listener.[__1]_0.http3.downstream.rx."; - std::string error_code = GetParam().second == QuicVersionType::Iquic - ? "quic_reset_stream_error_code_QUIC_STREAM_GENERAL_PROTOCOL_ERROR" - : "quic_reset_stream_error_code_QUIC_BAD_APPLICATION_PAYLOAD"; + std::string error_code = "quic_reset_stream_error_code_QUIC_STREAM_GENERAL_PROTOCOL_ERROR"; test_server_->waitForCounterEq(absl::StrCat(counter_scope, error_code), 1U); }