From e33c13fa777c1ab7c88f32412f75f82f9a568056 Mon Sep 17 00:00:00 2001 From: Antony Peacock Date: Mon, 18 Dec 2023 20:32:55 +0000 Subject: [PATCH] Networking primatives (#187) * Asio IP address program options test * Ip6 test cases * Pre-commit formatting * Formatting * Todo extend namedendpoint to convert to address * Work in progress URL support * Clean up * Remove header --- .../application/po/adapters/boost/asio.hpp | 34 ++++++++++ .../tests/po/adapters/boost/CMakeLists.txt | 1 + .../tests/po/adapters/boost/asio.tests.cpp | 63 +++++++++++++++++++ .../application/tests/po/config.tests.cpp | 14 +++-- libraries/core/src/CMakeLists.txt | 7 ++- .../core/src/morpheus/core/CMakeLists.txt | 1 + .../src/morpheus/core/network/CMakeLists.txt | 4 ++ .../morpheus/core/network/named_endpoint.hpp | 48 ++++++++++++++ libraries/core/tests/CMakeLists.txt | 1 + libraries/core/tests/network/CMakeLists.txt | 4 ++ .../tests/network/named_endpoint.tests.cpp | 13 ++++ 11 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 libraries/application/tests/po/adapters/boost/asio.tests.cpp create mode 100644 libraries/core/src/morpheus/core/network/CMakeLists.txt create mode 100644 libraries/core/src/morpheus/core/network/named_endpoint.hpp create mode 100644 libraries/core/tests/network/CMakeLists.txt create mode 100644 libraries/core/tests/network/named_endpoint.tests.cpp diff --git a/libraries/application/src/morpheus/application/po/adapters/boost/asio.hpp b/libraries/application/src/morpheus/application/po/adapters/boost/asio.hpp index e69de29b..f00e661e 100644 --- a/libraries/application/src/morpheus/application/po/adapters/boost/asio.hpp +++ b/libraries/application/src/morpheus/application/po/adapters/boost/asio.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace boost::asio::ip +{ + +template +void validate(boost::any& v, std::vector> const& values, address* p, int) +{ + namespace po = boost::program_options; + po::validators::check_first_occurrence(v); + auto const& s = po::validators::get_single_string(values); + + boost::system::error_code ec = make_error_code(boost::system::errc::success); + auto const address = boost::asio::ip::make_address(s, ec); + if (ec == boost::asio::error::invalid_argument) + { + throw po::validation_error(po::validation_error::invalid_option_value); + } + + v = address; +} + +} // namespace boost::asio::ip diff --git a/libraries/application/tests/po/adapters/boost/CMakeLists.txt b/libraries/application/tests/po/adapters/boost/CMakeLists.txt index fe223bdc..4599fd85 100644 --- a/libraries/application/tests/po/adapters/boost/CMakeLists.txt +++ b/libraries/application/tests/po/adapters/boost/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(MorpheusApplicationTests PRIVATE + asio.tests.cpp log.tests.cpp ) diff --git a/libraries/application/tests/po/adapters/boost/asio.tests.cpp b/libraries/application/tests/po/adapters/boost/asio.tests.cpp new file mode 100644 index 00000000..3d8b493f --- /dev/null +++ b/libraries/application/tests/po/adapters/boost/asio.tests.cpp @@ -0,0 +1,63 @@ +#include "morpheus/application/po/adapters/boost/asio.hpp" +#include "morpheus/application/po/options.hpp" +#include "morpheus/core/conformance/ranges.hpp" +#include "morpheus/logging.hpp" +#include "morpheus/redirect_stream.hpp" + +#include +#include + +namespace morpheus::application::po +{ + +struct BoostLogFixture +{ + LoggingFixture disableLogging; + RedirectStream captureCout = RedirectStream(std::cout); + RedirectStream captureCerr = RedirectStream(std::cerr); +}; + +struct Address +{ + boost::asio::ip::address ipAddress; + + void addOptions(boost::program_options::options_description& options) + { + namespace po = boost::program_options; + // clang-format off + options.add_options() + ("address", po::value(&ipAddress)->required(), "The ip address."); + // clang-format on + } +}; + +TEST_CASE_METHOD(BoostLogFixture, "Test parsing of boost asio address as program options", "[morpheus.application.po.adapters.boost.asio.address]") +{ + + SECTION("Ensure valid value parse correctly") + { + auto getAddress = [](std::string_view param) + { + Address address{}; + std::array cliOptions = {"dummyProgram.exe", "--address", param.data()}; + auto const result = parseProgramOptions(cliOptions.size(), cliOptions.data(), HelpDocumentation{}, address); + REQUIRE(!result); + return address.ipAddress; + }; + + REQUIRE(getAddress("127.0.0.1") == boost::asio::ip::address_v4::loopback()); + REQUIRE(getAddress("192.168.1.218") == boost::asio::ip::address_v4({192, 168, 1, 218})); + REQUIRE(getAddress("0:0:0:0:0:0:0:1") == boost::asio::ip::address_v6::loopback()); + REQUIRE(getAddress("fd00::f06b:9ee5:8bfa:666c") == + boost::asio::ip::address_v6({0xfd, 0, 0, 0, 0, 0, 0, 0, 0xf0, 0x6b, 0x9e, 0xe5, 0x8b, 0xfa, 0x66, 0x6c})); + } + SECTION("Ensure invalid value parse correctly") + { + std::array cliOptions = {"dummyProgram.exe", "--address", "invalid"}; + Address address; + auto const result = parseProgramOptions(cliOptions.size(), cliOptions.data(), HelpDocumentation{}, address); + REQUIRE(result); + } +} + +} // namespace morpheus::application::po diff --git a/libraries/application/tests/po/config.tests.cpp b/libraries/application/tests/po/config.tests.cpp index 2a76b664..98f592be 100644 --- a/libraries/application/tests/po/config.tests.cpp +++ b/libraries/application/tests/po/config.tests.cpp @@ -1,6 +1,8 @@ #include "morpheus/application/po/config.hpp" #include "morpheus/application/po/options.hpp" +#include + #include #include @@ -12,20 +14,20 @@ TEST_CASE("Ensure options parsing of standard application config", "[morpheus.ap { GIVEN("A type supporting addOptions") { - Config confg; + Config config; WHEN("Parsing valid parameters") { std::array const cliOptions = { "dummyProgram.exe", "--logging-enabled", "true", "--log-append", "false", "--log-level", "debug", "--logfile-path", "." }; - auto const result = parseProgramOptions(static_cast(cliOptions.size()), cliOptions.data(), HelpDocumentation{}, confg); + auto const result = parseProgramOptions(static_cast(cliOptions.size()), cliOptions.data(), HelpDocumentation{}, config); THEN("Expect no error results and valid values extracted") { REQUIRE(!result); - REQUIRE(confg.loggingEnabled == true); - REQUIRE(confg.logAppend == false); - REQUIRE(confg.logLevel == boost::log::trivial::debug); - REQUIRE(confg.logFilePath == "."); + REQUIRE(config.loggingEnabled == true); + REQUIRE(config.logAppend == false); + REQUIRE(config.logLevel == boost::log::trivial::debug); + REQUIRE(config.logFilePath == "."); } } } diff --git a/libraries/core/src/CMakeLists.txt b/libraries/core/src/CMakeLists.txt index 608f8189..da3d2074 100644 --- a/libraries/core/src/CMakeLists.txt +++ b/libraries/core/src/CMakeLists.txt @@ -22,11 +22,12 @@ target_link_libraries(MorpheusCore add_subdirectory(morpheus/core) -#target_compile_definitions(MorpheusCore -# PUBLIC +target_compile_definitions(MorpheusCore + PUBLIC # MORPHEUS_SHARED_BUILD=1 # MORPHEUS_DLL_EXPORTS=1 -#) + BOOST_USE_WINAPI_VERSION=0x600 +) target_compile_options(MorpheusCore PRIVATE diff --git a/libraries/core/src/morpheus/core/CMakeLists.txt b/libraries/core/src/morpheus/core/CMakeLists.txt index 12f4a7ce..36befdfc 100644 --- a/libraries/core/src/morpheus/core/CMakeLists.txt +++ b/libraries/core/src/morpheus/core/CMakeLists.txt @@ -17,4 +17,5 @@ add_subdirectory(concurrency) add_subdirectory(functional) add_subdirectory(memory) add_subdirectory(meta) +add_subdirectory(network) add_subdirectory(serialisation) diff --git a/libraries/core/src/morpheus/core/network/CMakeLists.txt b/libraries/core/src/morpheus/core/network/CMakeLists.txt new file mode 100644 index 00000000..f40e9f98 --- /dev/null +++ b/libraries/core/src/morpheus/core/network/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(MorpheusCore + PUBLIC + named_endpoint.hpp +) diff --git a/libraries/core/src/morpheus/core/network/named_endpoint.hpp b/libraries/core/src/morpheus/core/network/named_endpoint.hpp new file mode 100644 index 00000000..ac699d0f --- /dev/null +++ b/libraries/core/src/morpheus/core/network/named_endpoint.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include +#include +#include + +namespace morpheus::network +{ + +/// \class NamedEndpoint +/// Allows for hostname to endpont resolution via DNS lookup.a +/// \todo +/// https://stackoverflow.com/questions/31314433/how-do-i-convert-a-host-name-in-to-a-boost-address-or-endpoint +class NamedEndpoint +{ +public: + NamedEndpoint(std::string_view const name, std::uint16_t const port) + : mName(name) + , mPort(port) + {} + + [[nodisard]] auto name() const noexcept { return mName; } + + [[nodisard]] auto port() const noexcept { return mPort; } + + [[nodisard]] auto operator<=>(NamedEndpoint const& rhs) const noexcept = default; + +private: + std::string mName; + std::uint16_t mPort; +}; + +} // namespace morpheus::network + +/// std::hash specialisation for named enpoint +template <> +struct std::hash +{ + std::size_t operator()(morpheus::network::NamedEndpoint const& n) const + { + std::size_t seed = 0; + boost::hash_combine(seed, n.name()); + boost::hash_combine(seed, n.port()); + return seed; + } +}; diff --git a/libraries/core/tests/CMakeLists.txt b/libraries/core/tests/CMakeLists.txt index c39df4d5..863f870d 100644 --- a/libraries/core/tests/CMakeLists.txt +++ b/libraries/core/tests/CMakeLists.txt @@ -27,4 +27,5 @@ add_subdirectory(conversion) add_subdirectory(functional) add_subdirectory(memory) add_subdirectory(meta) +add_subdirectory(network) add_subdirectory(serialisation) diff --git a/libraries/core/tests/network/CMakeLists.txt b/libraries/core/tests/network/CMakeLists.txt new file mode 100644 index 00000000..99332494 --- /dev/null +++ b/libraries/core/tests/network/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(MorpheusCoreTests + PRIVATE + named_endpoint.tests.cpp +) diff --git a/libraries/core/tests/network/named_endpoint.tests.cpp b/libraries/core/tests/network/named_endpoint.tests.cpp new file mode 100644 index 00000000..22ca7f98 --- /dev/null +++ b/libraries/core/tests/network/named_endpoint.tests.cpp @@ -0,0 +1,13 @@ +#include "morpheus/core/network/named_endpoint.hpp" + +#include +#include + +namespace morpheus::network +{ + +using namespace ::testing; + +TEST_CASE("Verify construction of named endpoints", "[morpheus.network.named_endpoint.construction]") {} + +} // namespace morpheus::network