25#include <flow/log/log.hpp>
28#include <boost/move/unique_ptr.hpp>
29#include <boost/shared_ptr.hpp>
138template<
typename Session_server_t>
140 public flow::log::Log_context,
141 public kj::TaskSet::ErrorHandler,
142 private boost::noncopyable
149 static_assert(Session_server_obj::Server_session_obj::S_SHM_ENABLED,
150 "Context_server facilitates the use of Flow-IPC zero-copy (SHM) facilities for "
151 "zero-copy performance in using capnp-RPC; therefore the supplied Session_server_t template "
152 "parameter must be a SHM-enabled Session_server variant, session::shm::*::Session_server.");
153 static_assert(Session_server_obj::Server_session_obj::Channel_obj::S_HAS_NATIVE_HANDLE_PIPE,
154 "Context_server, per its protocol with the opposing Client_context, receives a native handle -- "
155 "the stream-socket underlying each capnp-RPC conversation -- over the first init-channel; "
156 "therefore the supplied Session_server_t must be configured to emit Native_handle-bearing "
157 "channels (e.g., see aliases in session::shm::*::rpc namespaces).");
229 explicit Context_server(flow::log::Logger* logger_ptr, kj::AsyncIoContext* kj_io,
336 kj::Promise<Accept_result>
accept(
bool enable_hndl_transport =
true);
375 template<
typename N_init_channels_by_srv_req_func>
376 kj::Promise<Accept_result>
accept(
bool enable_hndl_transport,
377 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func);
431 template<
typename N_init_channels_by_srv_req_func,
typename Transport_method_func>
432 kj::Promise<Accept_result>
accept(
bool enable_hndl_transport,
433 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
434 Transport_method_func&& transport_method_func);
509template<
typename Session_server_t>
514 m_streaming_flow_window_default_ki(0),
516 m_session_server(get_logger(), srv_app_ref, cli_app_master_set_ref,
521 FLOW_LOG_INFO(
"rpc::Ctx_server [" << *
this <<
"]: "
522 "Created. ipc::session::Session_server [" <<
m_session_server <<
"] listening.");
525template<
typename Session_server_t>
528 FLOW_LOG_INFO(
"rpc::Ctx_server [" << *
this <<
"]: "
529 "Shutting down. ipc::session::Session_server shall shut down. "
530 "Note that if any previously spawned `Server_context`s still live, there may be trouble.");
533template<
typename Session_server_t>
534kj::Promise<typename Context_server<Session_server_t>::Accept_result>
537 return accept(enable_hndl_transport, [](
auto&&...) ->
size_t {
return 0; });
543template<
typename Session_server_t>
544template<
typename N_init_channels_by_srv_req_func>
545kj::Promise<typename Context_server<Session_server_t>::Accept_result>
547 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func)
549 return accept(enable_hndl_transport, std::move(n_init_channels_by_srv_req_func),
550 [](
auto&&...) {
return false; });
553template<
typename Session_server_t>
554template<
typename N_init_channels_by_srv_req_func,
typename Transport_method_func>
555kj::Promise<typename Context_server<Session_server_t>::Accept_result>
557 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
558 Transport_method_func&& transport_method_func)
562 using flow::util::ostream_op_string;
563 using kj::newPromiseAndFulfiller;
564 using kj::newPromiseAndCrossThreadFulfiller;
565 using kj::heapString;
566 using Kj_exception = kj::Exception;
567 using boost::make_shared;
568 using boost::shared_ptr;
569 using Channels =
decltype(Accept_result::m_init_channels_by_cli_req);
573 struct Session_accept_result
575 typename Session_server_obj::Server_session_obj m_target_session;
576 Channels m_init_channels_by_srv_req;
579 Channels m_init_channels_by_cli_req;
584 struct Transport_hndl_receive
586 typename Channels::value_type::Async_io_obj m_chan;
588 uint8_t m_protocol_dummy_payload;
592 ~Transport_hndl_receive() { m_chan =
decltype(m_chan){}; m_hndl.
close(); }
631 auto accept_result_paf = newPromiseAndFulfiller<Accept_result_ptr>();
637 auto thread_w_to_u_paf = newPromiseAndCrossThreadFulfiller<Error_code>();
638 auto session_accept_result = make_shared<Session_accept_result>();
640 m_session_server.async_accept(&session_accept_result->m_target_session,
641 &session_accept_result->m_init_channels_by_srv_req,
643 &session_accept_result->m_init_channels_by_cli_req,
647 [n_init_channels_by_srv_req_func = std::move(n_init_channels_by_srv_req_func)]
648 (
const Client_app& app,
size_t n_init_channels_by_cli_req,
auto&&) mutable ->
size_t
649 { return n_init_channels_by_srv_req_func(app, n_init_channels_by_cli_req); },
656 session_accept_result,
658 thread_w_to_u_fulfiller = make_shared<
decltype(thread_w_to_u_paf.fulfiller)>
659 (std::move(thread_w_to_u_paf.fulfiller))]
664 if (err_code == session::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER)
670 FLOW_LOG_TRACE(
"rpc::Ctx_server [" << *
this <<
"]: "
671 "In Session_server::async_accept() completion handler got result "
672 "[" << err_code <<
"] [" << err_code.message() <<
"]; pinging our promise continuation handler "
673 "back in the original thread's KJ event loop with this result.");
675 (*thread_w_to_u_fulfiller)->fulfill(
Error_code{err_code});
680 (thread_w_to_u_paf.promise
681 .then([
this, enable_hndl_transport,
682 transport_method_func = std::move(transport_method_func),
683 session_accept_result = std::move(session_accept_result),
684 accept_result_fulfiller = std::move(accept_result_paf.fulfiller)]
692 FLOW_LOG_WARNING(
"rpc::Ctx_server [" << *this <<
"]: "
693 "In async-accept promise continuation reacting to session-async-accept which resulted in "
694 "failure ([" << err_code <<
"] [" << err_code.message() <<
"]). Rejecting promise to "
695 "inform our original caller.");
698 accept_result_fulfiller->reject
699 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
700 heapString(ostream_op_string(
"rpc::Ctx_server [", *this,
701 "]: ipc::session::Session::async_accept() failed with code ([",
702 err_code,
"] [", err_code.message(),
"]).").c_str())});
707 auto& init_chans_cli = session_accept_result->m_init_channels_by_cli_req;
708 if (init_chans_cli.empty())
710 FLOW_LOG_WARNING(
"rpc::Ctx_server [" << *this <<
"]: "
711 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
712 "but opposing peer did not request even one init-channel, which a properly functioning "
713 "Client_context or equivalent must do by our protocol, so we can use the first such "
714 "channel to feed into Vat_network. Rejecting promise to inform our original caller.");
715 accept_result_fulfiller->reject
716 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
717 heapString(ostream_op_string(
"rpc::Ctx_server [", *this,
718 "]: ipc::session::Session::async_accept() succeeded; but opposing "
719 "peer did not request even one init-channel, which a properly "
720 "functioning Client_context or equivalent must do by our "
721 "protocol, so we can use the first such channel to feed "
722 "into Vat_network.").c_str())});
726 auto chan_for_rpc = std::move(init_chans_cli.front());
728 init_chans_cli.erase(init_chans_cli.begin(), ++init_chans_cli.begin());
742 const bool sans_shm_transport = transport_method_func(*(session_accept_result->m_target_session.client_app()));
744 auto rcv = make_shared<Transport_hndl_receive>();
745 rcv->m_chan = chan_for_rpc.async_io_obj();
747 auto hndl_w_to_u_paf = newPromiseAndCrossThreadFulfiller<Error_code>();
748 rcv->m_chan.hndl_rcv()->async_receive_native_handle
749 (&rcv->m_hndl,
Blob_mutable{&rcv->m_protocol_dummy_payload,
750 sizeof(Transport_hndl_receive::m_protocol_dummy_payload)},
752 [hndl_w_to_u_fulfiller = make_shared<
decltype(hndl_w_to_u_paf.fulfiller)>(std::move(hndl_w_to_u_paf.fulfiller))]
756 (*hndl_w_to_u_fulfiller)->fulfill(Error_code{err_code});
760 (hndl_w_to_u_paf.promise
761 .then([
this, enable_hndl_transport, sans_shm_transport,
763 session_accept_result = std::move(session_accept_result),
764 accept_result_fulfiller = std::move(accept_result_fulfiller)]
769 if (err_code || rcv->m_hndl.null())
771 FLOW_LOG_WARNING(
"rpc::Ctx_server [" << *this <<
"]: "
772 "In async-accept promise continuation: session-async-accept had succeeded; but receiving "
773 "the capnp-RPC transport handle over the client's first init-channel then failed "
774 "(code [" << err_code <<
"] [" << err_code.message() <<
"]; "
775 "null handle with no error would mean opposing peer violated protocol). "
776 "Rejecting promise to inform our original caller.");
777 accept_result_fulfiller->reject
778 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
779 heapString(ostream_op_string(
"rpc::Ctx_server [", *this,
780 "]: session-async-accept had succeeded; but receiving the "
781 "capnp-RPC transport handle over the client's first "
782 "init-channel then failed (code [",
785 "]; null handle with no error would mean "
786 "opposing peer violated protocol).").c_str())});
794 auto& init_chans_cli = session_accept_result->m_init_channels_by_cli_req;
795 auto& session = session_accept_result->m_target_session;
798 typename Server_context_obj::Ptr srv_context;
801 srv_context = Server_context_dtl::ct_base<Server_context_obj>(get_logger(), m_kj_io,
803 std::move(rcv->m_hndl),
804 enable_hndl_transport,
807 catch (
const Kj_exception& exc)
809 FLOW_LOG_WARNING(
"rpc::Ctx_server [" << *
this <<
"]: "
810 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
811 "zero-copy-enabled? = [" << (!sans_shm_transport) <<
"]; "
812 "however Server_context ctor (presumably the Session_vat_network part of it) "
813 "threw exception [" << exc.getDescription().cStr() <<
"]. Rejecting promise with "
814 "that exception to inform our original caller.");
815 accept_result_fulfiller->reject(Kj_exception{exc});
820 if (m_streaming_flow_window_default_ki != 0)
823 srv_context->vat_network()->streaming_flow_window_ki(m_streaming_flow_window_default_ki);
827 FLOW_LOG_INFO(
"rpc::Ctx_server [" << *
this <<
"]: "
828 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
829 "zero-copy-enabled? = [" << (!sans_shm_transport) <<
"]; "
830 "and we created the PEER-state Server_context with all the goodies (Vat_network et al). "
831 "Returning all results via fulfiller to inform original caller.");
834 accept_result_fulfiller
835 ->fulfill(Accept_result_ptr
837 { std::move(srv_context),
838 std::move(session_accept_result->m_init_channels_by_srv_req),
839 std::move(init_chans_cli) }});
847 auto accept_result_promise_fork = accept_result_paf.promise.fork();
848 m_kj_tasks.add(accept_result_promise_fork.addBranch().ignoreResult());
852 return accept_result_promise_fork.addBranch().then([](Accept_result_ptr&& accept_result_ptr)
854 return Accept_result{std::move(*accept_result_ptr)};
861template<
typename Session_server_t>
862const typename Context_server<Session_server_t>::Session_server_obj&
865 return m_session_server;
868template<
typename Session_server_t>
871 return m_streaming_flow_window_default_ki;
874template<
typename Session_server_t>
877 FLOW_LOG_TRACE(
"rpc::Ctx_server [" << *
this <<
"]: User setting streaming flow control window default "
878 "for subsequently accepted sessions to [" << limit_ki_or_0 <<
"Ki] (0 = leave each at its default); "
879 "replacing current setting [" << m_streaming_flow_window_default_ki <<
"Ki].");
880 m_streaming_flow_window_default_ki = limit_ki_or_0;
883template<
typename Session_server_t>
886 FLOW_LOG_WARNING(
"rpc::Ctx_server [" << *
this <<
"]: "
887 "A promise was rejected (presumably an error such as accept failure) -- either an internal "
888 "one, or one the user did not catch -- so we will just log it: "
889 "[" << exc.getDescription().cStr() <<
"].");
892template<
typename Session_server_t>
895 return os <<
"[session_srv[" << val.
session_server() <<
"]]@" << &val;
An object of this type facilitates the establishment, in server style, of zero-copy-enabled capnp-RPC...
boost::shared_ptr< Accept_result > Accept_result_ptr
Short-hand for copyable handle to Accept_result; needed in particular when a relevant kj::Promise mus...
kj::Promise< Accept_result > accept(bool enable_hndl_transport, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func, Transport_method_func &&transport_method_func)
Identical to accept() overload 2 but allows the potential capnp-RPC session to disable zero-copy tran...
size_t streaming_flow_window_default_ki() const
The value, if not zero, to which each Server_context produced by accept() shall have its vat_network(...
size_t m_streaming_flow_window_default_ki
See streaming_flow_window_default_ki().
kj::Promise< Accept_result > accept(bool enable_hndl_transport, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func)
Identical to other accept() except provides access to the server-side-requested-init-channels feature...
void taskFailed(kj::Exception &&exc) override
Implements kj::TaskSet::ErrorHandler API.
kj::Promise< Accept_result > accept(bool enable_hndl_transport=true)
Asynchronously waits for an opposing Client_context to request capnp-RPC session, returning a KJ Prom...
kj::TaskSet m_kj_tasks
Promises/promise chains to auto-cancel on destruction.
void streaming_flow_window_default_ki(size_t limit_ki_or_0)
Sets the value as returned by streaming_flow_window_default_ki() accessor.
Session_server_obj m_session_server
Session in NULL state (until sync_connect() succeeds) or PEER state (subsequently).
const Session_server_obj & session_server() const
Returns pointer to Session_server_obj.
Session_server_t Session_server_obj
Convenience alias for template arg.
Context_server(flow::log::Logger *logger_ptr, kj::AsyncIoContext *kj_io, const session::Server_app &srv_app_ref, const session::Client_app::Master_set &cli_app_master_set_ref)
Constructs us: immediately begins listening for incoming session-open attempts from allowed client ap...
~Context_server()
Destroys this acceptor which will stop listening in the background; as well as destroy certain cross-...
kj::AsyncIoContext *const m_kj_io
KJ event loop context.
The mirror of an opposing PEER-state Client_context, as generated via successful Context_server::acce...
boost::movelib::unique_ptr< Server_context > Ptr
Movable smart-pointer handle to a *this.
Provides Flow-IPC's integration with Cap'n Proto's Remote Procedure Call (RPC) layer,...
std::ostream & operator<<(std::ostream &os, const Capnp_message_builder< Shm_arena > &val)
Prints string representation of the given Capnp_message_builder to the given ostream.
boost::asio::mutable_buffer Blob_mutable
Short-hand for a mutable blob somewhere in memory, stored as exactly a void* and a size_t.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
An App that is used as a client in at least one client-server IPC split.
boost::unordered_map< std::string, Client_app > Master_set
Type for storing master repository of all Client_apps. See App doc header for discussion.
An App that is used as a server in at least one client-server IPC split.
Bundles the results of a successful accept(); until then all elements default-cted.
Session_server_obj::Channels m_init_channels_by_cli_req
Opened init-channels, numbering as many as the opposing client requested; may well be ....
Server_context_obj::Ptr m_context
The kj::Own-like handle to established Server_context opposite the other guy's PEER-state Client_cont...
Session_server_obj::Channels m_init_channels_by_srv_req
Opened init-channels, numbering as many as you requested (see accept() doc header); may well be ....
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
void close() noexcept
Little utility that returns m_native_handle to the OS.