Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/test/integration/http_integration.cc
Line
Count
Source (jump to first uncovered line)
1
#include "test/integration/http_integration.h"
2
3
#include <functional>
4
#include <list>
5
#include <memory>
6
#include <regex>
7
#include <string>
8
#include <vector>
9
10
#include "envoy/buffer/buffer.h"
11
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
12
#include "envoy/event/dispatcher.h"
13
#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"
14
#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h"
15
#include "envoy/http/header_map.h"
16
#include "envoy/network/address.h"
17
#include "envoy/registry/registry.h"
18
19
#include "source/common/api/api_impl.h"
20
#include "source/common/buffer/buffer_impl.h"
21
#include "source/common/common/fmt.h"
22
#include "source/common/common/thread_annotations.h"
23
#include "source/common/http/headers.h"
24
#include "source/common/network/socket_option_impl.h"
25
#include "source/common/network/utility.h"
26
#include "source/common/protobuf/utility.h"
27
#include "source/common/runtime/runtime_impl.h"
28
#include "source/common/upstream/upstream_impl.h"
29
30
#ifdef ENVOY_ENABLE_QUIC
31
#include "source/common/quic/client_connection_factory_impl.h"
32
#include "source/common/quic/quic_client_transport_socket_factory.h"
33
#endif
34
35
#include "source/common/tls/context_config_impl.h"
36
#include "source/common/tls/context_impl.h"
37
#include "source/common/tls/client_ssl_socket.h"
38
#include "source/common/tls/server_ssl_socket.h"
39
40
#include "test/common/upstream/utility.h"
41
#include "test/integration/autonomous_upstream.h"
42
#include "test/integration/ssl_utility.h"
43
#include "test/integration/test_host_predicate_config.h"
44
#include "test/integration/utility.h"
45
#include "test/mocks/upstream/cluster_info.h"
46
#include "test/test_common/environment.h"
47
#include "test/test_common/network_utility.h"
48
#include "test/test_common/registry.h"
49
50
#include "absl/time/time.h"
51
#include "base_integration_test.h"
52
#include "gtest/gtest.h"
53
54
namespace Envoy {
55
namespace {
56
57
using testing::HasSubstr;
58
59
envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::CodecType
60
3.07k
typeToCodecType(Http::CodecType type) {
61
3.07k
  switch (type) {
62
862
  case Http::CodecType::HTTP1:
63
862
    return envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
64
862
        HTTP1;
65
2.21k
  case Http::CodecType::HTTP2:
66
2.21k
    return envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
67
2.21k
        HTTP2;
68
0
  case Http::CodecType::HTTP3:
69
0
    return envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
70
0
        HTTP3;
71
0
  default:
72
0
    RELEASE_ASSERT(0, "");
73
3.07k
  }
74
3.07k
}
75
76
} // namespace
77
78
IntegrationCodecClient::IntegrationCodecClient(
79
    Event::Dispatcher& dispatcher, Random::RandomGenerator& random,
80
    Network::ClientConnectionPtr&& conn, Upstream::HostDescriptionConstSharedPtr host_description,
81
    Http::CodecType type)
82
    : IntegrationCodecClient(dispatcher, random, std::move(conn), std::move(host_description), type,
83
0
                             true) {}
84
85
IntegrationCodecClient::IntegrationCodecClient(
86
    Event::Dispatcher& dispatcher, Random::RandomGenerator& random,
87
    Network::ClientConnectionPtr&& conn, Upstream::HostDescriptionConstSharedPtr host_description,
88
    Http::CodecType type, bool wait_till_connected)
89
    : CodecClientProd(type, std::move(conn), host_description, dispatcher, random, nullptr),
90
      dispatcher_(dispatcher), callbacks_(*this, wait_till_connected), codec_callbacks_(*this),
91
815
      codec_client_callbacks_(*this) {
92
815
  connection_->addConnectionCallbacks(callbacks_);
93
815
  setCodecConnectionCallbacks(codec_callbacks_);
94
815
  setCodecClientCallbacks(codec_client_callbacks_);
95
815
  if (wait_till_connected) {
96
815
    dispatcher.run(Event::Dispatcher::RunType::Block);
97
815
  }
98
815
}
99
100
10.2k
void IntegrationCodecClient::flushWrite() {
101
10.2k
  connection_->dispatcher().run(Event::Dispatcher::RunType::NonBlock);
102
  // NOTE: We should run blocking until all the body data is flushed.
103
10.2k
}
104
105
IntegrationStreamDecoderPtr
106
495
IntegrationCodecClient::makeHeaderOnlyRequest(const Http::RequestHeaderMap& headers) {
107
495
  auto response = std::make_unique<IntegrationStreamDecoder>(dispatcher_);
108
495
  Http::RequestEncoder& encoder = newStream(*response);
109
495
  encoder.getStream().addCallbacks(*response);
110
495
  encoder.encodeHeaders(headers, true).IgnoreError();
111
495
  flushWrite();
112
495
  return response;
113
495
}
114
115
IntegrationStreamDecoderPtr
116
IntegrationCodecClient::makeRequestWithBody(const Http::RequestHeaderMap& headers,
117
0
                                            uint64_t body_size, bool end_stream) {
118
0
  return makeRequestWithBody(headers, std::string(body_size, 'a'), end_stream);
119
0
}
120
121
IntegrationStreamDecoderPtr
122
IntegrationCodecClient::makeRequestWithBody(const Http::RequestHeaderMap& headers,
123
39
                                            const std::string& body, bool end_stream) {
124
39
  auto response = std::make_unique<IntegrationStreamDecoder>(dispatcher_);
125
39
  Http::RequestEncoder& encoder = newStream(*response);
126
39
  encoder.getStream().addCallbacks(*response);
127
39
  encoder.encodeHeaders(headers, false).IgnoreError();
128
39
  Buffer::OwnedImpl data(body);
129
39
  encoder.encodeData(data, end_stream);
130
39
  flushWrite();
131
39
  return response;
132
39
}
133
134
void IntegrationCodecClient::sendData(Http::RequestEncoder& encoder, absl::string_view data,
135
0
                                      bool end_stream) {
136
0
  Buffer::OwnedImpl buffer_data(data.data(), data.size());
137
0
  encoder.encodeData(buffer_data, end_stream);
138
0
  flushWrite();
139
0
}
140
141
void IntegrationCodecClient::sendData(Http::RequestEncoder& encoder, Buffer::Instance& data,
142
9.40k
                                      bool end_stream) {
143
9.40k
  encoder.encodeData(data, end_stream);
144
9.40k
  flushWrite();
145
9.40k
}
146
147
void IntegrationCodecClient::sendData(Http::RequestEncoder& encoder, uint64_t size,
148
9.12k
                                      bool end_stream) {
149
9.12k
  Buffer::OwnedImpl data(std::string(size, 'a'));
150
9.12k
  sendData(encoder, data, end_stream);
151
9.12k
}
152
153
void IntegrationCodecClient::sendTrailers(Http::RequestEncoder& encoder,
154
0
                                          const Http::RequestTrailerMap& trailers) {
155
0
  encoder.encodeTrailers(trailers);
156
0
  flushWrite();
157
0
}
158
159
0
void IntegrationCodecClient::sendReset(Http::RequestEncoder& encoder) {
160
0
  encoder.getStream().resetStream(Http::StreamResetReason::LocalReset);
161
0
  flushWrite();
162
0
}
163
164
void IntegrationCodecClient::sendMetadata(Http::RequestEncoder& encoder,
165
0
                                          Http::MetadataMap metadata_map) {
166
0
  Http::MetadataMapPtr metadata_map_ptr = std::make_unique<Http::MetadataMap>(metadata_map);
167
0
  Http::MetadataMapVector metadata_map_vector;
168
0
  metadata_map_vector.push_back(std::move(metadata_map_ptr));
169
0
  encoder.encodeMetadata(metadata_map_vector);
170
0
  flushWrite();
171
0
}
172
173
std::pair<Http::RequestEncoder&, IntegrationStreamDecoderPtr>
174
IntegrationCodecClient::startRequest(const Http::RequestHeaderMap& headers,
175
281
                                     bool header_only_request) {
176
281
  auto response = std::make_unique<IntegrationStreamDecoder>(dispatcher_);
177
281
  Http::RequestEncoder& encoder = newStream(*response);
178
281
  encoder.getStream().addCallbacks(*response);
179
281
  encoder.encodeHeaders(headers, /*end_stream=*/header_only_request).IgnoreError();
180
281
  flushWrite();
181
281
  return {encoder, std::move(response)};
182
281
}
183
184
0
AssertionResult IntegrationCodecClient::waitForDisconnect(std::chrono::milliseconds time_to_wait) {
185
0
  if (disconnected_) {
186
0
    return AssertionSuccess();
187
0
  }
188
0
  Event::TimerPtr wait_timer;
189
0
  bool wait_timer_triggered = false;
190
0
  if (time_to_wait.count()) {
191
0
    wait_timer = connection_->dispatcher().createTimer([this, &wait_timer_triggered] {
192
0
      connection_->dispatcher().exit();
193
0
      wait_timer_triggered = true;
194
0
    });
195
0
    wait_timer->enableTimer(time_to_wait);
196
0
  }
197
198
0
  connection_->dispatcher().run(Event::Dispatcher::RunType::Block);
199
200
  // Disable the timer if it was created. This call is harmless if the timer already triggered.
201
0
  if (wait_timer) {
202
0
    wait_timer->disableTimer();
203
0
  }
204
205
0
  if (wait_timer_triggered && !disconnected_) {
206
0
    if (time_to_wait == TestUtility::DefaultTimeout) {
207
0
      ADD_FAILURE() << "Please don't waitForDisconnect with a 5s timeout if failure is expected\n";
208
0
    }
209
0
    return AssertionFailure() << "Timed out waiting for disconnect";
210
0
  }
211
0
  EXPECT_TRUE(disconnected_);
212
213
0
  return AssertionSuccess();
214
0
}
215
216
1.63k
void IntegrationCodecClient::ConnectionCallbacks::onEvent(Network::ConnectionEvent event) {
217
1.63k
  parent_.last_connection_event_ = event;
218
1.63k
  if (event == Network::ConnectionEvent::Connected) {
219
815
    parent_.connected_ = true;
220
815
    if (block_till_connected_) {
221
815
      parent_.connection_->dispatcher().exit();
222
815
    }
223
815
  } else if (event == Network::ConnectionEvent::RemoteClose) {
224
0
    parent_.disconnected_ = true;
225
0
    parent_.connection_->dispatcher().exit();
226
815
  } else {
227
815
    if (parent_.type() == Http::CodecType::HTTP3 && !parent_.connected_ && block_till_connected_) {
228
      // Before handshake gets established, any connection failure should exit the loop. I.e. a QUIC
229
      // connection may fail of INVALID_VERSION if both this client doesn't support any of the
230
      // versions the server advertised before handshake established. In this case the connection is
231
      // closed locally and this is in a blocking event loop.
232
0
      parent_.connection_->dispatcher().exit();
233
0
    }
234
815
    parent_.disconnected_ = true;
235
815
  }
236
1.63k
}
237
238
Network::ClientConnectionPtr HttpIntegrationTest::makeClientConnectionWithOptions(
239
815
    uint32_t port, const Network::ConnectionSocket::OptionsSharedPtr& options) {
240
815
  if (downstream_protocol_ <= Http::CodecType::HTTP2) {
241
815
    return BaseIntegrationTest::makeClientConnectionWithOptions(port, options);
242
815
  }
243
0
#ifdef ENVOY_ENABLE_QUIC
244
  // Setting socket options is not supported for HTTP3.
245
0
  Network::Address::InstanceConstSharedPtr server_addr = *Network::Utility::resolveUrl(
246
0
      fmt::format("udp://{}:{}", Network::Test::getLoopbackAddressUrlString(version_), port));
247
0
  Network::Address::InstanceConstSharedPtr local_addr =
248
0
      Network::Test::getCanonicalLoopbackAddress(version_);
249
0
  return Quic::createQuicNetworkConnection(
250
0
      *quic_connection_persistent_info_, quic_transport_socket_factory_->getCryptoConfig(),
251
0
      quic::QuicServerId(
252
0
          quic_transport_socket_factory_->clientContextConfig()->serverNameIndication(),
253
0
          static_cast<uint16_t>(port)),
254
0
      *dispatcher_, server_addr, local_addr, quic_stat_names_, {}, *stats_store_.rootScope(),
255
0
      options, nullptr, connection_id_generator_, *quic_transport_socket_factory_);
256
#else
257
  ASSERT(false, "running a QUIC integration test without compiling QUIC");
258
  return nullptr;
259
#endif
260
815
}
261
262
0
IntegrationCodecClientPtr HttpIntegrationTest::makeHttpConnection(uint32_t port) {
263
0
  return makeHttpConnection(makeClientConnection(port));
264
0
}
265
266
IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection(
267
    Network::ClientConnectionPtr&& conn,
268
    absl::optional<envoy::config::core::v3::Http2ProtocolOptions> http2_options,
269
815
    bool wait_till_connected) {
270
815
  std::shared_ptr<Upstream::MockClusterInfo> cluster{new NiceMock<Upstream::MockClusterInfo>()};
271
815
  cluster->max_response_headers_count_ = 200;
272
815
  if (!http2_options.has_value()) {
273
815
    http2_options = Http2::Utility::initializeAndValidateOptions(
274
815
                        envoy::config::core::v3::Http2ProtocolOptions())
275
815
                        .value();
276
815
    http2_options.value().set_allow_connect(true);
277
815
    http2_options.value().set_allow_metadata(true);
278
815
  }
279
815
#ifdef ENVOY_ENABLE_QUIC
280
815
  cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions(
281
815
      http2_options.value(), quic::kStreamReceiveWindowLimit);
282
815
  cluster->http3_options_.set_allow_extended_connect(true);
283
815
  cluster->http3_options_.set_allow_metadata(true);
284
815
#endif
285
286
815
  cluster->http2_options_ = http2_options.value();
287
815
  cluster->http1_settings_.enable_trailers_ = true;
288
289
815
  if (!disable_client_header_validation_) {
290
815
    cluster->header_validator_factory_ = IntegrationUtil::makeHeaderValidationFactory(
291
815
        ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig());
292
815
  }
293
294
815
  Upstream::HostDescriptionConstSharedPtr host_description{Upstream::makeTestHostDescription(
295
815
      cluster, fmt::format("tcp://{}:80", Network::Test::getLoopbackAddressUrlString(version_)),
296
815
      timeSystem())};
297
  // This call may fail in QUICHE because of INVALID_VERSION. QUIC connection doesn't support
298
  // in-connection version negotiation.
299
815
  auto codec = std::make_unique<IntegrationCodecClient>(*dispatcher_, random_, std::move(conn),
300
815
                                                        host_description, downstream_protocol_,
301
815
                                                        wait_till_connected);
302
815
  if (downstream_protocol_ == Http::CodecType::HTTP3 && codec->disconnected()) {
303
    // Connection may get closed during version negotiation or handshake.
304
    // TODO(#8479) QUIC connection doesn't support in-connection version negotiationPropagate
305
    // INVALID_VERSION error to caller and let caller to use server advertised version list to
306
    // create a new connection with mutually supported version and make client codec again.
307
0
    ENVOY_LOG(error, "Fail to connect to server with error: {}",
308
0
              codec->connection()->transportFailureReason());
309
0
  }
310
815
  return codec;
311
815
}
312
313
IntegrationCodecClientPtr
314
815
HttpIntegrationTest::makeHttpConnection(Network::ClientConnectionPtr&& conn) {
315
815
  auto codec = makeRawHttpConnection(std::move(conn), absl::nullopt);
316
1.63k
  EXPECT_TRUE(codec->connected()) << codec->connection()->transportFailureReason();
317
815
  return codec;
318
815
}
319
320
HttpIntegrationTest::HttpIntegrationTest(Http::CodecType downstream_protocol,
321
                                         Network::Address::IpVersion version,
322
                                         const std::string& config)
323
    : HttpIntegrationTest::HttpIntegrationTest(
324
          downstream_protocol,
325
1.97k
          [version](int) {
326
1.97k
            return Network::Utility::parseInternetAddressNoThrow(
327
1.97k
                Network::Test::getLoopbackAddressString(version), 0);
328
1.97k
          },
329
1.97k
          version, config) {}
330
331
HttpIntegrationTest::HttpIntegrationTest(Http::CodecType downstream_protocol,
332
                                         const InstanceConstSharedPtrFn& upstream_address_fn,
333
                                         Network::Address::IpVersion version,
334
                                         const std::string& config)
335
    : BaseIntegrationTest(upstream_address_fn, version, config),
336
1.97k
      downstream_protocol_(downstream_protocol), quic_stat_names_(stats_store_.symbolTable()) {
337
  // Legacy integration tests expect the default listener to be named "http" for
338
  // lookupPort calls.
339
1.97k
  config_helper_.renameListener("http");
340
1.97k
  config_helper_.setClientCodec(typeToCodecType(downstream_protocol_));
341
  // Allow extension lookup by name in the integration tests.
342
1.97k
  config_helper_.addRuntimeOverride("envoy.reloadable_features.no_extension_lookup_by_name",
343
1.97k
                                    "false");
344
1.97k
}
345
346
void HttpIntegrationTest::useAccessLog(
347
    absl::string_view format,
348
0
    std::vector<envoy::config::core::v3::TypedExtensionConfig> formatters) {
349
0
  access_log_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename());
350
0
  ASSERT_TRUE(config_helper_.setAccessLog(access_log_name_, format, formatters));
351
0
}
352
353
1.97k
HttpIntegrationTest::~HttpIntegrationTest() {
354
  // Make sure any open streams have been closed. If there's an open stream, the decoder will
355
  // be out of scope, and so open streams result in writing to freed memory.
356
1.97k
  if (codec_client_) {
357
0
    EXPECT_EQ(codec_client_->numActiveRequests(), 0)
358
0
        << "test requires explicit cleanupUpstreamAndDownstream";
359
0
  }
360
1.97k
  cleanupUpstreamAndDownstream();
361
1.97k
}
362
363
1.97k
void HttpIntegrationTest::initialize() {
364
1.97k
  if (downstream_protocol_ != Http::CodecType::HTTP3) {
365
1.97k
    return BaseIntegrationTest::initialize();
366
1.97k
  }
367
0
#ifdef ENVOY_ENABLE_QUIC
368
  // Needs to be instantiated before base class calls initialize() which starts a QUIC listener
369
  // according to the config.
370
0
  quic_transport_socket_factory_ = IntegrationUtil::createQuicUpstreamTransportSocketFactory(
371
0
      *api_, stats_store_, context_manager_, thread_local_, san_to_match_);
372
373
  // Needed to config QUIC transport socket factory, and needs to be added before base class calls
374
  // initialize().
375
0
  config_helper_.addQuicDownstreamTransportSocketConfig(enable_quic_early_data_, custom_alpns_);
376
377
0
  BaseIntegrationTest::initialize();
378
0
  registerTestServerPorts({"http"}, test_server_);
379
380
  // Needs to outlive all QUIC connections.
381
0
  auto cluster = std::make_shared<NiceMock<Upstream::MockClusterInfo>>();
382
0
  auto quic_connection_persistent_info =
383
0
      Quic::createPersistentQuicInfoForCluster(*dispatcher_, *cluster);
384
  // Config IETF QUIC flow control window.
385
0
  quic_connection_persistent_info->quic_config_
386
0
      .SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(
387
0
          Http3::Utility::OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE);
388
  // Config Google QUIC flow control window.
389
0
  quic_connection_persistent_info->quic_config_.SetInitialStreamFlowControlWindowToSend(
390
0
      Http3::Utility::OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE);
391
  // Adjust timeouts.
392
0
  quic::QuicTime::Delta connect_timeout = quic::QuicTime::Delta::FromSeconds(5 * TIMEOUT_FACTOR);
393
0
  quic_connection_persistent_info->quic_config_.set_max_time_before_crypto_handshake(
394
0
      connect_timeout);
395
0
  quic_connection_persistent_info->quic_config_.set_max_idle_time_before_crypto_handshake(
396
0
      connect_timeout);
397
398
0
  quic_connection_persistent_info_ = std::move(quic_connection_persistent_info);
399
#else
400
  ASSERT(false, "running a QUIC integration test without compiling QUIC");
401
#endif
402
0
}
403
404
0
void HttpIntegrationTest::setupHttp1ImplOverrides(Http1ParserImpl http1_implementation) {
405
0
  switch (http1_implementation) {
406
0
  case Http1ParserImpl::HttpParser:
407
0
    config_helper_.addRuntimeOverride("envoy.reloadable_features.http1_use_balsa_parser", "false");
408
0
    break;
409
0
  case Http1ParserImpl::BalsaParser:
410
0
    config_helper_.addRuntimeOverride("envoy.reloadable_features.http1_use_balsa_parser", "true");
411
0
    break;
412
0
  }
413
0
}
414
415
0
void HttpIntegrationTest::setupHttp2ImplOverrides(Http2Impl http2_implementation) {
416
0
  switch (http2_implementation) {
417
0
  case Http2Impl::Nghttp2:
418
0
    config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_use_oghttp2", "false");
419
0
    break;
420
0
  case Http2Impl::Oghttp2:
421
0
    config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_use_oghttp2", "true");
422
0
    break;
423
0
  }
424
0
}
425
426
1.10k
void HttpIntegrationTest::setDownstreamProtocol(Http::CodecType downstream_protocol) {
427
1.10k
  downstream_protocol_ = downstream_protocol;
428
1.10k
  config_helper_.setClientCodec(typeToCodecType(downstream_protocol_));
429
1.10k
}
430
431
0
ConfigHelper::HttpModifierFunction HttpIntegrationTest::setEnableDownstreamTrailersHttp1() {
432
0
  return [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
433
0
                hcm) { hcm.mutable_http_protocol_options()->set_enable_trailers(true); };
434
0
}
435
436
0
ConfigHelper::ConfigModifierFunction HttpIntegrationTest::setEnableUpstreamTrailersHttp1() {
437
0
  return [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
438
0
    RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() == 1, "");
439
0
    if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP1) {
440
0
      ConfigHelper::HttpProtocolOptions protocol_options;
441
0
      protocol_options.mutable_explicit_http_config()
442
0
          ->mutable_http_protocol_options()
443
0
          ->set_enable_trailers(true);
444
0
      ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0),
