Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,37 @@ if(APPLE)
target_link_options(tsapi PRIVATE -undefined dynamic_lookup)
endif()

if(BUILD_TESTING)
add_executable(test_api unit_tests/test_HttpOverridableConfig.cc)
target_link_libraries(
test_api
PRIVATE Catch2::Catch2WithMain
ts::tscore
ts::tsapi
ts::overridable_txn_vars
ts::tsutil
ts::http
ts::http_remap
ts::http2
ts::logging
ts::hdrs
ts::diagsconfig
ts::inkutils
ts::inkdns
ts::inkhostdb
ts::inkcache
ts::aio
ts::proxy
ts::inknet
ts::records
ts::inkevent
libswoc::libswoc
ts::jsonrpc_protocol
ts::jsonrpc_server
ts::rpcpublichandlers
ts::configmanager
)
add_catch2_test(NAME test_api COMMAND test_api)
endif()

clang_tidy_check(tsapi)
139 changes: 2 additions & 137 deletions src/api/InkAPITest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@
#include "records/RecHttp.h"

#include "proxy/http/HttpSM.h"
#include "proxy/http/OverridableConfigDefs.h"
#include "iocore/net/ConnectionTracker.h"
#include "tscore/TestBox.h"

namespace
Expand Down Expand Up @@ -94,7 +92,8 @@ DbgCtl dbg_ctl_SockClient{"SockClient"};
#define ERROR_BODY "TESTING ERROR PAGE"
#define TRANSFORM_APPEND_STRING "This is a transformed response"

extern int dns_failover_period;
extern int dns_failover_period;
extern ClassAllocator<HttpSM> httpSMAllocator;

//////////////////////////////////////////////////////////////////////////////
// STRUCTURES
Expand Down Expand Up @@ -8704,140 +8703,6 @@ EXCLUSIVE_REGRESSION_TEST(SDK_API_TSHttpConnectServerIntercept)(RegressionTest *
return;
}

////////////////////////////////////////////////
// SDK_API_OVERRIDABLE_CONFIGS
//
// Unit Test for API: TSHttpTxnConfigFind
// TSHttpTxnConfigIntSet
// TSHttpTxnConfigIntGet
// TSHttpTxnConfigFloatSet
// TSHttpTxnConfigFloatGet
// TSHttpTxnConfigStringSet
// TSHttpTxnConfigStringGet
////////////////////////////////////////////////

// Generate the SDK_Overridable_Configs array from the X-macro.
// The order MUST match TSOverridableConfigKey enum order (enforced by static_assert).
// clang-format off
#define X_SDK_CONFIG(CONFIG_KEY, MEMBER, RECORD_NAME, DATA_TYPE, CONV) RECORD_NAME,
std::array<std::string_view, TS_CONFIG_LAST_ENTRY> SDK_Overridable_Configs = {{
OVERRIDABLE_CONFIGS(X_SDK_CONFIG)
}};
#undef X_SDK_CONFIG
// clang-format on

static_assert(SDK_Overridable_Configs.size() == TS_CONFIG_LAST_ENTRY,
"SDK_Overridable_Configs size must match TS_CONFIG_LAST_ENTRY");

extern ClassAllocator<HttpSM> httpSMAllocator;

REGRESSION_TEST(SDK_API_OVERRIDABLE_CONFIGS)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
{
TSOverridableConfigKey key;
TSRecordDataType type;
HttpSM *s = THREAD_ALLOC(httpSMAllocator, this_thread());
bool success = true;
TSHttpTxn txnp = reinterpret_cast<TSHttpTxn>(s);
InkRand generator(17);
TSMgmtInt ival_read, ival_rand;
TSMgmtFloat fval_read, fval_rand;
const char *sval_read;
const char *test_string = "The Apache Traffic Server";
int len;

s->init();
s->mutex = new_ProxyMutex();
SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread());

HttpCacheSM *c_sm = &(s->get_cache_sm());
c_sm->init(s, s->mutex);

