@@ -32,6 +32,15 @@ using testing::NiceMock;
32
32
33
33
namespace Envoy {
34
34
35
+ std::vector<TcpProxyIntegrationTestParams> newPoolTestParams () {
36
+ std::vector<TcpProxyIntegrationTestParams> ret;
37
+
38
+ for (auto ip_version : TestEnvironment::getIpVersionsForTest ()) {
39
+ ret.push_back (TcpProxyIntegrationTestParams{ip_version, false });
40
+ }
41
+ return ret;
42
+ }
43
+
35
44
std::vector<TcpProxyIntegrationTestParams> getProtocolTestParams () {
36
45
std::vector<TcpProxyIntegrationTestParams> ret;
37
46
@@ -1297,4 +1306,179 @@ TEST_P(TcpProxySslIntegrationTest, UpstreamHalfClose) {
1297
1306
ASSERT_TRUE (fake_upstream_connection_->waitForHalfClose ());
1298
1307
}
1299
1308
1309
+ // Integration test a Mysql upstream, where the upstream sends data immediately
1310
+ // after a connection is established.
1311
+ class FakeMysqlUpstream : public FakeUpstream {
1312
+ using FakeUpstream::FakeUpstream;
1313
+
1314
+ bool createNetworkFilterChain (Network::Connection& connection,
1315
+ const std::vector<Network::FilterFactoryCb>& cb) override {
1316
+ Buffer::OwnedImpl to_write (" P" );
1317
+ connection.write (to_write, false );
1318
+ return FakeUpstream::createNetworkFilterChain (connection, cb);
1319
+ }
1320
+ };
1321
+
1322
+ class MysqlIntegrationTest : public TcpProxyIntegrationTest {
1323
+ public:
1324
+ void createUpstreams () override {
1325
+ for (uint32_t i = 0 ; i < fake_upstreams_count_; ++i) {
1326
+ Network::TransportSocketFactoryPtr factory =
1327
+ upstream_tls_ ? createUpstreamTlsContext ()
1328
+ : Network::Test::createRawBufferSocketFactory ();
1329
+ auto endpoint = upstream_address_fn_ (i);
1330
+ fake_upstreams_.emplace_back (
1331
+ new FakeMysqlUpstream (std::move (factory), endpoint, upstreamConfig ()));
1332
+ }
1333
+ }
1334
+
1335
+ absl::optional<uint64_t >
1336
+ waitForNextUpstreamConnection (const std::vector<uint64_t >& upstream_indices,
1337
+ std::chrono::milliseconds connection_wait_timeout,
1338
+ FakeRawConnectionPtr& fake_upstream_connection) {
1339
+ AssertionResult result = AssertionFailure ();
1340
+ int upstream_index = 0 ;
1341
+ Event::TestTimeSystem::RealTimeBound bound (connection_wait_timeout);
1342
+ // Loop over the upstreams until the call times out or an upstream request is received.
1343
+ while (!result) {
1344
+ upstream_index = upstream_index % upstream_indices.size ();
1345
+ result = fake_upstreams_[upstream_indices[upstream_index]]->waitForRawConnection (
1346
+ fake_upstream_connection, std::chrono::milliseconds (5 ));
1347
+ if (result) {
1348
+ return upstream_index;
1349
+ } else if (!bound.withinBound ()) {
1350
+ RELEASE_ASSERT (0 , " Timed out waiting for new connection." );
1351
+ break ;
1352
+ }
1353
+ ++upstream_index;
1354
+ }
1355
+ RELEASE_ASSERT (result, result.message ());
1356
+ return {};
1357
+ }
1358
+
1359
+ void globalPreconnect () {
1360
+ config_helper_.addConfigModifier (
1361
+ [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void {
1362
+ bootstrap.mutable_static_resources ()
1363
+ ->mutable_clusters (0 )
1364
+ ->mutable_preconnect_policy ()
1365
+ ->mutable_predictive_preconnect_ratio ()
1366
+ ->set_value (1.5 );
1367
+ });
1368
+ }
1369
+
1370
+ void perUpstreamPreconnect () {
1371
+ config_helper_.addConfigModifier (
1372
+ [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void {
1373
+ bootstrap.mutable_static_resources ()
1374
+ ->mutable_clusters (0 )
1375
+ ->mutable_preconnect_policy ()
1376
+ ->mutable_per_upstream_preconnect_ratio ()
1377
+ ->set_value (1.5 );
1378
+ });
1379
+ }
1380
+
1381
+ void testPreconnect ();
1382
+ };
1383
+
1384
+ // This just verifies that FakeMysqlUpstream works as advertised, and the early data
1385
+ // makes it to the client.
1386
+ TEST_P (MysqlIntegrationTest, UpstreamWritesFirst) {
1387
+ globalPreconnect ();
1388
+ initialize ();
1389
+ IntegrationTcpClientPtr tcp_client = makeTcpConnection (lookupPort (" tcp_proxy" ));
1390
+ FakeRawConnectionPtr fake_upstream_connection;
1391
+ ASSERT_TRUE (fake_upstreams_[0 ]->waitForRawConnection (fake_upstream_connection));
1392
+
1393
+ tcp_client->waitForData (" P" , false );
1394
+
1395
+ ASSERT_TRUE (tcp_client->write (" F" ));
1396
+ ASSERT_TRUE (fake_upstream_connection->waitForData (1 ));
1397
+
1398
+ ASSERT_TRUE (fake_upstream_connection->write (" " , true ));
1399
+ tcp_client->waitForHalfClose ();
1400
+ ASSERT_TRUE (tcp_client->write (" " , true ));
1401
+ ASSERT_TRUE (fake_upstream_connection->waitForHalfClose ());
1402
+ ASSERT_TRUE (fake_upstream_connection->waitForDisconnect ());
1403
+ }
1404
+
1405
+ // Make sure that with the connection read disabled, that disconnect detection
1406
+ // works.
1407
+ // Early close notification does not work for OSX
1408
+ #if !defined(__APPLE__)
1409
+ TEST_P (MysqlIntegrationTest, DisconnectDetected) {
1410
+ // Switch to per-upstream preconnect.
1411
+ perUpstreamPreconnect ();
1412
+ initialize ();
1413
+ IntegrationTcpClientPtr tcp_client = makeTcpConnection (lookupPort (" tcp_proxy" ));
1414
+ FakeRawConnectionPtr fake_upstream_connection;
1415
+ FakeRawConnectionPtr fake_upstream_connection1;
1416
+ // The needed connection.
1417
+ ASSERT_TRUE (fake_upstreams_[0 ]->waitForRawConnection (fake_upstream_connection));
1418
+ // The prefetched connection.
1419
+ ASSERT_TRUE (fake_upstreams_[0 ]->waitForRawConnection (fake_upstream_connection1));
1420
+
1421
+ // Close the prefetched connection.
1422
+ ASSERT_TRUE (fake_upstream_connection1->close ());
1423
+ test_server_->waitForCounterGe (" cluster.cluster_0.upstream_cx_destroy" , 1 );
1424
+
1425
+ tcp_client->close ();
1426
+ }
1427
+ #endif
1428
+
1429
+ void MysqlIntegrationTest::testPreconnect () {
1430
+ globalPreconnect ();
1431
+ enableHalfClose (false );
1432
+ config_helper_.addConfigModifier ([this ](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
1433
+ auto * cluster = bootstrap.mutable_static_resources ()->mutable_clusters (0 );
1434
+ auto * load_assignment = cluster->mutable_load_assignment ();
1435
+ load_assignment->clear_endpoints ();
1436
+ for (int i = 0 ; i < 5 ; ++i) {
1437
+ auto locality = load_assignment->add_endpoints ();
1438
+ locality->add_lb_endpoints ()->mutable_endpoint ()->MergeFrom (
1439
+ ConfigHelper::buildEndpoint (Network::Test::getLoopbackAddressString (version_)));
1440
+ }
1441
+ });
1442
+
1443
+ setUpstreamCount (5 );
1444
+ initialize ();
1445
+ int num_clients = 10 ;
1446
+
1447
+ std::vector<IntegrationTcpClientPtr> clients{10 };
1448
+ std::vector<FakeRawConnectionPtr> fake_connections{15 };
1449
+
1450
+ int upstream_index = 0 ;
1451
+ for (int i = 0 ; i < num_clients; ++i) {
1452
+ // Start a new request.
1453
+ clients[i] = makeTcpConnection (lookupPort (" tcp_proxy" ));
1454
+ waitForNextUpstreamConnection (std::vector<uint64_t >({0 , 1 , 2 , 3 , 4 }),
1455
+ TestUtility::DefaultTimeout, fake_connections[upstream_index]);
1456
+ ++upstream_index;
1457
+
1458
+ // For every other connection, an extra connection should be preconnected.
1459
+ if (i % 2 == 0 ) {
1460
+ waitForNextUpstreamConnection (std::vector<uint64_t >({0 , 1 , 2 , 3 , 4 }),
1461
+ TestUtility::DefaultTimeout, fake_connections[upstream_index]);
1462
+ ++upstream_index;
1463
+ }
1464
+ }
1465
+ EXPECT_EQ (0 , test_server_->counter (" cluster.cluster_0.upstream_cx_destroy" )->value ());
1466
+ // Clean up.
1467
+ for (int i = 0 ; i < num_clients; ++i) {
1468
+ clients[i]->close ();
1469
+ }
1470
+ }
1471
+
1472
+ TEST_P (MysqlIntegrationTest, Preconnect) { testPreconnect (); }
1473
+
1474
+ TEST_P (MysqlIntegrationTest, PreconnectWithTls) {
1475
+ upstream_tls_ = true ;
1476
+ setUpstreamProtocol (FakeHttpConnection::Type::HTTP1);
1477
+ config_helper_.configureUpstreamTls ();
1478
+ testPreconnect ();
1479
+ }
1480
+
1481
+ INSTANTIATE_TEST_SUITE_P (TcpProxyIntegrationTestParams, MysqlIntegrationTest,
1482
+ testing::ValuesIn (newPoolTestParams()), protocolTestParamsToString);
1483
+
1300
1484
} // namespace Envoy
0 commit comments