445
0
                                       protocol_options);
446
0
    }
447
0
  };
448
0
}
449
450
0
ConfigHelper::HttpModifierFunction HttpIntegrationTest::configureProxyStatus() {
451
0
  return [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
452
0
                hcm) {
453
0
    auto* psc = hcm.mutable_proxy_status_config();
454
0
    psc->set_set_recommended_response_code(false);
455
0
  };
456
0
}
457
458
IntegrationStreamDecoderPtr HttpIntegrationTest::sendRequestAndWaitForResponse(
459
    const Http::TestRequestHeaderMapImpl& request_headers, uint32_t request_body_size,
460
    const Http::TestResponseHeaderMapImpl& response_headers, uint32_t response_body_size,
461
0
    const std::vector<uint64_t>& upstream_indices, std::chrono::milliseconds timeout) {
462
0
  ASSERT(codec_client_ != nullptr);
463
  // Send the request to Envoy.
464
0
  IntegrationStreamDecoderPtr response;
465
0
  if (request_body_size) {
466
0
    response = codec_client_->makeRequestWithBody(request_headers, request_body_size);
467
0
  } else {
468
0
    response = codec_client_->makeHeaderOnlyRequest(request_headers);
469
0
  }
470
0
  waitForNextUpstreamRequest(upstream_indices, timeout);
471
  // Send response headers, and end_stream if there is no response body.
472
0
  upstream_request_->encodeHeaders(response_headers, response_body_size == 0);
473
  // Send any response data, with end_stream true.
474
0
  if (response_body_size) {
475
0
    upstream_request_->encodeData(response_body_size, true);
476
0
  }
477
  // Wait for the response to be read by the codec client.
478
0
  RELEASE_ASSERT(response->waitForEndStream(timeout),
479
0
                 fmt::format("unexpected timeout after ", timeout.count(), " ms"));
480
0
  return response;
481
0
}
482
483
IntegrationStreamDecoderPtr HttpIntegrationTest::sendRequestAndWaitForResponse(
484
    const Http::TestRequestHeaderMapImpl& request_headers, uint32_t request_body_size,
485
    const Http::TestResponseHeaderMapImpl& response_headers, uint32_t response_body_size,
486
0
    uint64_t upstream_index, std::chrono::milliseconds timeout) {
487
0
  return sendRequestAndWaitForResponse(request_headers, request_body_size, response_headers,
488
0
                                       response_body_size, std::vector<uint64_t>{upstream_index},
489
0
                                       timeout);
490
0
}
491
492
2.79k
void HttpIntegrationTest::cleanupUpstreamAndDownstream() {
493
  // Close the upstream connection first. If there's an outstanding request,
494
  // closing the client may result in a FIN being sent upstream, and FakeConnectionBase::close
495
  // will interpret that as an unexpected disconnect. The codec client is not
496
  // subject to the same failure mode.
497
2.79k
  if (fake_upstream_connection_) {
498
0
    AssertionResult result = fake_upstream_connection_->close();
499
0
    RELEASE_ASSERT(result, result.message());
500
0
    result = fake_upstream_connection_->waitForDisconnect();
501
0
    RELEASE_ASSERT(result, result.message());
502
0
    fake_upstream_connection_.reset();
503
0
  }
504
2.79k
  if (codec_client_) {
505
815
    codec_client_->close();
506
815
  }
507
2.79k
}
508
509
void HttpIntegrationTest::sendRequestAndVerifyResponse(
510
    const Http::TestRequestHeaderMapImpl& request_headers, const int request_size,
511
    const Http::TestResponseHeaderMapImpl& response_headers, const int response_size,
512
    const int backend_idx,
513
0
    absl::optional<const Http::TestResponseHeaderMapImpl> expected_response_headers) {
514
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
515
0
  auto response = sendRequestAndWaitForResponse(request_headers, request_size, response_headers,
516
0
                                                response_size, backend_idx);
517
0
  verifyResponse(std::move(response), "200",
518
0
                 (expected_response_headers.has_value()) ? *expected_response_headers
519
0
                                                         : response_headers,
520
0
                 std::string(response_size, 'a'));
521
522
0
  EXPECT_TRUE(upstream_request_->complete());
523
0
  EXPECT_EQ(request_size, upstream_request_->bodyLength());
524
0
  cleanupUpstreamAndDownstream();
525
0
}
526
527
void HttpIntegrationTest::verifyResponse(IntegrationStreamDecoderPtr response,
528
                                         const std::string& response_code,
529
                                         const Http::TestResponseHeaderMapImpl& expected_headers,
530
0
                                         const std::string& expected_body) {
531
0
  EXPECT_TRUE(response->complete());
532
0
  EXPECT_EQ(response_code, response->headers().getStatusValue());
533
0
  expected_headers.iterate([response_headers = &response->headers()](
534
0
                               const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate {
535
0
    const auto entry =
536
0
        response_headers->get(Http::LowerCaseString{std::string(header.key().getStringView())});
537
0
    EXPECT_FALSE(entry.empty());
538
0
    EXPECT_EQ(header.value().getStringView(), entry[0]->value().getStringView());
539
0
    return Http::HeaderMap::Iterate::Continue;
540
0
  });
541
542
0
  EXPECT_EQ(response->body(), expected_body);
543
0
}
544
545
absl::optional<uint64_t> HttpIntegrationTest::waitForNextUpstreamConnection(
546
    const std::vector<uint64_t>& upstream_indices,
547
    std::chrono::milliseconds connection_wait_timeout,
548
0
    FakeHttpConnectionPtr& fake_upstream_connection) {
549
0
  AssertionResult result = AssertionFailure();
550
0
  int upstream_index = 0;
551
0
  Event::TestTimeSystem::RealTimeBound bound(connection_wait_timeout);
552
  // Loop over the upstreams until the call times out or an upstream request is received.
553
0
  while (!result) {
554
0
    upstream_index = upstream_index % upstream_indices.size();
555
0
    result = fake_upstreams_[upstream_indices[upstream_index]]->waitForHttpConnection(
556
0
        *dispatcher_, fake_upstream_connection, std::chrono::milliseconds(5));
557
0
    if (result) {
558
0
      return upstream_index;
559
0
    } else if (!bound.withinBound()) {
560
0
      RELEASE_ASSERT(0, "Timed out waiting for new connection.");
561
0
      break;
562
0
    }
563
0
    ++upstream_index;
564
0
  }
565
0
  RELEASE_ASSERT(result, result.message());
566
0
  return {};
567
0
}
568
569
absl::optional<uint64_t>
570
HttpIntegrationTest::waitForNextUpstreamRequest(const std::vector<uint64_t>& upstream_indices,
571
0
                                                std::chrono::milliseconds connection_wait_timeout) {
572
0
  absl::optional<uint64_t> upstream_with_request;
573
  // If there is no upstream connection, wait for it to be established.
574
0
  if (!fake_upstream_connection_) {
575
0
    upstream_with_request = waitForNextUpstreamConnection(upstream_indices, connection_wait_timeout,
576
0
                                                          fake_upstream_connection_);
577
0
  }
578
  // Wait for the next stream on the upstream connection.
579
0
  AssertionResult result =
580
0
      fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_);
581
0
  RELEASE_ASSERT(result, result.message());
582
  // Wait for the stream to be completely received.
583
0
  result = upstream_request_->waitForEndStream(*dispatcher_);
584
0
  RELEASE_ASSERT(result, result.message());
585
586
0
  return upstream_with_request;
587
0
}
588
589
void HttpIntegrationTest::waitForNextUpstreamRequest(
590
0
    uint64_t upstream_index, std::chrono::milliseconds connection_wait_timeout) {
591
0
  waitForNextUpstreamRequest(std::vector<uint64_t>({upstream_index}), connection_wait_timeout);
592
0
}
593
594
void HttpIntegrationTest::checkSimpleRequestSuccess(uint64_t expected_request_size,
595
                                                    uint64_t expected_response_size,
596
0
                                                    IntegrationStreamDecoder* response) {
597
0
  EXPECT_TRUE(upstream_request_->complete());
598
0
  EXPECT_EQ(expected_request_size, upstream_request_->bodyLength());
599
600
0
  ASSERT_TRUE(response->complete());
601
0
  EXPECT_EQ("200", response->headers().getStatusValue());
602
0
  EXPECT_EQ(expected_response_size, response->body().size());
603
0
}
604
605
void HttpIntegrationTest::testRouterRequestAndResponseWithBody(
606
    uint64_t request_size, uint64_t response_size, bool big_header, bool set_content_length_header,
607
0
    ConnectionCreationFunction* create_connection, std::chrono::milliseconds timeout) {
608
#ifdef ENVOY_CONFIG_COVERAGE
609
  // Avoid excessive logging at UDP packet level, which causes log spamming, as well as worse
610
  // contention: https://github.com/envoyproxy/envoy/issues/19595
611
  ENVOY_LOG_MISC(warn, "manually lowering logs to error");
612
  LogLevelSetter save_levels(spdlog::level::err);
613
#endif
614
0
  initialize();
615
0
  codec_client_ = makeHttpConnection(
616
0
      create_connection ? ((*create_connection)()) : makeClientConnection((lookupPort("http"))));
617
0
  if (set_content_length_header) {
618
0
    default_request_headers_.setContentLength(request_size);
619
0
    default_response_headers_.setContentLength(response_size);
620
0
  }
621
0
  if (big_header) {
622
0
    default_request_headers_.addCopy("big", std::string(4096, 'a'));
623
0
  }
624
0
  auto response = sendRequestAndWaitForResponse(
625
0
      default_request_headers_, request_size, default_response_headers_, response_size, 0, timeout);
626
0
  checkSimpleRequestSuccess(request_size, response_size, response.get());
627
0
}
628
629
void HttpIntegrationTest::testGiantRequestAndResponse(uint64_t request_size, uint64_t response_size,
630
                                                      bool set_content_length_header,
631
0
                                                      std::chrono::milliseconds timeout) {
632
0
  autonomous_upstream_ = true;
633
#ifdef ENVOY_CONFIG_COVERAGE
634
  // Avoid excessive logging at UDP packet level, which causes log spamming, as well as worse
635
  // contention: https://github.com/envoyproxy/envoy/issues/19595
636
  ENVOY_LOG_MISC(warn, "manually lowering logs to error");
637
  LogLevelSetter save_levels(spdlog::level::err);
638
#endif
639
0
  initialize();
640
0
  codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
641
0
  Http::TestRequestHeaderMapImpl request_headers{
642
0
      {":method", "GET"},
643
0
      {":path", "/test/long/url"},
644
0
      {":authority", "sni.lyft.com"},
645
0
      {":scheme", "http"},
646
0
      {AutonomousStream::RESPONSE_SIZE_BYTES, std::to_string(response_size)},
647
0
      {AutonomousStream::EXPECT_REQUEST_SIZE_BYTES, std::to_string(request_size)},
648
0
      {AutonomousStream::NO_TRAILERS, "0"}};
649
0
  auto response_headers =
650
0
      std::make_unique<Http::TestResponseHeaderMapImpl>(default_response_headers_);
651
0
  if (set_content_length_header) {
652
0
    request_headers.setContentLength(request_size);
653
0
    response_headers->setContentLength(response_size);
654
0
  }
655
0
  reinterpret_cast<AutonomousUpstream*>(fake_upstreams_.front().get())
656
0
      ->setResponseHeaders(std::move(response_headers));
657
658
0
  auto response = codec_client_->makeRequestWithBody(request_headers, request_size);
659
660
  // Wait for the response to be read by the codec client.
661
0
  RELEASE_ASSERT(response->waitForEndStream(timeout), "unexpected timeout");
662
0
  ASSERT_TRUE(response->complete());
663
0
  EXPECT_EQ("200", response->headers().getStatusValue());
664
0
  EXPECT_EQ(response_size, response->body().size());
665
0
}
666
667
void HttpIntegrationTest::testRouterUpstreamProtocolError(const std::string& expected_code,
668
0
                                                          const std::string& expected_flag) {
669
0
  useAccessLog("%RESPONSE_CODE% %RESPONSE_FLAGS%");
670
0
  initialize();
671
672
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
673
674
0
  auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{
675
0
      {":method", "GET"}, {":path", "/test/long/url"}, {":authority", "sni.lyft.com"}});
