module; #include #include #include #include #include export module routemon:http.client; export import :http.common; namespace net = boost::asio; namespace ssl = net::ssl; using tcp = net::ip::tcp; namespace routemon::http { export class client { net::io_context& ioc_; ssl::context sslc_{ssl::context::tlsv12_client}; tcp::resolver resolver_; public: explicit client(net::io_context& ioc) : ioc_{ioc}, resolver_{ioc} { sslc_.set_default_verify_paths(); sslc_.set_verify_mode( net::ssl::verify_peer | net::ssl::verify_fail_if_no_peer_cert); } template auto do_request(bhttp::request& req) -> bhttp::response { auto stream = ssl::stream{ioc_, sslc_}; auto host = std::string{req.at(bhttp::field::host)}; if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) { throw beast::system_error( static_cast(::ERR_get_error()), net::error::get_ssl_category()); } stream.set_verify_callback(ssl::host_name_verification(host)); auto const results = resolver_.resolve(host, "443"); beast::get_lowest_layer(stream).connect(results); stream.handshake(ssl::stream_base::client); req.set(bhttp::field::user_agent, "routemon/1.0"); bhttp::write(stream, req); auto buffer = beast::flat_buffer{}; auto res = bhttp::response{}; bhttp::read(stream, buffer, res); auto ec = beast::error_code{}; stream.shutdown(ec); if (ec != net::ssl::error::stream_truncated) throw beast::system_error{ec}; return res; } }; } // namespace routemon::http