#include "util/TestHttpClient.hpp" #include "util/Assert.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include namespace http = boost::beast::http; namespace net = boost::asio; namespace ssl = boost::asio::ssl; using tcp = boost::asio::ip::tcp; namespace { std::pair syncRequest( std::string const& host, std::string const& port, std::string const& body, std::vector additionalHeaders, http::verb method, std::string target = "/" ) { boost::asio::io_context ioc; net::ip::tcp::resolver resolver(ioc); boost::beast::tcp_stream stream(ioc); auto const results = resolver.resolve(host, port); stream.connect(results); http::request req{method, "/", 10}; req.set(http::field::host, host); req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); for (auto const& header : additionalHeaders) { std::visit([&header, &req](auto const& name) { req.set(name, header.value); }, header.name); } req.target(target); req.body() = std::string(body); req.prepare_payload(); http::write(stream, req); boost::beast::flat_buffer buffer; http::response res; http::read(stream, buffer, res); boost::beast::error_code ec; stream.socket().shutdown(tcp::socket::shutdown_both, ec); return {res.result(), res.body()}; } } // namespace WebHeader::WebHeader(http::field name, std::string value) : name(name), value(std::move(value)) { } WebHeader::WebHeader(std::string_view name, std::string value) : name(std::string{name}), value(std::move(value)) { } std::pair HttpSyncClient::post( std::string const& host, std::string const& port, std::string const& body, std::vector additionalHeaders ) { return syncRequest(host, port, body, std::move(additionalHeaders), http::verb::post); } std::pair HttpSyncClient::get( std::string const& host, std::string const& port, std::string const& body, std::string const& target, std::vector additionalHeaders ) { return syncRequest(host, port, body, std::move(additionalHeaders), http::verb::get, target); } bool HttpsSyncClient:: verifyCertificate(bool /* preverified */, boost::asio::ssl::verify_context& /* ctx */) { return true; } std::string HttpsSyncClient::syncPost(std::string const& host, std::string const& port, std::string const& body) { net::io_context ioc; boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); ctx.set_default_verify_paths(); ctx.set_verify_mode(ssl::verify_none); tcp::resolver resolver(ioc); boost::asio::ssl::stream stream(ioc, ctx); // We can't fix this so have to ignore #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) #pragma GCC diagnostic pop { boost::beast::error_code const ec{ static_cast(::ERR_get_error()), net::error::get_ssl_category() }; throw boost::beast::system_error{ec}; } auto const results = resolver.resolve(host, port); boost::beast::get_lowest_layer(stream).connect(results); stream.handshake(ssl::stream_base::client); http::request req{http::verb::post, "/", 10}; req.set(http::field::host, host); req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); req.body() = std::string(body); req.prepare_payload(); http::write(stream, req); boost::beast::flat_buffer buffer; http::response res; http::read(stream, buffer, res); boost::beast::error_code ec; stream.shutdown(ec); return res.body(); } HttpAsyncClient::HttpAsyncClient(boost::asio::io_context& ioContext) : stream_{ioContext} { } std::expected HttpAsyncClient::connect( std::string_view host, std::string_view port, boost::asio::yield_context yield, std::chrono::steady_clock::duration timeout ) { boost::system::error_code error; boost::asio::ip::tcp::resolver resolver{stream_.get_executor()}; auto const resolverResults = resolver.resolve(host, port, error); if (error) return std::unexpected{error}; ASSERT(!resolverResults.empty(), "No results from resolver"); boost::beast::get_lowest_layer(stream_).expires_after(timeout); stream_.async_connect(resolverResults.begin()->endpoint(), yield[error]); if (error) return std::unexpected{error}; return {}; } std::expected HttpAsyncClient::send( boost::beast::http::request request, boost::asio::yield_context yield, std::chrono::steady_clock::duration timeout ) { request.prepare_payload(); boost::system::error_code error; boost::beast::get_lowest_layer(stream_).expires_after(timeout); http::async_write(stream_, request, yield[error]); if (error) return std::unexpected{error}; return {}; } std::expected< boost::beast::http::response, boost::system::error_code> HttpAsyncClient::receive( boost::asio::yield_context yield, std::chrono::steady_clock::duration timeout ) { boost::system::error_code error; http::response response; boost::beast::get_lowest_layer(stream_).expires_after(timeout); http::async_read(stream_, buffer_, response, yield[error]); if (error) return std::unexpected{error}; return response; } void HttpAsyncClient::gracefulShutdown() { boost::system::error_code error; stream_.socket().shutdown(tcp::socket::shutdown_both, error); } void HttpAsyncClient::disconnect() { boost::system::error_code error; stream_.socket().close(error); }