676
0
  auto response = std::move(encoder_decoder.second);
677
678
0
  FakeRawConnectionPtr fake_upstream_connection;
679
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection));
680
0
  std::string data;
681
0
  ASSERT_TRUE(fake_upstream_connection->waitForData(
682
0
      FakeRawConnection::waitForInexactMatch("\r\n\r\n"), &data));
683
0
  ASSERT_TRUE(fake_upstream_connection->write("bad protocol data!"));
684
0
  ASSERT_TRUE(fake_upstream_connection->waitForDisconnect());
685
0
  ASSERT_TRUE(codec_client_->waitForDisconnect());
686
687
0
  EXPECT_TRUE(response->complete());
688
0
  EXPECT_EQ(expected_code, response->headers().getStatusValue());
689
0
  std::string log = waitForAccessLog(access_log_name_);
690
0
  EXPECT_THAT(log, HasSubstr(expected_code));
691
0
  EXPECT_THAT(log, HasSubstr(expected_flag));
692
0
}
693
694
IntegrationStreamDecoderPtr
695
HttpIntegrationTest::makeHeaderOnlyRequest(ConnectionCreationFunction* create_connection,
696
                                           int upstream_index, const std::string& path,
697
0
                                           const std::string& authority) {
698
  // This is called multiple times per test in ads_integration_test. Only call
699
  // initialize() the first time.
700
0
  if (!initialized()) {
701
0
    initialize();
702
0
  }
703
0
  codec_client_ = makeHttpConnection(
704
0
      create_connection ? ((*create_connection)()) : makeClientConnection((lookupPort("http"))));
705
0
  if (!authority.empty()) {
706
0
    default_request_headers_.setHost(authority);
707
0
  }
708
0
  default_request_headers_.setPath(path);
709
0
  return sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0,
710
0
                                       upstream_index);
