7#include "fastmm/net/dns.hpp"
10#include "fastmm/net/source_address.hpp"
11#include "fastmm/net/tls_stream.hpp"
24struct RestChannelConfig {
37[[nodiscard]]
constexpr std::size_t
rest_queue_for(std::size_t symbols)
noexcept {
38 return 32 + 4 * symbols;
45 rc.base_url = cfg.rest_url;
46 rc.ca_file = cfg.ca_file;
47 rc.insecure_tls = cfg.insecure_tls;
48 rc.timeout_ms = cfg.http_timeout_ms;
50 if constexpr (
requires { cfg.source; }) rc.source = cfg.source;
61 : reactor_(reactor), cfg_(std::move(cfg)) {
63 if (!url)
throw std::invalid_argument(
"RestChannel: bad base url '" + cfg_.
base_url +
"'");
64 host_ = std::string(url->host);
67 prefix_ = url->path ==
"/" ? std::string{} : std::string(url->path);
68 while (!prefix_.empty() && prefix_.back() ==
'/') prefix_.pop_back();
70 tls_ctx_ = std::make_unique<net::TlsContext>();
82 bool request(std::string_view method,
83 std::string_view target,
84 std::string_view extra_headers,
85 std::string_view body,
87 if (!ensure_client())
return false;
88 const std::string full = prefix_ + std::string(target);
90 auto wrapped = wrap(std::move(cb));
91 const bool ok = plain_ ? plain_->request(method, full, extra_headers, body, wrapped)
92 : tls_->request(method, full, extra_headers, body, wrapped);
101 bool request(std::string_view method,
103 std::string_view extra_headers,
104 std::string_view body,
106 if (!make_target || !ensure_client())
return false;
109 std::string target = make();
110 return target.empty() ? target : prefix + target;
112 auto wrapped = wrap(std::move(cb));
113 const bool ok = plain_ ? plain_->request(method, full, extra_headers, body, wrapped)
114 : tls_->request(method, full, extra_headers, body, wrapped);
121 if (reset_pending_)
return;
122 reset_pending_ =
true;
123 std::weak_ptr<int> alive = alive_;
124 reactor_.
post([
this, alive] {
125 if (alive.expired())
return;
126 reset_pending_ =
false;
130 void reset() noexcept {
138 if (plain_) plain_->set_max_queue(n);
139 if (tls_) tls_->set_max_queue(n);
142 [[nodiscard]]
bool connected() const noexcept {
return plain_ || tls_; }
144 [[nodiscard]] std::size_t
queued() const noexcept {
145 if (plain_)
return plain_->queued();
146 return tls_ ? tls_->queued() : 0;
149 [[nodiscard]] std::uint64_t
requests() const noexcept {
return requests_; }
150 [[nodiscard]] std::uint64_t
errors() const noexcept {
return errors_; }
151 [[nodiscard]]
const std::string&
host() const noexcept {
return host_; }
152 [[nodiscard]]
bool tls() const noexcept {
return use_tls_; }
157 if (r.error != net::NetError::None) {
166 bool ensure_client() {
169 if (plain_ || tls_)
return true;
170 if (addrs_.empty() || addr_gen_ != resolve_gen_) {
172 const net::ResolveResult r = net::resolve_sync(host_, port_);
178 net::keep_reachable(cfg_.
source, addrs_);
179 if (addrs_.empty()) {
183 addr_gen_ = resolve_gen_;
186 const net::SockAddr& addr = addrs_[addr_index_++ % addrs_.size()];
187 const std::optional<net::SockAddr> local = net::source_bind_address(cfg_.
source, addr.family());
188 if (sock.connect(addr, local ? &*local : nullptr) == net::ConnectStatus::
Error) {
193 sock.set_nodelay(
true);
194 net::HttpClientConfig hc;
198 tls_ = std::make_unique<TlsClient>(
200 net::TlsStream<net::PlainStream>(*tls_ctx_, net::PlainStream(std::move(sock)), host_),
205 if (!tls_->start()) {
210 plain_ = std::make_unique<PlainClient>(
211 reactor_, net::PlainStream(std::move(sock)), host_, port_,
false, hc);
212 if (!plain_->start()) {
220 net::Reactor& reactor_;
221 RestChannelConfig cfg_;
223 std::uint16_t port_ = 0;
224 bool use_tls_ =
false;
226 std::unique_ptr<net::TlsContext> tls_ctx_;
227 std::unique_ptr<PlainClient> plain_;
228 std::unique_ptr<TlsClient> tls_;
229 std::vector<net::SockAddr> addrs_;
230 std::size_t addr_index_ = 0;
231 std::uint64_t resolve_gen_ = 0;
232 std::uint64_t addr_gen_ = ~0ULL;
233 std::shared_ptr<int> alive_ = std::make_shared<int>(0);
234 bool reset_pending_ =
false;
235 std::uint64_t requests_ = 0;
236 std::uint64_t errors_ = 0;
Definition http_client.hpp:118
Definition reactor.hpp:72
void post(Task task)
Thread-safe: queue a task for the reactor thread and wake it.
Definition rest_channel.hpp:56
const std::string & host() const noexcept
Definition rest_channel.hpp:153
std::size_t max_queue() const noexcept
Definition rest_channel.hpp:150
bool tls() const noexcept
Definition rest_channel.hpp:154
std::uint64_t requests() const noexcept
Definition rest_channel.hpp:151
net::HttpResponseCallback Callback
Definition rest_channel.hpp:58
bool connected() const noexcept
Definition rest_channel.hpp:144
std::size_t queued() const noexcept
Requests queued on the connection, the one in flight included.
Definition rest_channel.hpp:146
RestChannel(net::Reactor &reactor, RestChannelConfig cfg)
Definition rest_channel.hpp:62
void reset() noexcept
Definition rest_channel.hpp:132
void schedule_reset()
Drops the connection (deferred to the reactor loop so it is safe from callbacks).
Definition rest_channel.hpp:122
std::uint64_t errors() const noexcept
Definition rest_channel.hpp:152
RestChannel & operator=(const RestChannel &)=delete
~RestChannel()
Definition rest_channel.hpp:77
bool request(std::string_view method, std::string_view target, std::string_view extra_headers, std::string_view body, Callback cb)
target is the path plus query without the base prefix.
Definition rest_channel.hpp:84
void set_max_queue(std::size_t n) noexcept
For a subscription that grew after connect: see rest_queue_for().
Definition rest_channel.hpp:138
Keep-alive HTTP/1.1 client over any ByteStream: one request in flight per connection plus a small FIF...
std::function< void(const HttpResponse &)> HttpResponseCallback
Definition http_client.hpp:41
std::function< std::string()> HttpTargetMaker
Makes a request's target (path and query) when the request is written.
Definition http_client.hpp:44
RestChannelConfig rest_channel_config(const Cfg &cfg, std::size_t symbols)
The RestChannelConfig every connector config spells the same way.
Definition rest_channel.hpp:45
constexpr std::size_t rest_queue_for(std::size_t symbols) noexcept
Queue room for a connector's REST channel with symbols subscribed: every connector fans some requests...
Definition rest_channel.hpp:39
@ Error
venue-level error payload
Single-threaded event loop with a timer min-heap and a thread-safe mailbox (post/wake via eventfd),...
Definition http_client.hpp:29
static std::optional< Url > parse(std::string_view s) noexcept
Accepts scheme://host[:port][/path][?query].
Definition url.hpp:38
Definition rest_channel.hpp:26
std::string base_url
https://host[:port][/prefix]
Definition rest_channel.hpp:27
std::string ca_file
Definition rest_channel.hpp:28
bool insecure_tls
Definition rest_channel.hpp:29
net::SourceAddress source
where the connection leaves this host from; empty: anywhere
Definition rest_channel.hpp:32
std::size_t max_queue
Definition rest_channel.hpp:31
std::uint32_t timeout_ms
Definition rest_channel.hpp:30
Minimal URL parsing / query building for venue endpoints.