Commit b7aedb1d authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/neverlord/caf-net-prometheus'

parents 4b9e5e2a 02bb479c
......@@ -421,7 +421,9 @@ strong_actor_ptr middleman::remote_lookup(std::string name,
void middleman::start() {
CAF_LOG_TRACE("");
// Launch background tasks.
// Launch background tasks unless caf-net is also available. In that case, the
// net::middleman takes care of these.
if (!system().has_network_manager()) {
if (auto prom = get_if<config_value::dictionary>(
&system().config(), "caf.middleman.prometheus-http")) {
auto ptr = std::make_unique<prometheus_scraping>(system());
......@@ -431,6 +433,7 @@ void middleman::start() {
background_tasks_.emplace_back(std::move(ptr));
}
}
}
// Launch backend.
if (!get_or(config(), "caf.middleman.manual-multiplexing", false))
backend_supervisor_ = backend().make_supervisor();
......
......@@ -51,6 +51,7 @@ caf_add_component(
src/net/network_socket.cpp
src/net/pipe_socket.cpp
src/net/pollset_updater.cpp
src/net/prometheus/server.cpp
src/net/socket.cpp
src/net/socket_event_layer.cpp
src/net/socket_manager.cpp
......@@ -94,6 +95,7 @@ caf_add_component(
net.operation
net.pipe_socket
net.producer_adapter
net.prometheus.server
net.socket
net.socket_guard
net.ssl.transport
......
......@@ -21,10 +21,6 @@ public:
using factory_type = Factory;
using read_result = typename socket_event_layer::read_result;
using write_result = typename socket_event_layer::write_result;
// -- constructors, destructors, and assignment operators --------------------
template <class... Ts>
......@@ -54,40 +50,34 @@ public:
return none;
}
read_result handle_read_event() override {
void handle_read_event() override {
CAF_LOG_TRACE("");
if (auto x = accept(fd_)) {
socket_manager_ptr child = factory_.make(owner_->mpx_ptr(), *x);
if (!child) {
CAF_LOG_ERROR("factory failed to create a new child");
return read_result::abort;
owner_->deregister();
return;
}
if (auto err = child->init(cfg_)) {
CAF_LOG_ERROR("failed to initialize new child:" << err);
return read_result::abort;
owner_->deregister();
return;
}
if (limit_ == 0) {
return read_result::again;
} else {
return ++accepted_ < limit_ ? read_result::again : read_result::stop;
if (limit_ != 0 && ++accepted_ == limit_) {
// TODO: ask owner to close socket.
owner_->deregister();
return;
}
} else {
CAF_LOG_ERROR("accept failed:" << x.error());
return read_result::stop;
}
owner_->deregister();
}
read_result handle_buffered_data() override {
return read_result::again;
}
read_result handle_continue_reading() override {
return read_result::again;
}
write_result handle_write_event() override {
void handle_write_event() override {
CAF_LOG_ERROR("connection_acceptor received write event");
return write_result::stop;
owner_->deregister_writing();
}
void abort(const error& reason) override {
......
......@@ -5,27 +5,28 @@
#pragma once
#include "caf/async/consumer.hpp"
#include "caf/detail/atomic_ref_counted.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/socket_manager.hpp"
#include "caf/ref_counted.hpp"
namespace caf::net {
/// Connects a socket manager to an asynchronous consumer resource. Whenever new
/// data becomes ready, the adapter registers the socket manager for writing.
template <class Buffer>
class consumer_adapter final : public ref_counted, public async::consumer {
class consumer_adapter final : public detail::atomic_ref_counted,
public async::consumer {
public:
using buf_ptr = intrusive_ptr<Buffer>;
using ptr_type = intrusive_ptr<consumer_adapter>;
void on_producer_ready() override {
// nop
}
void on_producer_wakeup() override {
mgr_->mpx().schedule_fn([adapter = strong_this()] { //
adapter->on_wakeup();
});
mgr_->schedule(do_wakeup_);
}
void ref_consumer() const noexcept override {
......@@ -44,22 +45,20 @@ public:
void cancel() {
buf_->cancel();
buf_ = nullptr;
mgr_ = nullptr;
do_wakeup_.dispose();
do_wakeup_ = nullptr;
}
bool has_data() const noexcept {
return buf_->has_data();
}
/// Tries to open the resource for reading.
/// @returns a connected adapter that reads from the resource on success,
/// `nullptr` otherwise.
template <class Resource>
static intrusive_ptr<consumer_adapter>
try_open(socket_manager* owner, Resource src) {
CAF_ASSERT(owner != nullptr);
if (auto buf = src.try_open()) {
using ptr_type = intrusive_ptr<consumer_adapter>;
auto adapter = ptr_type{new consumer_adapter(owner, buf), false};
static ptr_type make(buf_ptr buf, socket_manager_ptr mgr, action do_wakeup) {
if (buf) {
auto adapter = ptr_type{new consumer_adapter(buf, mgr,
std::move(do_wakeup)), //
false};
buf->set_consumer(adapter);
return adapter;
} else {
......@@ -67,32 +66,18 @@ public:
}
}
friend void intrusive_ptr_add_ref(const consumer_adapter* ptr) noexcept {
ptr->ref();
}
friend void intrusive_ptr_release(const consumer_adapter* ptr) noexcept {
ptr->deref();
}
private:
consumer_adapter(socket_manager* owner, buf_ptr buf)
: mgr_(owner), buf_(std::move(buf)) {
consumer_adapter(buf_ptr buf, socket_manager_ptr mgr, action do_wakeup)
: buf_(std::move(buf)),
mgr_(std::move(mgr)),
do_wakeup_(std::move(do_wakeup)) {
// nop
}
auto strong_this() {
return intrusive_ptr{this};
}
void on_wakeup() {
if (buf_ && buf_->has_consumer_event()) {
mgr_->mpx().register_writing(mgr_);
}
}
intrusive_ptr<socket_manager> mgr_;
intrusive_ptr<Buffer> buf_;
socket_manager_ptr mgr_;
action do_wakeup_;
action do_cancel_;
};
template <class T>
......
......@@ -5,6 +5,7 @@
#pragma once
#include "caf/detail/net_export.hpp"
#include "caf/fwd.hpp"
namespace caf::net {
......@@ -17,12 +18,18 @@ public:
/// Queries whether the output device can accept more data straight away.
[[nodiscard]] virtual bool can_send_more() const noexcept = 0;
/// Halt receiving of additional bytes or messages.
virtual void suspend_reading() = 0;
/// Queries whether the lower layer is currently configured to halt receiving
/// of additional bytes or messages.
[[nodiscard]] virtual bool stopped_reading() const noexcept = 0;
[[nodiscard]] virtual bool is_reading() const noexcept = 0;
/// Shuts down any connection or session gracefully. Any pending data gets
/// flushed before closing the socket.
virtual void close() = 0;
/// Shuts down any connection or session du to an error. Any pending data gets
/// flushed before closing the socket. Protocols with a dedicated closing
/// handshake such as WebSocket may send the close reason to the peer.
virtual void close(const error& reason);
};
} // namespace caf::net
......@@ -24,11 +24,6 @@ public:
/// function to decide whether it has to wait for write events on the socket.
[[nodiscard]] virtual bool done_sending() = 0;
/// Called by the lower layer after some event triggered re-registering the
/// socket manager for read operations after it has been stopped previously
/// by the read policy. May restart consumption of bytes or messages.
virtual void continue_reading() = 0;
/// Called by the lower layer for cleaning up any state in case of an error.
virtual void abort(const error& reason) = 0;
};
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
namespace caf::net::http {
/// Stores context information for a request. For HTTP/2, this is the stream ID.
class context {};
} // namespace caf::net::http
......@@ -10,7 +10,6 @@
namespace caf::net::http {
class context;
class header;
class lower_layer;
class server;
......
......@@ -18,28 +18,33 @@ class CAF_NET_EXPORT lower_layer : public generic_lower_layer {
public:
virtual ~lower_layer();
/// Start or re-start reading data from the client.
virtual void request_messages() = 0;
/// Stops reading messages until calling `request_messages`.
virtual void suspend_reading() = 0;
/// Sends the next header to the client.
virtual bool
send_header(context ctx, status code, const header_fields_map& fields)
= 0;
virtual bool send_header(status code, const header_fields_map& fields) = 0;
/// Sends the payload after the header.
virtual bool send_payload(context, const_byte_span bytes) = 0;
virtual bool send_payload(const_byte_span bytes) = 0;
/// Sends a chunk of data if the full payload is unknown when starting to
/// send.
virtual bool send_chunk(context, const_byte_span bytes) = 0;
virtual bool send_chunk(const_byte_span bytes) = 0;
/// Sends the last chunk, completing a chunked payload.
virtual bool send_end_of_chunks() = 0;
/// Terminates an HTTP context.
virtual void fin(context) = 0;
/// Convenience function for sending header and payload. Automatically sets
/// the header fields `Content-Type` and `Content-Length`.
bool send_response(context ctx, status codek, std::string_view content_type,
bool send_response(status code, std::string_view content_type,
const_byte_span content);
/// @copydoc send_response
bool send_response(status code, std::string_view content_type,
std::string_view content);
};
} // namespace caf::net::http
......@@ -10,7 +10,6 @@
#include "caf/logger.hpp"
#include "caf/net/connection_acceptor.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/http/context.hpp"
#include "caf/net/http/header.hpp"
#include "caf/net/http/header_fields_map.hpp"
#include "caf/net/http/lower_layer.hpp"
......@@ -39,8 +38,6 @@ public:
using status_code_type = status;
using context_type = context;
using header_type = header;
enum class mode {
......@@ -80,20 +77,21 @@ public:
bool can_send_more() const noexcept override;
void suspend_reading() override;
bool is_reading() const noexcept override;
bool stopped_reading() const noexcept override;
void close() override;
bool send_header(context, status code,
const header_fields_map& fields) override;
void request_messages() override;
bool send_payload(context, const_byte_span bytes) override;
void suspend_reading() override;
bool send_chunk(context, const_byte_span bytes) override;
bool send_header(status code, const header_fields_map& fields) override;
bool send_end_of_chunks() override;
bool send_payload(const_byte_span bytes) override;
void fin(context) override;
bool send_chunk(const_byte_span bytes) override;
bool send_end_of_chunks() override;
// -- stream_oriented::upper_layer implementation ----------------------------
......@@ -106,8 +104,6 @@ public:
bool done_sending() override;
void continue_reading() override;
ptrdiff_t consume(byte_span input, byte_span) override;
private:
......
......@@ -28,18 +28,12 @@ public:
= 0;
/// Consumes an HTTP message.
/// @param ctx Identifies this request. The response message must pass this
/// back to the lower layer. Allows clients to send multiple
/// requests in "parallel" (i.e., send multiple requests before
/// receiving the response on the first one).
/// @param hdr The header fields for the received message.
/// @param payload The payload of the received message.
/// @returns The number of consumed bytes or a negative value to signal an
/// error.
/// @note Discarded data is lost permanently.
virtual ptrdiff_t
consume(context ctx, const header& hdr, const_byte_span payload)
= 0;
virtual ptrdiff_t consume(const header& hdr, const_byte_span payload) = 0;
};
} // namespace caf::net::http
......@@ -81,8 +81,6 @@ public:
ptrdiff_t consume(byte_span buffer, byte_span delta) override;
void continue_reading() override;
bool prepare_send() override;
bool done_sending() override;
......@@ -95,7 +93,7 @@ public:
void suspend_reading() override;
bool stopped_reading() const noexcept override;
bool is_reading() const noexcept override;
void begin_message() override;
......@@ -103,9 +101,7 @@ public:
bool end_message() override;
void send_close_message() override;
void send_close_message(const error& reason) override;
void close() override;
// -- utility functions ------------------------------------------------------
......
......@@ -116,9 +116,9 @@ public:
auto [again, consumed] = in_->pull(async::delay_errors, 1, helper);
if (!again) {
if (helper.err) {
down_->send_close_message(helper.err);
down_->close(helper.err);
} else {
down_->send_close_message();
down_->close();
}
in_ = nullptr;
} else if (helper.aborted) {
......
......@@ -47,6 +47,9 @@ public:
/// Pulls messages from the transport until calling `suspend_reading`.
virtual void request_messages() = 0;
/// Stops reading messages until calling `request_messages`.
virtual void suspend_reading() = 0;
/// Prepares the layer for an outgoing message, e.g., by allocating an
/// output buffer as necessary.
virtual void begin_message() = 0;
......@@ -62,17 +65,6 @@ public:
/// @note When returning `false`, clients must also call
/// `down.set_read_error(...)` with an appropriate error code.
virtual bool end_message() = 0;
/// Inform the remote endpoint that no more messages will arrive.
/// @note Not every protocol has a dedicated close message. Some
/// implementation may simply do nothing.
virtual void send_close_message() = 0;
/// Inform the remote endpoint that no more messages will arrive because of
/// an error.
/// @note Not every protocol has a dedicated close message. Some
/// implementation may simply do nothing.
virtual void send_close_message(const error& reason) = 0;
};
} // namespace caf::net::message_oriented
......@@ -73,10 +73,10 @@ public:
size_t num_socket_managers() const noexcept;
/// Returns the index of `mgr` in the pollset or `-1`.
ptrdiff_t index_of(const socket_manager_ptr& mgr);
ptrdiff_t index_of(const socket_manager_ptr& mgr) const noexcept;
/// Returns the index of `fd` in the pollset or `-1`.
ptrdiff_t index_of(socket fd);
ptrdiff_t index_of(socket fd) const noexcept;
/// Returns the owning @ref middleman instance.
middleman& owner();
......@@ -89,25 +89,9 @@ public:
// -- thread-safe signaling --------------------------------------------------
/// Registers `mgr` for read events.
/// @thread-safe
void register_reading(const socket_manager_ptr& mgr);
/// Registers `mgr` for write events.
/// @thread-safe
void register_writing(const socket_manager_ptr& mgr);
/// Triggers a continue reading event for `mgr`.
/// @thread-safe
void continue_reading(const socket_manager_ptr& mgr);
/// Triggers a continue writing event for `mgr`.
/// Schedules a call to `mgr->handle_error(sec::disposed)`.
/// @thread-safe
void continue_writing(const socket_manager_ptr& mgr);
/// Schedules a call to `mgr->handle_error(sec::discarded)`.
/// @thread-safe
void discard(const socket_manager_ptr& mgr);
void dispose(const socket_manager_ptr& mgr);
/// Stops further reading by `mgr`.
/// @thread-safe
......@@ -136,6 +120,29 @@ public:
/// @thread-safe
void shutdown();
// -- callbacks for socket managers ------------------------------------------
/// Registers `mgr` for read events.
void register_reading(socket_manager* mgr);
/// Registers `mgr` for write events.
void register_writing(socket_manager* mgr);
/// Deregisters `mgr` from read events.
void deregister_reading(socket_manager* mgr);
/// Deregisters `mgr` from write events.
void deregister_writing(socket_manager* mgr);
/// Deregisters @p mgr from read and write events.
void deregister(socket_manager* mgr);
/// Queries whether `mgr` is currently registered for reading.
bool is_reading(const socket_manager* mgr) const noexcept;
/// Queries whether `mgr` is currently registered for writing.
bool is_writing(const socket_manager* mgr) const noexcept;
// -- control flow -----------------------------------------------------------
/// Polls I/O activity once and runs all socket event handlers that become
......@@ -160,15 +167,12 @@ protected:
/// Handles an I/O event on given manager.
void handle(const socket_manager_ptr& mgr, short events, short revents);
/// Transfers socket ownership from one manager to another.
void do_handover(const socket_manager_ptr& mgr);
/// Returns a change entry for the socket at given index. Lazily creates a new
/// entry before returning if necessary.
poll_update& update_for(ptrdiff_t index);
/// Returns a change entry for the socket of the manager.
poll_update& update_for(const socket_manager_ptr& mgr);
poll_update& update_for(socket_manager* mgr);
/// Writes `opcode` and pointer to `mgr` the the pipe for handling an event
/// later via the pollset updater.
......@@ -182,13 +186,7 @@ protected:
}
/// Queries the currently active event bitmask for `mgr`.
short active_mask_of(const socket_manager_ptr& mgr);
/// Queries whether `mgr` is currently registered for reading.
bool is_reading(const socket_manager_ptr& mgr);
/// Queries whether `mgr` is currently registered for writing.
bool is_writing(const socket_manager_ptr& mgr);
short active_mask_of(const socket_manager* mgr) const noexcept;
// -- member variables -------------------------------------------------------
......@@ -226,13 +224,7 @@ private:
void do_register_reading(const socket_manager_ptr& mgr);
void do_register_writing(const socket_manager_ptr& mgr);
void do_continue_reading(const socket_manager_ptr& mgr);
void do_continue_writing(const socket_manager_ptr& mgr);
void do_discard(const socket_manager_ptr& mgr);
void do_dispose(const socket_manager_ptr& mgr);
void do_shutdown_reading(const socket_manager_ptr& mgr);
......
......@@ -25,12 +25,8 @@ public:
using msg_buf = std::array<std::byte, sizeof(intptr_t) + 1>;
enum class code : uint8_t {
register_reading,
continue_reading,
register_writing,
continue_writing,
init_manager,
discard_manager,
dispose_manager,
shutdown_reading,
shutdown_writing,
run_action,
......@@ -49,18 +45,15 @@ public:
error init(socket_manager* owner, const settings& cfg) override;
read_result handle_read_event() override;
void handle_read_event() override;
read_result handle_buffered_data() override;
read_result handle_continue_reading() override;
write_result handle_write_event() override;
void handle_write_event() override;
void abort(const error& reason) override;
private:
pipe_socket fd_;
socket_manager* owner_ = nullptr;
multiplexer* mpx_ = nullptr;
msg_buf buf_;
size_t buf_size_ = 0;
......
......@@ -8,36 +8,35 @@
#include <new>
#include "caf/async/producer.hpp"
#include "caf/detail/atomic_ref_counted.hpp"
#include "caf/flow/observer.hpp"
#include "caf/flow/subscription.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/socket_manager.hpp"
#include "caf/ref_counted.hpp"
namespace caf::net {
/// Connects a socket manager to an asynchronous producer resource.
template <class Buffer>
class producer_adapter final : public ref_counted, public async::producer {
class producer_adapter final : public detail::atomic_ref_counted,
public async::producer {
public:
using atomic_count = std::atomic<size_t>;
using buf_ptr = intrusive_ptr<Buffer>;
using value_type = typename Buffer::value_type;
using ptr_type = intrusive_ptr<producer_adapter>;
void on_consumer_ready() override {
// nop
}
void on_consumer_cancel() override {
mgr_->mpx().schedule_fn([adapter = strong_this()] { //
adapter->on_cancel();
});
mgr_->schedule(do_cancel_);
}
void on_consumer_demand(size_t) override {
mgr_->continue_reading();
mgr_->schedule(do_resume_);
}
void ref_producer() const noexcept override {
......@@ -48,16 +47,13 @@ public:
this->deref();
}
/// Tries to open the resource for writing.
/// @returns a connected adapter that writes to the resource on success,
/// `nullptr` otherwise.
template <class Resource>
static intrusive_ptr<producer_adapter>
try_open(socket_manager* owner, Resource src) {
CAF_ASSERT(owner != nullptr);
if (auto buf = src.try_open()) {
using ptr_type = intrusive_ptr<producer_adapter>;
auto adapter = ptr_type{new producer_adapter(owner, buf), false};
static ptr_type make(buf_ptr buf, socket_manager_ptr mgr, action do_resume,
action do_cancel) {
if (buf) {
auto adapter
= ptr_type{new producer_adapter(buf, mgr, std::move(do_resume),
std::move(do_cancel)),
false};
buf->set_producer(adapter);
return adapter;
} else {
......@@ -88,47 +84,40 @@ public:
void close() {
if (buf_) {
buf_->close();
buf_ = nullptr;
mgr_ = nullptr;
reset();
}
}
void abort(error reason) {
if (buf_) {
buf_->abort(std::move(reason));
buf_ = nullptr;
mgr_ = nullptr;
}
reset();
}
friend void intrusive_ptr_add_ref(const producer_adapter* ptr) noexcept {
ptr->ref();
}
friend void intrusive_ptr_release(const producer_adapter* ptr) noexcept {
ptr->deref();
}
private:
producer_adapter(socket_manager* owner, buf_ptr buf)
: mgr_(owner), buf_(std::move(buf)) {
producer_adapter(buf_ptr buf, socket_manager_ptr mgr, action do_resume,
action do_cancel)
: buf_(std::move(buf)),
mgr_(std::move(mgr)),
do_resume_(std::move(do_resume)),
do_cancel_(std::move(do_cancel)) {
// nop
}
void on_cancel() {
if (buf_) {
mgr_->mpx().shutdown_reading(mgr_);
void reset() {
buf_ = nullptr;
mgr_ = nullptr;
}
do_resume_.dispose();
do_resume_ = nullptr;
do_cancel_.dispose();
do_cancel_ = nullptr;
}
auto strong_this() {
return intrusive_ptr{this};
}
intrusive_ptr<socket_manager> mgr_;
intrusive_ptr<Buffer> buf_;
intrusive_ptr<socket_manager> mgr_;
action do_resume_;
action do_cancel_;
};
template <class T>
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/actor_system.hpp"
#include "caf/fwd.hpp"
#include "caf/net/http/server.hpp"
#include "caf/net/prometheus/server.hpp"
#include "caf/net/stream_transport.hpp"
namespace caf::detail {
template <class Transport>
class prometheus_acceptor_factory {
public:
using state_ptr = net::prometheus::server::scrape_state_ptr;
explicit prometheus_acceptor_factory(state_ptr ptr) : ptr_(std::move(ptr)) {
// nop
}
error init(net::socket_manager*, const settings&) {
return none;
}
template <class Socket>
net::socket_manager_ptr make(net::multiplexer* mpx, Socket fd) {
auto prom_serv = net::prometheus::server::make(ptr_);
auto http_serv = net::http::server::make(std::move(prom_serv));
auto transport = Transport::make(fd, std::move(http_serv));
return net::socket_manager::make(mpx, fd, std::move(transport));
}
void abort(const error&) {
// nop
}
private:
state_ptr ptr_;
};
} // namespace caf::detail
namespace caf::net::prometheus {
/// Listens for incoming WebSocket connection on @p fd.
/// @param sys The host system.
/// @param fd An accept socket in listening mode. For a TCP socket, this socket
/// must already listen to a port.
template <class Transport = stream_transport, class Socket>
disposable serve(actor_system& sys, Socket fd) {
using factory_t = detail::prometheus_acceptor_factory<Transport>;
using impl_t = connection_acceptor<Socket, factory_t>;
auto mpx = &sys.network_manager().mpx();
auto registry = &sys.metrics();
auto state = prometheus::server::scrape_state::make(registry);
auto factory = factory_t{std::move(state)};
auto impl = impl_t::make(fd, 0, std::move(factory));
auto mgr = socket_manager::make(mpx, fd, std::move(impl));
mpx->init(mgr);
return mgr->make_disposer();
}
} // namespace caf::net::prometheus
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/fwd.hpp"
#include "caf/net/http/fwd.hpp"
#include "caf/net/http/upper_layer.hpp"
#include "caf/telemetry/collector/prometheus.hpp"
#include "caf/telemetry/importer/process.hpp"
#include <chrono>
#include <memory>
#include <string_view>
namespace caf::net::prometheus {
/// Makes metrics available to clients via the Prometheus exposition format.
class server : public http::upper_layer {
public:
// -- member types -----------------------------------------------------------
/// State for scraping Metrics data. Shared between all server instances.
class scrape_state {
public:
using clock_type = std::chrono::steady_clock;
using time_point = clock_type::time_point;
using duration = clock_type::duration;
std::string_view scrape();
explicit scrape_state(telemetry::metric_registry* ptr)
: registry(ptr), last_scrape(duration{0}), proc_importer(*ptr) {
// nop
}
static std::shared_ptr<scrape_state> make(telemetry::metric_registry* ptr) {
return std::make_shared<scrape_state>(ptr);
}
telemetry::metric_registry* registry;
std::chrono::steady_clock::time_point last_scrape;
telemetry::importer::process proc_importer;
telemetry::collector::prometheus collector;
};
using scrape_state_ptr = std::shared_ptr<scrape_state>;
// -- factories --------------------------------------------------------------
static std::unique_ptr<server> make(scrape_state_ptr state) {
return std::unique_ptr<server>{new server(std::move(state))};
}
// -- implementation of http::upper_layer ------------------------------------
bool prepare_send() override;
bool done_sending() override;
void abort(const error& reason) override;
error init(socket_manager* owner, http::lower_layer* down,
const settings& config) override;
ptrdiff_t consume(const http::header& hdr, const_byte_span payload) override;
private:
explicit server(scrape_state_ptr state) : state_(std::move(state)) {
// nop
}
scrape_state_ptr state_;
http::lower_layer* down_ = nullptr;
};
} // namespace caf::net::prometheus
......@@ -16,58 +16,14 @@ class CAF_NET_EXPORT socket_event_layer {
public:
virtual ~socket_event_layer();
/// Encodes how to proceed after a read operation.
enum class read_result {
/// Indicates that a manager wants to read again later.
again,
/// Indicates that a manager wants to stop reading until explicitly resumed.
stop,
/// Indicates that a manager wants to write to the socket instead of reading
/// from the socket.
want_write,
/// Indicates that the manager no longer reads from the socket.
close,
/// Indicates that the manager encountered a fatal error and stops both
/// reading and writing.
abort,
/// Indicates that a manager is done with the socket and hands ownership to
/// another manager.
handover,
};
/// Encodes how to proceed after a write operation.
enum class write_result {
/// Indicates that a manager wants to read again later.
again,
/// Indicates that a manager wants to stop reading until explicitly resumed.
stop,
/// Indicates that a manager wants to read from the socket instead of
/// writing to the socket.
want_read,
/// Indicates that the manager no longer writes to the socket.
close,
/// Indicates that the manager encountered a fatal error and stops both
/// reading and writing.
abort,
/// Indicates that a manager is done with the socket and hands ownership to
/// another manager.
handover,
};
/// Initializes the layer.
virtual error init(socket_manager* owner, const settings& cfg) = 0;
/// Handles a read event on the managed socket.
virtual read_result handle_read_event() = 0;
/// Handles internally buffered data.
virtual read_result handle_buffered_data() = 0;
/// Handles a request to continue reading on the socket.
virtual read_result handle_continue_reading() = 0;
virtual void handle_read_event() = 0;
/// Handles a write event on the managed socket.
virtual write_result handle_write_event() = 0;
virtual void handle_write_event() = 0;
/// Called after returning `handover` from a read or write handler.
virtual bool do_handover(std::unique_ptr<socket_event_layer>& next);
......
......@@ -4,6 +4,7 @@
#pragma once
#include "caf/action.hpp"
#include "caf/actor.hpp"
#include "caf/actor_system.hpp"
#include "caf/callback.hpp"
......@@ -28,10 +29,6 @@ class CAF_NET_EXPORT socket_manager : public ref_counted {
public:
// -- member types -----------------------------------------------------------
using read_result = socket_event_layer::read_result;
using write_result = socket_event_layer::write_result;
using event_handler_ptr = std::unique_ptr<socket_event_layer>;
/// Stores manager-related flags in a single block.
......@@ -77,6 +74,10 @@ public:
return make_actor_shell<Handle>(std::move(f));
}
/// Returns a thread-safe disposer for stopping the socket manager from an
/// outside context.
disposable make_disposer();
// -- properties -------------------------------------------------------------
/// Returns the handle for the managed socket.
......@@ -122,31 +123,54 @@ public:
return flags_.write_closed;
}
/// Queries whether the manager is registered for reading.
bool is_reading() const noexcept;
/// Queries whether the manager is registered for writing.
bool is_writing() const noexcept;
// -- event loop management --------------------------------------------------
/// Registers the manager for read operations on the @ref multiplexer.
/// @thread-safe
/// Registers the manager for read operations.
void register_reading();
/// Registers the manager for write operations on the @ref multiplexer.
/// @thread-safe
/// Registers the manager for write operations.
void register_writing();
/// Schedules a call to `handle_continue_reading` on the @ref multiplexer.
/// This mechanism allows users to signal changes in the environment to the
/// manager that allow it to make progress, e.g., new demand in asynchronous
/// buffer that allow the manager to push available data downstream. The event
/// is a no-op if the manager is already registered for reading.
/// @thread-safe
void continue_reading();
/// Schedules a call to `handle_continue_reading` on the @ref multiplexer.
/// This mechanism allows users to signal changes in the environment to the
/// manager that allow it to make progress, e.g., new data for writing in an
/// asynchronous buffer. The event is a no-op if the manager is already
/// registered for writing.
/// @thread-safe
void continue_writing();
/// Deregisters the manager from read operations.
void deregister_reading();
/// Deregisters the manager from write operations.
void deregister_writing();
/// Deregisters the manager from both read and write operations.
void deregister();
/// Deregisters the manager from read operations and blocks any future
/// attempts to re-register it.
void shutdown_read();
/// Deregisters the manager from write operations and blocks any future
/// attempts to re-register it.
void shutdown_write();
/// Deregisters the manager from both read and write operations and blocks any
/// future attempts to re-register it.
void shutdown();
// -- callbacks for the handler ----------------------------------------------
/// Schedules a call to `do_handover` on the handler.
void schedule_handover();
/// Schedules @p what to run later.
void schedule(action what);
/// Schedules @p what to run later.
template <class F>
void schedule_fn(F&& what) {
schedule(make_action(std::forward<F>(what)));
}
// -- callbacks for the multiplexer ------------------------------------------
......@@ -160,30 +184,20 @@ public:
error init(const settings& cfg);
/// Called whenever the socket received new data.
read_result handle_read_event();
/// Called after handovers to allow the manager to process any data that is
/// already buffered at the transport policy and thus would not trigger a read
/// event on the socket.
read_result handle_buffered_data();
/// Restarts a socket manager that suspended reads. Calling this member
/// function on active managers is a no-op. This function also should read any
/// data buffered outside of the socket.
read_result handle_continue_reading();
void handle_read_event();
/// Called whenever the socket is allowed to send data.
write_result handle_write_event();
void handle_write_event();
/// Called when the remote side becomes unreachable due to an error.
/// @param code The error code as reported by the operating system.
void handle_error(sec code);
/// Performs a handover to another transport after `handle_read_event` or
/// `handle_read_event` returned `handover`.
[[nodiscard]] bool do_handover();
private:
// -- utility functions ------------------------------------------------------
socket_manager_ptr strong_this();
// -- member variables -------------------------------------------------------
/// Stores the socket file descriptor. The socket manager automatically closes
......
......@@ -45,6 +45,10 @@ class CAF_NET_EXPORT lower_layer : public generic_lower_layer {
public:
virtual ~lower_layer();
/// Queries whether the transport is currently configured to read from its
/// socket.
virtual bool is_reading() const noexcept = 0;
/// Configures threshold for the next receive operations. Policies remain
/// active until calling this function again.
/// @warning Calling this function in `consume` invalidates both byte spans.
......
......@@ -85,6 +85,9 @@ public:
/// Stores whether we left a write handler due to want_read.
bool wanted_write_from_read_event : 1;
/// Stores whether we shutdown write on the socket after flushing.
bool shutting_down : 1;
};
// -- constants --------------------------------------------------------------
......@@ -118,9 +121,9 @@ public:
bool end_output() override;
void suspend_reading() override;
bool is_reading() const noexcept override;
bool stopped_reading() const noexcept override;
void close() override;
// -- properties -------------------------------------------------------------
......@@ -152,17 +155,23 @@ public:
error init(socket_manager* owner, const settings& config) override;
read_result handle_read_event() override;
read_result handle_buffered_data() override;
void handle_read_event() override;
write_result handle_write_event() override;
read_result handle_continue_reading() override;
void handle_write_event() override;
void abort(const error& reason) override;
protected:
// -- utility functions ------------------------------------------------------
/// Consumes as much data from the buffer as possible.
void handle_buffered_data();
/// Calls abort on the upper layer and deregisters the transport from events.
void fail(const error& reason);
// -- member variables -------------------------------------------------------
/// The socket file descriptor.
stream_socket fd_;
......
......@@ -24,8 +24,8 @@ struct CAF_NET_EXPORT tcp_accept_socket : network_socket {
/// Creates a new TCP socket to accept connections on a given port.
/// @param node The endpoint to listen on and the filter for incoming addresses.
/// Passing the address `0.0.0.0` will accept incoming connection from any host.
/// Passing port 0 lets the OS choose the port.
/// Passing the address `0.0.0.0` will accept incoming connection
/// from any host. Passing port 0 lets the OS choose the port.
/// @relates tcp_accept_socket
expected<tcp_accept_socket>
CAF_NET_EXPORT make_tcp_accept_socket(ip_endpoint node,
......@@ -33,14 +33,24 @@ expected<tcp_accept_socket>
/// Creates a new TCP socket to accept connections on a given port.
/// @param node The endpoint to listen on and the filter for incoming addresses.
/// Passing the address `0.0.0.0` will accept incoming connection from any host.
/// Passing port 0 lets the OS choose the port.
/// Passing the address `0.0.0.0` will accept incoming connection
/// from any host. Passing port 0 lets the OS choose the port.
/// @param reuse_addr Optionally sets the SO_REUSEADDR option on the socket.
/// @relates tcp_accept_socket
expected<tcp_accept_socket>
CAF_NET_EXPORT make_tcp_accept_socket(const uri::authority_type& node,
bool reuse_addr = false);
/// Creates a new TCP socket to accept connections on a given port.
/// @param port The port for listening to incoming connection. Passing 0 lets
/// the OS choose a port.
/// @param addr The filter for incoming addresses. Passing the address `0.0.0.0`
/// will accept incoming connection from any host.
/// @param reuse_addr Optionally sets the SO_REUSEADDR option on the socket.
/// @relates tcp_accept_socket
expected<tcp_accept_socket> CAF_NET_EXPORT make_tcp_accept_socket(
uint16_t port, std::string addr = "0.0.0.0", bool reuse_addr = false);
/// Accepts a connection on `x`.
/// @param x Listening endpoint.
/// @returns The socket that handles the accepted connection on success, an
......
......@@ -74,8 +74,6 @@ public:
ptrdiff_t consume(byte_span buffer, byte_span delta) override;
void continue_reading() override;
bool prepare_send() override;
bool done_sending() override;
......
......@@ -79,6 +79,10 @@ public:
}
};
bool running() const noexcept {
return in_ || out_;
}
// -- implementation of web_socket::lower_layer ------------------------------
error init(net::socket_manager* mgr, web_socket::lower_layer* down,
......@@ -86,38 +90,48 @@ public:
down_ = down;
auto [err, pull, push] = conn_->on_request(cfg);
if (!err) {
in_ = consumer_type::try_open(mgr, pull);
out_ = producer_type::try_open(mgr, push);
CAF_ASSERT(in_ != nullptr);
CAF_ASSERT(out_ != nullptr);
auto do_wakeup = make_action([this] {
prepare_send();
if (!running())
down_->close();
});
auto do_resume = make_action([this] { down_->request_messages(); });
auto do_cancel = make_action([this] {
out_ = nullptr;
if (!running())
down_->close();
});
in_ = consumer_type::make(pull.try_open(), mgr, std::move(do_wakeup));
out_ = producer_type::make(push.try_open(), mgr, std::move(do_resume),
std::move(do_cancel));
conn_ = nullptr;
if (running())
return none;
else
return make_error(sec::runtime_error,
"cannot init flow bridge: no buffers");
} else {
conn_ = nullptr;
return err;
}
}
void continue_reading() override {
// nop
}
bool prepare_send() override {
write_helper helper{this};
while (down_->can_send_more() && in_) {
auto [again, consumed] = in_->pull(async::delay_errors, 1, helper);
if (!again) {
if (helper.err) {
down_->send_close_message(helper.err);
down_->close(helper.err);
} else {
down_->send_close_message();
down_->close();
}
in_ = nullptr;
return false;
return true;
} else if (helper.aborted) {
in_->cancel();
in_ = nullptr;
return false;
return true;
} else if (consumed == 0) {
return true;
}
......
......@@ -77,11 +77,15 @@ public:
// -- web_socket::lower_layer implementation ---------------------------------
using web_socket::lower_layer::close;
bool can_send_more() const noexcept override;
void suspend_reading() override;
bool stopped_reading() const noexcept override;
bool is_reading() const noexcept override;
void close(status code, std::string_view desc) override;
void request_messages() override;
......@@ -97,17 +101,8 @@ public:
bool end_text_message() override;
void send_close_message() override;
void send_close_message(status code, std::string_view desc) override;
// -- interface for the lower layer ------------------------------------------
template <class LowerLayerPtr>
static void continue_reading(LowerLayerPtr down) {
down->configure_read(receive_policy::up_to(2048));
}
ptrdiff_t consume(byte_span input, byte_span);
private:
......@@ -117,10 +112,6 @@ private:
void ship_pong(byte_span payload);
void ship_close(uint16_t code, std::string_view msg);
void ship_close();
template <class T>
void ship_frame(std::vector<T>& buf);
......
......@@ -22,6 +22,9 @@ public:
/// Pulls messages from the transport until calling `suspend_reading`.
virtual void request_messages() = 0;
/// Stops reading messages until calling `request_messages`.
virtual void suspend_reading() = 0;
/// Begins transmission of a binary message.
virtual void begin_binary_message() = 0;
......@@ -42,14 +45,12 @@ public:
/// Seals the current text message buffer and ships a new WebSocket frame.
virtual bool end_text_message() = 0;
/// Sends the close message with @ref status `normal_close`.
virtual void send_close_message() = 0;
void close() override;
/// Sends the close message with an custom @ref status.
virtual void send_close_message(status code, std::string_view desc) = 0;
void close(const error& reason) override;
/// Sends the close message with @ref status `unexpected_condition`.
void send_close_message(const error& reason);
/// Sends a close message with custom @ref status @p code and @p msg text.
virtual void close(status code, std::string_view msg) = 0;
};
} // namespace caf::net::web_socket
......@@ -81,8 +81,6 @@ public:
ptrdiff_t consume(byte_span input, byte_span) override;
void continue_reading() override;
bool prepare_send() override;
bool done_sending() override;
......
......@@ -10,4 +10,8 @@ generic_lower_layer::~generic_lower_layer() {
// nop
}
void generic_lower_layer::close(const error&) {
close();
}
} // namespace caf::net
......@@ -4,7 +4,6 @@
#include "caf/net/http/lower_layer.hpp"
#include "caf/net/http/context.hpp"
#include "caf/net/http/header_fields_map.hpp"
#include "caf/net/http/status.hpp"
......@@ -19,14 +18,18 @@ lower_layer::~lower_layer() {
// nop
}
bool lower_layer::send_response(context ctx, status code,
std::string_view content_type,
bool lower_layer::send_response(status code, std::string_view content_type,
const_byte_span content) {
auto content_size = std::to_string(content.size());
header_fields_map fields;
fields.emplace("Content-Type"sv, content_type);
fields.emplace("Content-Length"sv, content_size);
return send_header(ctx, code, fields) && send_payload(ctx, content);
return send_header(code, fields) && send_payload(content);
}
bool lower_layer::send_response(status code, std::string_view content_type,
std::string_view content) {
return send_response(code, content_type, as_bytes(make_span(content)));
}
} // namespace caf::net::http
......@@ -14,23 +14,31 @@ bool server::can_send_more() const noexcept {
return down_->can_send_more();
}
void server::suspend_reading() {
return down_->suspend_reading();
bool server::is_reading() const noexcept {
return down_->is_reading();
}
bool server::stopped_reading() const noexcept {
return down_->stopped_reading();
void server::close() {
down_->close();
}
bool server::send_header(context, status code,
const header_fields_map& fields) {
void server::request_messages() {
if (!down_->is_reading())
down_->configure_read(receive_policy::up_to(max_request_size_));
}
void server::suspend_reading() {
down_->configure_read(receive_policy::stop());
}
bool server::send_header(status code, const header_fields_map& fields) {
down_->begin_output();
v1::write_header(code, fields, down_->output_buffer());
down_->end_output();
return true;
}
bool server::send_payload(context, const_byte_span bytes) {
bool server::send_payload(const_byte_span bytes) {
down_->begin_output();
auto& buf = down_->output_buffer();
buf.insert(buf.end(), bytes.begin(), bytes.end());
......@@ -38,7 +46,7 @@ bool server::send_payload(context, const_byte_span bytes) {
return true;
}
bool server::send_chunk(context, const_byte_span bytes) {
bool server::send_chunk(const_byte_span bytes) {
down_->begin_output();
auto& buf = down_->output_buffer();
auto size = bytes.size();
......@@ -60,20 +68,15 @@ bool server::send_end_of_chunks() {
return down_->end_output();
}
void server::fin(context) {
// nop
}
// -- stream_oriented::upper_layer implementation ------------------------------
error server::init(socket_manager* owner, stream_oriented::lower_layer* down,
const settings& cfg) {
down_ = down;
if (auto err = up_->init(owner, this, cfg))
return err;
if (auto max_size = get_as<uint32_t>(cfg, "http.max-request-size"))
max_request_size_ = *max_size;
down_->configure_read(receive_policy::up_to(max_request_size_));
if (auto err = up_->init(owner, this, cfg))
return err;
return none;
}
......@@ -89,10 +92,6 @@ bool server::done_sending() {
return up_->done_sending();
}
void server::continue_reading() {
down_->configure_read(receive_policy::up_to(max_request_size_));
}
ptrdiff_t server::consume(byte_span input, byte_span) {
using namespace literals;
CAF_LOG_TRACE(CAF_ARG2("bytes", input.size()));
......@@ -174,7 +173,7 @@ void server::write_response(status code, std::string_view content) {
}
bool server::invoke_upper_layer(const_byte_span payload) {
return up_->consume(context{}, hdr_, payload) >= 0;
return up_->consume(hdr_, payload) >= 0;
}
bool server::handle_header(std::string_view http) {
......
......@@ -36,7 +36,7 @@ void length_prefix_framing::abort(const error& reason) {
}
ptrdiff_t length_prefix_framing::consume(byte_span input, byte_span) {
CAF_LOG_TRACE("got" << input.size() << "bytes");
CAF_LOG_TRACE("got" << input.size() << "bytes\n");
if (input.size() < sizeof(uint32_t)) {
CAF_LOG_ERROR("received too few bytes from underlying transport");
up_->abort(make_error(sec::logic_error,
......@@ -65,7 +65,7 @@ ptrdiff_t length_prefix_framing::consume(byte_span input, byte_span) {
if (msg_size == msg.size() && msg_size + hdr_size == input.size()) {
CAF_LOG_DEBUG("got message of size" << msg_size);
if (up_->consume(msg) >= 0) {
if (!down_->stopped_reading())
if (down_->is_reading())
down_->configure_read(receive_policy::exactly(hdr_size));
return static_cast<ptrdiff_t>(input.size());
} else {
......@@ -79,10 +79,6 @@ ptrdiff_t length_prefix_framing::consume(byte_span input, byte_span) {
}
}
void length_prefix_framing::continue_reading() {
up_->continue_reading();
}
bool length_prefix_framing::prepare_send() {
return up_->prepare_send();
}
......@@ -98,15 +94,15 @@ bool length_prefix_framing::can_send_more() const noexcept {
}
void length_prefix_framing::suspend_reading() {
down_->suspend_reading();
down_->configure_read(receive_policy::stop());
}
bool length_prefix_framing::stopped_reading() const noexcept {
return down_->stopped_reading();
bool length_prefix_framing::is_reading() const noexcept {
return down_->is_reading();
}
void length_prefix_framing::request_messages() {
if (down_->stopped_reading())
if (!down_->is_reading())
down_->configure_read(receive_policy::exactly(hdr_size));
}
......@@ -139,12 +135,8 @@ bool length_prefix_framing::end_message() {
}
}
void length_prefix_framing::send_close_message() {
// nop: this layer has no close message
}
void length_prefix_framing::send_close_message(const error&) {
// nop: this layer has no close message
void length_prefix_framing::close() {
down_->close();
}
// -- utility functions ------------------------------------------------------
......
......@@ -7,6 +7,9 @@
#include "caf/actor_system_config.hpp"
#include "caf/detail/set_thread_name.hpp"
#include "caf/expected.hpp"
#include "caf/net/prometheus/serve.hpp"
#include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/tcp_stream_socket.hpp"
#include "caf/raise_error.hpp"
#include "caf/sec.hpp"
#include "caf/send.hpp"
......@@ -14,6 +17,40 @@
namespace caf::net {
namespace {
struct prom_config {
uint16_t port;
std::string address = "0.0.0.0";
bool reuse_address = false;
};
template <class Inspector>
bool inspect(Inspector& f, prom_config& x) {
return f.object(x).fields(
f.field("port", x.port), f.field("address", x.address).fallback("0.0.0.0"),
f.field("reuse-address", x.reuse_address).fallback(false));
}
void launch_prom_server(actor_system& sys, const prom_config& cfg) {
if (auto fd = make_tcp_accept_socket(cfg.port, cfg.address,
cfg.reuse_address)) {
CAF_LOG_INFO("start Prometheus server at port" << local_port(*fd));
prometheus::serve(sys, std::move(*fd));
} else {
CAF_LOG_WARNING("failed to start Prometheus server: " << fd.error());
}
}
void launch_background_tasks(actor_system& sys) {
auto& cfg = sys.config();
if (auto pcfg = get_as<prom_config>(cfg, "caf.middleman.prometheus-http")) {
launch_prom_server(sys, *pcfg);
}
}
} // namespace
void middleman::init_global_meta_objects() {
// nop
}
......@@ -33,6 +70,7 @@ void middleman::start() {
detail::set_thread_name("caf.net.mpx");
sys_.thread_started();
mpx_.set_thread_id();
launch_background_tasks(sys_);
mpx_.run();
sys_.thread_terminates();
}};
......@@ -89,6 +127,9 @@ void middleman::add_module_options(actor_system_config& cfg) {
"max. time between messages before declaring a node dead "
"(disabled if 0, ignored if heartbeats are disabled)")
.add<std::string>("network-backend", "legacy option");
config_option_adder{cfg.custom_options(), "caf.middleman.prometheus-http"}
.add<uint16_t>("port", "listening port for incoming scrapes")
.add<std::string>("address", "bind address for the HTTP server socket");
}
} // namespace caf::net
This diff is collapsed.
......@@ -31,11 +31,12 @@ std::unique_ptr<pollset_updater> pollset_updater::make(pipe_socket fd) {
error pollset_updater::init(socket_manager* owner, const settings&) {
CAF_LOG_TRACE("");
owner_ = owner;
mpx_ = owner->mpx_ptr();
return nonblocking(fd_, true);
}
pollset_updater::read_result pollset_updater::handle_read_event() {
void pollset_updater::handle_read_event() {
CAF_LOG_TRACE("");
auto as_mgr = [](intptr_t ptr) {
return intrusive_ptr{reinterpret_cast<socket_manager*>(ptr), false};
......@@ -56,23 +57,11 @@ pollset_updater::read_result pollset_updater::handle_read_event() {
intptr_t ptr;
memcpy(&ptr, buf_.data() + 1, sizeof(intptr_t));
switch (static_cast<code>(opcode)) {
case code::register_reading:
mpx_->do_register_reading(as_mgr(ptr));
break;
case code::continue_reading:
mpx_->do_continue_reading(as_mgr(ptr));
break;
case code::register_writing:
mpx_->do_register_writing(as_mgr(ptr));
break;
case code::continue_writing:
mpx_->do_continue_writing(as_mgr(ptr));
break;
case code::init_manager:
mpx_->do_init(as_mgr(ptr));
break;
case code::discard_manager:
mpx_->do_discard(as_mgr(ptr));
case code::dispose_manager:
mpx_->do_dispose(as_mgr(ptr));
break;
case code::shutdown_reading:
mpx_->do_shutdown_reading(as_mgr(ptr));
......@@ -94,25 +83,20 @@ pollset_updater::read_result pollset_updater::handle_read_event() {
}
} else if (num_bytes == 0) {
CAF_LOG_DEBUG("pipe closed, assume shutdown");
return read_result::stop;
owner_->shutdown();
return;
} else if (last_socket_error_is_temporary()) {
return read_result::again;
return;
} else {
return read_result::stop;
CAF_LOG_DEBUG("pollset updater failed to read from the pipe");
owner_->shutdown();
return;
}
}
}
pollset_updater::read_result pollset_updater::handle_buffered_data() {
return read_result::again;
}
pollset_updater::read_result pollset_updater::handle_continue_reading() {
return read_result::again;
}
pollset_updater::write_result pollset_updater::handle_write_event() {
return write_result::stop;
void pollset_updater::handle_write_event() {
owner_->deregister_writing();
}
void pollset_updater::abort(const error&) {
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/net/prometheus/server.hpp"
#include "caf/net/http/header.hpp"
#include "caf/net/http/lower_layer.hpp"
using namespace std::literals;
namespace caf::net::prometheus {
// -- server::scrape_state -----------------------------------------------------
std::string_view server::scrape_state::scrape() {
// Scrape system metrics at most once per second. TODO: make configurable.
if (auto now = std::chrono::steady_clock::now(); last_scrape + 1s <= now) {
last_scrape = now;
proc_importer.update();
}
return collector.collect_from(*registry);
}
// -- implementation of http::upper_layer --------------------------------------
bool server::prepare_send() {
return true;
}
bool server::done_sending() {
return true;
}
void server::abort(const error&) {
// nop
}
error server::init(socket_manager*, http::lower_layer* down, const settings&) {
down_ = down;
down_->request_messages();
return caf::none;
}
ptrdiff_t server::consume(const http::header& hdr, const_byte_span payload) {
if (hdr.path() != "/metrics") {
down_->send_response(http::status::not_found, "text/plain", "Not found.");
down_->close();
} else if (hdr.method() != http::method::get) {
down_->send_response(http::status::method_not_allowed, "text/plain",
"Method not allowed.");
down_->close();
} else if (!hdr.query().empty() || !hdr.fragment().empty()) {
down_->send_response(http::status::bad_request, "text/plain",
"No fragment or query allowed.");
down_->close();
} else {
auto str = state_->scrape();
down_->send_response(http::status::ok, "text/plain;version=0.0.4", str);
down_->close();
}
return static_cast<ptrdiff_t>(payload.size());
}
} // namespace caf::net::prometheus
......@@ -33,6 +33,49 @@ socket_manager_ptr socket_manager::make(multiplexer* mpx, socket handle,
return make_counted<socket_manager>(mpx, handle, std::move(handler));
}
namespace {
class disposer : public detail::atomic_ref_counted, public disposable_impl {
public:
disposer(multiplexer* mpx, socket_manager_ptr mgr)
: mpx_(mpx), mgr_(std::move(mgr)) {
// nop
}
void dispose() {
std::unique_lock guard{mtx_};
if (mpx_) {
mpx_->dispose(mgr_);
mpx_ = nullptr;
mgr_ = nullptr;
}
}
bool disposed() const noexcept {
std::unique_lock guard{mtx_};
return mpx_ == nullptr;
}
void ref_disposable() const noexcept {
ref();
}
void deref_disposable() const noexcept {
deref();
}
private:
mutable std::mutex mtx_;
multiplexer* mpx_;
socket_manager_ptr mgr_;
};
} // namespace
disposable socket_manager::make_disposer() {
return disposable{make_counted<disposer>(mpx_, this)};
}
// -- properties ---------------------------------------------------------------
actor_system& socket_manager::system() noexcept {
......@@ -40,22 +83,73 @@ actor_system& socket_manager::system() noexcept {
return mpx_->system();
}
bool socket_manager::is_reading() const noexcept {
return mpx_->is_reading(this);
}
bool socket_manager::is_writing() const noexcept {
return mpx_->is_writing(this);
}
// -- event loop management ----------------------------------------------------
void socket_manager::register_reading() {
if (!flags_.read_closed)
mpx_->register_reading(this);
}
void socket_manager::continue_reading() {
mpx_->continue_reading(this);
}
void socket_manager::register_writing() {
if (!flags_.write_closed)
mpx_->register_writing(this);
}
void socket_manager::continue_writing() {
mpx_->continue_writing(this);
void socket_manager::deregister_reading() {
mpx_->deregister_reading(this);
}
void socket_manager::deregister_writing() {
mpx_->deregister_writing(this);
}
void socket_manager::deregister() {
mpx_->deregister(this);
}
void socket_manager::shutdown_read() {
deregister_reading();
flags_.read_closed = true;
}
void socket_manager::shutdown_write() {
deregister_writing();
flags_.write_closed = true;
}
void socket_manager::shutdown() {
flags_.read_closed = true;
flags_.write_closed = true;
deregister();
}
// -- callbacks for the handler ------------------------------------------------
void socket_manager::schedule_handover() {
deregister();
mpx_->schedule_fn([ptr = strong_this()] { //
event_handler_ptr next;
if (ptr->handler_->do_handover(next)) {
ptr->handler_.swap(next);
}
});
}
void socket_manager::schedule(action what) {
// Wrap the action to make sure the socket manager is still alive when running
// the action later.
mpx_->schedule_fn([ptr = strong_this(), f = std::move(what)]() mutable {
CAF_IGNORE_UNUSED(ptr);
f.run();
});
}
// -- callbacks for the multiplexer --------------------------------------------
......@@ -70,16 +164,6 @@ void socket_manager::close_write() noexcept {
flags_.write_closed = true;
}
bool socket_manager::do_handover() {
event_handler_ptr next;
if (handler_->do_handover(next)) {
handler_.swap(next);
return true;
} else {
return false;
}
}
error socket_manager::init(const settings& cfg) {
CAF_LOG_TRACE(CAF_ARG(cfg));
if (auto err = nonblocking(fd_, true)) {
......@@ -89,32 +173,12 @@ error socket_manager::init(const settings& cfg) {
return handler_->init(this, cfg);
}
socket_manager::read_result socket_manager::handle_read_event() {
auto result = handler_->handle_read_event();
switch (result) {
default:
break;
case read_result::close:
flags_.read_closed = true;
break;
case read_result::abort:
flags_.read_closed = true;
flags_.write_closed = true;
break;
}
return result;
}
socket_manager::read_result socket_manager::handle_buffered_data() {
return handler_->handle_buffered_data();
}
socket_manager::read_result socket_manager::handle_continue_reading() {
return handler_->handle_continue_reading();
void socket_manager::handle_read_event() {
handler_->handle_read_event();
}
socket_manager::write_result socket_manager::handle_write_event() {
return handler_->handle_write_event();
void socket_manager::handle_write_event() {
handler_->handle_write_event();
}
void socket_manager::handle_error(sec code) {
......@@ -124,4 +188,10 @@ void socket_manager::handle_error(sec code) {
}
}
// -- utility functions --------------------------------------------------------
socket_manager_ptr socket_manager::strong_this() {
return socket_manager_ptr{this};
}
} // namespace caf::net
......@@ -26,10 +26,6 @@ public:
using super = socket_event_layer;
using read_result = typename super::read_result;
using write_result = typename super::write_result;
using upper_layer_ptr = transport::upper_layer_ptr;
handshake_worker(connection conn, bool is_server, upper_layer_ptr up)
......@@ -46,55 +42,56 @@ public:
return caf::none;
}
read_result handle_read_event() override {
void handle_read_event() override {
if (auto res = advance_handshake(); res > 0) {
return read_result::handover;
owner_->deregister();
owner_->schedule_handover();
} else if (res == 0) {
up_->abort(make_error(sec::connection_closed));
return read_result::stop;
owner_->deregister();
} else {
auto err = policy_.last_error(policy_.conn.fd(), res);
switch (err) {
case stream_transport_error::want_read:
case stream_transport_error::temporary:
return read_result::again;
break;
case stream_transport_error::want_write:
return read_result::want_write;
default:
owner_->deregister_reading();
owner_->register_writing();
break;
default: {
auto err = make_error(sec::cannot_connect_to_node,
policy_.conn.last_error_string(res));
up_->abort(std::move(err));
return read_result::stop;
}
owner_->deregister();
}
}
read_result handle_buffered_data() override {
return read_result::again;
}
read_result handle_continue_reading() override {
return read_result::again;
}
write_result handle_write_event() override {
void handle_write_event() override {
if (auto res = advance_handshake(); res > 0) {
return write_result::handover;
owner_->deregister();
owner_->schedule_handover();
return;
} else if (res == 0) {
up_->abort(make_error(sec::connection_closed));
return write_result::stop;
owner_->deregister();
} else {
switch (policy_.last_error(policy_.conn.fd(), res)) {
case stream_transport_error::want_write:
case stream_transport_error::temporary:
return write_result::again;
break;
case stream_transport_error::want_read:
return write_result::want_read;
default:
owner_->deregister_writing();
owner_->register_reading();
break;
default: {
auto err = make_error(sec::cannot_connect_to_node,
policy_.conn.last_error_string(res));
up_->abort(std::move(err));
return write_result::stop;
owner_->deregister();
}
}
}
}
......
This diff is collapsed.
......@@ -121,6 +121,14 @@ make_tcp_accept_socket(const uri::authority_type& node, bool reuse_addr) {
to_string(node));
}
expected<tcp_accept_socket>
make_tcp_accept_socket(uint16_t port, std::string addr, bool reuse_addr) {
uri::authority_type auth;
auth.port = port;
auth.host = std::move(addr);
return make_tcp_accept_socket(auth, reuse_addr);
}
expected<tcp_stream_socket> accept(tcp_accept_socket x) {
auto sock = ::accept(x.id, nullptr, nullptr);
if (sock == net::invalid_socket_id) {
......
......@@ -95,11 +95,6 @@ ptrdiff_t client::consume(byte_span buffer, byte_span delta) {
}
}
void client::continue_reading() {
if (handshake_completed())
upper_layer().continue_reading();
}
bool client::prepare_send() {
return handshake_completed() ? upper_layer().prepare_send() : true;
}
......
......@@ -23,15 +23,34 @@ bool framing::can_send_more() const noexcept {
}
void framing::suspend_reading() {
down_->suspend_reading();
down_->configure_read(receive_policy::stop());
}
bool framing::stopped_reading() const noexcept {
return down_->stopped_reading();
bool framing::is_reading() const noexcept {
return down_->is_reading();
}
void framing::close(status code, std::string_view msg) {
auto code_val = static_cast<uint16_t>(code);
uint32_t mask_key = 0;
byte_buffer payload;
payload.reserve(msg.size() + 2);
payload.push_back(static_cast<std::byte>((code_val & 0xFF00) >> 8));
payload.push_back(static_cast<std::byte>(code_val & 0x00FF));
for (auto c : msg)
payload.push_back(static_cast<std::byte>(c));
if (mask_outgoing_frames) {
mask_key = static_cast<uint32_t>(rng_());
detail::rfc6455::mask_data(mask_key, payload);
}
down_->begin_output();
detail::rfc6455::assemble_frame(detail::rfc6455::connection_close, mask_key,
payload, down_->output_buffer());
down_->end_output();
}
void framing::request_messages() {
if (down_->stopped_reading())
if (!down_->is_reading())
down_->configure_read(receive_policy::up_to(2048));
}
......@@ -61,14 +80,6 @@ bool framing::end_text_message() {
return true;
}
void framing::send_close_message() {
ship_close();
}
void framing::send_close_message(status code, std::string_view desc) {
ship_close(static_cast<uint16_t>(code), desc);
}
// -- interface for the lower layer --------------------------------------------
ptrdiff_t framing::consume(byte_span input, byte_span) {
......@@ -99,9 +110,6 @@ ptrdiff_t framing::consume(byte_span input, byte_span) {
// Wait for more data if necessary.
size_t frame_size = hdr_bytes + hdr.payload_len;
if (buffer.size() < frame_size) {
// Ask for more data unless the upper layer called suspend_reading.
if (!down_->stopped_reading())
down_->configure_read(receive_policy::up_to(2048));
down_->configure_read(receive_policy::exactly(frame_size));
return consumed;
}
......@@ -170,7 +178,7 @@ ptrdiff_t framing::consume(byte_span input, byte_span) {
buffer = buffer.subspan(frame_size);
if (buffer.empty()) {
// Ask for more data unless the upper layer called suspend_reading.
if (!down_->stopped_reading())
if (down_->is_reading())
down_->configure_read(receive_policy::up_to(2048));
return consumed + static_cast<ptrdiff_t>(frame_size);
}
......@@ -215,40 +223,6 @@ void framing::ship_pong(byte_span payload) {
down_->end_output();
}
void framing::ship_close(uint16_t code, std::string_view msg) {
uint32_t mask_key = 0;
byte_buffer payload;
payload.reserve(msg.size() + 2);
payload.push_back(static_cast<std::byte>((code & 0xFF00) >> 8));
payload.push_back(static_cast<std::byte>(code & 0x00FF));
for (auto c : msg)
payload.push_back(static_cast<std::byte>(c));
if (mask_outgoing_frames) {
mask_key = static_cast<uint32_t>(rng_());
detail::rfc6455::mask_data(mask_key, payload);
}
down_->begin_output();
detail::rfc6455::assemble_frame(detail::rfc6455::connection_close, mask_key,
payload, down_->output_buffer());
down_->end_output();
}
void framing::ship_close() {
uint32_t mask_key = 0;
std::byte payload[] = {// Error code 1000: normal close.
std::byte{0x03}, std::byte{0xE8},
// "EOF" string as goodbye message.
std::byte{'E'}, std::byte{'O'}, std::byte{'F'}};
if (mask_outgoing_frames) {
mask_key = static_cast<uint32_t>(rng_());
detail::rfc6455::mask_data(mask_key, payload);
}
down_->begin_output();
detail::rfc6455::assemble_frame(detail::rfc6455::connection_close, mask_key,
payload, down_->output_buffer());
down_->end_output();
}
template <class T>
void framing::ship_frame(std::vector<T>& buf) {
uint32_t mask_key = 0;
......
......@@ -13,8 +13,13 @@ lower_layer::~lower_layer() {
// nop
}
void lower_layer::send_close_message(const error& reason) {
send_close_message(status::unexpected_condition, to_string(reason));
void lower_layer::close() {
using namespace std::literals;
close(status::normal_close, "EOF"sv);
}
void lower_layer::close(const error& reason) {
close(status::unexpected_condition, to_string(reason));
}
} // namespace caf::net::web_socket
......@@ -74,11 +74,6 @@ ptrdiff_t server::consume(byte_span input, byte_span delta) {
}
}
void server::continue_reading() {
auto rp = receive_policy::up_to(handshake::max_http_size);
lower_layer().configure_read(rp);
}
bool server::prepare_send() {
return handshake_complete_ ? upper_layer().prepare_send() : true;
}
......
......@@ -19,8 +19,12 @@ bool mock_stream_transport::can_send_more() const noexcept {
return true;
}
void mock_stream_transport::suspend_reading() {
configure_read(net::receive_policy::stop());
bool mock_stream_transport::is_reading() const noexcept {
return max_read_size > 0;
}
void mock_stream_transport::close() {
// nop
}
void mock_stream_transport::configure_read(net::receive_policy policy) {
......@@ -40,10 +44,6 @@ bool mock_stream_transport::end_output() {
return true;
}
bool mock_stream_transport::stopped_reading() const noexcept {
return max_read_size == 0;
}
ptrdiff_t mock_stream_transport::handle_input() {
ptrdiff_t result = 0;
while (max_read_size > 0) {
......
......@@ -32,7 +32,9 @@ public:
bool can_send_more() const noexcept override;
void suspend_reading() override;
bool is_reading() const noexcept override;
void close() override;
void configure_read(caf::net::receive_policy policy) override;
......@@ -42,8 +44,6 @@ public:
bool end_output() override;
bool stopped_reading() const noexcept override;
// -- initialization ---------------------------------------------------------
caf::error init(const caf::settings& cfg) {
......
......@@ -65,10 +65,6 @@ public:
return !self->terminated();
}
void continue_reading() override {
CAF_FAIL("continue_reading called");
}
bool done_sending() override {
return self->try_block_mailbox();
}
......
......@@ -81,8 +81,9 @@ public:
net::stream_oriented::lower_layer* down_ptr,
const settings&) override {
down = down_ptr;
if (auto ptr = adapter_type::try_open(mgr, std::move(input))) {
adapter = std::move(ptr);
if (auto buf = input.try_open()) {
auto do_wakeup = make_action([this] { prepare_send(); });
adapter = adapter_type::make(std::move(buf), mgr, std::move(do_wakeup));
return none;
} else {
FAIL("unable to open the resource");
......@@ -137,10 +138,6 @@ public:
return done || !adapter->has_data();
}
void continue_reading() override {
CAF_FAIL("continue_reading called");
}
void abort(const error& reason) override {
MESSAGE("app::abort: " << reason);
}
......
......@@ -51,6 +51,7 @@ public:
error init(net::socket_manager*, net::http::lower_layer* down_ptr,
const settings&) override {
down = down_ptr;
down->request_messages();
return none;
}
......@@ -66,20 +67,15 @@ public:
return true;
}
ptrdiff_t consume(net::http::context ctx,
const net::http::header& request_hdr,
ptrdiff_t consume(const net::http::header& request_hdr,
const_byte_span body) override {
hdr = request_hdr;
auto content = "Hello world!"sv;
down->send_response(ctx, net::http::status::ok, "text/plain",
down->send_response(net::http::status::ok, "text/plain",
as_bytes(make_span(content)));
payload.assign(body.begin(), body.end());
return static_cast<ptrdiff_t>(body.size());
}
void continue_reading() override {
// nop
}
};
} // namespace
......
......@@ -45,10 +45,11 @@ public:
// nop
}
caf::error init(net::socket_manager*,
caf::error init(net::socket_manager* mgr_ptr,
net::message_oriented::lower_layer* down_ptr,
const settings&) override {
// Start reading immediately.
mgr = mgr_ptr;
down = down_ptr;
down->request_messages();
return none;
......@@ -66,23 +67,23 @@ public:
// nop
}
void continue_reading() override {
down->request_messages();
void continue_reading() {
mgr->schedule_fn([this] { down->request_messages(); });
}
ptrdiff_t consume(byte_span buf) override {
printf("app_t::consume %d\n", __LINE__);
auto printable = [](std::byte x) {
return ::isprint(static_cast<uint8_t>(x));
};
if (CHECK(std::all_of(buf.begin(), buf.end(), printable))) {
printf("app_t::consume %d\n", __LINE__);
auto str_buf = reinterpret_cast<char*>(buf.data());
inputs->emplace_back(std::string{str_buf, buf.size()});
printf("app_t::consume %d added %s\n", __LINE__, inputs->back().c_str());
MESSAGE("app: consumed " << inputs->back());
if constexpr (EnableSuspend)
if (inputs->back() == "pause")
if (inputs->back() == "pause") {
MESSAGE("app: suspend reading");
down->suspend_reading();
}
std::string response = "ok ";
response += std::to_string(inputs->size());
auto response_bytes = as_bytes(make_span(response));
......@@ -92,11 +93,12 @@ public:
CHECK(down->end_message());
return static_cast<ptrdiff_t>(buf.size());
} else {
printf("app_t::consume %d\n", __LINE__);
return -1;
}
}
net::socket_manager* mgr = nullptr;
net::message_oriented::lower_layer* down = nullptr;
shared_string_list inputs;
......@@ -157,7 +159,7 @@ SCENARIO("length-prefix framing reads data with 32-bit size headers") {
}
}
SCENARIO("calling suspend_reading removes message apps temporarily") {
SCENARIO("calling suspend_reading temporarily halts receiving of messages") {
using namespace std::literals;
GIVEN("a length_prefix_framing with an app that consumes strings") {
auto [fd1, fd2] = unbox(net::make_stream_socket_pair());
......@@ -185,6 +187,7 @@ SCENARIO("calling suspend_reading removes message apps temporarily") {
CAF_FAIL("nonblocking returned an error: " << err);
auto buf = std::make_shared<string_list>();
auto app = app_t<true>::make(buf);
auto app_ptr = app.get();
auto framing = net::length_prefix_framing::make(std::move(app));
auto transport = net::stream_transport::make(fd2, std::move(framing));
auto mgr = net::socket_manager::make(&mpx, fd2, std::move(transport));
......@@ -201,8 +204,8 @@ SCENARIO("calling suspend_reading removes message apps temporarily") {
CHECK_EQ(buf->at(1), "second");
CHECK_EQ(buf->at(2), "pause");
}
THEN("users can resume it via continue_reading ") {
mgr->continue_reading();
THEN("users can resume it manually") {
app_ptr->continue_reading();
mpx.apply_updates();
mpx.poll_once(true);
CHECK_EQ(mpx.mask_of(mgr), net::operation::read);
......
......@@ -63,53 +63,33 @@ public:
// -- implementation of socket_event_layer -----------------------------------
error init(net::socket_manager*, const settings&) override {
error init(net::socket_manager* mgr, const settings&) override {
mgr_ = mgr;
return none;
}
read_result handle_read_event() override {
// if (trigger_handover) {
// MESSAGE(name << " triggered a handover");
// return read_result::handover;
// }
void handle_read_event() override {
if (read_capacity() < 1024)
rd_buf_.resize(rd_buf_.size() + 2048);
auto num_bytes = read(fd_,
make_span(read_position_begin(), read_capacity()));
if (num_bytes > 0) {
CAF_ASSERT(num_bytes > 0);
rd_buf_pos_ += num_bytes;
return read_result::again;
} else if (num_bytes < 0 && net::last_socket_error_is_temporary()) {
return read_result::again;
} else {
return read_result::stop;
auto res = read(fd_, make_span(read_position_begin(), read_capacity()));
if (res > 0) {
CAF_ASSERT(res > 0);
rd_buf_pos_ += res;
} else if (res == 0 || !net::last_socket_error_is_temporary()) {
mgr_->deregister();
}
}
read_result handle_buffered_data() override {
return read_result::again;
}
read_result handle_continue_reading() override {
return read_result::again;
}
write_result handle_write_event() override {
// if (trigger_handover) {
// MESSAGE(name << " triggered a handover");
// return write_result::handover;
// }
void handle_write_event() override {
if (wr_buf_.size() == 0) {
mgr_->deregister_writing();
} else if (auto res = write(fd_, wr_buf_); res > 0) {
wr_buf_.erase(wr_buf_.begin(), wr_buf_.begin() + res);
if (wr_buf_.size() == 0)
return write_result::stop;
auto num_bytes = write(fd_, wr_buf_);
if (num_bytes > 0) {
wr_buf_.erase(wr_buf_.begin(), wr_buf_.begin() + num_bytes);
return wr_buf_.size() > 0 ? write_result::again : write_result::stop;
mgr_->deregister_writing();
} else if (res == 0 || !net::last_socket_error_is_temporary()) {
mgr_->deregister();
}
return num_bytes < 0 && net::last_socket_error_is_temporary()
? write_result::again
: write_result::stop;
}
void abort(const error& reason) override {
......@@ -130,7 +110,6 @@ private:
size_t read_capacity() const {
return rd_buf_.size() - rd_buf_pos_;
}
net::stream_socket fd_;
shared_count count_;
......@@ -140,6 +119,8 @@ private:
byte_buffer wr_buf_;
byte_buffer rd_buf_;
net::socket_manager* mgr_ = nullptr;
};
struct fixture {
......@@ -168,7 +149,9 @@ struct fixture {
make_manager(net::stream_socket fd, std::string name) {
auto mock = mock_event_layer::make(fd, std::move(name), manager_count);
auto mock_ptr = mock.get();
return {mock_ptr, net::socket_manager::make(&mpx, fd, std::move(mock))};
auto mgr = net::socket_manager::make(&mpx, fd, std::move(mock));
std::ignore = mgr->init(settings{});
return {mock_ptr, std::move(mgr)};
}
void init() {
......
......@@ -75,18 +75,19 @@ public:
const settings&) override {
down = down_ptr;
down->request_messages();
if (auto ptr = adapter_type::try_open(mgr, std::move(output_))) {
adapter_ = std::move(ptr);
if (auto buf = output_.try_open()) {
// Note: the execution unit is the owner of this object. As as long as the
// execution unit lives, accessing `this` is safe.
auto do_resume = make_action([this] { down->request_messages(); });
auto do_cancel = make_action([this] { down->close(); });
adapter_ = adapter_type::make(std::move(buf), mgr, std::move(do_resume),
std::move(do_cancel));
return none;
} else {
FAIL("unable to open the resource");
}
}
void continue_reading() override {
down->request_messages();
}
bool prepare_send() override {
return true;
}
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// prometheuss://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE net.prometheus.server
#include "caf/net/prometheus/server.hpp"
#include "net-test.hpp"
#include "caf/net/http/server.hpp"
#include "caf/telemetry/metric_registry.hpp"
using namespace caf;
using namespace caf::net;
using namespace std::literals;
constexpr std::string_view request_str
= "GET /metrics HTTP/1.1\r\n"
"Host: localhost:8090\r\n"
"User-Agent: Prometheus/2.18.1\r\n"
"Accept: text/plain;version=0.0.4\r\n"
"Accept-Encoding: gzip\r\n"
"X-Prometheus-Scrape-Timeout-Seconds: 5.000000\r\n\r\n";
SCENARIO("the Prometheus server responds to requests with scrape results") {
GIVEN("a valid Prometheus GET request") {
WHEN("sending it to an prometheus server") {
THEN("the Prometheus server responds with metrics text data") {
telemetry::metric_registry registry;
auto fb = registry.counter_singleton("foo", "bar", "test metric");
auto bf = registry.counter_singleton("bar", "foo", "test metric");
fb->inc(3);
bf->inc(7);
auto prom_state = prometheus::server::scrape_state::make(&registry);
auto prom_serv = prometheus::server::make(prom_state);
auto http_serv = net::http::server::make(std::move(prom_serv));
auto serv = mock_stream_transport::make(std::move(http_serv));
CHECK_EQ(serv->init(settings{}), error{});
serv->push(request_str);
CHECK_EQ(serv->handle_input(),
static_cast<ptrdiff_t>(request_str.size()));
auto out = serv->output_as_str();
CHECK(out.find("foo_bar 3"sv) != std::string_view::npos);
CHECK(out.find("bar_foo 7"sv) != std::string_view::npos);
}
}
}
}
......@@ -80,10 +80,6 @@ public:
return true;
}
void continue_reading() override {
// nop
}
ptrdiff_t consume(byte_span data, byte_span) override {
MESSAGE("dummy app received " << data.size() << " bytes");
// Store the received bytes.
......@@ -193,6 +189,7 @@ SCENARIO("ssl::transport::make_client performs the client handshake") {
THEN("CAF transparently calls SSL_connect") {
net::multiplexer mpx{nullptr};
mpx.set_thread_id();
std::ignore = mpx.init();
auto ctx = unbox(ssl::context::make_client(ssl::tls::any));
auto conn = unbox(ctx.new_connection(client_fd));
auto done = std::make_shared<bool>(false);
......@@ -230,6 +227,7 @@ SCENARIO("ssl::transport::make_server performs the server handshake") {
THEN("CAF transparently calls SSL_accept") {
net::multiplexer mpx{nullptr};
mpx.set_thread_id();
std::ignore = mpx.init();
auto ctx = unbox(ssl::context::make_server(ssl::tls::any));
REQUIRE(ctx.use_certificate_from_file(cert_1_pem_path, //
ssl::format::pem));
......
......@@ -93,10 +93,6 @@ public:
return static_cast<ptrdiff_t>(recv_buf_->size());
}
void continue_reading() override {
FAIL("continue_reading called");
}
bool prepare_send() override {
MESSAGE("prepare_send called");
auto& buf = down->output_buffer();
......
......@@ -65,10 +65,6 @@ public:
return !self->terminated();
}
void continue_reading() override {
CAF_FAIL("continue_reading called");
}
bool done_sending() override {
return self->try_block_mailbox();
}
......
......@@ -40,10 +40,6 @@ public:
return true;
}
void continue_reading() override {
// nop
}
void abort(const error& reason) override {
CAF_FAIL("app::abort called: " << reason);
}
......
......@@ -47,10 +47,6 @@ public:
return true;
}
void continue_reading() override {
// nop
}
void abort(const error& reason) override {
CAF_FAIL("app::abort called: " << reason);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment