// 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. #define CAF_SUITE endpoint_manager #include "caf/net/endpoint_manager.hpp" #include "caf/net/test/host_fixture.hpp" #include "caf/test/dsl.hpp" #include "caf/binary_deserializer.hpp" #include "caf/binary_serializer.hpp" #include "caf/byte_buffer.hpp" #include "caf/detail/scope_guard.hpp" #include "caf/make_actor.hpp" #include "caf/net/actor_proxy_impl.hpp" #include "caf/net/make_endpoint_manager.hpp" #include "caf/net/multiplexer.hpp" #include "caf/net/stream_socket.hpp" #include "caf/node_id.hpp" #include "caf/span.hpp" using namespace caf; using namespace caf::net; namespace { using byte_buffer_ptr = std::shared_ptr; string_view hello_manager{"hello manager!"}; string_view hello_test{"hello test!"}; struct fixture : test_coordinator_fixture<>, host_fixture { fixture() { mpx = std::make_shared(); mpx->set_thread_id(); if (auto err = mpx->init()) CAF_FAIL("mpx->init failed: " << err); if (mpx->num_socket_managers() != 1) CAF_FAIL("mpx->num_socket_managers() != 1"); } bool handle_io_event() override { return mpx->poll_once(false); } multiplexer_ptr mpx; }; class dummy_application { // nop }; class dummy_transport { public: using application_type = dummy_application; dummy_transport(stream_socket handle, byte_buffer_ptr data) : handle_(handle), data_(data), read_buf_(1024) { // nop } stream_socket handle() { return handle_; } template error init(Manager& manager) { auto test_bytes = as_bytes(make_span(hello_test)); buf_.insert(buf_.end(), test_bytes.begin(), test_bytes.end()); manager.register_writing(); return none; } template bool handle_read_event(Manager&) { auto num_bytes = read(handle_, read_buf_); if (num_bytes > 0) { data_->insert(data_->end(), read_buf_.begin(), read_buf_.begin() + num_bytes); return true; } return num_bytes < 0 && last_socket_error_is_temporary(); } template bool handle_write_event(Manager& mgr) { for (auto x = mgr.next_message(); x != nullptr; x = mgr.next_message()) { binary_serializer sink{mgr.system(), buf_}; if (auto err = sink(x->msg->payload)) CAF_FAIL("serializing failed: " << err); } auto num_bytes = write(handle_, buf_); if (num_bytes > 0) { buf_.erase(buf_.begin(), buf_.begin() + num_bytes); return buf_.size() > 0; } return num_bytes < 0 && last_socket_error_is_temporary(); } void handle_error(sec) { // nop } template void resolve(Manager& mgr, const uri& locator, const actor& listener) { actor_id aid = 42; auto hid = string_view("0011223344556677889900112233445566778899"); auto nid = unbox(make_node_id(42, hid)); actor_config cfg; auto p = make_actor(aid, nid, &mgr.system(), cfg, &mgr); std::string path{locator.path().begin(), locator.path().end()}; anon_send(listener, resolve_atom_v, std::move(path), p); } template void timeout(Manager&, const std::string&, uint64_t) { // nop } template void new_proxy(Parent&, const node_id&, actor_id) { // nop } template void local_actor_down(Parent&, const node_id&, actor_id, error) { // nop } private: stream_socket handle_; byte_buffer_ptr data_; byte_buffer read_buf_; byte_buffer buf_; }; } // namespace CAF_TEST_FIXTURE_SCOPE(endpoint_manager_tests, fixture) CAF_TEST(send and receive) { byte_buffer read_buf(1024); auto buf = std::make_shared(); auto sockets = unbox(make_stream_socket_pair()); CAF_CHECK_EQUAL(nonblocking(sockets.second, true), none); CAF_CHECK_LESS(read(sockets.second, read_buf), 0); CAF_CHECK(last_socket_error_is_temporary()); auto guard = detail::make_scope_guard([&] { close(sockets.second); }); auto mgr = make_endpoint_manager(mpx, sys, dummy_transport{sockets.first, buf}); CAF_CHECK_EQUAL(mgr->mask(), operation::none); CAF_CHECK_EQUAL(mgr->init(), none); CAF_CHECK_EQUAL(mgr->mask(), operation::read_write); CAF_CHECK_EQUAL(mpx->num_socket_managers(), 2u); CAF_CHECK_EQUAL(write(sockets.second, as_bytes(make_span(hello_manager))), hello_manager.size()); run(); CAF_CHECK_EQUAL(string_view(reinterpret_cast(buf->data()), buf->size()), hello_manager); CAF_CHECK_EQUAL(read(sockets.second, read_buf), hello_test.size()); CAF_CHECK_EQUAL(string_view(reinterpret_cast(read_buf.data()), hello_test.size()), hello_test); } CAF_TEST(resolve and proxy communication) { byte_buffer read_buf(1024); auto buf = std::make_shared(); auto sockets = unbox(make_stream_socket_pair()); CAF_CHECK_EQUAL(nonblocking(sockets.second, true), none); auto guard = detail::make_scope_guard([&] { close(sockets.second); }); auto mgr = make_endpoint_manager(mpx, sys, dummy_transport{sockets.first, buf}); CAF_CHECK_EQUAL(mgr->init(), none); CAF_CHECK_EQUAL(mgr->mask(), operation::read_write); run(); CAF_CHECK_EQUAL(read(sockets.second, read_buf), hello_test.size()); mgr->resolve(unbox(make_uri("test:id/42")), self); run(); self->receive( [&](resolve_atom, const std::string&, const strong_actor_ptr& p) { CAF_MESSAGE("got a proxy, send a message to it"); self->send(actor_cast(p), "hello proxy!"); }, after(std::chrono::seconds(0)) >> [&] { CAF_FAIL("manager did not respond with a proxy."); }); run(); auto read_res = read(sockets.second, read_buf); if (read_res <= 0) { std::string msg = "socket closed"; if (read_res < 0) msg = last_socket_error_as_string(); CAF_ERROR("read() failed: " << msg); return; } read_buf.resize(static_cast(read_res)); CAF_MESSAGE("receive buffer contains " << read_buf.size() << " bytes"); message msg; binary_deserializer source{sys, read_buf}; CAF_CHECK_EQUAL(source(msg), none); if (msg.match_elements()) CAF_CHECK_EQUAL(msg.get_as(0), "hello proxy!"); else CAF_ERROR("expected a string, got: " << to_string(msg)); } CAF_TEST_FIXTURE_SCOPE_END()