/proc/self/cwd/test/common/quic/test_utils.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include "envoy/common/optref.h" |
4 | | #include "envoy/stream_info/stream_info.h" |
5 | | |
6 | | #include "source/common/quic/envoy_quic_client_connection.h" |
7 | | #include "source/common/quic/envoy_quic_client_session.h" |
8 | | #include "source/common/quic/envoy_quic_connection_debug_visitor_factory_interface.h" |
9 | | #include "source/common/quic/envoy_quic_network_observer_registry_factory.h" |
10 | | #include "source/common/quic/envoy_quic_proof_verifier.h" |
11 | | #include "source/common/quic/envoy_quic_server_connection.h" |
12 | | #include "source/common/quic/envoy_quic_utils.h" |
13 | | #include "source/common/quic/quic_filter_manager_connection_impl.h" |
14 | | #include "source/common/stats/isolated_store_impl.h" |
15 | | |
16 | | #include "test/common/config/dummy_config.pb.h" |
17 | | #include "test/test_common/environment.h" |
18 | | #include "test/test_common/utility.h" |
19 | | |
20 | | #include "quiche/quic/core/http/quic_spdy_session.h" |
21 | | #include "quiche/quic/core/qpack/qpack_encoder.h" |
22 | | #include "quiche/quic/core/quic_utils.h" |
23 | | #include "quiche/quic/test_tools/crypto_test_utils.h" |
24 | | #include "quiche/quic/test_tools/first_flight.h" |
25 | | #include "quiche/quic/test_tools/qpack/qpack_test_utils.h" |
26 | | #include "quiche/quic/test_tools/quic_config_peer.h" |
27 | | #include "quiche/quic/test_tools/quic_test_utils.h" |
28 | | |
29 | | namespace Envoy { |
30 | | namespace Quic { |
31 | | |
32 | | class MockEnvoyQuicServerConnection : public EnvoyQuicServerConnection { |
33 | | public: |
34 | | MockEnvoyQuicServerConnection(quic::QuicConnectionHelperInterface& helper, |
35 | | quic::QuicAlarmFactory& alarm_factory, |
36 | | quic::QuicPacketWriter& writer, |
37 | | const quic::ParsedQuicVersionVector& supported_versions, |
38 | | Network::Socket& listen_socket, |
39 | | quic::ConnectionIdGeneratorInterface& generator) |
40 | | : MockEnvoyQuicServerConnection( |
41 | | helper, alarm_factory, writer, |
42 | | quic::QuicSocketAddress(quic::QuicIpAddress::Any4(), 12345), |
43 | | quic::QuicSocketAddress(quic::QuicIpAddress::Loopback4(), 12345), supported_versions, |
44 | 0 | listen_socket, generator) {} |
45 | | |
46 | | MockEnvoyQuicServerConnection( |
47 | | quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, |
48 | | quic::QuicPacketWriter& writer, quic::QuicSocketAddress self_address, |
49 | | quic::QuicSocketAddress peer_address, const quic::ParsedQuicVersionVector& supported_versions, |
50 | | Network::Socket& listen_socket, quic::ConnectionIdGeneratorInterface& generator) |
51 | | : EnvoyQuicServerConnection( |
52 | | quic::test::TestConnectionId(), self_address, peer_address, helper, alarm_factory, |
53 | | &writer, /*owns_writer=*/false, supported_versions, |
54 | | createServerConnectionSocket(listen_socket.ioHandle(), self_address, peer_address, |
55 | | "example.com", "h3-29"), |
56 | 0 | generator, nullptr) {} |
57 | | |
58 | 0 | Network::Connection::ConnectionStats& connectionStats() const { |
59 | 0 | return QuicNetworkConnection::connectionStats(); |
60 | 0 | } |
61 | | |
62 | | MOCK_METHOD(void, SendConnectionClosePacket, |
63 | | (quic::QuicErrorCode, quic::QuicIetfTransportErrorCodes, const std::string&)); |
64 | | MOCK_METHOD(bool, SendControlFrame, (const quic::QuicFrame& frame)); |
65 | | MOCK_METHOD(quic::MessageStatus, SendMessage, |
66 | | (quic::QuicMessageId, absl::Span<quiche::QuicheMemSlice>, bool)); |
67 | | MOCK_METHOD(void, dumpState, (std::ostream&, int), (const)); |
68 | | }; |
69 | | |
70 | | class MockEnvoyQuicClientConnection : public EnvoyQuicClientConnection { |
71 | | public: |
72 | | MockEnvoyQuicClientConnection(const quic::QuicConnectionId& server_connection_id, |
73 | | quic::QuicConnectionHelperInterface& helper, |
74 | | quic::QuicAlarmFactory& alarm_factory, |
75 | | quic::QuicPacketWriter* writer, bool owns_writer, |
76 | | const quic::ParsedQuicVersionVector& supported_versions, |
77 | | Event::Dispatcher& dispatcher, |
78 | | Network::ConnectionSocketPtr&& connection_socket, |
79 | | quic::ConnectionIdGeneratorInterface& generator) |
80 | | : EnvoyQuicClientConnection(server_connection_id, helper, alarm_factory, writer, owns_writer, |
81 | | supported_versions, dispatcher, std::move(connection_socket), |
82 | 0 | generator, /*prefer_gro=*/true) {} |
83 | | |
84 | | MOCK_METHOD(quic::MessageStatus, SendMessage, |
85 | | (quic::QuicMessageId, absl::Span<quiche::QuicheMemSlice>, bool)); |
86 | | }; |
87 | | |
88 | | class TestQuicCryptoStream : public quic::test::MockQuicCryptoStream { |
89 | | public: |
90 | | explicit TestQuicCryptoStream(quic::QuicSession* session) |
91 | 0 | : quic::test::MockQuicCryptoStream(session) {} |
92 | | |
93 | 0 | bool encryption_established() const override { return true; } |
94 | | }; |
95 | | |
96 | | class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterManagerConnectionImpl { |
97 | | public: |
98 | | MockEnvoyQuicSession(const quic::QuicConfig& config, |
99 | | const quic::ParsedQuicVersionVector& supported_versions, |
100 | | EnvoyQuicServerConnection* connection, Event::Dispatcher& dispatcher, |
101 | | uint32_t send_buffer_limit, QuicStatNames& quic_stat_names, |
102 | | Stats::Scope& scope) |
103 | | : quic::QuicSpdySession(connection, /*visitor=*/nullptr, config, supported_versions), |
104 | | QuicFilterManagerConnectionImpl( |
105 | | *connection, connection->connection_id(), dispatcher, send_buffer_limit, {nullptr}, |
106 | | std::make_unique<StreamInfo::StreamInfoImpl>( |
107 | | dispatcher.timeSource(), |
108 | | connection->connectionSocket()->connectionInfoProviderSharedPtr(), |
109 | | StreamInfo::FilterState::LifeSpan::Connection), |
110 | | quic_stat_names, scope), |
111 | 0 | crypto_stream_(std::make_unique<TestQuicCryptoStream>(this)) {} |
112 | | |
113 | 0 | void Initialize() override { |
114 | 0 | quic::QuicSpdySession::Initialize(); |
115 | 0 | initialized_ = true; |
116 | 0 | } |
117 | | |
118 | | // From QuicSession. |
119 | | MOCK_METHOD(quic::QuicSpdyStream*, CreateIncomingStream, (quic::QuicStreamId id)); |
120 | | MOCK_METHOD(quic::QuicSpdyStream*, CreateIncomingStream, (quic::PendingStream * pending)); |
121 | | MOCK_METHOD(quic::QuicSpdyStream*, CreateOutgoingBidirectionalStream, ()); |
122 | | MOCK_METHOD(quic::QuicSpdyStream*, CreateOutgoingUnidirectionalStream, ()); |
123 | | MOCK_METHOD(bool, ShouldCreateIncomingStream, (quic::QuicStreamId id)); |
124 | | MOCK_METHOD(bool, ShouldCreateOutgoingBidirectionalStream, ()); |
125 | | MOCK_METHOD(bool, ShouldCreateOutgoingUnidirectionalStream, ()); |
126 | | MOCK_METHOD(quic::QuicConsumedData, WritevData, |
127 | | (quic::QuicStreamId id, size_t write_length, quic::QuicStreamOffset offset, |
128 | | quic::StreamSendingState state, quic::TransmissionType type, |
129 | | quic::EncryptionLevel level)); |
130 | | MOCK_METHOD(bool, ShouldYield, (quic::QuicStreamId id)); |
131 | | MOCK_METHOD(void, MaybeSendRstStreamFrame, |
132 | | (quic::QuicStreamId id, quic::QuicResetStreamError error, |
133 | | quic::QuicStreamOffset bytes_written)); |
134 | | MOCK_METHOD(void, MaybeSendStopSendingFrame, |
135 | | (quic::QuicStreamId id, quic::QuicResetStreamError error)); |
136 | | MOCK_METHOD(void, dumpState, (std::ostream&, int), (const)); |
137 | | |
138 | 0 | absl::string_view requestedServerName() const override { |
139 | 0 | return {GetCryptoStream()->crypto_negotiated_params().sni}; |
140 | 0 | } |
141 | | |
142 | 0 | quic::QuicCryptoStream* GetMutableCryptoStream() override { return crypto_stream_.get(); } |
143 | | |
144 | 0 | const quic::QuicCryptoStream* GetCryptoStream() const override { return crypto_stream_.get(); } |
145 | | |
146 | | using quic::QuicSpdySession::ActivateStream; |
147 | | |
148 | | protected: |
149 | 0 | quic::HttpDatagramSupport LocalHttpDatagramSupport() override { |
150 | 0 | return quic::HttpDatagramSupport::kRfc; |
151 | 0 | } |
152 | 0 | bool hasDataToWrite() override { return HasDataToWrite(); } |
153 | 0 | const quic::QuicConnection* quicConnection() const override { |
154 | 0 | return initialized_ ? connection() : nullptr; |
155 | 0 | } |
156 | 0 | quic::QuicConnection* quicConnection() override { return initialized_ ? connection() : nullptr; } |
157 | | |
158 | | private: |
159 | | std::unique_ptr<quic::QuicCryptoStream> crypto_stream_; |
160 | | }; |
161 | | |
162 | | class TestQuicCryptoClientStream : public quic::QuicCryptoClientStream { |
163 | | public: |
164 | | TestQuicCryptoClientStream(const quic::QuicServerId& server_id, quic::QuicSession* session, |
165 | | std::unique_ptr<quic::ProofVerifyContext> verify_context, |
166 | | quic::QuicCryptoClientConfig* crypto_config, |
167 | | ProofHandler* proof_handler, bool has_application_state) |
168 | | : quic::QuicCryptoClientStream(server_id, session, std::move(verify_context), crypto_config, |
169 | 0 | proof_handler, has_application_state) {} |
170 | | |
171 | 0 | bool encryption_established() const override { return true; } |
172 | 0 | quic::HandshakeState GetHandshakeState() const override { return quic::HANDSHAKE_CONFIRMED; } |
173 | | }; |
174 | | |
175 | | class TestQuicCryptoClientStreamFactory : public EnvoyQuicCryptoClientStreamFactoryInterface { |
176 | | public: |
177 | | std::unique_ptr<quic::QuicCryptoClientStreamBase> |
178 | | createEnvoyQuicCryptoClientStream(const quic::QuicServerId& server_id, quic::QuicSession* session, |
179 | | std::unique_ptr<quic::ProofVerifyContext> verify_context, |
180 | | quic::QuicCryptoClientConfig* crypto_config, |
181 | | quic::QuicCryptoClientStream::ProofHandler* proof_handler, |
182 | 0 | bool has_application_state) override { |
183 | 0 | last_verify_context_ = *verify_context; |
184 | 0 | return std::make_unique<TestQuicCryptoClientStream>(server_id, session, |
185 | 0 | std::move(verify_context), crypto_config, |
186 | 0 | proof_handler, has_application_state); |
187 | 0 | } |
188 | | |
189 | 0 | OptRef<quic::ProofVerifyContext> lastVerifyContext() const { return last_verify_context_; } |
190 | | |
191 | | private: |
192 | | OptRef<quic::ProofVerifyContext> last_verify_context_; |
193 | | }; |
194 | | |
195 | | class IsolatedStoreProvider { |
196 | | protected: |
197 | | Stats::IsolatedStoreImpl stats_store_; |
198 | | }; |
199 | | |
200 | | class MockEnvoyQuicClientSession : public IsolatedStoreProvider, public EnvoyQuicClientSession { |
201 | | public: |
202 | | MockEnvoyQuicClientSession(const quic::QuicConfig& config, |
203 | | const quic::ParsedQuicVersionVector& supported_versions, |
204 | | std::unique_ptr<EnvoyQuicClientConnection> connection, |
205 | | Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, |
206 | | EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory) |
207 | | : EnvoyQuicClientSession(config, supported_versions, std::move(connection), |
208 | | quic::QuicServerId("example.com", 443, false), |
209 | | std::make_shared<quic::QuicCryptoClientConfig>( |
210 | | quic::test::crypto_test_utils::ProofVerifierForTesting()), |
211 | | dispatcher, send_buffer_limit, crypto_stream_factory, |
212 | 0 | quic_stat_names_, {}, *stats_store_.rootScope(), nullptr, {}) {} |
213 | | |
214 | 0 | void Initialize() override { |
215 | 0 | EnvoyQuicClientSession::Initialize(); |
216 | 0 | initialized_ = true; |
217 | 0 | } |
218 | | |
219 | | // From QuicSession. |
220 | | MOCK_METHOD(quic::QuicSpdyClientStream*, CreateIncomingStream, (quic::QuicStreamId id)); |
221 | | MOCK_METHOD(quic::QuicSpdyClientStream*, CreateIncomingStream, (quic::PendingStream * pending)); |
222 | | MOCK_METHOD(quic::QuicSpdyClientStream*, CreateOutgoingBidirectionalStream, ()); |
223 | | MOCK_METHOD(quic::QuicSpdyClientStream*, CreateOutgoingUnidirectionalStream, ()); |
224 | | MOCK_METHOD(bool, ShouldCreateIncomingStream, (quic::QuicStreamId id)); |
225 | | MOCK_METHOD(bool, ShouldCreateOutgoingBidirectionalStream, ()); |
226 | | MOCK_METHOD(bool, ShouldCreateOutgoingUnidirectionalStream, ()); |
227 | | MOCK_METHOD(quic::QuicConsumedData, WritevData, |
228 | | (quic::QuicStreamId id, size_t write_length, quic::QuicStreamOffset offset, |
229 | | quic::StreamSendingState state, quic::TransmissionType type, |
230 | | quic::EncryptionLevel level)); |
231 | | MOCK_METHOD(bool, ShouldYield, (quic::QuicStreamId id)); |
232 | | MOCK_METHOD(void, dumpState, (std::ostream&, int), (const)); |
233 | | |
234 | 0 | absl::string_view requestedServerName() const override { |
235 | 0 | return {GetCryptoStream()->crypto_negotiated_params().sni}; |
236 | 0 | } |
237 | | |
238 | | using quic::QuicSession::closed_streams; |
239 | | using quic::QuicSpdySession::ActivateStream; |
240 | | |
241 | | protected: |
242 | 0 | quic::HttpDatagramSupport LocalHttpDatagramSupport() override { |
243 | 0 | return quic::HttpDatagramSupport::kRfc; |
244 | 0 | } |
245 | 0 | bool hasDataToWrite() override { return HasDataToWrite(); } |
246 | 0 | const quic::QuicConnection* quicConnection() const override { |
247 | 0 | return initialized_ ? connection() : nullptr; |
248 | 0 | } |
249 | 0 | quic::QuicConnection* quicConnection() override { return initialized_ ? connection() : nullptr; } |
250 | | |
251 | | QuicStatNames quic_stat_names_{stats_store_.symbolTable()}; |
252 | | }; |
253 | | |
254 | | Buffer::OwnedImpl generateChloPacketToSend(quic::ParsedQuicVersion quic_version, |
255 | | quic::QuicConfig& quic_config, |
256 | 0 | quic::QuicConnectionId connection_id) { |
257 | 0 | std::unique_ptr<quic::QuicReceivedPacket> packet = |
258 | 0 | std::move(quic::test::GetFirstFlightOfPackets(quic_version, quic_config, connection_id)[0]); |
259 | 0 | return {packet->data(), packet->length()}; |
260 | 0 | } |
261 | | |
262 | 7 | void setQuicConfigWithDefaultValues(quic::QuicConfig* config) { |
263 | 7 | quic::test::QuicConfigPeer::SetReceivedMaxBidirectionalStreams( |
264 | 7 | config, quic::kDefaultMaxStreamsPerConnection); |
265 | 7 | quic::test::QuicConfigPeer::SetReceivedMaxUnidirectionalStreams( |
266 | 7 | config, quic::kDefaultMaxStreamsPerConnection); |
267 | 7 | quic::test::QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional( |
268 | 7 | config, quic::kMinimumFlowControlSendWindow); |
269 | 7 | quic::test::QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesIncomingBidirectional( |
270 | 7 | config, quic::kMinimumFlowControlSendWindow); |
271 | 7 | quic::test::QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesOutgoingBidirectional( |
272 | 7 | config, quic::kMinimumFlowControlSendWindow); |
273 | 7 | quic::test::QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( |
274 | 7 | config, quic::kMinimumFlowControlSendWindow); |
275 | 7 | } |
276 | | |
277 | 0 | std::string spdyHeaderToHttp3StreamPayload(const quiche::HttpHeaderBlock& header) { |
278 | 0 | quic::test::NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; |
279 | 0 | quic::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; |
280 | 0 | auto qpack_encoder = std::make_unique<quic::QpackEncoder>(&decoder_stream_error_delegate, |
281 | 0 | quic::HuffmanEncoding::kEnabled, |
282 | 0 | quic::CookieCrumbling::kEnabled); |
283 | 0 | qpack_encoder->set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate); |
284 | | // QpackEncoder does not use the dynamic table by default, |
285 | | // therefore the value of |stream_id| does not matter. |
286 | 0 | std::string payload = qpack_encoder->EncodeHeaderList(/* stream_id = */ 0, header, nullptr); |
287 | 0 | std::string headers_frame_header = |
288 | 0 | quic::HttpEncoder::SerializeHeadersFrameHeader(payload.length()); |
289 | 0 | return absl::StrCat(headers_frame_header, payload); |
290 | 0 | } |
291 | | |
292 | 0 | std::string bodyToHttp3StreamPayload(const std::string& body) { |
293 | 0 | quiche::SimpleBufferAllocator allocator; |
294 | 0 | quiche::QuicheBuffer header = |
295 | 0 | quic::HttpEncoder::SerializeDataFrameHeader(body.length(), &allocator); |
296 | 0 | return absl::StrCat(header.AsStringView(), body); |
297 | 0 | } |
298 | | |
299 | | // A test suite with variation of ip version and a knob to turn on/off IETF QUIC implementation. |
300 | | class QuicMultiVersionTest : public testing::TestWithParam< |
301 | | std::pair<Network::Address::IpVersion, quic::ParsedQuicVersion>> { |
302 | | }; |
303 | | |
304 | 0 | std::vector<std::pair<Network::Address::IpVersion, quic::ParsedQuicVersion>> generateTestParam() { |
305 | 0 | std::vector<std::pair<Network::Address::IpVersion, quic::ParsedQuicVersion>> param; |
306 | 0 | for (auto ip_version : TestEnvironment::getIpVersionsForTest()) { |
307 | 0 | for (const auto& quic_version : quic::CurrentSupportedHttp3Versions()) { |
308 | 0 | param.emplace_back(ip_version, quic_version); |
309 | 0 | } |
310 | 0 | } |
311 | 0 | return param; |
312 | 0 | } |
313 | | |
314 | | std::string testParamsToString( |
315 | | const ::testing::TestParamInfo<std::pair<Network::Address::IpVersion, quic::ParsedQuicVersion>>& |
316 | 0 | params) { |
317 | 0 | return absl::StrCat(TestUtility::ipVersionToString(params.param.first), |
318 | 0 | quic::QuicVersionToString(params.param.second.transport_version)); |
319 | 0 | } |
320 | | |
321 | | class MockProofVerifyContext : public EnvoyQuicProofVerifyContext { |
322 | | public: |
323 | | MOCK_METHOD(Event::Dispatcher&, dispatcher, (), (const)); |
324 | | MOCK_METHOD(bool, isServer, (), (const)); |
325 | | MOCK_METHOD(const Network::TransportSocketOptionsConstSharedPtr&, transportSocketOptions, (), |
326 | | (const)); |
327 | | MOCK_METHOD(Extensions::TransportSockets::Tls::CertValidator::ExtraValidationContext, |
328 | | extraValidationContext, (), (const)); |
329 | | }; |
330 | | |
331 | | class MockQuicConnectionDebugVisitor : public quic::QuicConnectionDebugVisitor { |
332 | | public: |
333 | | MockQuicConnectionDebugVisitor(quic::QuicSession* session, |
334 | | const StreamInfo::StreamInfo& stream_info) |
335 | 0 | : session_(session), stream_info_(stream_info) {} |
336 | | |
337 | | MOCK_METHOD(void, OnConnectionClosed, |
338 | | (const quic::QuicConnectionCloseFrame&, quic::ConnectionCloseSource), ()); |
339 | | MOCK_METHOD(void, OnConnectionCloseFrame, (const quic::QuicConnectionCloseFrame&), ()); |
340 | | |
341 | | quic::QuicSession* session_; |
342 | | const StreamInfo::StreamInfo& stream_info_; |
343 | | }; |
344 | | |
345 | | class TestEnvoyQuicConnectionDebugVisitorFactory |
346 | | : public EnvoyQuicConnectionDebugVisitorFactoryInterface { |
347 | | public: |
348 | 4 | std::string name() const override { return "envoy.quic.connection_debug_visitor.mock"; } |
349 | | |
350 | 0 | Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() override { |
351 | 0 | return std::make_unique<::test::common::config::DummyConfig>(); |
352 | 0 | } |
353 | | std::unique_ptr<quic::QuicConnectionDebugVisitor> |
354 | | createQuicConnectionDebugVisitor(quic::QuicSession* session, |
355 | 0 | const StreamInfo::StreamInfo& stream_info) override { |
356 | 0 | auto debug_visitor = std::make_unique<MockQuicConnectionDebugVisitor>(session, stream_info); |
357 | 0 | mock_debug_visitor_ = debug_visitor.get(); |
358 | 0 | return debug_visitor; |
359 | 0 | } |
360 | | |
361 | 0 | Envoy::ProcessContextOptRef processContext() const { return context_; } |
362 | | |
363 | | MockQuicConnectionDebugVisitor* mock_debug_visitor_; |
364 | | }; |
365 | | |
366 | | DECLARE_FACTORY(TestEnvoyQuicConnectionDebugVisitorFactory); |
367 | | |
368 | | REGISTER_FACTORY(TestEnvoyQuicConnectionDebugVisitorFactory, |
369 | | Envoy::Quic::EnvoyQuicConnectionDebugVisitorFactoryInterface); |
370 | | |
371 | | class TestNetworkObserverRegistry : public Quic::EnvoyQuicNetworkObserverRegistry { |
372 | | public: |
373 | 0 | void onNetworkChanged() { |
374 | 0 | std::list<Quic::QuicNetworkConnectivityObserver*> existing_observers; |
375 | 0 | for (Quic::QuicNetworkConnectivityObserver* observer : registeredQuicObservers()) { |
376 | 0 | existing_observers.push_back(observer); |
377 | 0 | } |
378 | 0 | for (auto* observer : existing_observers) { |
379 | 0 | observer->onNetworkChanged(); |
380 | 0 | } |
381 | 0 | } |
382 | | using Quic::EnvoyQuicNetworkObserverRegistry::registeredQuicObservers; |
383 | | }; |
384 | | |
385 | | } // namespace Quic |
386 | | } // namespace Envoy |