Commit d6461dac authored by Dominik Charousset's avatar Dominik Charousset

Implement new DSL for HTTP servers

parent ebcddbb6
...@@ -107,7 +107,6 @@ else() ...@@ -107,7 +107,6 @@ else()
endfunction() endfunction()
endif() endif()
add_net_example(http secure-time-server)
add_net_example(http time-server) add_net_example(http time-server)
add_net_example(length_prefix_framing chat-client) add_net_example(length_prefix_framing chat-client)
......
// Simple HTTPS server that tells the time.
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/caf_main.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/net/http/serve.hpp"
#include "caf/net/middleman.hpp"
#include "caf/net/ssl/acceptor.hpp"
#include "caf/net/ssl/context.hpp"
#include "caf/net/stream_transport.hpp"
#include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/tcp_stream_socket.hpp"
#include "caf/scheduled_actor/flow.hpp"
#include <iostream>
#include <string>
#include <string_view>
#include <utility>
using namespace std::literals;
static constexpr uint16_t default_port = 8080;
struct config : caf::actor_system_config {
config() {
opt_group{custom_options_, "global"} //
.add<uint16_t>("port,p", "port to listen for incoming connections")
.add<std::string>("cert-file", "PEM server certificate file")
.add<std::string>("key-file", "PEM key file for the certificate");
}
};
int caf_main(caf::actor_system& sys, const config& cfg) {
using namespace caf;
// Sanity checking.
auto cert_file = get_or(cfg, "cert-file", ""sv);
auto key_file = get_or(cfg, "key-file", ""sv);
if (cert_file.empty()) {
std::cerr << "*** mandatory parameter 'cert-file' missing or empty\n";
return EXIT_FAILURE;
}
if (key_file.empty()) {
std::cerr << "*** mandatory parameter 'key-file' missing or empty\n";
return EXIT_FAILURE;
}
// Open up a TCP port for incoming connections.
auto port = caf::get_or(cfg, "port", default_port);
auto acc = net::ssl::acceptor::make_with_cert_file(port, cert_file, key_file);
if (!acc) {
std::cerr << "*** unable to initialize TLS: " << to_string(acc.error())
<< '\n';
return EXIT_FAILURE;
}
std::cout << "*** started listening for incoming connections on port " << port
<< '\n';
// Create buffers to signal events from the WebSocket server to the worker.
auto [worker_pull, server_push] = net::http::make_request_resource();
// Spin up the HTTP server.
net::http::serve(sys, std::move(*acc), std::move(server_push));
// Spin up a worker to handle the HTTP requests.
auto worker = sys.spawn([wres = worker_pull](event_based_actor* self) {
// For each incoming request ...
wres
.observe_on(self) //
.for_each([](const net::http::request& req) {
// ... we simply return the current time as string.
// Note: we cannot respond more than once to a request.
auto str = caf::deep_to_string(make_timestamp());
req.respond(net::http::status::ok, "text/plain", str);
});
});
sys.await_all_actors_done();
return EXIT_SUCCESS;
}
CAF_MAIN(caf::net::middleman)
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include "caf/caf_main.hpp" #include "caf/caf_main.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/net/http/serve.hpp" #include "caf/net/http/with.hpp"
#include "caf/net/middleman.hpp" #include "caf/net/middleman.hpp"
#include "caf/net/stream_transport.hpp" #include "caf/net/stream_transport.hpp"
#include "caf/net/tcp_accept_socket.hpp" #include "caf/net/tcp_accept_socket.hpp"
...@@ -16,47 +16,76 @@ ...@@ -16,47 +16,76 @@
#include <string> #include <string>
#include <utility> #include <utility>
// -- constants ----------------------------------------------------------------
static constexpr uint16_t default_port = 8080; static constexpr uint16_t default_port = 8080;
static constexpr size_t default_max_connections = 128;
// -- configuration ------------------------------------------------------------
struct config : caf::actor_system_config { struct config : caf::actor_system_config {
config() { config() {
opt_group{custom_options_, "global"} // opt_group{custom_options_, "global"} //
.add<uint16_t>("port,p", "port to listen for incoming connections"); .add<uint16_t>("port,p", "port to listen for incoming connections")
.add<size_t>("max-connections,m", "limit for concurrent clients");
opt_group{custom_options_, "tls"} //
.add<std::string>("key-file,k", "path to the private key file")
.add<std::string>("cert-file,c", "path to the certificate file");
} }
}; };
// -- main ---------------------------------------------------------------------
int caf_main(caf::actor_system& sys, const config& cfg) { int caf_main(caf::actor_system& sys, const config& cfg) {
using namespace caf; namespace http = caf::net::http;
// Open up a TCP port for incoming connections. namespace ssl = caf::net::ssl;
// Read the configuration.
auto port = caf::get_or(cfg, "port", default_port); auto port = caf::get_or(cfg, "port", default_port);
net::tcp_accept_socket fd; auto pem = ssl::format::pem;
if (auto maybe_fd = net::make_tcp_accept_socket({ipv4_address{}, port}, auto key_file = caf::get_as<std::string>(cfg, "tls.key-file");
true)) { auto cert_file = caf::get_as<std::string>(cfg, "tls.cert-file");
std::cout << "*** started listening for incoming connections on port " auto max_connections = caf::get_or(cfg, "max-connections",
<< port << '\n'; default_max_connections);
fd = std::move(*maybe_fd); if (!key_file != !cert_file) {
} else { std::cerr << "*** inconsistent TLS config: declare neither file or both\n";
std::cerr << "*** unable to open port " << port << ": " return EXIT_FAILURE;
<< to_string(maybe_fd.error()) << '\n'; }
// Open up a TCP port for incoming connections and start the server.
auto server
= caf::net::http::with(sys)
// Optionally enable TLS.
.context(ssl::context::enable(key_file && cert_file)
.and_then(ssl::emplace_server(ssl::tls::v1_2))
.and_then(ssl::use_private_key_file(key_file, pem))
.and_then(ssl::use_certificate_file(cert_file, pem)))
// Bind to the user-defined port.
.accept(port)
// Limit how many clients may be connected at any given time.
.max_connections(max_connections)
// When started, run our worker actor to handle incoming connections.
.start([&sys](auto requests) {
// Note: requests is an async::consumer_resource<http::request>.
sys.spawn([requests](caf::event_based_actor* self) {
// For each incoming HTTP request ...
requests
.observe_on(self) //
.for_each([](const http::request& req) {
// ... respond with the current time as string.
auto str = caf::deep_to_string(caf::make_timestamp());
req.respond(http::status::ok, "text/plain", str);
// Note: we cannot respond more than once to a request.
});
});
});
// Report any error to the user.
if (!server) {
std::cerr << "*** unable to run at port " << port << ": "
<< to_string(server.error()) << '\n';
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Create buffers to signal events from the WebSocket server to the worker. // Note: the actor system will keep the application running for as long as the
auto [worker_pull, server_push] = net::http::make_request_resource(); // workers are still alive.
// Spin up the HTTP server.
net::http::serve(sys, fd, std::move(server_push));
// Spin up a worker to handle the HTTP requests.
auto worker = sys.spawn([wres = worker_pull](event_based_actor* self) {
// For each incoming request ...
wres
.observe_on(self) //
.for_each([](const net::http::request& req) {
// ... we simply return the current time as string.
// Note: we cannot respond more than once to a request.
auto str = caf::deep_to_string(make_timestamp());
req.respond(net::http::status::ok, "text/plain", str);
});
});
sys.await_all_actors_done();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
......
...@@ -47,9 +47,8 @@ caf_add_component( ...@@ -47,9 +47,8 @@ caf_add_component(
src/net/http/method.cpp src/net/http/method.cpp
src/net/http/request.cpp src/net/http/request.cpp
src/net/http/response.cpp src/net/http/response.cpp
src/net/http/serve.cpp
src/net/http/serve.cpp
src/net/http/server.cpp src/net/http/server.cpp
src/net/http/server_factory.cpp
src/net/http/status.cpp src/net/http/status.cpp
src/net/http/upper_layer.cpp src/net/http/upper_layer.cpp
src/net/http/v1.cpp src/net/http/v1.cpp
......
// 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/async/spsc_buffer.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/fwd.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/http/request.hpp"
#include "caf/net/ssl/fwd.hpp"
#include <memory>
#include <type_traits>
namespace caf::net::http {
/// Convenience function for creating async resources for connecting the HTTP
/// server to a worker.
inline auto make_request_resource() {
return async::make_spsc_buffer_resource<request>();
}
/// Listens for incoming HTTP requests.
/// @param sys The host system.
/// @param fd An accept socket in listening mode, already bound to a port.
/// @param out A buffer resource that connects the server to a listener that
/// processes the requests.
/// @param cfg Optional configuration parameters for the HTTP layer.
disposable CAF_NET_EXPORT serve(actor_system& sys, tcp_accept_socket fd,
async::producer_resource<request> out,
const settings& cfg = {});
/// Listens for incoming HTTPS requests.
/// @param sys The host system.
/// @param acc An SSL connection acceptor with a socket that in listening mode.
/// @param out A buffer resource that connects the server to a listener that
/// processes the requests.
/// @param cfg Optional configuration parameters for the HTTP layer.
disposable CAF_NET_EXPORT serve(actor_system& sys, ssl::acceptor acc,
async::producer_resource<request> out,
const settings& cfg = {});
} // namespace caf::net::http
...@@ -2,29 +2,34 @@ ...@@ -2,29 +2,34 @@
// the main distribution directory for license terms and copyright or visit // the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE. // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/net/http/serve.hpp" #pragma once
#include "caf/async/blocking_producer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/accept_handler.hpp" #include "caf/detail/accept_handler.hpp"
#include "caf/detail/binary_flow_bridge.hpp"
#include "caf/detail/connection_factory.hpp" #include "caf/detail/connection_factory.hpp"
#include "caf/logger.hpp" #include "caf/detail/net_export.hpp"
#include "caf/net/http/lower_layer.hpp" #include "caf/detail/shared_ssl_acceptor.hpp"
#include "caf/fwd.hpp"
#include "caf/net/checked_socket.hpp"
#include "caf/net/dsl/server_factory_base.hpp"
#include "caf/net/http/request.hpp"
#include "caf/net/http/server.hpp" #include "caf/net/http/server.hpp"
#include "caf/net/http/upper_layer.hpp" #include "caf/net/http/upper_layer.hpp"
#include "caf/net/middleman.hpp" #include "caf/net/multiplexer.hpp"
#include "caf/net/ssl/acceptor.hpp"
#include "caf/net/ssl/transport.hpp" #include "caf/net/ssl/transport.hpp"
#include "caf/net/stream_transport.hpp" #include "caf/net/stream_transport.hpp"
#include "caf/net/tcp_accept_socket.hpp" #include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/tcp_stream_socket.hpp" #include "caf/none.hpp"
namespace caf::detail { #include <cstdint>
#include <memory>
// TODO: there is currently no back-pressure from the worker to the server.
// -- http_request_producer ---------------------------------------------------- namespace caf::detail {
class http_request_producer : public atomic_ref_counted, class CAF_NET_EXPORT http_request_producer : public atomic_ref_counted,
public async::producer { public async::producer {
public: public:
using buffer_ptr = async::spsc_buffer_ptr<net::http::request>; using buffer_ptr = async::spsc_buffer_ptr<net::http::request>;
...@@ -38,29 +43,17 @@ public: ...@@ -38,29 +43,17 @@ public:
return ptr; return ptr;
} }
void on_consumer_ready() override { void on_consumer_ready() override;
// nop
}
void on_consumer_cancel() override { void on_consumer_cancel() override;
// nop
}
void on_consumer_demand(size_t) override { void on_consumer_demand(size_t) override;
// nop
}
void ref_producer() const noexcept override { void ref_producer() const noexcept override;
ref();
}
void deref_producer() const noexcept override { void deref_producer() const noexcept override;
deref();
}
bool push(const net::http::request& item) { bool push(const net::http::request& item);
return buf_->push(item);
}
private: private:
buffer_ptr buf_; buffer_ptr buf_;
...@@ -68,9 +61,7 @@ private: ...@@ -68,9 +61,7 @@ private:
using http_request_producer_ptr = intrusive_ptr<http_request_producer>; using http_request_producer_ptr = intrusive_ptr<http_request_producer>;
// -- http_flow_adapter -------------------------------------------------------- class CAF_NET_EXPORT http_flow_adapter : public net::http::upper_layer {
class http_flow_adapter : public net::http::upper_layer {
public: public:
explicit http_flow_adapter(async::execution_context_ptr loop, explicit http_flow_adapter(async::execution_context_ptr loop,
http_request_producer_ptr ptr) http_request_producer_ptr ptr)
...@@ -78,56 +69,16 @@ public: ...@@ -78,56 +69,16 @@ public:
// nop // nop
} }
void prepare_send() override { void prepare_send() override;
// nop
}
bool done_sending() override { bool done_sending() override;
return true;
}
void abort(const error&) override { void abort(const error&) override;
for (auto& pending : pending_)
pending.dispose();
}
error start(net::http::lower_layer* down) override { error start(net::http::lower_layer* down) override;
down_ = down;
down_->request_messages();
return none;
}
ptrdiff_t consume(const net::http::header& hdr, ptrdiff_t consume(const net::http::header& hdr,
const_byte_span payload) override { const_byte_span payload) override;
using namespace net::http;
if (!pending_.empty()) {
CAF_LOG_WARNING("received multiple requests from the same HTTP client: "
"not implemented yet (drop request)");
return static_cast<ptrdiff_t>(payload.size());
}
auto prom = async::promise<response>();
auto fut = prom.get_future();
auto buf = std::vector<std::byte>{payload.begin(), payload.end()};
auto impl = request::impl{hdr, std::move(buf), std::move(prom)};
producer_->push(request{std::make_shared<request::impl>(std::move(impl))});
auto hdl = fut.bind_to(*loop_).then(
[this](const response& res) {
down_->begin_header(res.code());
for (auto& [key, val] : res.header_fields())
down_->add_header_field(key, val);
std::ignore = down_->end_header();
down_->send_payload(res.body());
down_->shutdown();
},
[this](const error& err) {
auto description = to_string(err);
down_->send_response(status::internal_server_error, "text/plain",
description);
down_->shutdown();
});
pending_.emplace_back(std::move(hdl));
return static_cast<ptrdiff_t>(payload.size());
}
static auto make(async::execution_context_ptr loop, static auto make(async::execution_context_ptr loop,
http_request_producer_ptr ptr) { http_request_producer_ptr ptr) {
...@@ -141,16 +92,16 @@ private: ...@@ -141,16 +92,16 @@ private:
http_request_producer_ptr producer_; http_request_producer_ptr producer_;
}; };
// -- http_conn_factory --------------------------------------------------------
template <class Transport> template <class Transport>
class http_conn_factory class http_conn_factory
: public connection_factory<typename Transport::connection_handle> { : public connection_factory<typename Transport::connection_handle> {
public: public:
using connection_handle = typename Transport::connection_handle; using connection_handle = typename Transport::connection_handle;
explicit http_conn_factory(http_request_producer_ptr producer) http_conn_factory(http_request_producer_ptr producer,
: producer_(std::move(producer)) { size_t max_consecutive_reads)
: producer_(std::move(producer)),
max_consecutive_reads_(max_consecutive_reads) {
// nop // nop
} }
...@@ -160,6 +111,7 @@ public: ...@@ -160,6 +111,7 @@ public:
auto serv = net::http::server::make(std::move(app)); auto serv = net::http::server::make(std::move(app));
auto fd = conn.fd(); auto fd = conn.fd();
auto transport = Transport::make(std::move(conn), std::move(serv)); auto transport = Transport::make(std::move(conn), std::move(serv));
transport->max_consecutive_reads(max_consecutive_reads_);
transport->active_policy().accept(fd); transport->active_policy().accept(fd);
auto res = net::socket_manager::make(mpx, std::move(transport)); auto res = net::socket_manager::make(mpx, std::move(transport));
mpx->watch(res->as_disposable()); mpx->watch(res->as_disposable());
...@@ -168,44 +120,81 @@ public: ...@@ -168,44 +120,81 @@ public:
private: private:
http_request_producer_ptr producer_; http_request_producer_ptr producer_;
size_t max_consecutive_reads_;
}; };
// -- http_serve_impl ----------------------------------------------------------
template <class Transport, class Acceptor>
disposable http_serve_impl(actor_system& sys, Acceptor acc,
async::producer_resource<net::http::request> out,
const settings& cfg = {}) {
using factory_t = http_conn_factory<Transport>;
using impl_t = accept_handler<Acceptor>;
auto max_conn = get_or(cfg, defaults::net::max_connections);
if (auto buf = out.try_open()) {
auto& mpx = sys.network_manager().mpx();
auto producer = http_request_producer::make(std::move(buf));
auto factory = std::make_unique<factory_t>(std::move(producer));
auto impl = impl_t::make(std::move(acc), std::move(factory), max_conn);
auto ptr = net::socket_manager::make(&mpx, std::move(impl));
mpx.start(ptr);
return ptr->as_disposable();
} else {
return disposable{};
}
}
} // namespace caf::detail } // namespace caf::detail
namespace caf::net::http { namespace caf::net::http {
disposable serve(actor_system& sys, tcp_accept_socket fd, /// Factory type for the `with(...).accept(...).start(...)` DSL.
async::producer_resource<request> out, const settings& cfg) { class server_factory
return detail::http_serve_impl<stream_transport>(sys, fd, std::move(out), : public dsl::server_factory_base<dsl::config_base, server_factory> {
cfg); public:
} using super = dsl::server_factory_base<dsl::config_base, server_factory>;
disposable serve(actor_system& sys, ssl::acceptor acc, using config_type = typename super::config_type;
async::producer_resource<request> out, const settings& cfg) {
return detail::http_serve_impl<ssl::transport>(sys, std::move(acc), using super::super;
std::move(out), cfg);
} /// Starts a server that accepts incoming connections with the
/// length-prefixing protocol.
template <class OnStart>
[[nodiscard]] expected<disposable> start(OnStart on_start) {
using consumer_resource = async::consumer_resource<request>;
static_assert(std::is_invocable_v<OnStart, consumer_resource>);
auto& cfg = super::config();
return cfg.visit([this, &cfg, &on_start](auto& data) {
return this->do_start(cfg, data, on_start)
.or_else([&cfg](const error& err) { cfg.call_on_error(err); });
});
}
private:
template <class Acceptor, class OnStart>
expected<disposable>
do_start_impl(config_type& cfg, Acceptor acc, OnStart& on_start) {
using transport_t = typename Acceptor::transport_type;
using factory_t = detail::http_conn_factory<transport_t>;
using impl_t = detail::accept_handler<Acceptor>;
auto [pull, push] = async::make_spsc_buffer_resource<request>();
auto producer = detail::http_request_producer::make(push.try_open());
auto factory = std::make_unique<factory_t>(std::move(producer),
cfg.max_consecutive_reads);
auto impl = impl_t::make(std::move(acc), std::move(factory),
cfg.max_connections);
auto impl_ptr = impl.get();
auto ptr = net::socket_manager::make(cfg.mpx, std::move(impl));
impl_ptr->self_ref(ptr->as_disposable());
cfg.mpx->start(ptr);
on_start(std::move(pull));
return expected<disposable>{disposable{std::move(ptr)}};
}
template <class OnStart>
expected<disposable> do_start(config_type& cfg,
dsl::server_config::socket& data,
OnStart& on_start) {
return checked_socket(data.take_fd())
.and_then(data.acceptor_with_ctx([this, &cfg, &on_start](auto& acc) {
return this->do_start_impl(cfg, std::move(acc), on_start);
}));
}
template <class OnStart>
expected<disposable> do_start(config_type& cfg,
dsl::server_config::lazy& data,
OnStart& on_start) {
return make_tcp_accept_socket(data.port, data.bind_address, data.reuse_addr)
.and_then(data.acceptor_with_ctx([this, &cfg, &on_start](auto& acc) {
return this->do_start_impl(cfg, std::move(acc), on_start);
}));
}
template <class OnStart>
expected<disposable> do_start(config_type&, error& err, OnStart&) {
return expected<disposable>{std::move(err)};
}
};
} // namespace caf::net::http } // namespace caf::net::http
// 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/dsl/base.hpp"
#include "caf/net/dsl/generic_config.hpp"
#include "caf/net/dsl/has_accept.hpp"
#include "caf/net/dsl/has_connect.hpp"
#include "caf/net/dsl/has_context.hpp"
#include "caf/net/http/request.hpp"
#include "caf/net/http/server_factory.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/ssl/acceptor.hpp"
#include "caf/net/ssl/context.hpp"
#include "caf/net/tcp_accept_socket.hpp"
#include <cstdint>
namespace caf::net::http {
/// Entry point for the `with(...)` DSL.
class with_t : public extend<dsl::base, with_t>:: //
with<dsl::has_accept, dsl::has_context> {
public:
using config_type = dsl::generic_config_value<dsl::config_base>;
template <class... Ts>
explicit with_t(multiplexer* mpx) : config_(config_type::make(mpx)) {
// nop
}
with_t(with_t&&) noexcept = default;
with_t(const with_t&) noexcept = default;
with_t& operator=(with_t&&) noexcept = default;
with_t& operator=(const with_t&) noexcept = default;
/// @private
config_type& config() {
return *config_;
}
/// @private
template <class T, class... Ts>
auto make(dsl::server_config_tag<T> token, Ts&&... xs) {
return server_factory{token, *config_, std::forward<Ts>(xs)...};
}
private:
intrusive_ptr<config_type> config_;
};
inline with_t with(actor_system& sys) {
return with_t{multiplexer::from(sys)};
}
inline with_t with(multiplexer* mpx) {
return with_t{mpx};
}
} // namespace caf::net::http
#include "caf/net/http/server_factory.hpp"
namespace caf::detail {
// TODO: there is currently no back-pressure from the worker to the server.
// -- http_request_producer ----------------------------------------------------
void http_request_producer::on_consumer_ready() {
// nop
}
void http_request_producer::on_consumer_cancel() {
// nop
}
void http_request_producer::on_consumer_demand(size_t) {
// nop
}
void http_request_producer::ref_producer() const noexcept {
ref();
}
void http_request_producer::deref_producer() const noexcept {
deref();
}
bool http_request_producer::push(const net::http::request& item) {
return buf_->push(item);
}
// -- http_flow_adapter --------------------------------------------------------
void http_flow_adapter::prepare_send() {
// nop
}
bool http_flow_adapter::done_sending() {
return true;
}
void http_flow_adapter::abort(const error&) {
for (auto& pending : pending_)
pending.dispose();
}
error http_flow_adapter::start(net::http::lower_layer* down) {
down_ = down;
down_->request_messages();
return none;
}
ptrdiff_t http_flow_adapter::consume(const net::http::header& hdr,
const_byte_span payload) {
using namespace net::http;
if (!pending_.empty()) {
CAF_LOG_WARNING("received multiple requests from the same HTTP client: "
"not implemented yet (drop request)");
return static_cast<ptrdiff_t>(payload.size());
}
auto prom = async::promise<response>();
auto fut = prom.get_future();
auto buf = std::vector<std::byte>{payload.begin(), payload.end()};
auto impl = request::impl{hdr, std::move(buf), std::move(prom)};
producer_->push(request{std::make_shared<request::impl>(std::move(impl))});
auto hdl = fut.bind_to(*loop_).then(
[this](const response& res) {
down_->begin_header(res.code());
for (auto& [key, val] : res.header_fields())
down_->add_header_field(key, val);
std::ignore = down_->end_header();
down_->send_payload(res.body());
down_->shutdown();
},
[this](const error& err) {
auto description = to_string(err);
down_->send_response(status::internal_server_error, "text/plain",
description);
down_->shutdown();
});
pending_.emplace_back(std::move(hdl));
return static_cast<ptrdiff_t>(payload.size());
}
} // namespace caf::detail
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