| /** @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> |
| #include <string_view> |
| |
| 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); } |
| |
| const HttpForwarded::OptionBitSet & |
| forwarded_options() const |
| { |
| return _sm.t_state.txn_conf->insert_forwarded; |
| } |
| |
| 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(), static_cast<int>(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("Round trip HTTP overridable configurations", "[api][overridable-config]") |
| { |
| TestHttpTxn txn; |
| |
| for (auto const &descriptor : CONFIG_DESCRIPTORS) { |
| INFO(descriptor.name); |
| |
| switch (descriptor.type) { |
| case TS_RECORDDATATYPE_INT: { |
| TSMgmtInt expected; |
| TSMgmtInt actual; |
| |
| REQUIRE(TSHttpTxnConfigIntGet(txn, descriptor.key, &expected) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigIntSet(txn, descriptor.key, expected) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigIntGet(txn, descriptor.key, &actual) == TS_SUCCESS); |
| CHECK(actual == expected); |
| break; |
| } |
| case TS_RECORDDATATYPE_FLOAT: { |
| TSMgmtFloat expected; |
| TSMgmtFloat actual; |
| |
| REQUIRE(TSHttpTxnConfigFloatGet(txn, descriptor.key, &expected) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigFloatSet(txn, descriptor.key, expected) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigFloatGet(txn, descriptor.key, &actual) == TS_SUCCESS); |
| CHECK(actual == expected); |
| break; |
| } |
| case TS_RECORDDATATYPE_STRING: { |
| if (descriptor.key == TS_CONFIG_SSL_CERT_FILEPATH) { |
| REQUIRE(TSHttpTxnConfigStringSet(txn, descriptor.key, "", 0) == TS_SUCCESS); |
| break; |
| } |
| |
| if (descriptor.key == TS_CONFIG_HTTP_INSERT_FORWARDED) { |
| static constexpr std::string_view expected{"none"}; |
| |
| REQUIRE(TSHttpTxnConfigStringSet(txn, descriptor.key, expected.data(), static_cast<int>(expected.size())) == TS_SUCCESS); |
| CHECK(txn.forwarded_options().none()); |
| break; |
| } |
| |
| const char *value; |
| int length; |
| |
| REQUIRE(TSHttpTxnConfigStringGet(txn, descriptor.key, &value, &length) == TS_SUCCESS); |
| REQUIRE(length >= 0); |
| REQUIRE((value != nullptr || length == 0)); |
| |
| std::string expected; |
| |
| if (value != nullptr) { |
| expected.assign(value, length); |
| } |
| |
| REQUIRE(TSHttpTxnConfigStringSet(txn, descriptor.key, expected.data(), static_cast<int>(expected.size())) == TS_SUCCESS); |
| |
| const char *actual; |
| int actual_length; |
| |
| REQUIRE(TSHttpTxnConfigStringGet(txn, descriptor.key, &actual, &actual_length) == TS_SUCCESS); |
| REQUIRE(actual_length >= 0); |
| REQUIRE((actual != nullptr || actual_length == 0)); |
| CHECK(std::string_view(actual != nullptr ? actual : "", actual_length) == expected); |
| break; |
| } |
| default: |
| FAIL("Unexpected overridable configuration data type"); |
| break; |
| } |
| } |
| } |
| |
| TEST_CASE("Clamp constrained HTTP overridable configuration", "[api][overridable-config]") |
| { |
| TestHttpTxn txn; |
| 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); |
| } |
| |
| TEST_CASE("Round trip bounded SSL string overrides", "[api][overridable-config]") |
| { |
| static constexpr std::array SSL_STRING_CONFIGS{ |
| TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY, TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES, TS_CONFIG_SSL_CLIENT_SNI_POLICY, |
| TS_CONFIG_SSL_CLIENT_CERT_FILENAME, TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME, TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME, |
| TS_CONFIG_SSL_CLIENT_CA_CERT_PATH, TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS, |
| }; |
| static constexpr std::array<char, 3> EXPECTED{'a', '\0', 'b'}; |
| |
| TestHttpTxn txn; |
| |
| for (auto const key : SSL_STRING_CONFIGS) { |
| const char *actual; |
| int actual_length; |
| |
| REQUIRE(TSHttpTxnConfigStringSet(txn, key, EXPECTED.data(), EXPECTED.size()) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigStringGet(txn, key, &actual, &actual_length) == TS_SUCCESS); |
| REQUIRE(actual != nullptr); |
| CHECK(actual_length == EXPECTED.size()); |
| CHECK(std::string_view(actual, actual_length) == std::string_view(EXPECTED.data(), EXPECTED.size())); |
| |
| REQUIRE(TSHttpTxnConfigStringSet(txn, key, nullptr, 0) == TS_SUCCESS); |
| REQUIRE(TSHttpTxnConfigStringGet(txn, key, &actual, &actual_length) == TS_SUCCESS); |
| CHECK(actual == nullptr); |
| CHECK(actual_length == 0); |
| } |
| } |