711
0
}
712
713
void HttpIntegrationTest::testRouterHeaderOnlyRequestAndResponse(
714
    ConnectionCreationFunction* create_connection, int upstream_index, const std::string& path,
715
0
    const std::string& authority) {
716
0
  auto response = makeHeaderOnlyRequest(create_connection, upstream_index, path, authority);
717
0
  checkSimpleRequestSuccess(0U, 0U, response.get());
718
0
}
719
720
// Change the default route to be restrictive, and send a request to an alternate route.
721
0
void HttpIntegrationTest::testRouterNotFound() {
722
0
  config_helper_.setDefaultHostAndRoute("foo.com", "/found");
723
0
  config_helper_.addConfigModifier(configureProxyStatus());
724
0
  initialize();
725
726
0
  BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest(
727
0
      lookupPort("http"), "GET", "/notfound", "", downstream_protocol_, version_);
728
0
  ASSERT_TRUE(response->complete());
729
0
  EXPECT_EQ("404", response->headers().getStatusValue());
730
0
  EXPECT_EQ(response->headers().getProxyStatusValue(),
731
0
            "envoy; error=destination_not_found; details=\"route_not_found; NR\"");
732
0
}
733
734
// Change the default route to be restrictive, and send a POST to an alternate route.
735
0
void HttpIntegrationTest::testRouterNotFoundWithBody() {
736
0
  config_helper_.setDefaultHostAndRoute("foo.com", "/found");
737
0
  config_helper_.addConfigModifier(configureProxyStatus());
738
0
  initialize();
739
0
  BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest(
740
0
      lookupPort("http"), "POST", "/notfound", "foo", downstream_protocol_, version_);
741
0
  ASSERT_TRUE(response->complete());
742
0
  EXPECT_EQ("404", response->headers().getStatusValue());
743
0
  EXPECT_EQ(response->headers().getProxyStatusValue(),
744
0
            "envoy; error=destination_not_found; details=\"route_not_found; NR\"");
745
0
}
746
747
// Make sure virtual cluster stats are charged to the appropriate virtual cluster.
748
0
void HttpIntegrationTest::testRouterVirtualClusters() {
749
0
  const std::string matching_header = "x-use-test-vcluster";
750
0
  config_helper_.addConfigModifier(
751
0
      [matching_header](
752
0
          envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
753
0
              hcm) {
754
0
        auto* route_config = hcm.mutable_route_config();
755
0
        ASSERT_EQ(1, route_config->virtual_hosts_size());
756
0
        auto* virtual_host = route_config->mutable_virtual_hosts(0);
757
0
        {
758
0
          auto* virtual_cluster = virtual_host->add_virtual_clusters();
759
0
          virtual_cluster->set_name("test_vcluster");
760
0
          auto* headers = virtual_cluster->add_headers();
761
0
          headers->set_name(matching_header);
762
0
          headers->set_present_match(true);
763
0
        }
764
0
      });
765
0
  initialize();
766
767
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
768
0
  Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"},
769
0
                                                 {":path", "/test/long/url"},
770
0
                                                 {":scheme", "http"},
771
0
                                                 {":authority", "sni.lyft.com"},
772
0
                                                 {matching_header, "true"}};
773
774
0
  auto response = sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 0);
775
0
  checkSimpleRequestSuccess(0, 0, response.get());
776
777
0
  test_server_->waitForCounterEq("vhost.integration.vcluster.test_vcluster.upstream_rq_total", 1);
778
0
  test_server_->waitForCounterEq("vhost.integration.vcluster.other.upstream_rq_total", 0);
779
780
0
  auto response2 =
781
0
      sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0);
782
0
  checkSimpleRequestSuccess(0, 0, response2.get());
783
784
0
  test_server_->waitForCounterEq("vhost.integration.vcluster.test_vcluster.upstream_rq_total", 1);
785
0
  test_server_->waitForCounterEq("vhost.integration.vcluster.other.upstream_rq_total", 1);
786
0
}
787
788
// Make sure route level stats are generated correctly.
789
0
void HttpIntegrationTest::testRouteStats() {
790
0
  config_helper_.addConfigModifier(
791
0
      [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
792
0
             hcm) {
793
0
        auto* route_config = hcm.mutable_route_config();
794
0
        ASSERT_EQ(1, route_config->virtual_hosts_size());
795
0
        auto* virtual_host = route_config->mutable_virtual_hosts(0);
796
0
        auto* route = virtual_host->mutable_routes(0);
797
0
        route->set_stat_prefix("test_route");
798
0
      });
799
0
  initialize();
800
801
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
802
0
  Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"},
803
0
                                                 {":path", "/test/long/url"},
804
0
                                                 {":scheme", "http"},
805
0
                                                 {":authority", "sni.lyft.com"}};
806
807
0
  auto response = sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 0);
808
0
  checkSimpleRequestSuccess(0, 0, response.get());
809
810
0
  test_server_->waitForCounterEq("vhost.integration.route.test_route.upstream_rq_total", 1);
811
0
  test_server_->waitForCounterEq("vhost.integration.route.test_route.upstream_rq_completed", 1);
