blob: a6010773f8396310ed18b3e07ecbb2c61d01e523 [file]
/** @file
Catch based unit tests for ConnectingEntry.
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "proxy/http/ConnectingEntry.h"
#include "iocore/net/ConnectionTracker.h"
#include "iocore/net/Net.h"
#include "tscore/ink_inet.h"
#include <catch2/catch_test_macros.hpp>
#include <memory>
#include <string_view>
#include <vector>
namespace
{
constexpr std::string_view FQDN{"connecting.entry.test.origin"};
ConnectionTracker::TxnConfig &
test_config()
{
// config_init keeps pointers to these for the records callbacks, so they must outlive the test.
static ConnectionTracker::GlobalConfig global;
static ConnectionTracker::TxnConfig txn;
static bool initialized = false;
if (!initialized) {
ink_net_init(NET_SYSTEM_MODULE_PUBLIC_VERSION);
ConnectionTracker::config_init(&global, &txn, [](const char *, RecDataT, RecData, void *) -> int { return REC_ERR_OKAY; });
initialized = true;
}
return txn;
}
// A reservation on the outbound group for @a addr_text, as HttpSM makes before it creates a ConnectingEntry.
std::shared_ptr<ConnectionTracker::Group>
reserved_group(char const *addr_text)
{
IpEndpoint addr;
REQUIRE(0 == ats_ip_pton(addr_text, &addr));
auto state = ConnectionTracker::obtain_outbound(test_config(), FQDN, addr);
REQUIRE(state.is_active());
state.reserve();
return state.drop();
}
bool
in_outbound_table(std::shared_ptr<ConnectionTracker::Group> const &group)
{
std::vector<std::shared_ptr<ConnectionTracker::Group const>> groups;
ConnectionTracker::get_outbound_groups(groups);
for (auto const &g : groups) {
if (g == group) {
return true;
}
}
return false;
}
} // namespace
// A failed or cancelled connect deletes the entry while it still holds the reservation.
TEST_CASE("ConnectingEntry destructor returns the outbound reservation", "[ConnectingEntry]")
{
auto group = reserved_group("192.0.2.10:80");
REQUIRE(group->_count == 1);
REQUIRE(in_outbound_table(group));
auto *entry = new ConnectingEntry();
entry->enable_outbound_connection_tracking(group);
delete entry;
CHECK(group->_count == 0);
CHECK_FALSE(in_outbound_table(group));
}
TEST_CASE("ConnectingEntry destructor releases exactly one reservation", "[ConnectingEntry]")
{
auto group = reserved_group("192.0.2.11:80");
auto other = reserved_group("192.0.2.11:80");
REQUIRE(group == other);
REQUIRE(group->_count == 2);
auto *entry = new ConnectingEntry();
entry->enable_outbound_connection_tracking(group);
delete entry;
CHECK(other->_count == 1);
CHECK(in_outbound_table(other));
other->release();
}
// With connection tracking off, HttpSM still hands the entry its (empty) group; deleting it must not touch the tracker.
TEST_CASE("ConnectingEntry destructor without connection tracking", "[ConnectingEntry]")
{
auto *entry = new ConnectingEntry();
entry->enable_outbound_connection_tracking({});
delete entry;
}