3#include "type_traits.hpp"
4#include "http/connection_plain.hpp"
5#include "websocket/connection.hpp"
6#include "../core/controller.hpp"
7#include "../core/error.hpp"
8#include "../core/detail/controller_run_result.hpp"
9#include "../core/http/request.hpp"
10#include "../core/http/response.hpp"
11#include "../core/http/type_traits.hpp"
12#include "../core/http/utils.hpp"
13#include "../core/tcp/stream.hpp"
15 #include "http/connection_tls.hpp"
17 #include <boost/beast/ssl.hpp>
20#include <boost/asio/strand.hpp>
21#include <spdlog/logger.h>
25namespace boost::asio::ssl
30namespace malloy::client
34 class connection_plain;
49 using header_type = boost::beast::http::response_header<>;
50 using value_type = std::string;
53 std::variant<boost::beast::http::string_body>
54 body_for(
const header_type&)
const
60 setup_body(
const header_type&, std::string&)
const
111#if MALLOY_FEATURE_TLS
117 [[nodiscard(
"init might fail")]]
141 std::future<malloy::error_code>
148 return make_http_connection<false>(std::move(req), std::forward<Callback>(done), std::move(filter));
151#if MALLOY_FEATURE_TLS
160 concepts::response_filter Filter = detail::default_resp_filter
162 requires concepts::http_callback<Callback, Filter>
164 std::future<malloy::error_code>
171 return make_http_connection<true>(std::move(req), std::forward<Callback>(done), std::move(filter));
182 const std::string& host,
184 const std::string& resource,
185 std::invocable<
malloy::error_code, std::shared_ptr<websocket::connection>>
auto&& handler
191 make_ws_connection<true>(host, port, resource, std::forward<
decltype(handler)>(handler));
212 add_ca(
const std::string& contents);
233 const std::string& host,
235 const std::string& resource,
236 std::invocable<
malloy::error_code, std::shared_ptr<websocket::connection>>
auto&& handler
240 make_ws_connection<false>(host, port, resource, std::forward<
decltype(handler)>(handler));
244 std::shared_ptr<boost::asio::ssl::context> m_tls_ctx{
nullptr};
245 std::unique_ptr<boost::asio::io_context> m_ioc_sm{std::make_unique<boost::asio::io_context>()};
246 boost::asio::io_context* m_ioc{m_ioc_sm.get()};
252 start(controller& ctrl)
254 return session{ctrl.m_cfg, ctrl.m_tls_ctx, std::move(ctrl.m_ioc_sm)};
266 throw std::logic_error(
"TLS context not initialized.");
275 std::future<malloy::error_code>
278 std::promise<malloy::error_code> prom;
279 auto err_channel = prom.get_future();
280 [req,
this](
auto&& cb) {
281#if MALLOY_FEATURE_TLS
282 if constexpr (isHttps) {
284 auto conn = std::make_shared<http::connection_tls<Body, Filter, std::decay_t<Callback>>>(
285 m_cfg.logger->clone(m_cfg.logger->name() +
" | HTTPS connection"),
293 const std::string hostname{ req.base()[malloy::http::field::host] };
294 if (!SSL_set_tlsext_host_name(conn->stream().native_handle(), hostname.c_str())) {
296 m_cfg.logger->error(
"could not set SNI hostname.");
297 boost::system::error_code ec{
static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
298 throw boost::system::system_error{ec};
307 cb(std::make_shared<http::connection_plain<Body, Filter, std::decay_t<Callback>>>(
308 m_cfg.logger->clone(m_cfg.logger->name() +
" | HTTP connection"),
314 ([
this, prom = std::move(prom), req = std::move(req), filter = std::forward<Filter>(filter), cb = std::forward<Callback>(cb)](
auto&& conn)
mutable {
315 if (!malloy::http::has_field(req, malloy::http::field::user_agent))
316 req.set(malloy::http::field::user_agent, m_cfg.user_agent);
322 std::forward<Callback>(cb),
323 std::forward<Filter>(filter)
331 template<
bool isSecure>
334 const std::string& host,
336 const std::string& resource,
337 std::invocable<
malloy::error_code, std::shared_ptr<websocket::connection>>
auto&& handler
341 auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(boost::asio::make_strand(*m_ioc));
342 resolver->async_resolve(
344 std::to_string(port),
345 [
this, resolver, done = std::forward<
decltype(handler)>(handler), resource](
auto ec,
auto results)
mutable {
347 std::invoke(std::forward<
decltype(done)>(done), ec, std::shared_ptr<websocket::connection>{
nullptr});
350#if MALLOY_FEATURE_TLS
351 if constexpr (isSecure) {
352 return malloy::websocket::stream{boost::beast::ssl_stream<malloy::tcp::stream<>>{
353 malloy::tcp::stream<>{boost::asio::make_strand(*m_ioc)}, *m_tls_ctx}};
358 }(), m_cfg.user_agent);
360 conn->connect(results, resource, [conn, done = std::forward<
decltype(done)>(done)](
auto ec)
mutable {
362 std::invoke(std::forward<
decltype(handler)>(done), ec, std::shared_ptr<websocket::connection>{
nullptr});
364 std::invoke(std::forward<
decltype(handler)>(done), ec, conn);
372 auto start(controller&) -> controller::session;
Definition: controller.hpp:73
std::future< malloy::error_code > http_request(malloy::http::request< ReqBody > req, Callback &&done, Filter filter={})
Definition: controller.hpp:142
bool init_tls()
Definition: controller.cpp:19
void ws_connect(const std::string &host, std::uint16_t port, const std::string &resource, std::invocable< malloy::error_code, std::shared_ptr< websocket::connection > > auto &&handler)
Definition: controller.hpp:232
void wss_connect(const std::string &host, std::uint16_t port, const std::string &resource, std::invocable< malloy::error_code, std::shared_ptr< websocket::connection > > auto &&handler)
Same as ws_connect() but uses TLS.
Definition: controller.hpp:181
std::future< malloy::error_code > https_request(malloy::http::request< ReqBody > req, Callback &&done, Filter filter={})
Definition: controller.hpp:165
void add_ca(const std::string &contents)
Like add_ca_file(std::filesystem::path) but loads from an in-memory string.
Definition: controller.cpp:40
malloy::detail::controller_run_result< std::shared_ptr< boost::asio::ssl::context > > session
Definition: controller.hpp:78
void add_ca_file(const std::filesystem::path &file)
Load a certificate authority for use with TLS validation.
Definition: controller.cpp:30
Definition: controller_run_result.hpp:43
Definition: request.hpp:19
Definition: response.hpp:22
static std::shared_ptr< connection > make(const std::shared_ptr< spdlog::logger > logger, stream &&ws, const std::string &agent_string)
Construct a new connection object.
Definition: connection.hpp:106
Websocket stream. May use TLS.
Definition: stream.hpp:50
Definition: type_traits.hpp:68
Definition: type_traits.hpp:56
Definition: type_traits.hpp:9
boost::beast::error_code error_code
Error code used to signify errors without throwing. Truthy means it holds an error.
Definition: error.hpp:9
Definition: controller.hpp:85
std::string user_agent
Agent string used for connections.
Definition: controller.hpp:90
std::uint64_t body_limit
Definition: controller.hpp:95
Default filter provided to ease use of interface.
Definition: controller.hpp:47
Definition: controller_run_result.hpp:19