812
0
}
813
814
0
void HttpIntegrationTest::testRouterUpstreamDisconnectBeforeRequestComplete() {
815
0
  config_helper_.addConfigModifier(configureProxyStatus());
816
0
  initialize();
817
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
818
819
0
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
820
0
  auto response = std::move(encoder_decoder.second);
821
822
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
823
824
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
825
0
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
826
0
  ASSERT_TRUE(fake_upstream_connection_->close());
827
0
  ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
828
0
  ASSERT_TRUE(response->waitForEndStream());
829
830
0
  if (downstream_protocol_ == Http::CodecType::HTTP1) {
831
0
    ASSERT_TRUE(codec_client_->waitForDisconnect());
832
0
  } else {
833
0
    codec_client_->close();
834
0
  }
835
836
0
  EXPECT_FALSE(upstream_request_->complete());
837
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
838
839
0
  EXPECT_TRUE(response->complete());
840
0
  EXPECT_EQ("503", response->headers().getStatusValue());
841
0
  if (upstreamProtocol() == Http::CodecType::HTTP3) {
842
0
    EXPECT_EQ(response->headers().getProxyStatusValue(),
843
0
              "envoy; error=connection_terminated; "
844
0
              "details=\"upstream_reset_before_response_started{connection_termination|QUIC_NO_"
845
0
              "ERROR|FROM_PEER|Closed_by_application}; UC\"");
846
0
  } else {
847
0
    EXPECT_EQ(response->headers().getProxyStatusValue(),
848
0
              "envoy; error=connection_terminated; "
849
0
              "details=\"upstream_reset_before_response_started{connection_termination}; UC\"");
850
0
  }
851
0
}
852
853
void HttpIntegrationTest::testRouterUpstreamDisconnectBeforeResponseComplete(
854
0
    ConnectionCreationFunction* create_connection) {
855
0
  initialize();
856
0
  codec_client_ = makeHttpConnection(
857
0
      create_connection ? ((*create_connection)()) : makeClientConnection((lookupPort("http"))));
858
0
  auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
859
0
  waitForNextUpstreamRequest();
860
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
861
0
  response->waitForHeaders();
862
0
  ASSERT_TRUE(fake_upstream_connection_->close());
863
0
  ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
864
865
0
  if (downstream_protocol_ == Http::CodecType::HTTP1) {
866
0
    ASSERT_TRUE(codec_client_->waitForDisconnect());
867
0
  } else {
868
0
    ASSERT_TRUE(response->waitForReset());
869
0
    codec_client_->close();
870
0
  }
871
872
0
  EXPECT_TRUE(upstream_request_->complete());
873
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
874
875
0
  EXPECT_FALSE(response->complete());
876
0
  EXPECT_EQ("200", response->headers().getStatusValue());
877
0
  EXPECT_EQ(0U, response->body().size());
878
0
}
879
880
void HttpIntegrationTest::testRouterDownstreamDisconnectBeforeRequestComplete(
881
0
    ConnectionCreationFunction* create_connection) {
882
0
  initialize();
883
884
0
  codec_client_ = makeHttpConnection(
885
0
      create_connection ? ((*create_connection)()) : makeClientConnection((lookupPort("http"))));
886
0
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
887
0
  auto response = std::move(encoder_decoder.second);
888
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
889
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
890
0
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
891
0
  codec_client_->close();
892
893
0
  if (upstreamProtocol() == Http::CodecType::HTTP1) {
894
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
895
0
  } else {
896
0
    ASSERT_TRUE(upstream_request_->waitForReset());
897
0
    ASSERT_TRUE(fake_upstream_connection_->close());
898
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
899
0
  }
900
901
0
  EXPECT_FALSE(upstream_request_->complete());
902
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
903
904
0
  EXPECT_FALSE(response->complete());
905
0
}
906
907
void HttpIntegrationTest::testRouterDownstreamDisconnectBeforeResponseComplete(
908
0
    ConnectionCreationFunction* create_connection) {
909
#if defined(__APPLE__) || defined(WIN32)
910
  // Skip this test on OS/X + Windows: we can't detect the early close, and we
911
  // won't clean up the upstream connection until it times out. See #4294.
912
  if (downstream_protocol_ == Http::CodecType::HTTP1) {
913
    return;
914
  }
915
#endif
916
0
  initialize();
917
0
  codec_client_ = makeHttpConnection(
918
0
      create_connection ? ((*create_connection)()) : makeClientConnection((lookupPort("http"))));
919
0
  auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
920
0
  waitForNextUpstreamRequest();
921
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
922
0
  upstream_request_->encodeData(512, false);
923
0
  response->waitForBodyData(512);
924
0
  codec_client_->close();
925
926
0
  if (upstreamProtocol() == Http::CodecType::HTTP1) {
927
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
928
0
  } else {
929
0
    ASSERT_TRUE(upstream_request_->waitForReset());
930
0
    ASSERT_TRUE(fake_upstream_connection_->close());
931
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
932
0
  }
933
934
0
  EXPECT_TRUE(upstream_request_->complete());
935
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
936
937
0
  EXPECT_FALSE(response->complete());
938
0
  EXPECT_EQ("200", response->headers().getStatusValue());
939
0
  EXPECT_EQ(512U, response->body().size());
940
0
}
941
942
0
void HttpIntegrationTest::testRouterUpstreamResponseBeforeRequestComplete(uint32_t status_code) {
943
0
  initialize();
944
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
945
0
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
946
0
  auto response = std::move(encoder_decoder.second);
947
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
948
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
949
0
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
950
0
  if (status_code != 0) {
951
0
    default_response_headers_.setStatus(status_code);
952
0
  }
953
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
954
0
  upstream_request_->encodeData(512, true);
955
0
  ASSERT_TRUE(response->waitForEndStream());
956
957
0
  if (upstreamProtocol() == Http::CodecType::HTTP1) {
958
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
959
0
  } else {
960
0
    ASSERT_TRUE(upstream_request_->waitForReset());
961
0
    ASSERT_TRUE(fake_upstream_connection_->close());
962
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
963
0
  }
964
965
0
  if (downstream_protocol_ == Http::CodecType::HTTP1) {
966
0
    ASSERT_TRUE(codec_client_->waitForDisconnect());
967
0
  } else {
968
0
    codec_client_->close();
969
0
  }
970
971
0
  EXPECT_FALSE(upstream_request_->complete());
972
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
973
974
0
  EXPECT_TRUE(response->complete());
975
0
  EXPECT_EQ(status_code != 0 ? absl::StrCat(status_code) : "200",
976
0
            response->headers().getStatusValue());
977
0
  EXPECT_EQ(512U, response->body().size());
978
0
}
979
980
0
void HttpIntegrationTest::testRouterRetryOnResetBeforeRequestAfterHeaders() {
981
0
  initialize();
982
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
983
984
0
  Http::TestRequestHeaderMapImpl headers = {{":method", "POST"},
985
0
                                            {":path", "/test/long/url"},
986
0
                                            {":scheme", "http"},
987
0
                                            {":authority", "sni.lyft.com"},
988
0
                                            {"x-forwarded-for", "10.0.0.1"},
989
0
                                            {"x-envoy-retry-on", "reset-before-request"}};
990
0
  auto encoder_decoder = codec_client_->startRequest(headers);
991
0
  waitForNextUpstreamConnection(std::vector<uint64_t>({0}), TestUtility::DefaultTimeout,
992
0
                                fake_upstream_connection_);
993
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
994
0
  request_encoder_ = &encoder_decoder.first;
995
0
  auto response = std::move(encoder_decoder.second);
996
0
  auto status = request_encoder_->encodeHeaders(headers, false);
997
  // Make sure we transmit headers successfully
998
0
  ASSERT_TRUE(status.ok());
999
0
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
1000
  // Reset the upstream connection after the headers have been sent
1001
0
  ASSERT_TRUE(fake_upstream_connection_->close());
1002
1003
  // We should get no new stream/retry
1004
0
  ASSERT_FALSE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
1005
0
  cleanupUpstreamAndDownstream();
1006
0
}
1007
1008
0
void HttpIntegrationTest::testRouterRetryOnResetBeforeRequestBeforeHeaders() {
1009
0
  initialize();
1010
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1011
0
  auto encoder_decoder = codec_client_->startRequest(
1012
0
      Http::TestRequestHeaderMapImpl{{":method", "POST"},
1013
0
                                     {":path", "/test/long/url"},
1014
0
                                     {":scheme", "http"},
1015
0
                                     {":authority", "sni.lyft.com"},
1016
0
                                     {"x-forwarded-for", "10.0.0.1"},
1017
0
                                     {"x-envoy-retry-on", "reset-before-request"}});
1018
0
  waitForNextUpstreamConnection(std::vector<uint64_t>({0}), TestUtility::DefaultTimeout,
1019
0
                                fake_upstream_connection_);
1020
0
  request_encoder_ = &encoder_decoder.first;
1021
0
  auto response = std::move(encoder_decoder.second);
1022
  // Reset the upstream connection before the headers have been sent
1023
0
  ASSERT_TRUE(fake_upstream_connection_->close());
1024
  // We should get a retry
1025
0
  waitForNextUpstreamConnection(std::vector<uint64_t>({0}), TestUtility::DefaultTimeout,
1026
0
                                fake_upstream_connection_);
1027
  // Send the request body to unblock the buffer filter
1028
0
  codec_client_->sendData(*request_encoder_, 1024, true);
1029
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
1030
0
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
1031
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1032
0
  upstream_request_->encodeData(1024, true);
1033
1034
0
  ASSERT_TRUE(response->waitForEndStream());
1035
1036
0
  if (upstreamProtocol() == Http::CodecType::HTTP1) {
1037
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1038
0
  } else {
1039
0
    ASSERT_TRUE(upstream_request_->waitForReset());
1040
0
    ASSERT_TRUE(fake_upstream_connection_->close());
1041
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1042
0
  }
1043
1044
0
  codec_client_->close();
1045
1046
0
  EXPECT_TRUE(response->complete());
1047
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1048
0
  EXPECT_EQ(1024U, response->body().size());
1049
0
}
1050
1051
0
void HttpIntegrationTest::testRetry() {
1052
0
  initialize();
1053
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1054
0
  auto response = codec_client_->makeRequestWithBody(
1055
0
      Http::TestRequestHeaderMapImpl{{":method", "POST"},
1056
0
                                     {":path", "/test/long/url"},
1057
0
                                     {":scheme", "http"},
1058
0
                                     {":authority", "sni.lyft.com"},
1059
0
                                     {"x-forwarded-for", "10.0.0.1"},
1060
0
                                     {"x-envoy-retry-on", "5xx"}},
1061
0
      1024);
1062
0
  waitForNextUpstreamRequest();
1063
0
  upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, false);
1064
1065
0
  if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP1) {
1066
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1067
0
    ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1068
0
  } else {
1069
0
    ASSERT_TRUE(upstream_request_->waitForReset());
1070
0
  }
1071
0
  waitForNextUpstreamRequest();
1072
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1073
0
  upstream_request_->encodeData(512, true);
1074
1075
0
  ASSERT_TRUE(response->waitForEndStream());
1076
0
  EXPECT_TRUE(upstream_request_->complete());
1077
0
  EXPECT_EQ(1024U, upstream_request_->bodyLength());
1078
1079
0
  EXPECT_TRUE(response->complete());
1080
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1081
0
  EXPECT_EQ(512U, response->body().size());
1082
0
}
1083
1084
// Tests that the x-envoy-attempt-count header is properly set on the upstream request
1085
// and updated after the request is retried.
1086
0
void HttpIntegrationTest::testRetryAttemptCountHeader() {
1087
0
  auto host = config_helper_.createVirtualHost("sni.lyft.com", "/test_retry");
1088
0
  host.set_include_request_attempt_count(true);
1089
0
  host.set_include_attempt_count_in_response(true);
1090
0
  config_helper_.addVirtualHost(host);
1091
1092
0
  initialize();
1093
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1094
0
  auto response = codec_client_->makeRequestWithBody(
1095
0
      Http::TestRequestHeaderMapImpl{{":method", "POST"},
1096
0
                                     {":path", "/test_retry"},
1097
0
                                     {":scheme", "http"},
1098
0
                                     {":authority", "sni.lyft.com"},
1099
0
                                     {"x-forwarded-for", "10.0.0.1"},
1100
0
                                     {"x-envoy-retry-on", "5xx"}},
1101
0
      1024);
1102
0
  waitForNextUpstreamRequest();
1103
0
  upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, false);
1104
1105
0
  EXPECT_EQ(atoi(std::string(upstream_request_->headers().getEnvoyAttemptCountValue()).c_str()), 1);
1106
1107
0
  if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP1) {
1108
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1109
0
    ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1110
0
  } else {
1111
0
    ASSERT_TRUE(upstream_request_->waitForReset());
1112
0
  }
1113
0
  waitForNextUpstreamRequest();
1114
0
  EXPECT_EQ(atoi(std::string(upstream_request_->headers().getEnvoyAttemptCountValue()).c_str()), 2);
1115
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1116
0
  upstream_request_->encodeData(512, true);
1117
1118
0
  ASSERT_TRUE(response->waitForEndStream());
1119
0
  EXPECT_TRUE(upstream_request_->complete());
1120
0
  EXPECT_EQ(1024U, upstream_request_->bodyLength());
1121
1122
0
  EXPECT_TRUE(response->complete());
1123
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1124
0
  EXPECT_EQ(512U, response->body().size());
1125
0
  EXPECT_EQ(2, atoi(std::string(response->headers().getEnvoyAttemptCountValue()).c_str()));
1126
0
}
1127
1128
0
void HttpIntegrationTest::testGrpcRetry() {
1129
0
  Http::TestResponseTrailerMapImpl response_trailers{{"response1", "trailer1"},
1130
0
                                                     {"grpc-status", "0"}};
1131
0
  initialize();
1132
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1133
0
  auto encoder_decoder = codec_client_->startRequest(
1134
0
      Http::TestRequestHeaderMapImpl{{":method", "POST"},
1135
0
                                     {":path", "/test/long/url"},
1136
0
                                     {":scheme", "http"},
1137
0
                                     {":authority", "sni.lyft.com"},
1138
0
                                     {"x-forwarded-for", "10.0.0.1"},
1139
0
                                     {"x-envoy-retry-grpc-on", "cancelled"}});
1140
0
  request_encoder_ = &encoder_decoder.first;
1141
0
  auto response = std::move(encoder_decoder.second);
1142
0
  codec_client_->sendData(*request_encoder_, 1024, true);
1143
0
  waitForNextUpstreamRequest();
1144
0
  upstream_request_->encodeHeaders(
1145
0
      Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "1"}}, false);
1146
0
  if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP1) {
1147
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1148
0
    ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1149
0
  } else {
1150
0
    ASSERT_TRUE(upstream_request_->waitForReset());
1151
0
  }
1152
0
  waitForNextUpstreamRequest();
1153
1154
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1155
0
  upstream_request_->encodeData(512, fake_upstreams_[0]->httpType() != Http::CodecType::HTTP2);
1156
0
  if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP2) {
1157
0
    upstream_request_->encodeTrailers(response_trailers);
1158
0
  }
1159
1160
0
  ASSERT_TRUE(response->waitForEndStream());
1161
0
  EXPECT_TRUE(upstream_request_->complete());
1162
0
  EXPECT_EQ(1024U, upstream_request_->bodyLength());
1163
1164
0
  EXPECT_TRUE(response->complete());
1165
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1166
0
  EXPECT_EQ(512U, response->body().size());
1167
0
  if (fake_upstreams_[0]->httpType() == Http::CodecType::HTTP2) {
1168
0
    EXPECT_THAT(*response->trailers(), HeaderMapEqualRef(&response_trailers));
1169
0
  }
1170
0
}
1171
1172
void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_upstream,
1173
0
                                               const std::string& via, bool disconnect_after_100) {
1174
0
  useAccessLog("%RESPONSE_CODE%");
1175
0
  initialize();
1176
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1177
1178
0
  auto encoder_decoder =
1179
0
      codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"},
1180
0
                                                                 {":path", "/dynamo/url"},
1181
0
                                                                 {":scheme", "http"},
1182
0
                                                                 {":authority", "sni.lyft.com"},
1183
0
                                                                 {"expect", "100-contINUE"}});
1184
0
  request_encoder_ = &encoder_decoder.first;
1185
0
  auto response = std::move(encoder_decoder.second);
1186
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1187
  // The continue headers should arrive immediately.
1188
0
  response->waitFor1xxHeaders();
1189
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
1190
1191
  // Send the rest of the request.
