Coverage Report

Created: 2023-11-12 09:30

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