*pstatus = REGRESSION_TEST_INPROGRESS;
for (int i = 0; i < static_cast<int>(SDK_Overridable_Configs.size()); ++i) {
std::string_view conf{SDK_Overridable_Configs[i]};

if (TS_SUCCESS == TSHttpTxnConfigFind(conf.data(), -1, &key, &type)) {
if (key != i) {
SDK_RPRINT(test, "TSHttpTxnConfigFind", "TestCase1", TC_FAIL, "Failed on %s, expected %d, got %d", conf.data(), i, key);
success = false;
continue;
}
} else {
SDK_RPRINT(test, "TSHttpTxnConfigFind", "TestCase1", TC_FAIL, "Call returned unexpected TS_ERROR for %s", conf.data());
success = false;
continue;
}

if (TS_SUCCESS == TSHttpTxnConfigFind(conf.data(), conf.size(), &key, &type)) {
if (key != i) {
SDK_RPRINT(test, "TSHttpTxnConfigFind", "TestCase1", TC_FAIL, "Failed on %s, expected %d, got %d", conf.data(), i, key);
success = false;
continue;
}
} else {
SDK_RPRINT(test, "TSHttpTxnConfigFind", "TestCase1", TC_FAIL, "Call returned unexpected TS_ERROR for %s", conf.data());
success = false;
continue;
}

// Now check the getters / setters
switch (type) {
case TS_RECORDDATATYPE_INT:
ival_rand = generator.random() % 126; // to fit in a signed byte
TSHttpTxnConfigIntSet(txnp, key, ival_rand);
TSHttpTxnConfigIntGet(txnp, key, &ival_read);
if (ival_rand != ival_read) {
SDK_RPRINT(test, "TSHttpTxnConfigIntSet", "TestCase1", TC_FAIL, "Failed on %s, %d != %d", conf.data(), ival_read,
ival_rand);
success = false;
continue;
}
break;

case TS_RECORDDATATYPE_FLOAT:
fval_rand = generator.random();
TSHttpTxnConfigFloatSet(txnp, key, fval_rand);
TSHttpTxnConfigFloatGet(txnp, key, &fval_read);
if (fval_rand != fval_read) {
SDK_RPRINT(test, "TSHttpTxnConfigFloatSet", "TestCase1", TC_FAIL, "Failed on %s, %f != %f", conf.data(), fval_read,
fval_rand);
success = false;
continue;
}
break;

case TS_RECORDDATATYPE_STRING:
TSHttpTxnConfigStringSet(txnp, key, test_string, -1);
TSHttpTxnConfigStringGet(txnp, key, &sval_read, &len);
// Compare string content, not pointers - the implementation may store
// a copy of the string (e.g., in ParsedConfigCache for efficiency).
if (sval_read == nullptr || std::string_view(test_string) != std::string_view(sval_read, len)) {
SDK_RPRINT(test, "TSHttpTxnConfigStringSet", "TestCase1", TC_FAIL, "Failed on %s, %s != %s", conf.data(),
sval_read ? sval_read : "(null)", test_string);
success = false;
continue;
}
break;

default:
break;
}
}

s->destroy();
if (success) {
*pstatus = REGRESSION_TEST_PASSED;
SDK_RPRINT(test, "TSHttpTxnConfigFind", "TestCase1", TC_PASS, "ok");
SDK_RPRINT(test, "TSHttpTxnConfigIntSet", "TestCase1", TC_PASS, "ok");
SDK_RPRINT(test, "TSHttpTxnConfigFloatSet", "TestCase1", TC_PASS, "ok");
SDK_RPRINT(test, "TSHttpTxnConfigStringSet", "TestCase1", TC_PASS, "ok");
} else {
*pstatus = REGRESSION_TEST_FAILED;
}

return;
}