1192
0
  codec_client_->sendData(*request_encoder_, 10, true);
1193
0
  ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_));
1194
  // Verify the Expect header is stripped.
1195
0
  EXPECT_TRUE(upstream_request_->headers().get(Http::Headers::get().Expect).empty());
1196
0
  if (via.empty()) {
1197
0
    EXPECT_TRUE(upstream_request_->headers().get(Http::Headers::get().Via).empty());
1198
0
  } else {
1199
0
    EXPECT_EQ(
1200
0
        via,
1201
0
        upstream_request_->headers().get(Http::Headers::get().Via)[0]->value().getStringView());
1202
0
  }
1203
1204
0
  if (additional_continue_from_upstream) {
1205
    // Make sure if upstream sends an 100-Continue Envoy doesn't send its own and proxy the one
1206
    // from upstream!
1207
0
    upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}});
1208
0
  }
1209
1210
0
  if (disconnect_after_100) {
1211
0
    response->waitFor1xxHeaders();
1212
0
    codec_client_->close();
1213
0
    EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("100"));
1214
0
    ASSERT_TRUE(fake_upstream_connection_->close());
1215
0
    return;
1216
0
  }
1217
1218
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1219
0
  upstream_request_->encodeData(12, true);
1220
1221
0
  ASSERT_TRUE(response->waitForEndStream());
1222
0
  ASSERT_TRUE(response->complete());
1223
0
  ASSERT(response->informationalHeaders() != nullptr);
1224
0
  EXPECT_EQ("100", response->informationalHeaders()->getStatusValue());
1225
0
  EXPECT_EQ(nullptr, response->informationalHeaders()->Via());
1226
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1227
0
  if (via.empty()) {
1228
0
    EXPECT_EQ(nullptr, response->headers().Via());
1229
0
  } else {
1230
0
    EXPECT_EQ(via.c_str(), response->headers().getViaValue());
1231
0
  }
1232
0
  EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("200"));
1233
0
}
1234
1235
void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_complete,
1236
                                               bool with_encoder_filter,
1237
                                               bool with_multiple_1xx_headers,
1238
0
                                               absl::string_view initial_code) {
1239
0
  if (with_encoder_filter) {
1240
    // Add a filter to make sure 100s play well with them.
1241
0
    config_helper_.prependFilter("name: passthrough-filter");
1242
0
  }
1243
0
  config_helper_.addConfigModifier(
1244
0
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
1245
0
              hcm) -> void { hcm.set_proxy_100_continue(true); });
1246
0
  initialize();
1247
1248
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1249
0
  auto encoder_decoder =
1250
0
      codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "GET"},
1251
0
                                                                 {":path", "/dynamo/url"},
1252
0
                                                                 {":scheme", "http"},
1253
0
                                                                 {":authority", "sni.lyft.com"},
1254
0
                                                                 {"expect", "100-contINUE"}});
1255
0
  request_encoder_ = &encoder_decoder.first;
1256
0
  auto response = std::move(encoder_decoder.second);
1257
1258
  // Wait for the request headers to be received upstream.
1259
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1260
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
1261
1262
  // This case tests sending on 100-Continue headers before the client has sent all the
1263
  // request data.
1264
0
  if (continue_before_upstream_complete) {
1265
0
    upstream_request_->encode1xxHeaders(
1266
0
        Http::TestResponseHeaderMapImpl{{":status", initial_code.data()}});
1267
0
    if (with_multiple_1xx_headers) {
1268
0
      upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}});
1269
0
      upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false);
1270
0
      upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}});
1271
0
    }
1272
0
    response->waitFor1xxHeaders();
1273
0
  }
1274
  // Send all of the request data and wait for it to be received upstream.
1275
0
  codec_client_->sendData(*request_encoder_, 10, true);
1276
0
  ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_));
1277
1278
  // This case tests forwarding 100-Continue after the client has sent all data.
1279
0
  if (!continue_before_upstream_complete) {
1280
0
    upstream_request_->encode1xxHeaders(
1281
0
        Http::TestResponseHeaderMapImpl{{":status", initial_code.data()}});
1282
0
    if (with_multiple_1xx_headers) {
1283
0
      upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}});
1284
0
      upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false);
1285
0
      upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}});
1286
0
    }
1287
0
    response->waitFor1xxHeaders();
1288
0
  }
1289
  // Now send the rest of the response.
1290
0
  upstream_request_->encodeHeaders(default_response_headers_, true);
1291
0
  ASSERT_TRUE(response->waitForEndStream());
1292
0
  EXPECT_TRUE(response->complete());
1293
0
  ASSERT(response->informationalHeaders() != nullptr);
1294
0
  EXPECT_EQ(initial_code, response->informationalHeaders()->getStatusValue());
1295
1296
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1297
0
}
1298
1299
0
void HttpIntegrationTest::testTwoRequests(bool network_backup) {
1300
  // if network_backup is false, this simply tests that Envoy can handle multiple
1301
  // requests on a connection.
1302
  //
1303
  // If network_backup is true, the first request will explicitly set the TCP level flow control
1304
  // as blocked as it finishes the encode and set a timer to unblock. The second stream should be
1305
  // created while the socket appears to be in the high watermark state, and regression tests that
1306
  // flow control will be corrected as the socket "becomes unblocked"
1307
0
  if (network_backup) {
1308
0
    config_helper_.prependFilter(
1309
0
        fmt::format(R"EOF(
1310
0
  name: pause-filter{}
1311
0
  )EOF",
1312
0
                    downstreamProtocol() == Http::CodecType::HTTP3 ? "-for-quic" : ""));
1313
0
  }
1314
0
  initialize();
1315
1316
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1317
1318
  // Request 1.
1319
0
  auto response = codec_client_->makeRequestWithBody(default_request_headers_, 1024);
1320
0
  waitForNextUpstreamRequest();
1321
1322
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1323
0
  upstream_request_->encodeData(512, true);
1324
0
  ASSERT_TRUE(response->waitForEndStream());
1325
1326
0
  EXPECT_TRUE(upstream_request_->complete());
1327
0
  EXPECT_EQ(1024U, upstream_request_->bodyLength());
1328
0
  EXPECT_TRUE(response->complete());
1329
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1330
0
  EXPECT_EQ(512U, response->body().size());
1331
1332
  // Request 2.
1333
0
  response = codec_client_->makeRequestWithBody(default_request_headers_, 512);
1334
0
  waitForNextUpstreamRequest();
1335
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1336
0
  upstream_request_->encodeData(1024, true);
1337
0
  ASSERT_TRUE(response->waitForEndStream());
1338
1339
0
  EXPECT_TRUE(upstream_request_->complete());
1340
0
  EXPECT_EQ(512U, upstream_request_->bodyLength());
1341
0
  EXPECT_TRUE(response->complete());
1342
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1343
0
  EXPECT_EQ(1024U, response->body().size());
1344
0
}
1345
1346
0
void HttpIntegrationTest::testLargeRequestUrl(uint32_t url_size, uint32_t max_headers_size) {
1347
  // `size` parameter dictates the size of each header that will be added to the request and `count`
1348
  // parameter is the number of headers to be added. The actual request byte size will exceed `size`
1349
  // due to the keys and other headers. The actual request header count will exceed `count` by four
1350
  // due to default headers.
1351
1352
0
  config_helper_.addConfigModifier(
1353
0
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
1354
0
              hcm) -> void { hcm.mutable_max_request_headers_kb()->set_value(max_headers_size); });
1355
0
  setMaxRequestHeadersKb(max_headers_size);
1356
1357
0
  Http::TestRequestHeaderMapImpl big_headers{{":method", "GET"},
1358
0
                                             {":path", "/" + std::string(url_size * 1024, 'a')},
1359
0
                                             {":scheme", "http"},
1360
0
                                             {":authority", "sni.lyft.com"}};
1361
1362
0
  initialize();
1363
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1364
0
  if (url_size >= max_headers_size) {
1365
    // header size includes keys too, so expect rejection when equal
1366
0
    auto encoder_decoder = codec_client_->startRequest(big_headers);
1367
0
    auto response = std::move(encoder_decoder.second);
1368
1369
0
    if (downstream_protocol_ == Http::CodecType::HTTP1) {
1370
0
      ASSERT_TRUE(codec_client_->waitForDisconnect());
1371
0
      ASSERT_TRUE(response->complete());
1372
0
      EXPECT_EQ("431", response->headers().Status()->value().getStringView());
1373
0
    } else {
1374
0
      ASSERT_TRUE(response->waitForReset());
1375
0
      codec_client_->close();
1376
0
    }
1377
0
  } else {
1378
0
    auto response = sendRequestAndWaitForResponse(big_headers, 0, default_response_headers_, 0);
1379
0
    EXPECT_TRUE(response->complete());
1380
0
    EXPECT_EQ("200", response->headers().Status()->value().getStringView());
1381
0
  }
1382
0
}
1383
1384
void HttpIntegrationTest::testLargeRequestHeaders(uint32_t size, uint32_t count, uint32_t max_size,
1385
                                                  uint32_t max_count,
1386
0
                                                  std::chrono::milliseconds timeout) {
1387
0
  autonomous_upstream_ = true;
1388
0
  useAccessLog("%RESPONSE_CODE_DETAILS%");
1389
  // `size` parameter dictates the size of each header that will be added to the request and `count`
1390
  // parameter is the number of headers to be added. The actual request byte size will exceed `size`
1391
  // due to the keys and other headers. The actual request header count will exceed `count` by four
1392
  // due to default headers.
1393
1394
0
  config_helper_.addConfigModifier(
1395
0
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
1396
0
              hcm) -> void {
1397
0
        hcm.mutable_max_request_headers_kb()->set_value(max_size);
1398
0
        hcm.mutable_common_http_protocol_options()->mutable_max_headers_count()->set_value(
1399
0
            max_count);
1400
0
      });
1401
0
  setMaxRequestHeadersKb(max_size);
1402
0
  setMaxRequestHeadersCount(max_count);
1403
1404
0
  Http::TestRequestHeaderMapImpl big_headers{{":method", "GET"},
1405
0
                                             {":path", "/test/long/url"},
1406
0
                                             {":scheme", "http"},
1407
0
                                             {":authority", "sni.lyft.com"}};
1408
1409
  // Already added four headers.
1410
0
  for (unsigned int i = 0; i < count; i++) {
1411
0
    big_headers.addCopy(std::to_string(i), std::string(size * 1024, 'a'));
1412
0
  }
1413
1414
0
  initialize();
1415
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1416
0
  reinterpret_cast<AutonomousUpstream*>(fake_upstreams_.front().get())
1417
0
      ->setResponseHeaders(
1418
0
          std::make_unique<Http::TestResponseHeaderMapImpl>(default_response_headers_));
1419
1420
0
  if (size >= max_size || count > max_count) {
1421
    // header size includes keys too, so expect rejection when equal
1422
0
    auto encoder_decoder = codec_client_->startRequest(big_headers);
1423
0
    auto response = std::move(encoder_decoder.second);
1424
1425
0
    if (downstream_protocol_ == Http::CodecType::HTTP1) {
1426
0
      ASSERT_TRUE(codec_client_->waitForDisconnect());
1427
0
      ASSERT_TRUE(response->complete());
1428
0
      EXPECT_EQ("431", response->headers().getStatusValue());
1429
0
    } else {
1430
0
      ASSERT_TRUE(response->waitForReset());
1431
0
      codec_client_->close();
1432
0
    }
1433
0
  } else {
1434
0
    IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(big_headers);
1435
0
    RELEASE_ASSERT(response->waitForEndStream(timeout),
1436
0
                   fmt::format("unexpected timeout after ", timeout.count(), " ms"));
1437
0
    EXPECT_TRUE(response->complete());
1438
0
    EXPECT_EQ("200", response->headers().getStatusValue());
1439
0
  }
