Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/test/integration/http_integration.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include <cstdint>
4
#include <memory>
5
#include <string>
6
7
#include "source/common/http/codec_client.h"
8
#include "source/common/network/filter_impl.h"
9
10
#include "test/common/http/http2/http2_frame.h"
11
#include "test/integration/integration.h"
12
#include "test/integration/utility.h"
13
#include "test/test_common/printers.h"
14
#include "test/test_common/utility.h"
15
16
#ifdef ENVOY_ENABLE_QUIC
17
#include "quiche/quic/core/deterministic_connection_id_generator.h"
18
#endif
19
20
namespace Envoy {
21
22
using ::Envoy::Http::Http2::Http2Frame;
23
24
enum class Http2Impl {
25
  Nghttp2,
26
  Oghttp2,
27
};
28
29
/**
30
 * HTTP codec client used during integration testing.
31
 */
32
class IntegrationCodecClient : public Http::CodecClientProd {
33
public:
34
  IntegrationCodecClient(Event::Dispatcher& dispatcher, Random::RandomGenerator& random,
35
                         Network::ClientConnectionPtr&& conn,
36
                         Upstream::HostDescriptionConstSharedPtr host_description,
37
                         Http::CodecType type);
38
  IntegrationCodecClient(Event::Dispatcher& dispatcher, Random::RandomGenerator& random,
39
                         Network::ClientConnectionPtr&& conn,
40
                         Upstream::HostDescriptionConstSharedPtr host_description,
41
                         Http::CodecType type, bool wait_till_connected);
42
43
  IntegrationStreamDecoderPtr makeHeaderOnlyRequest(const Http::RequestHeaderMap& headers);
44
  IntegrationStreamDecoderPtr makeRequestWithBody(const Http::RequestHeaderMap& headers,
45
                                                  uint64_t body_size, bool end_stream = true);
46
  IntegrationStreamDecoderPtr makeRequestWithBody(const Http::RequestHeaderMap& headers,
47
                                                  const std::string& body, bool end_stream = true);
48
0
  bool sawGoAway() const { return saw_goaway_; }
49
815
  bool connected() const { return connected_; }
50
9.40k
  bool streamOpen() const { return !stream_gone_; }
51
  void sendData(Http::RequestEncoder& encoder, absl::string_view data, bool end_stream);
52
  void sendData(Http::RequestEncoder& encoder, Buffer::Instance& data, bool end_stream);
53
  void sendData(Http::RequestEncoder& encoder, uint64_t size, bool end_stream);
54
  void sendTrailers(Http::RequestEncoder& encoder, const Http::RequestTrailerMap& trailers);
55
  void sendReset(Http::RequestEncoder& encoder);
56
  // Intentionally makes a copy of metadata_map.
57
  void sendMetadata(Http::RequestEncoder& encoder, Http::MetadataMap metadata_map);
58
  IntegrationStreamDecoderPtr initRequest(const Http::RequestHeaderMap& headers,
59
                                          bool header_only_request = false);
60
  std::pair<Http::RequestEncoder&, IntegrationStreamDecoderPtr>
61
  startRequest(const Http::RequestHeaderMap& headers, bool header_only_request = false);
62
  ABSL_MUST_USE_RESULT AssertionResult
63
  waitForDisconnect(std::chrono::milliseconds time_to_wait = TestUtility::DefaultTimeout);
64
0
  Network::ClientConnection* connection() const { return connection_.get(); }
65
0
  Network::ConnectionEvent lastConnectionEvent() const { return last_connection_event_; }
66
0
  Network::Connection& rawConnection() { return *connection_; }
67
0
  bool disconnected() { return disconnected_; }
68
69
private:
70
  struct ConnectionCallbacks : public Network::ConnectionCallbacks {
71
    ConnectionCallbacks(IntegrationCodecClient& parent, bool block_till_connected)
72
815
        : parent_(parent), block_till_connected_(block_till_connected) {}
73
74
    // Network::ConnectionCallbacks
75
    void onEvent(Network::ConnectionEvent event) override;
76
0
    void onAboveWriteBufferHighWatermark() override {}
77
0
    void onBelowWriteBufferLowWatermark() override {}
78
79
    IntegrationCodecClient& parent_;
80
    bool block_till_connected_;
81
  };
82
83
  struct CodecCallbacks : public Http::ConnectionCallbacks {
84
815
    CodecCallbacks(IntegrationCodecClient& parent) : parent_(parent) {}
85
86
    // Http::ConnectionCallbacks
87
0
    void onGoAway(Http::GoAwayErrorCode) override { parent_.saw_goaway_ = true; }
88
89
    IntegrationCodecClient& parent_;
90
  };
91
92
  struct CodecClientCallbacks : public Http::CodecClientCallbacks {
93
815
    CodecClientCallbacks(IntegrationCodecClient& parent) : parent_(parent) {}
94
95
    // Http::CodecClientCallbacks
96
815
    void onStreamDestroy() override { parent_.stream_gone_ = true; }
97
11
    void onStreamReset(Http::StreamResetReason) override { parent_.stream_gone_ = true; }
98
99
    IntegrationCodecClient& parent_;
100
  };
101
102
  void flushWrite();
103
104
  Event::Dispatcher& dispatcher_;
105
  ConnectionCallbacks callbacks_;
106
  CodecCallbacks codec_callbacks_;
107
  CodecClientCallbacks codec_client_callbacks_;
108
  bool connected_{};
109
  bool disconnected_{};
110
  bool saw_goaway_{};
111
  bool stream_gone_{};
112
  Network::ConnectionEvent last_connection_event_;
113
};
114
115
using IntegrationCodecClientPtr = std::unique_ptr<IntegrationCodecClient>;
116
117
/**
118
 * Test fixture for HTTP and HTTP/2 integration tests.
119
 */
120
class HttpIntegrationTest : public BaseIntegrationTest {
121
public:
122
  HttpIntegrationTest(Http::CodecType downstream_protocol, Network::Address::IpVersion version)
123
      : HttpIntegrationTest(
124
            downstream_protocol, version,
125
            ConfigHelper::httpProxyConfig(/*downstream_use_quic=*/downstream_protocol ==
126
1.96k
                                          Http::CodecType::HTTP3)) {}
127
  HttpIntegrationTest(Http::CodecType downstream_protocol, Network::Address::IpVersion version,
128
                      const std::string& config);
129
130
  HttpIntegrationTest(Http::CodecType downstream_protocol,
131
                      const InstanceConstSharedPtrFn& upstream_address_fn,
132
                      Network::Address::IpVersion version)
133
      : HttpIntegrationTest(
134
            downstream_protocol, upstream_address_fn, version,
135
            ConfigHelper::httpProxyConfig(/*downstream_use_quic=*/downstream_protocol ==
136
0
                                          Http::CodecType::HTTP3)) {}
137
  HttpIntegrationTest(Http::CodecType downstream_protocol,
138
                      const InstanceConstSharedPtrFn& upstream_address_fn,
139
                      Network::Address::IpVersion version, const std::string& config);
140
  ~HttpIntegrationTest() override;
141
142
  void initialize() override;
143
  void setupHttp1ImplOverrides(Http1ParserImpl http1_implementation);
144
  void setupHttp2ImplOverrides(Http2Impl http2_implementation);
145
146
protected:
147
  void useAccessLog(absl::string_view format = "",
148
                    std::vector<envoy::config::core::v3::TypedExtensionConfig> formatters = {});
149
  std::string waitForAccessLog(const std::string& filename, uint32_t entry = 0,
150
                               bool allow_excess_entries = false,
151
0
                               Network::ClientConnection* client_connection = nullptr) {
152
0
    if (client_connection == nullptr && codec_client_) {
153
0
      client_connection = codec_client_->connection();
154
0
    }
155
0
    return BaseIntegrationTest::waitForAccessLog(filename, entry, allow_excess_entries,
156
0
                                                 client_connection);
157
0
  };
158
159
  IntegrationCodecClientPtr makeHttpConnection(uint32_t port);
160
  // Makes a http connection object without checking its connected state.
161
  virtual IntegrationCodecClientPtr
162
  makeRawHttpConnection(Network::ClientConnectionPtr&& conn,
163
                        absl::optional<envoy::config::core::v3::Http2ProtocolOptions> http2_options,
164
                        bool wait_till_connected = true);
165
  // Makes a downstream network connection object based on client codec version.
166
  Network::ClientConnectionPtr makeClientConnectionWithOptions(
167
      uint32_t port, const Network::ConnectionSocket::OptionsSharedPtr& options) override;
168
  // Makes a http connection object with asserting a connected state.
169
  IntegrationCodecClientPtr makeHttpConnection(Network::ClientConnectionPtr&& conn);
170
171
  // Sets downstream_protocol_ and alters the HTTP connection manager codec type in the
172
  // config_helper_.
173
  void setDownstreamProtocol(Http::CodecType type);
174
175
  // Enable the encoding/decoding of Http1 trailers downstream
176
  ConfigHelper::HttpModifierFunction setEnableDownstreamTrailersHttp1();
177
178
  // Enable the encoding/decoding of Http1 trailers upstream
179
  ConfigHelper::ConfigModifierFunction setEnableUpstreamTrailersHttp1();
180
181
  // Enable Proxy-Status response header.
182
  ConfigHelper::HttpModifierFunction configureProxyStatus();
183
184
  // Sends |request_headers| and |request_body_size| bytes of body upstream.
185
  // Configured upstream to send |response_headers| and |response_body_size|
186
  // bytes of body downstream.
187
  // Waits |time| ms for both the request to be proxied upstream and the
188
  // response to be proxied downstream.
189
  //
190
  // Waits for the complete downstream response before returning.
191
  // Requires |codec_client_| to be initialized.
192
  IntegrationStreamDecoderPtr sendRequestAndWaitForResponse(
193
      const Http::TestRequestHeaderMapImpl& request_headers, uint32_t request_body_size,
194
      const Http::TestResponseHeaderMapImpl& response_headers, uint32_t response_body_size,
195
      uint64_t upstream_index = 0, std::chrono::milliseconds timeout = TestUtility::DefaultTimeout);
196
197
  IntegrationStreamDecoderPtr sendRequestAndWaitForResponse(
198
      const Http::TestRequestHeaderMapImpl& request_headers, uint32_t request_body_size,
199
      const Http::TestResponseHeaderMapImpl& response_headers, uint32_t response_body_size,
200
      const std::vector<uint64_t>& upstream_indices,
201
      std::chrono::milliseconds timeout = TestUtility::DefaultTimeout);
202
203
  // Wait for the end of stream on the next upstream stream on any of the provided fake upstreams.
204
  // Sets fake_upstream_connection_ to the connection and upstream_request_ to stream.
205
  // In cases where the upstream that will receive the request is not deterministic, a second
206
  // upstream index may be provided, in which case both upstreams will be checked for requests.
207
  absl::optional<uint64_t> waitForNextUpstreamRequest(
208
      const std::vector<uint64_t>& upstream_indices,
209
      std::chrono::milliseconds connection_wait_timeout = TestUtility::DefaultTimeout);
210
  void waitForNextUpstreamRequest(
211
      uint64_t upstream_index = 0,
212
      std::chrono::milliseconds connection_wait_timeout = TestUtility::DefaultTimeout);
213
214
  absl::optional<uint64_t>
215
  waitForNextUpstreamConnection(const std::vector<uint64_t>& upstream_indices,
216
                                std::chrono::milliseconds connection_wait_timeout,
217
                                FakeHttpConnectionPtr& fake_upstream_connection);
218
219
  // Close |codec_client_| and |fake_upstream_connection_| cleanly.
220
  void cleanupUpstreamAndDownstream();
221
222
  // Verifies the response_headers contains the expected_headers, and response body matches given
223
  // body string.
224
  void verifyResponse(IntegrationStreamDecoderPtr response, const std::string& response_code,
225
                      const Http::TestResponseHeaderMapImpl& expected_headers,
226
                      const std::string& expected_body);
227
228
  // Helper that sends a request to Envoy, and verifies if Envoy response headers and body size is
229
  // the same as the expected headers map.
230
  // Requires the "http" port has been registered.
231
  void sendRequestAndVerifyResponse(const Http::TestRequestHeaderMapImpl& request_headers,
232
                                    const int request_size,
233
                                    const Http::TestResponseHeaderMapImpl& response_headers,
234
                                    const int response_size, const int backend_idx,
235
                                    absl::optional<const Http::TestResponseHeaderMapImpl>
236
                                        expected_response_headers = absl::nullopt);
237
238
  // Check for completion of upstream_request_, and a simple "200" response.
239
  void checkSimpleRequestSuccess(uint64_t expected_request_size, uint64_t expected_response_size,
240
                                 IntegrationStreamDecoder* response);
241
242
  using ConnectionCreationFunction = std::function<Network::ClientConnectionPtr()>;
243
  // Sends a simple header-only HTTP request, and waits for a response.
244
  IntegrationStreamDecoderPtr makeHeaderOnlyRequest(ConnectionCreationFunction* create_connection,
245
                                                    int upstream_index,
246
                                                    const std::string& path = "/test/long/url",
247
                                                    const std::string& overwrite_authority = "");
248
  void testRouterNotFound();
249
  void testRouterNotFoundWithBody();
250
  void testRouterVirtualClusters();
251
  void testRouteStats();
252
  void testRouterUpstreamProtocolError(const std::string&, const std::string&);
253
254
  void testRouterRequestAndResponseWithBody(
255
      uint64_t request_size, uint64_t response_size, bool big_header,
256
      bool set_content_length_header = false, ConnectionCreationFunction* creator = nullptr,
257
      std::chrono::milliseconds timeout = TestUtility::DefaultTimeout);
258
  void testRouterHeaderOnlyRequestAndResponse(ConnectionCreationFunction* creator = nullptr,
259
                                              int upstream_index = 0,
260
                                              const std::string& path = "/test/long/url",
261
                                              const std::string& overwrite_authority = "");
262
263
  // Disconnect tests
264
  void testRouterUpstreamDisconnectBeforeRequestComplete();
265
  void
266
  testRouterUpstreamDisconnectBeforeResponseComplete(ConnectionCreationFunction* creator = nullptr);
267
  void testRouterDownstreamDisconnectBeforeRequestComplete(
268
      ConnectionCreationFunction* creator = nullptr);
269
  void testRouterDownstreamDisconnectBeforeResponseComplete(
270
      ConnectionCreationFunction* creator = nullptr);
271
  void testRouterUpstreamResponseBeforeRequestComplete(uint32_t status_code = 0);
272
273
  void testTwoRequests(bool force_network_backup = false);
274
  void testLargeHeaders(Http::TestRequestHeaderMapImpl request_headers,
275
                        Http::TestRequestTrailerMapImpl request_trailers, uint32_t size,
276
                        uint32_t max_size);
277
  void testLargeRequestUrl(uint32_t url_size, uint32_t max_headers_size);
278
  void testLargeRequestHeaders(uint32_t size, uint32_t count, uint32_t max_size = 60,
279
                               uint32_t max_count = 100,
280
                               std::chrono::milliseconds timeout = TestUtility::DefaultTimeout);
281
  void testLargeRequestTrailers(uint32_t size, uint32_t max_size = 60);
282
  void testManyRequestHeaders(std::chrono::milliseconds time = TestUtility::DefaultTimeout);
283
284
  void testAddEncodedTrailers();
285
  void testRetry();
286
  void testRouterRetryOnResetBeforeRequestBeforeHeaders();
287
  void testRouterRetryOnResetBeforeRequestAfterHeaders();
288
  void testRetryHittingBufferLimit();
289
  void testRetryAttemptCountHeader();
290
  void testGrpcRetry();
291
292
  void testEnvoyHandling1xx(bool additional_continue_from_upstream = false,
293
                            const std::string& via = "", bool disconnect_after_100 = false);
294
  void testEnvoyProxying1xx(bool continue_before_upstream_complete = false,
295
                            bool with_encoder_filter = false,
296
                            bool with_multiple_1xx_headers = false,
297
                            absl::string_view initial_code = "100");
298
  void simultaneousRequest(uint32_t request1_bytes, uint32_t request2_bytes,
299
                           uint32_t response1_bytes, uint32_t response2_bytes);
300
301
  // HTTP/2 client tests.
302
  void testDownstreamResetBeforeResponseComplete();
303
  // Test that trailers are sent. request_trailers_present and
304
  // response_trailers_present will check if the trailers are present, otherwise
305
  // makes sure they were dropped.
306
  void testTrailers(uint64_t request_size, uint64_t response_size, bool request_trailers_present,
307
                    bool response_trailers_present);
308
  // Test /drain_listener from admin portal.
309
  void testAdminDrain(Http::CodecClient::Type admin_request_type);
310
311
  // Test sending and receiving large request and response bodies with autonomous upstream.
312
  void testGiantRequestAndResponse(uint64_t request_size, uint64_t response_size,
313
                                   bool set_content_length_header,
314
                                   std::chrono::milliseconds timeout = 2 *
315
                                                                       TestUtility::DefaultTimeout);
316
317
  struct BytesCountExpectation {
318
    BytesCountExpectation(int wire_bytes_sent, int wire_bytes_received, int header_bytes_sent,
319
                          int header_bytes_received)
320
        : wire_bytes_sent_{wire_bytes_sent}, wire_bytes_received_{wire_bytes_received},
321
0
          header_bytes_sent_{header_bytes_sent}, header_bytes_received_{header_bytes_received} {}
322
    int wire_bytes_sent_;
323
    int wire_bytes_received_;
324
    int header_bytes_sent_;
325
    int header_bytes_received_;
326
  };
327
328
  void expectUpstreamBytesSentAndReceived(BytesCountExpectation h1_expectation,
329
                                          BytesCountExpectation h2_expectation,
330
                                          BytesCountExpectation h3_expectation, const int id = 0);
331
332
  void expectDownstreamBytesSentAndReceived(BytesCountExpectation h1_expectation,
333
                                            BytesCountExpectation h2_expectation,
334
                                            BytesCountExpectation h3_expectation, const int id = 0);
335
336
0
  Http::CodecClient::Type downstreamProtocol() const { return downstream_protocol_; }
337
  std::string downstreamProtocolStatsRoot() const;
338
  // Return the upstream protocol part of the stats root.
339
  std::string upstreamProtocolStatsRoot() const;
340
  // Prefix listener stat with IP:port, including IP version dependent loopback address.
341
  std::string listenerStatPrefix(const std::string& stat_name);
342
343
  Network::UpstreamTransportSocketFactoryPtr quic_transport_socket_factory_;
344
  // Must outlive |codec_client_| because it may not close connection till the end of its life
345
  // scope.
346
  std::unique_ptr<Http::PersistentQuicInfo> quic_connection_persistent_info_;
347
  // The client making requests to Envoy.
348
  IntegrationCodecClientPtr codec_client_;
349
  // A placeholder for the first upstream connection.
350
  FakeHttpConnectionPtr fake_upstream_connection_;
351
  // A placeholder for the first request received at upstream.
352
  FakeStreamPtr upstream_request_;
353
  // A pointer to the request encoder, if used.
354
  Http::RequestEncoder* request_encoder_{nullptr};
355
  // The response headers sent by sendRequestAndWaitForResponse() by default.
356
  Http::TestResponseHeaderMapImpl default_response_headers_{{":status", "200"}};
357
  Http::TestRequestHeaderMapImpl default_request_headers_{{":method", "GET"},
358
                                                          {":path", "/test/long/url"},
359
                                                          {":scheme", "http"},
360
                                                          {":authority", "sni.lyft.com"}};
361
  // The codec type for the client-to-Envoy connection
362
  Http::CodecType downstream_protocol_{Http::CodecType::HTTP1};
363
  std::string access_log_name_;
364
  testing::NiceMock<Random::MockRandomGenerator> random_;
365
  Quic::QuicStatNames quic_stat_names_;
366
  std::string san_to_match_{"spiffe://lyft.com/backend-team"};
367
  bool enable_quic_early_data_{true};
368
  std::vector<absl::string_view> custom_alpns_;
369
  // Set this to true when sending malformed requests to avoid test client codec rejecting it.
370
  // This flag is only valid when UHV build flag is enabled.
371
  bool disable_client_header_validation_{false};
372
#ifdef ENVOY_ENABLE_QUIC
373
  quic::DeterministicConnectionIdGenerator connection_id_generator_{
374
      quic::kQuicDefaultConnectionIdLength};
375
#endif
376
};
377
378
// Helper class for integration tests using raw HTTP/2 frames
379
class Http2RawFrameIntegrationTest : public HttpIntegrationTest {
380
public:
381
  Http2RawFrameIntegrationTest(Network::Address::IpVersion version)
382
0
      : HttpIntegrationTest(Http::CodecType::HTTP2, version) {}
383
384
protected:
385
  void startHttp2Session();
386
  Http2Frame readFrame();
387
  void sendFrame(const Http2Frame& frame);
388
  virtual void beginSession();
389
390
  IntegrationTcpClientPtr tcp_client_;
391
};
392
393
absl::string_view upstreamToString(Http::CodecType type);
394
absl::string_view downstreamToString(Http::CodecType type);
395
absl::string_view http2ImplementationToString(Http2Impl impl);
396
397
} // namespace Envoy