diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bcaae77..cfc5bbf8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,71 +1,108 @@ name: CI -on: +on: push: - pull_request: + pull_request: branches: [main] -env: - BUILD_TYPE: Release - INSTALL_LOCATION: .local +env: + BUILD_TYPE: Release + INSTALL_LOCATION: .local jobs: build: - strategy: + strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - experimental: [false] boost_version: [1.74.0, 1.75.0, 1.76.0] - malloy_tls: [ON, OFF] - msvc: [false, true] - exclude: - - os: ubuntu-latest - msvc: false - - os: windows-latest - msvc: false # choco package for mingw 10.2.0 does not support threads, see: #21 + toolchain: + [ + { + name: "Ubuntu GCC TLS", + cxx: "g++-11", + cc: "gcc-11", + packages: "libssl-dev gcc-11 g++-11", + tls: ON, + os: ubuntu-latest, + }, + { + name: "Ubuntu GCC", + cxx: "g++-11", + cc: "gcc-11", + packages: "gcc-11 g++-11", + tls: OFF, + os: ubuntu-latest, + }, + { + name: "Ubuntu Clang TLS", + cxx: "clang++", + cc: "clang", + packages: "libssl-dev", + tls: ON, + os: ubuntu-latest, + }, + { + name: "Ubuntu Clang", + cxx: "clang++", + cc: "clang", + packages: "", + tls: OFF, + os: ubuntu-latest, + }, + { + name: "VS2019", + cxx: "cl.exe", + cc: "cl.exe", + tls: OFF, + packages: "openssl", # We have to install something + os: windows-latest, + }, + { + name: "VS2019 TLS", + cxx: "cl.exe", + cc: "cl.exe", + tls: ON, + packages: "openssl", + os: windows-latest, + }, + ] - continue-on-error: ${{ matrix.experimental }} - runs-on: ${{ matrix.os }} + continue-on-error: false + runs-on: ${{ matrix.toolchain.os }} env: BOOST_ROOT: ${{github.workspace}}/3rdparty/boost-${{ matrix.boost_version }} + CC: ${{ matrix.toolchain.cc }} + CXX: ${{ matrix.toolchain.cxx }} - name: '${{ matrix.os }}: boost ${{ matrix.boost_version }} tls: ${{ matrix.malloy_tls }}' + name: "${{ matrix.toolchain.name }} (boost v${{ matrix.boost_version }})" if: "!contains(github.event.head_commit.message, '[ci skip]')" steps: - uses: actions/checkout@v2 - + - name: cache boost - uses: actions/cache@v2 + uses: actions/cache@v2 id: cache-boost - with: + with: path: ${{ env.BOOST_ROOT }} key: boost-${{ matrix.boost_version }} - - name: Install Ninja + - name: Install Ninja uses: seanmiddleditch/gha-setup-ninja@master - name: Setup boost env run: | BOOST_URL="https://boostorg.jfrog.io/artifactory/main/release/${{ matrix.boost_version }}/source/boost_$(echo ${{ matrix.boost_version }} | sed 's/\./_/g').tar.bz2" echo "BOOST_URL=$BOOST_URL" >> $GITHUB_ENV shell: bash - - name: install gcc11 - if: runner.os == 'Linux' - run: | - sudo apt install gcc-11 g++-11 -y - echo "CC=gcc-11" >> $GITHUB_ENV - echo "CXX=g++-11" >> $GITHUB_ENV - - - name: Install openssl (via choco) + - name: Install packages (via choco) if: runner.os == 'Windows' - run: choco install openssl + run: choco upgrade ${{ matrix.toolchain.packages }} - - name: Install openssl (via apt) + - name: Install packages (via apt) if: runner.os == 'Linux' - run: sudo apt install libssl-dev -y + run: sudo apt install ${{ matrix.toolchain.packages }} -y - - name: Install openssl (via homebrew) + - name: Install packages (via homebrew) if: runner.os == 'macOS' - run: brew install openssl + run: brew install ${{ matrix.toolchain.packages }} - name: Install Boost if: steps.cache-boost.outputs.cache-hit != 'true' @@ -81,22 +118,16 @@ jobs: cd $BOOST_ROOT && cp -r boost_*/* . rm -rf boost_*/* download.tar.bz2 download.tar shell: bash - - name: Install latest mingw - if: runner.os == 'Windows' && !matrix.msvc - run: choco upgrade mingw - - name: Install latest MSVC - if: runner.os == 'Windows' && matrix.msvc - uses: ilammy/msvc-dev-cmd@v1 - - - name: Configure - run: cmake -Bbuild -GNinja -DMALLOY_BUILD_EXAMPLES=ON -DMALLOY_BUILD_TESTS=ON -DMALLOY_FEATURE_TLS=${{ matrix.malloy_tls }} - - name: Build - run: cmake --build build - - - name: Run tests - run: ./build/test/malloy-tests + - name: Install msvc + if: ${{ matrix.toolchain.cxx == 'cl.exe' }} # This is a bit of a hack + uses: ilammy/msvc-dev-cmd@v1 + - name: Configure + run: cmake -Bbuild -GNinja -DMALLOY_BUILD_EXAMPLES=ON -DMALLOY_BUILD_TESTS=ON -DMALLOY_FEATURE_TLS=${{ matrix.toolchain.tls }} - + - name: Build + run: cmake --build build + - name: Run tests + run: ./build/test/malloy-tests diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index e38bd5a7..685097e3 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -1,48 +1,47 @@ name: MSYS2 CI -on: +on: push: - pull_request: + pull_request: branches: [main] -env: - BUILD_TYPE: Release - INSTALL_LOCATION: .local +env: + BUILD_TYPE: Release + INSTALL_LOCATION: .local jobs: build: - strategy: + strategy: fail-fast: false matrix: - boost_version: [1.74.0, 1.76.0] + boost_version: [1.76.0] malloy_tls: [ON, OFF] - + runs-on: windows-latest env: BOOST_ROOT: ${{github.workspace}}/3rdparty/boost-${{ matrix.boost_version }} - name: 'mingw: boost ${{ matrix.boost_version }} tls: ${{ matrix.malloy_tls }}' + name: "msys2/clang (boost v${{ matrix.boost_version }}) (tls: ${{ matrix.malloy_tls }})" if: "!contains(github.event.head_commit.message, '[ci skip]')" defaults: - run: - shell: msys2 {0} + run: + shell: msys2 {0} steps: - - uses: msys2/setup-msys2@v2 with: - msystem: MINGW64 - update: false - install: >- - base-devel - mingw-w64-x86_64-toolchain - mingw-w64-x86_64-cmake - p7zip + msystem: MINGW64 + update: false + install: >- + base-devel + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + p7zip - uses: actions/checkout@v2 - name: cache boost - uses: actions/cache@v2 + uses: actions/cache@v2 id: cache-boost - with: + with: path: ${{ env.BOOST_ROOT }} key: boost-${{ matrix.boost_version }} - name: Setup boost env @@ -65,18 +64,12 @@ jobs: 7z -o$BOOST_ROOT x $BOOST_ROOT/download.tar -y -bd cd $BOOST_ROOT && cp -r boost_*/* . rm -rf boost_*/* download.tar.bz2 download.tar - - - name: Configure + + - name: Configure run: cmake -Bbuild -G"MSYS Makefiles" -DMALLOY_BUILD_EXAMPLES=ON -DMALLOY_BUILD_TESTS=ON -DMALLOY_FEATURE_TLS=${{ matrix.malloy_tls }} -DMALLOY_DEPENDENCY_SPDLOG_DOWNLOAD=ON - - name: Build - run: cmake --build build + - name: Build + run: cmake --build build - - name: Run tests + - name: Run tests run: ./build/test/malloy-tests - - - - - - diff --git a/lib/malloy/client/type_traits.hpp b/lib/malloy/client/type_traits.hpp index f80990df..a3dc5146 100644 --- a/lib/malloy/client/type_traits.hpp +++ b/lib/malloy/client/type_traits.hpp @@ -4,11 +4,25 @@ #include #include +#include "malloy/http/type_traits.hpp" #include "malloy/type_traits.hpp" namespace malloy::client::concepts { namespace detail { + template + struct response_filter_body_helper { + template + void operator()(T&& v) + { +F f2; + typename F::header_type h2; + typename std::decay_t::value_type r; + f2.setup_body(h2, r); + } + }; + + /** * @class http_cb_helper * @brief Helper for http_callback concept @@ -35,17 +49,14 @@ namespace malloy::client::concepts { } template - concept response_filter = std::move_constructible && requires(const F& f, const typename F::header_type& h) { - { f.body_for(h) } -> malloy::concepts::is_variant; + concept response_filter = std::move_constructible && requires(const F& f, const typename F::header_type& h) + { { - std::visit([](auto&& v) { - F f2; - typename F::header_type h2; - typename std::decay_t::value_type r; - f2.setup_body(h2, r); - }, f.body_for(h)) - }; - }; + f.body_for(h) + } -> malloy::concepts::is_variant; + { + std::visit(detail::response_filter_body_helper{}, f.body_for(h))}; + }; template concept http_callback = response_filter&& std::move_constructible&& requires(F cb, const Filter& f, const typename Filter::header_type& h){ diff --git a/lib/malloy/server/routing/router.cpp b/lib/malloy/server/routing/router.cpp index 56b520e1..2065e6c9 100644 --- a/lib/malloy/server/routing/router.cpp +++ b/lib/malloy/server/routing/router.cpp @@ -20,11 +20,9 @@ bool router::add_http_endpoint(std::shared_ptr&& ep) { try { m_endpoints_http.emplace_back(std::move(ep)); - } - catch (const std::exception& e) { + } catch (const std::exception& e) { return log_or_throw(e, spdlog::level::critical, "could not add HTTP endpoint: {}", e.what()); - } - catch (...) { + } catch (...) { return log_or_throw(std::runtime_error("unknown exception"), spdlog::level::critical, "could not add HTTP endpoint. unknown exception."); } @@ -35,11 +33,9 @@ bool router::add_websocket_endpoint(std::shared_ptr&& ep) { try { m_endpoints_websocket.emplace_back(std::move(ep)); - } - catch (const std::exception& e) { + } catch (const std::exception& e) { return log_or_throw(e, spdlog::level::critical, "could not add WebSocket endpoint: {}", e.what()); - } - catch (...) { + } catch (...) { return log_or_throw(std::runtime_error("unknown exception"), spdlog::level::critical, "could not add WebSocket endpoint. unknown exception."); } @@ -73,8 +69,7 @@ bool router::add_subrouter(std::string resource, std::shared_ptr sub_rou // Add router try { m_sub_routers.try_emplace(std::move(resource), std::move(sub_router)); - } - catch (const std::exception& e) { + } catch (const std::exception& e) { return log_or_throw(e, spdlog::level::critical, "could not add router: {}", e.what()); } @@ -82,7 +77,6 @@ bool router::add_subrouter(std::string resource, std::shared_ptr sub_rou } - bool router::add_file_serving(std::string resource, std::filesystem::path storage_base_path) { // Log @@ -94,8 +88,8 @@ bool router::add_file_serving(std::string resource, std::filesystem::path storag ep->resource_base = resource; ep->base_path = std::move(storage_base_path); - ep->writer = [](const auto& req, auto&& resp, const auto& conn) { - std::visit([&](auto&& resp) { detail::send_response(req, std::move(resp), conn); }, std::move(resp)); + ep->writer = [](const auto& req, auto&& resp, const auto& conn) { + std::visit([&](auto&& resp) { detail::send_response(req, std::move(resp), conn); }, std::move(resp)); }; // Add @@ -154,14 +148,14 @@ router::response_type router::generate_preflight_response(const request_header& // Create a string representing all supported methods std::string methods_string; - for (const auto &str : method_strings) { + for (const auto& str : method_strings) { methods_string += str; if (&str not_eq &method_strings.back()) methods_string += ", "; } - malloy::http::response<> resp{ malloy::http::status::ok }; + malloy::http::response<> resp{malloy::http::status::ok}; resp.set(boost::beast::http::field::content_type, "text/html"); resp.base().set("Access-Control-Allow-Origin", "http://127.0.0.1:8080"); resp.base().set("Access-Control-Allow-Methods", methods_string); @@ -170,4 +164,3 @@ router::response_type router::generate_preflight_response(const request_header& return resp; } - diff --git a/lib/malloy/server/routing/router.hpp b/lib/malloy/server/routing/router.hpp index 9140b9d6..61cbaef0 100644 --- a/lib/malloy/server/routing/router.hpp +++ b/lib/malloy/server/routing/router.hpp @@ -3,17 +3,17 @@ #include "endpoint_http.hpp" #include "endpoint_http_regex.hpp" #include "endpoint_websocket.hpp" +#include "malloy/http/generator.hpp" +#include "malloy/http/http.hpp" #include "malloy/http/request.hpp" #include "malloy/http/response.hpp" -#include "malloy/http/http.hpp" -#include "malloy/http/generator.hpp" -#include "malloy/server/http/connection/connection_t.hpp" -#include "malloy/server/http/connection/connection_plain.hpp" #include "malloy/server/http/connection/connection.hpp" +#include "malloy/server/http/connection/connection_plain.hpp" +#include "malloy/server/http/connection/connection_t.hpp" #include "malloy/server/routing/type_traits.hpp" #include "malloy/type_traits.hpp" #if MALLOY_FEATURE_TLS - #include "malloy/server/http/connection/connection_tls.hpp" +#include "malloy/server/http/connection/connection_tls.hpp" #endif #include @@ -25,11 +25,10 @@ #include #include #include -#include -#include #include #include #include +#include namespace spdlog { @@ -43,7 +42,10 @@ namespace malloy::server { template - concept has_write = requires(T t, Args... args) { t.do_write(std::forward(args)...); }; + concept has_write = requires(T t, Args... args) + { + t.do_write(std::forward(args)...); + }; /** * @brief Provides a default Filter to ease use of interface @@ -57,7 +59,6 @@ namespace malloy::server void setup_body(const header_type&, typename request_type::body_type::value_type&) const {} - }; static_assert(concepts::request_filter, "Default handler must satisfy route filter"); @@ -73,16 +74,17 @@ namespace malloy::server void send_response(const boost::beast::http::request_header<>& req, malloy::http::response&& resp, http::connection_t connection) { // Add more information to the response - //resp.keep_alive(req.keep_alive); // TODO: Is this needed?, if so its a spanner in the works + //resp.keep_alive(req.keep_alive); // TODO: Is this needed?, if so its a spanner in the works resp.version(req.version()); resp.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); resp.prepare_payload(); std::visit([resp = std::move(resp)](auto& c) mutable { c->do_write(std::move(resp)); - }, connection); + }, + connection); } - } + } // namespace detail // TODO: This might not be thread-safe the way we pass an instance to the listener and then from // there to each session. Investigate and fix this! @@ -104,12 +106,12 @@ namespace malloy::server /** * The method type to use. */ - using method_type = malloy::http::method; + using method_type = malloy::http::method; /** * The request type to use. */ - using request_type = malloy::http::request<>; + using request_type = malloy::http::request<>; /** * The response type to use. @@ -201,8 +203,7 @@ namespace malloy::server */ template< concepts::request_filter ExtraInfo, - concepts::route_handler Func - > + concepts::route_handler Func> bool add(const method_type method, const std::string_view target, Func&& handler, ExtraInfo&& extra) { using func_t = std::decay_t; @@ -217,18 +218,17 @@ namespace malloy::server std::invoke_result_t&>>( method, target, std::forward(handler), std::forward(extra)); - } - else { + } else { return add_regex_endpoint< uses_captures, - std::invoke_result_t - >( + std::invoke_result_t>( method, target, std::forward(handler), std::forward(extra)); } } template Func> - auto add(const method_type method, const std::string_view target, Func&& handler) { + auto add(const method_type method, const std::string_view target, Func&& handler) + { return add(method, target, std::forward(handler), detail::default_route_filter{}); } @@ -258,7 +258,8 @@ namespace malloy::server * @param handler The handler for incoming websocket requests. * @return Whether adding the endpoint was successful. */ - auto add_websocket(const std::string& resource, typename websocket::connection::handler_t&& handler) { + auto add_websocket(const std::string& resource, typename websocket::connection::handler_t&& handler) + { // Log if (m_logger) m_logger->debug("adding websocket endpoint at {}", resource); @@ -296,14 +297,12 @@ namespace malloy::server template< bool isWebsocket = false, typename Derived, - typename Connection - > + typename Connection> void handle_request( const std::filesystem::path& doc_root, const req_generator& req, Connection&& connection, - malloy::http::uri location - ) + malloy::http::uri location) { // Check sub-routers for (const auto& [resource_base, router] : m_sub_routers) { @@ -345,8 +344,7 @@ namespace malloy::server const std::filesystem::path& doc_root, const req_generator& req, const http::connection_t& connection, - const malloy::http::uri& location - ) + const malloy::http::uri& location) { // Log if (m_logger) { @@ -368,11 +366,11 @@ namespace malloy::server if (m_logger) { m_logger->debug("automatically constructing preflight response."); } - // Generate - auto resp = generate_preflight_response(header); + // Generate + auto resp = generate_preflight_response(header); - // Send the response - detail::send_response(header, std::move(resp), connection); + // Send the response + detail::send_response(header, std::move(resp), connection); // We're done handling this request return; } @@ -404,13 +402,11 @@ namespace malloy::server void handle_ws_request( const req_generator& gen, std::shared_ptr connection, - const malloy::http::uri& location - ) + const malloy::http::uri& location) { m_logger->debug("handling WS request: {} {}", - gen->header().method_string(), - location.resource_string() - ); + gen->header().method_string(), + location.resource_string()); // Check routes for (const auto& ep : m_endpoints_websocket) { @@ -427,7 +423,7 @@ namespace malloy::server malloy::http::request req; req.base() = gen->header(); ep->handler(std::move(req), connection); - + // We're done handling this request. The route handler will handle everything from hereon. return; @@ -445,8 +441,7 @@ namespace malloy::server bool UsesCaptures, typename Body, concepts::request_filter ExtraInfo, - typename Func - > + typename Func> auto add_regex_endpoint(method_type method, std::string_view target, Func&& handler, ExtraInfo&& extra) -> bool { // Log @@ -456,9 +451,8 @@ namespace malloy::server // Build regex std::regex regex; try { - regex = std::move(std::regex{ target.cbegin(), target.cend() }); - } - catch (const std::regex_error& e) { + regex = std::move(std::regex{target.cbegin(), target.cend()}); + } catch (const std::regex_error& e) { if (m_logger) m_logger->error("invalid route target supplied \"{}\": {}", target, e.what()); return false; @@ -476,9 +470,9 @@ namespace malloy::server if constexpr (wrapped) { ep->handler = std::move(handler); } else { - ep->handler = - [w = std::forward(handler)](auto&&... args) { - return std::variant{w(std::forward(args)...)}; + ep->handler = + [w = std::forward(handler)](auto&&... args) { + return std::variant{w(std::forward(args)...)}; }; } @@ -488,9 +482,9 @@ namespace malloy::server m_logger->warn("route has invalid handler. ignoring."); return false; } - - ep->writer = [this](const auto& req, auto&& resp, const auto& conn) { - std::visit([&, this](auto&& resp) { detail::send_response(req, std::move(resp), conn); }, std::move(resp)); + + ep->writer = [this](const auto& req, auto&& resp, const auto& conn) { + std::visit([&, this](auto&& resp) { detail::send_response(req, std::move(resp), conn); }, std::move(resp)); }; // Add route @@ -537,7 +531,7 @@ namespace malloy::server * @return */ template - bool log_or_throw(const std::exception& exception, const spdlog::level::level_enum level, const FormatString& fmt, Args&&...args) + bool log_or_throw(const std::exception& exception, const spdlog::level::level_enum level, const FormatString& fmt, Args&&... args) { if (m_logger) { m_logger->log(level, fmt, std::forward(args)...); @@ -550,4 +544,4 @@ namespace malloy::server } }; -} +} // namespace malloy::server diff --git a/lib/malloy/type_traits.hpp b/lib/malloy/type_traits.hpp index 75410458..9694a76a 100644 --- a/lib/malloy/type_traits.hpp +++ b/lib/malloy/type_traits.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include "malloy/error.hpp" @@ -10,6 +10,13 @@ namespace malloy::concepts { + namespace detail + { + struct is_variant_helper { + template + void operator()(const std::variant&) const {}; + }; + } // namespace detail template concept const_buffer_sequence = boost::asio::is_const_buffer_sequence::value; @@ -26,17 +33,10 @@ namespace malloy::concepts template concept is_variant = requires(V v) { - [](const std::variant& vs){}(v); // https://stackoverflow.com/q/68115853/12448530 + detail::is_variant_helper{}(v); }; - template typename T> - concept is_variant_of = requires(const V& v) - { - [](const std::variant...>&) {}(v); - }; - -} - +} // namespace malloy::concepts /** * @page general_concepts Core Concepts * @section const_buffer_sequence @@ -57,5 +57,3 @@ namespace malloy::concepts * * */ - - diff --git a/lib/malloy/websocket/CMakeLists.txt b/lib/malloy/websocket/CMakeLists.txt index 6dc66ac5..05be22e5 100644 --- a/lib/malloy/websocket/CMakeLists.txt +++ b/lib/malloy/websocket/CMakeLists.txt @@ -2,7 +2,6 @@ target_sources( ${TARGET_OBJS} PRIVATE websocket.hpp - connection.cpp connection.hpp stream.hpp ) diff --git a/lib/malloy/websocket/connection.cpp b/lib/malloy/websocket/connection.cpp deleted file mode 100644 index f4837bc0..00000000 --- a/lib/malloy/websocket/connection.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "connection.hpp" - -namespace malloy::websocket -{ - - template<> - const std::string connection::agent_string = std::string(BOOST_BEAST_VERSION_STRING) + " websocket-client-async"; - - template<> - const std::string connection::agent_string = std::string{BOOST_BEAST_VERSION_STRING} + " malloy"; - -} diff --git a/lib/malloy/websocket/connection.hpp b/lib/malloy/websocket/connection.hpp index 2dfe9aa2..392c0606 100644 --- a/lib/malloy/websocket/connection.hpp +++ b/lib/malloy/websocket/connection.hpp @@ -3,21 +3,34 @@ #include #include #include -#include #include +#include +#include "malloy/error.hpp" +#include "malloy/http/request.hpp" #include "malloy/type_traits.hpp" +#include "malloy/utils.hpp" #include "malloy/websocket/stream.hpp" -#include "malloy/http/request.hpp" -#include "malloy/error.hpp" #include -#include #include #include namespace malloy::websocket { + namespace detail + { + constexpr std::string_view beast_version = BOOST_BEAST_VERSION_STRING; + template + constexpr auto ws_agent_string() -> std::string_view + { + if (isClient) { + return {BOOST_BEAST_VERSION_STRING " websocket-client-async"}; + } else { + return {BOOST_BEAST_VERSION_STRING " malloy"}; + } + } + } // namespace detail /** * @class connection @@ -44,7 +57,7 @@ namespace malloy::websocket /** * The agent string. */ - static const std::string agent_string; + constexpr static std::string_view agent_string = detail::ws_agent_string(); /** * See stream::set_binary(bool) @@ -54,8 +67,7 @@ namespace malloy::websocket /** * See stream::binary() */ - [[nodiscard]] - bool binary() { return m_ws.binary(); } + [[nodiscard]] bool binary() { return m_ws.binary(); } /** * @brief Construct a new connection object @@ -63,16 +75,15 @@ namespace malloy::websocket * @param ws Stream to use. May be unopened/connected but in that case * `connect` must be called before this connection can be used */ - static - auto - make(const std::shared_ptr logger, stream&& ws) -> std::shared_ptr { + static auto + make(const std::shared_ptr logger, stream&& ws) -> std::shared_ptr + { // We have to emulate make_shared here because the ctor is private connection* me = nullptr; try { - me = new connection{ logger, std::move(ws) }; - return std::shared_ptr{ me }; - } - catch (...) { + me = new connection{logger, std::move(ws)}; + return std::shared_ptr{me}; + } catch (...) { delete me; throw; } @@ -87,8 +98,7 @@ namespace malloy::websocket */ template void - connect(const boost::asio::ip::tcp::resolver::results_type& target, const std::string& resource, Callback&& done) - requires (isClient) + connect(const boost::asio::ip::tcp::resolver::results_type& target, const std::string& resource, Callback&& done) requires(isClient) { // Save these for later @@ -100,14 +110,12 @@ namespace malloy::websocket sock.async_connect( target, [me, target, done = std::forward(done), resource](auto ec, auto ep) mutable { - if (ec) { - done(ec); - } - else { - me->on_connect(ec, ep, resource, std::forward(done)); - } - } - ); + if (ec) { + done(ec); + } else { + me->on_connect(ec, ep, resource, std::forward(done)); + } + }); }); } @@ -124,8 +132,7 @@ namespace malloy::websocket */ template Callback> void - accept(const boost::beast::http::request& req, Callback&& done) - requires (!isClient) + accept(const boost::beast::http::request& req, Callback&& done) requires(!isClient) { // Update state m_state = state::handshaking; @@ -212,22 +219,18 @@ namespace malloy::websocket boost::asio::post( m_ws.get_executor(), [this, payload, me = this->shared_from_this(), done = std::forward(done)]() mutable { - msg_queue_.push_back([this, me, payload, done = std::forward(done)]() mutable { - me->m_ws.async_write(payload, - [me, done = std::forward(done)](auto ec, auto size) mutable { - std::invoke(std::forward(done), ec, size); - me->on_write(ec, size); - }); } - ); - - if (msg_queue_.size() > 1) { - return; - } - else { - msg_queue_.back()(); // Execute this now - } - } - ); + msg_queue_.push_back([this, me, payload, done = std::forward(done)]() mutable { me->m_ws.async_write(payload, + [me, done = std::forward(done)](auto ec, auto size) mutable { + std::invoke(std::forward(done), ec, size); + me->on_write(ec, size); + }); }); + + if (msg_queue_.size() > 1) { + return; + } else { + msg_queue_.back()(); // Execute this now + } + }); }; private: @@ -246,10 +249,9 @@ namespace malloy::websocket enum state m_state = state::closed; connection( - std::shared_ptr logger, stream&& ws - ) : + std::shared_ptr logger, stream&& ws) : m_logger(std::move(logger)), - m_ws{ std::move(ws) } + m_ws{std::move(ws)} { // Sanity check logger if (!m_logger) @@ -262,31 +264,22 @@ namespace malloy::websocket // Set suggested timeout settings for the websocket m_ws.set_option( boost::beast::websocket::stream_base::timeout::suggested( - isClient ? boost::beast::role_type::client : boost::beast::role_type::server - ) - ); + isClient ? boost::beast::role_type::client : boost::beast::role_type::server)); if constexpr (isClient) { // Set a decorator to change the User-Agent of the handshake m_ws.set_option( boost::beast::websocket::stream_base::decorator( - [](boost::beast::websocket::request_type& req) - { + [](boost::beast::websocket::request_type& req) { req.set(boost::beast::http::field::user_agent, agent_string); - } - ) - ); - } - else { + })); + } else { // Set a decorator to change the Server of the handshake m_ws.set_option( boost::beast::websocket::stream_base::decorator( - [](boost::beast::websocket::response_type& res) - { + [](boost::beast::websocket::response_type& res) { res.set(boost::beast::http::field::server, agent_string); - } - ) - ); + })); } } @@ -328,7 +321,8 @@ namespace malloy::websocket #endif on_ready_for_handshake(host, resource, std::forward(on_handshake)); } - void on_ready_for_handshake(const std::string& host, const std::string& resource, concepts::accept_handler auto&& on_handshake) { + void on_ready_for_handshake(const std::string& host, const std::string& resource, concepts::accept_handler auto&& on_handshake) + { // Turn off the timeout on the tcp_stream, because // the websocket stream has its own timeout system. m_ws.get_lowest_layer([](auto& s) { s.expires_never(); }); @@ -339,8 +333,7 @@ namespace malloy::websocket m_ws.async_handshake( host, resource, - std::forward(on_handshake) - ); + std::forward(on_handshake)); } void @@ -370,4 +363,4 @@ namespace malloy::websocket } }; -} +} // namespace malloy::websocket diff --git a/test/mocks.hpp b/test/mocks.hpp index f9a03132..b2500468 100644 --- a/test/mocks.hpp +++ b/test/mocks.hpp @@ -3,22 +3,30 @@ #include -#define MALLOY_INTERNAL_TESTING // Enable testing overloads +#define MALLOY_INTERNAL_TESTING // Enable testing overloads -namespace malloy::mock::http { - class connection { +namespace malloy::mock::http +{ + class connection + { public: - class request_generator { + class request_generator + { public: malloy::http::request_header<> header_; - auto header() const -> const malloy::http::request_header<>& { + explicit request_generator(malloy::http::request_header<> header) : + header_{std::move(header)} {} + + auto header() const -> const malloy::http::request_header<>& + { return header_; } template&&> Callback, typename Setup> - void body(Callback&& done, Setup&& s) { + void body(Callback&& done, Setup&& s) + { malloy::http::request r; r.base() = header_; s(r.body()); @@ -26,11 +34,10 @@ namespace malloy::mock::http { } template&&> Callback> - void body(Callback&& done) { + void body(Callback&& done) + { return done(malloy::http::request{header_}); } - }; -}; -} - + }; +} // namespace malloy::mock::http