1440
0
  if (count > max_count) {
1441
0
    EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("too_many_headers"));
1442
0
  }
1443
0
}
1444
1445
0
void HttpIntegrationTest::testLargeRequestTrailers(uint32_t size, uint32_t max_size) {
1446
  // `size` parameter is the size of the trailer that will be added to the
1447
  // request. The actual request byte size will exceed `size` due to keys
1448
  // and other headers.
1449
1450
0
  config_helper_.addConfigModifier(
1451
0
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
1452
0
              hcm) -> void { hcm.mutable_max_request_headers_kb()->set_value(max_size); });
1453
0
  setMaxRequestHeadersKb(max_size);
1454
0
  Http::TestRequestTrailerMapImpl request_trailers{{"trailer", "trailer"}};
1455
0
  request_trailers.addCopy("big", std::string(size * 1024, 'a'));
1456
1457
0
  initialize();
1458
1459
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1460
1461
0
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
1462
0
  request_encoder_ = &encoder_decoder.first;
1463
0
  auto response = std::move(encoder_decoder.second);
1464
0
  codec_client_->sendData(*request_encoder_, 10, false);
1465
0
  codec_client_->sendTrailers(*request_encoder_, request_trailers);
1466
1467
0
  if (size >= max_size) {
1468
0
    if (downstream_protocol_ == Http::CodecType::HTTP1) {
1469
0
      ASSERT_TRUE(codec_client_->waitForDisconnect());
1470
0
      EXPECT_TRUE(response->complete());
1471
0
      EXPECT_EQ("431", response->headers().getStatusValue());
1472
0
    } else {
1473
      // Expect a stream reset when the size of the trailers is larger than the maximum
1474
      // limit.
1475
0
      ASSERT_TRUE(response->waitForReset());
1476
0
      codec_client_->close();
1477
0
      EXPECT_FALSE(response->complete());
1478
0
    }
1479
0
  } else {
1480
0
    waitForNextUpstreamRequest();
1481
0
    upstream_request_->encodeHeaders(default_response_headers_, true);
1482
0
    ASSERT_TRUE(response->waitForEndStream());
1483
0
    EXPECT_TRUE(response->complete());
1484
0
  }
1485
0
}
1486
1487
0
void HttpIntegrationTest::testManyRequestHeaders(std::chrono::milliseconds time) {
1488
  // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid
1489
  // time-consuming asserts when using a large number of headers.
1490
0
  setMaxRequestHeadersKb(96);
1491
0
  setMaxRequestHeadersCount(10010);
1492
1493
0
  config_helper_.addConfigModifier(
1494
0
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
1495
0
              hcm) -> void {
1496
0
        hcm.mutable_max_request_headers_kb()->set_value(upstreamConfig().max_request_headers_kb_);
1497
0
        hcm.mutable_common_http_protocol_options()->mutable_max_headers_count()->set_value(
1498
0
            upstreamConfig().max_request_headers_count_);
1499
0
      });
1500
1501
0
  auto big_headers = Http::createHeaderMap<Http::RequestHeaderMapImpl>(
1502
0
      {{Http::Headers::get().Method, "GET"},
1503
0
       {Http::Headers::get().Path, "/test/long/url"},
1504
0
       {Http::Headers::get().Scheme, "http"},
1505
0
       {Http::Headers::get().Host, "sni.lyft.com"}});
1506
1507
0
  for (int i = 0; i < 10000; i++) {
1508
0
    big_headers->addCopy(Http::LowerCaseString(std::to_string(i)), std::string(0, 'a'));
1509
0
  }
1510
0
  initialize();
1511
1512
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1513
1514
0
  auto response =
1515
0
      sendRequestAndWaitForResponse(*big_headers, 0, default_response_headers_, 0, 0, time);
1516
1517
0
  EXPECT_TRUE(response->complete());
1518
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1519
0
}
1520
1521
0
void HttpIntegrationTest::testDownstreamResetBeforeResponseComplete() {
1522
0
  initialize();
1523
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1524
1525
0
  auto encoder_decoder =
1526
0
      codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "GET"},
1527
0
                                                                 {":path", "/test/long/url"},
1528
0
                                                                 {":scheme", "http"},
1529
0
                                                                 {":authority", "sni.lyft.com"},
1530
0
                                                                 {"cookie", "a=b"},
1531
0
                                                                 {"cookie", "c=d"}});
1532
0
  request_encoder_ = &encoder_decoder.first;
1533
0
  auto response = std::move(encoder_decoder.second);
1534
0
  codec_client_->sendData(*request_encoder_, 0, true);
1535
0
  waitForNextUpstreamRequest();
1536
1537
0
  EXPECT_EQ(upstream_request_->headers().get(Http::Headers::get().Cookie)[0]->value(), "a=b; c=d");
1538
1539
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1540
0
  upstream_request_->encodeData(512, false);
1541
1542
0
  response->waitForBodyData(512);
1543
0
  codec_client_->sendReset(*request_encoder_);
1544
1545
0
  if (upstreamProtocol() == Http::CodecType::HTTP1) {
1546
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1547
0
  } else {
1548
0
    ASSERT_TRUE(upstream_request_->waitForReset());
1549
0
    ASSERT_TRUE(fake_upstream_connection_->close());
1550
0
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1551
0
  }
1552
1553
0
  codec_client_->close();
1554
1555
0
  EXPECT_TRUE(upstream_request_->complete());
1556
0
  EXPECT_EQ(0U, upstream_request_->bodyLength());
1557
1558
0
  EXPECT_FALSE(response->complete());
1559
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1560
0
  EXPECT_EQ(512U, response->body().size());
1561
0
}
1562
1563
void HttpIntegrationTest::testTrailers(uint64_t request_size, uint64_t response_size,
1564
0
                                       bool check_request, bool check_response) {
1565
0
  Http::TestRequestTrailerMapImpl request_trailers{{"request1", "trailer1"},
1566
0
                                                   {"request2", "trailer2"}};
1567
0
  Http::TestResponseTrailerMapImpl response_trailers{{"response1", "trailer1"},
1568
0
                                                     {"response2", "trailer2"}};
1569
1570
0
  initialize();
1571
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1572
0
  auto encoder_decoder =
1573
0
      codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"},
1574
0
                                                                 {":path", "/test/long/url"},
1575
0
                                                                 {":scheme", "http"},
1576
0
                                                                 {":authority", "sni.lyft.com"}});
1577
0
  request_encoder_ = &encoder_decoder.first;
1578
0
  auto response = std::move(encoder_decoder.second);
1579
0
  codec_client_->sendData(*request_encoder_, request_size, false);
1580
0
  codec_client_->sendTrailers(*request_encoder_, request_trailers);
1581
0
  waitForNextUpstreamRequest();
1582
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1583
0
  upstream_request_->encodeData(response_size, false);
1584
0
  upstream_request_->encodeTrailers(response_trailers);
1585
0
  ASSERT_TRUE(response->waitForEndStream());
1586
1587
0
  EXPECT_TRUE(upstream_request_->complete());
1588
0
  EXPECT_EQ(request_size, upstream_request_->bodyLength());
1589
0
  if (check_request) {
1590
0
    EXPECT_THAT(*upstream_request_->trailers(), HeaderMapEqualRef(&request_trailers));
1591
0
  } else {
1592
0
    EXPECT_EQ(upstream_request_->trailers(), nullptr);
1593
0
  }
1594
1595
0
  EXPECT_TRUE(response->complete());
1596
0
  EXPECT_EQ("200", response->headers().getStatusValue());
1597
0
  EXPECT_EQ(response_size, response->body().size());
1598
0
  if (check_response) {
1599
0
    EXPECT_THAT(*response->trailers(), HeaderMapEqualRef(&response_trailers));
1600
0
  } else {
1601
0
    EXPECT_EQ(response->trailers(), nullptr);
1602
0
  }
1603
0
}
1604
1605
0
void HttpIntegrationTest::testAdminDrain(Http::CodecType admin_request_type) {
1606
0
  initialize();
1607
1608
0
  uint32_t http_port = lookupPort("http");
1609
0
  codec_client_ = makeHttpConnection(http_port);
1610
0
  Http::TestRequestHeaderMapImpl request_headers{{":method", "HEAD"},
1611
0
                                                 {":path", "/test/long/url"},
1612
0
                                                 {":scheme", "http"},
1613
0
                                                 {":authority", "sni.lyft.com"}};
1614
0
  IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(request_headers);
1615
0
  waitForNextUpstreamRequest(0);
1616
1617
0
  upstream_request_->encodeHeaders(default_response_headers_, false);
1618
1619
  // Invoke drain listeners endpoint and validate that we can still work on inflight requests.
1620
0
  BufferingStreamDecoderPtr admin_response = IntegrationUtil::makeSingleRequest(
1621
0
      lookupPort("admin"), "POST", "/drain_listeners", "", admin_request_type, version_);
1622
0
  EXPECT_TRUE(admin_response->complete());
1623
0
  EXPECT_EQ("200", admin_response->headers().getStatusValue());
1624
0
  EXPECT_EQ("OK\n", admin_response->body());
1625
1626
0
  upstream_request_->encodeData(512, true);
1627
1628
0
  ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1629
1630
  // Wait for the response to be read by the codec client.
1631
0
  ASSERT_TRUE(response->waitForEndStream());
1632
1633
0
  ASSERT_TRUE(response->complete());
1634
0
  EXPECT_THAT(response->headers(), Http::HttpStatusIs("200"));
1635
1636
  // Validate that the listeners have been stopped.
1637
0
  test_server_->waitForCounterEq("listener_manager.listener_stopped", 1);
1638
1639
  // Validate that port is closed and can be bound by other sockets.
1640
  // This does not work for HTTP/3 because the port is not closed until the listener is completely
1641
  // destroyed. TODO(danzh) Match TCP behavior as much as possible.
1642
0
  if (downstreamProtocol() != Http::CodecType::HTTP3) {
1643
0
    ASSERT_TRUE(waitForPortAvailable(http_port));
1644
0
  }
1645
0
}
1646
1647
void HttpIntegrationTest::simultaneousRequest(uint32_t request1_bytes, uint32_t request2_bytes,
1648
0
                                              uint32_t response1_bytes, uint32_t response2_bytes) {
1649
0
  config_helper_.prependFilter(fmt::format(R"EOF(
1650
0
  name: stream-info-to-headers-filter
1651
0
)EOF"));
1652
1653
0
  FakeStreamPtr upstream_request1;
1654
0
  FakeStreamPtr upstream_request2;
1655
0
  initialize();
1656
0
  codec_client_ = makeHttpConnection(lookupPort("http"));
1657
1658
  // Start request 1
1659
0
  auto encoder_decoder1 = codec_client_->startRequest(default_request_headers_);
1660
0
  Http::RequestEncoder* encoder1 = &encoder_decoder1.first;
1661
0
  auto response1 = std::move(encoder_decoder1.second);
1662
0
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1663
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request1));
1664
1665
  // Start request 2
1666
0
  auto encoder_decoder2 = codec_client_->startRequest(default_request_headers_);
1667
0
  Http::RequestEncoder* encoder2 = &encoder_decoder2.first;
1668
0
  auto response2 = std::move(encoder_decoder2.second);
1669
0
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request2));
1670
1671
  // Finish request 1
1672
0
  codec_client_->sendData(*encoder1, request1_bytes, true);
1673
0
  ASSERT_TRUE(upstream_request1->waitForEndStream(*dispatcher_));
1674
1675
  // Finish request 2
1676
0
  codec_client_->sendData(*encoder2, request2_bytes, true);
