Coverage Report

Created: 2023-11-12 09:30

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