////////////////////////////////////////////////
// SDK_API_TXN_HTTP_INFO_INFO_GET
//
Expand Down
144 changes: 144 additions & 0 deletions src/api/unit_tests/test_HttpOverridableConfig.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/** @file

Catch based unit tests for HTTP overridable configuration APIs.

@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 "iocore/eventsystem/ConfigProcessor.h"
#include "iocore/net/ConnectionTracker.h"
#include "proxy/http/HttpConfig.h"
#include "proxy/http/HttpSM.h"
#include "proxy/http/OverridableConfigDefs.h"
#include "ts/ts.h"

#include <catch2/catch_test_macros.hpp>

#include <array>
#include <string_view>

using namespace std::literals;

namespace
{
struct ConfigDescriptor {
std::string_view name;
TSOverridableConfigKey key;
TSRecordDataType type;
};

// clang-format off
static constexpr std::array<ConfigDescriptor, TS_CONFIG_LAST_ENTRY> CONFIG_DESCRIPTORS{{
#define X_CONFIG_DESCRIPTOR(CONFIG_KEY, MEMBER, RECORD_NAME, DATA_TYPE, CONV) \
{RECORD_NAME, TS_CONFIG_##CONFIG_KEY, TS_RECORDDATATYPE_##DATA_TYPE},
OVERRIDABLE_CONFIGS(X_CONFIG_DESCRIPTOR)
#undef X_CONFIG_DESCRIPTOR
}};
// clang-format on

class TestHttpTxn
{
public:
TestHttpTxn()
{
if (HttpConfig::m_id == 0) {
HttpConfig::m_id = configProcessor.set(HttpConfig::m_id, new HttpConfigParams);
}

_sm.magic = HttpSmMagic_t::ALIVE;
_sm.t_state.http_config_param = HttpConfig::acquire();
REQUIRE(_sm.t_state.http_config_param != nullptr);
_sm.t_state.txn_conf = &_sm.t_state.http_config_param->oride;
}

operator TSHttpTxn() { return reinterpret_cast<TSHttpTxn>(&_sm); }

private:
HttpSM _sm;
};
} // namespace

TEST_CASE("Find HTTP overridable configurations", "[api][overridable-config]")
{
for (auto const &descriptor : CONFIG_DESCRIPTORS) {
TSOverridableConfigKey key;
TSRecordDataType type;

INFO(descriptor.name);

REQUIRE(TSHttpTxnConfigFind(descriptor.name.data(), -1, &key, &type) == TS_SUCCESS);
CHECK(key == descriptor.key);
CHECK(type == descriptor.type);

REQUIRE(TSHttpTxnConfigFind(descriptor.name.data(), descriptor.name.size(), &key, &type) == TS_SUCCESS);
CHECK(key == descriptor.key);
CHECK(type == descriptor.type);
}

TSOverridableConfigKey key;
TSRecordDataType type;

CHECK(TSHttpTxnConfigFind("proxy.config.invalid", -1, &key, &type) == TS_ERROR);
}

TEST_CASE("Set and get HTTP overridable configurations", "[api][overridable-config]")
{
TestHttpTxn txn;

SECTION("integer")
{
static constexpr TSMgmtInt expected = 0;
TSMgmtInt actual;

REQUIRE(TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_CACHE_HTTP, expected) == TS_SUCCESS);
REQUIRE(TSHttpTxnConfigIntGet(txn, TS_CONFIG_HTTP_CACHE_HTTP, &actual) == TS_SUCCESS);
CHECK(actual == expected);
}

SECTION("float")
{
static constexpr TSMgmtFloat expected = 0.25;
TSMgmtFloat actual;

REQUIRE(TSHttpTxnConfigFloatSet(txn, TS_CONFIG_HTTP_CACHE_HEURISTIC_LM_FACTOR, expected) == TS_SUCCESS);
REQUIRE(TSHttpTxnConfigFloatGet(txn, TS_CONFIG_HTTP_CACHE_HEURISTIC_LM_FACTOR, &actual) == TS_SUCCESS);
CHECK(actual == expected);
}

SECTION("constrained integer")
{
TSMgmtInt actual;

REQUIRE(TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_PER_SERVER_CONNECTION_MATCH, 95) == TS_SUCCESS);
REQUIRE(TSHttpTxnConfigIntGet(txn, TS_CONFIG_HTTP_PER_SERVER_CONNECTION_MATCH, &actual) == TS_SUCCESS);
CHECK(actual == TS_SERVER_OUTBOUND_MATCH_BOTH);
}

SECTION("string")
{
static constexpr auto expected = "Catch test"sv;
const char *actual;
int length;

REQUIRE(TSHttpTxnConfigStringSet(txn, TS_CONFIG_HTTP_RESPONSE_SERVER_STR, expected.data(), expected.size()) == TS_SUCCESS);
REQUIRE(TSHttpTxnConfigStringGet(txn, TS_CONFIG_HTTP_RESPONSE_SERVER_STR, &actual, &length) == TS_SUCCESS);
REQUIRE(actual != nullptr);
CHECK(std::string_view(actual, length) == expected);
}
}
1 change: 1 addition & 0 deletions src/iocore/net/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ if(BUILD_TESTING)
test_net
libinknet_stub.cc
NetVCTest.cc
unit_tests/test_ConnectionTracker.cc
unit_tests/test_ProxyProtocol.cc
unit_tests/test_SSLCertLookup.cc
unit_tests/test_SSLNetVConnectionAsyncEp.cc
Expand Down
10 changes: 5 additions & 5 deletions src/iocore/net/ConnectionTracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "records/RecCore.h"
#include "swoc/IPAddr.h"

#include <algorithm>

using namespace std::literals;

ConnectionTracker::TableSingleton ConnectionTracker::_inbound_table;
Expand All @@ -49,11 +51,9 @@ const MgmtConverter ConnectionTracker::MIN_SERVER_CONV(
const MgmtConverter ConnectionTracker::SERVER_MATCH_CONV{
[](const void *data) -> MgmtInt { return static_cast<MgmtInt>(*static_cast<const decltype(TxnConfig::server_match) *>(data)); },
[](void *data, MgmtInt i) -> void {
// Problem - the InkAPITest requires being able to set an arbitrary value, so this can either
// correctly clamp or pass the regression tests. Currently it passes the tests.
// *static_cast<decltype(TxnConfig::match) *>(data) = std::clamp(static_cast<decltype(TxnConfig::match)>(i), MATCH_IP,
// MATCH_BOTH);
*static_cast<decltype(TxnConfig::server_match) *>(data) = static_cast<decltype(TxnConfig::server_match)>(i);
auto const value = std::clamp(i, static_cast<MgmtInt>(MATCH_IP), static_cast<MgmtInt>(MATCH_BOTH));

*static_cast<decltype(TxnConfig::server_match) *>(data) = static_cast<decltype(TxnConfig::server_match)>(value);
},
nullptr,
nullptr,
Expand Down
Loading