1677
0
  ASSERT_TRUE(upstream_request2->waitForEndStream(*dispatcher_));
1678
1679
  // Respond to request 2
1680
0
  upstream_request2->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false);
1681
0
  upstream_request2->encodeData(response2_bytes, true);
1682
0
  ASSERT_TRUE(response2->waitForEndStream());
1683
0
  EXPECT_TRUE(upstream_request2->complete());
1684
0
  EXPECT_EQ(request2_bytes, upstream_request2->bodyLength());
1685
0
  EXPECT_TRUE(response2->complete());
1686
0
  EXPECT_EQ("200", response2->headers().getStatusValue());
1687
0
  EXPECT_EQ(response2_bytes, response2->body().size());
1688
1689
  // Respond to request 1
1690
0
  upstream_request1->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false);
1691
0
  upstream_request1->encodeData(response1_bytes, true);
1692
0
  ASSERT_TRUE(response1->waitForEndStream());
1693
0
  EXPECT_TRUE(upstream_request1->complete());
1694
0
  EXPECT_EQ(request1_bytes, upstream_request1->bodyLength());
1695
0
  EXPECT_TRUE(response1->complete());
1696
0
  EXPECT_EQ("200", response1->headers().getStatusValue());
1697
0
  EXPECT_EQ(response1_bytes, response1->body().size());
1698
1699
0
  ASSERT_FALSE(response1->headers().get(Http::LowerCaseString("num_streams")).empty());
1700
0
  ASSERT_FALSE(response2->headers().get(Http::LowerCaseString("num_streams")).empty());
1701
0
  EXPECT_EQ(
1702
0
      response1->headers().get(Http::LowerCaseString("num_streams"))[0]->value().getStringView(),
1703
0
      "1");
1704
0
  EXPECT_EQ(
1705
0
      response2->headers().get(Http::LowerCaseString("num_streams"))[0]->value().getStringView(),
1706
0
      upstreamProtocol() == Http::CodecType::HTTP1 ? "1" : "2");
1707
0
}
1708
1709
0
std::string HttpIntegrationTest::downstreamProtocolStatsRoot() const {
1710
0
  switch (downstreamProtocol()) {
1711
0
  case Http::CodecClient::Type::HTTP1:
1712
0
    return "http1";
1713
0
  case Http::CodecClient::Type::HTTP2:
1714
0
    return "http2";
1715
0
  case Http::CodecClient::Type::HTTP3:
1716
0
    return "http3";
1717
0
  }
1718
0
  return "invalid";
1719
0
}
1720
1721
0
std::string HttpIntegrationTest::upstreamProtocolStatsRoot() const {
1722
0
  switch (upstreamProtocol()) {
1723
0
  case FakeHttpConnection::Type::HTTP1:
1724
0
    return "http1";
1725
0
  case FakeHttpConnection::Type::HTTP2:
1726
0
    return "http2";
1727
0
  case FakeHttpConnection::Type::HTTP3:
1728
0
    return "http3";
1729
0
  }
1730
0
  return "invalid";
1731
0
}
1732
1733
0
std::string HttpIntegrationTest::listenerStatPrefix(const std::string& stat_name) {
1734
0
  if (version_ == Network::Address::IpVersion::v4) {
1735
0
    return "listener.127.0.0.1_0." + stat_name;
1736
0
  }
1737
0
  return "listener.[__1]_0." + stat_name;
1738
0
}
1739
1740
void HttpIntegrationTest::expectUpstreamBytesSentAndReceived(BytesCountExpectation h1_expectation,
1741
                                                             BytesCountExpectation h2_expectation,
1742
                                                             BytesCountExpectation h3_expectation,
1743
0
                                                             const int id) {
1744
0
  std::string access_log = waitForAccessLog(access_log_name_, id, true);
1745
0
  std::vector<std::string> log_entries = absl::StrSplit(access_log, ' ');
1746
0
  int wire_bytes_sent = std::stoi(log_entries[0]), wire_bytes_received = std::stoi(log_entries[1]),
1747
0
      header_bytes_sent = std::stoi(log_entries[2]),
1748
0
      header_bytes_received = std::stoi(log_entries[3]);
1749
0
  switch (upstreamProtocol()) {
1750
0
  case Http::CodecType::HTTP1: {
1751
0
    EXPECT_EQ(h1_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1752
0
    EXPECT_EQ(h1_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1753
0
    EXPECT_EQ(h1_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1754
0
    EXPECT_EQ(h1_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1755
0
    return;
1756
0
  }
1757
0
  case Http::CodecType::HTTP2: {
1758
0
    EXPECT_EQ(h2_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1759
0
    EXPECT_EQ(h2_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1760
0
    EXPECT_EQ(h2_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1761
0
    EXPECT_EQ(h2_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1762
0
    return;
1763
0
  }
1764
0
  case Http::CodecType::HTTP3: {
1765
0
    EXPECT_EQ(h3_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1766
0
    EXPECT_EQ(h3_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1767
0
    EXPECT_EQ(h3_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1768
0
    EXPECT_EQ(h3_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1769
0
    return;
1770
0
  }
1771
1772
0
  default:
1773
0
    EXPECT_TRUE(false) << "Unexpected codec type: " << static_cast<int>(upstreamProtocol());
1774
0
  }
1775
0
}
1776
1777
void HttpIntegrationTest::expectDownstreamBytesSentAndReceived(BytesCountExpectation h1_expectation,
1778
                                                               BytesCountExpectation h2_expectation,
1779
                                                               BytesCountExpectation h3_expectation,
1780
0
                                                               const int id) {
1781
0
  std::string access_log = waitForAccessLog(access_log_name_, id);
1782
0
  std::vector<std::string> log_entries = absl::StrSplit(access_log, ' ');
1783
0
  int wire_bytes_sent = std::stoi(log_entries[0]), wire_bytes_received = std::stoi(log_entries[1]),
1784
0
      header_bytes_sent = std::stoi(log_entries[2]),
1785
0
      header_bytes_received = std::stoi(log_entries[3]);
1786
0
  switch (downstreamProtocol()) {
1787
0
  case Http::CodecType::HTTP1: {
1788
0
    EXPECT_EQ(h1_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1789
0
    EXPECT_EQ(h1_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1790
0
    EXPECT_EQ(h1_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1791
0
    EXPECT_EQ(h1_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1792
0
    return;
1793
0
  }
1794
0
  case Http::CodecType::HTTP2: {
1795
0
    EXPECT_EQ(h2_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1796
0
    EXPECT_EQ(h2_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1797
0
    EXPECT_EQ(h2_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1798
0
    EXPECT_EQ(h2_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1799
0
    return;
1800
0
  }
1801
0
  case Http::CodecType::HTTP3: {
1802
0
    EXPECT_EQ(h3_expectation.wire_bytes_sent_ == 0, wire_bytes_sent == 0);
1803
0
    EXPECT_EQ(h3_expectation.wire_bytes_received_ == 0, wire_bytes_received == 0);
1804
0
    EXPECT_EQ(h3_expectation.header_bytes_sent_ == 0, header_bytes_sent == 0);
1805
0
    EXPECT_EQ(h3_expectation.header_bytes_received_ == 0, header_bytes_received == 0);
1806
0
    return;
1807
0
  }
1808
0
  default:
1809
0
    EXPECT_TRUE(false) << "Unexpected codec type: " << static_cast<int>(downstreamProtocol());
1810
0
  }
1811
0
}
1812
1813
0
void Http2RawFrameIntegrationTest::startHttp2Session() {
1814
0
  ASSERT_TRUE(tcp_client_->write(Http2Frame::Preamble, false, false));
1815
1816
  // Send empty initial SETTINGS frame.
1817
0
  auto settings = Http2Frame::makeEmptySettingsFrame();
1818
0
  ASSERT_TRUE(tcp_client_->write(std::string(settings), false, false));
1819
1820
  // Read initial SETTINGS frame from the server.
1821
0
  readFrame();
1822
1823
  // Send an SETTINGS ACK.
1824
0
  settings = Http2Frame::makeEmptySettingsFrame(Http2Frame::SettingsFlags::Ack);
1825
0
  ASSERT_TRUE(tcp_client_->write(std::string(settings), false, false));
1826
1827
  // read pending SETTINGS and WINDOW_UPDATE frames
1828
0
  readFrame();
1829
0
  readFrame();
1830
0
}
1831
1832
0
void Http2RawFrameIntegrationTest::beginSession() {
1833
0
  setDownstreamProtocol(Http::CodecType::HTTP2);
1834
0
  setUpstreamProtocol(Http::CodecType::HTTP2);
1835
  // set lower outbound frame limits to make tests run faster
1836
0
  config_helper_.setDownstreamOutboundFramesLimits(1000, 100);
1837
0
  initialize();
1838
  // Set up a raw connection to easily send requests without reading responses.
1839
0
  auto options = std::make_shared<Network::Socket::Options>();
1840
0
  options->emplace_back(std::make_shared<Network::SocketOptionImpl>(
1841
0
      envoy::config::core::v3::SocketOption::STATE_PREBIND,
1842
0
      ENVOY_MAKE_SOCKET_OPTION_NAME(SOL_SOCKET, SO_RCVBUF), 1024));
1843
0
  tcp_client_ = makeTcpConnection(lookupPort("http"), options);
1844
0
  startHttp2Session();
1845
0
}
1846
1847
0
Http2Frame Http2RawFrameIntegrationTest::readFrame() {
1848
0
  Http2Frame frame;
1849
0
  EXPECT_TRUE(tcp_client_->waitForData(frame.HeaderSize));
1850
0
  frame.setHeader(tcp_client_->data());
1851
0
  tcp_client_->clearData(frame.HeaderSize);
1852
0
  auto len = frame.payloadSize();
1853
0
  if (len) {
1854
0
    EXPECT_TRUE(tcp_client_->waitForData(len));
1855
0
    frame.setPayload(tcp_client_->data());
1856
0
    tcp_client_->clearData(len);
1857
0
  }
1858
0
  return frame;
1859
0
}
1860
1861
0
void Http2RawFrameIntegrationTest::sendFrame(const Http2Frame& frame) {
1862
0
  ASSERT_TRUE(tcp_client_->connected());
1863
0
  ASSERT_TRUE(tcp_client_->write(std::string(frame), false, false));
1864
0
}
1865
1866
0
absl::string_view upstreamToString(Http::CodecType type) {
1867
0
  switch (type) {
1868
0
  case Http::CodecType::HTTP1:
1869
0
    return "HttpUpstream";
1870
0
  case Http::CodecType::HTTP2:
1871
0
    return "Http2Upstream";
1872
0
  case Http::CodecType::HTTP3:
1873
0
    return "Http3Upstream";
1874
0
  }
1875
0
  return "UnknownUpstream";
1876
0
}
1877
1878
0
absl::string_view downstreamToString(Http::CodecType type) {
1879
0
  switch (type) {
1880
0
  case Http::CodecType::HTTP1:
1881
0
    return "HttpDownstream_";
1882
0
  case Http::CodecType::HTTP2:
1883
0
    return "Http2Downstream_";
1884
0
  case Http::CodecType::HTTP3:
1885
0
    return "Http3Downstream_";
1886
0
  }
1887
0
  return "UnknownDownstream";
1888
0
}
1889
1890
0
absl::string_view http2ImplementationToString(Http2Impl impl) {
1891
0
  switch (impl) {
1892
0
  case Http2Impl::Nghttp2:
1893
0
    return "Nghttp2";
1894
0
  case Http2Impl::Oghttp2:
1895
0
    return "Oghttp2";
1896
0
  }
1897
0
  return "UnknownHttp2Impl";
1898
0
}
1899
1900
} // namespace Envoy