// 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 stream_transport #include "caf/net/stream_transport.hpp" #include "net-test.hpp" #include "caf/binary_deserializer.hpp" #include "caf/binary_serializer.hpp" #include "caf/byte.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/multiplexer.hpp" #include "caf/net/socket_guard.hpp" #include "caf/net/socket_manager.hpp" #include "caf/net/stream_socket.hpp" #include "caf/span.hpp" using namespace caf; using namespace caf::net; namespace { constexpr string_view hello_manager = "hello manager!"; struct fixture : test_coordinator_fixture<>, host_fixture { using byte_buffer_ptr = std::shared_ptr; fixture() : mpx(nullptr), recv_buf(1024), shared_recv_buf{std::make_shared()}, shared_send_buf{std::make_shared()} { mpx.set_thread_id(); mpx.apply_updates(); if (auto err = mpx.init()) FAIL("mpx.init failed: " << err); REQUIRE_EQ(mpx.num_socket_managers(), 1u); auto sockets = unbox(make_stream_socket_pair()); send_socket_guard.reset(sockets.first); recv_socket_guard.reset(sockets.second); if (auto err = nonblocking(recv_socket_guard.socket(), true)) FAIL("nonblocking returned an error: " << err); } bool handle_io_event() override { return mpx.poll_once(false); } settings config; multiplexer mpx; byte_buffer recv_buf; socket_guard send_socket_guard; socket_guard recv_socket_guard; byte_buffer_ptr shared_recv_buf; byte_buffer_ptr shared_send_buf; }; class dummy_application { public: using byte_buffer_ptr = std::shared_ptr; using input_tag = tag::stream_oriented; explicit dummy_application(byte_buffer_ptr recv_buf, byte_buffer_ptr send_buf) : recv_buf_(std::move(recv_buf)), send_buf_(std::move(send_buf)){ // nop }; ~dummy_application() = default; template error init(socket_manager*, ParentPtr parent, const settings&) { parent->configure_read(receive_policy::exactly(hello_manager.size())); return none; } template bool prepare_send(ParentPtr parent) { MESSAGE("prepare_send called"); auto& buf = parent->output_buffer(); auto data = as_bytes(make_span(hello_manager)); buf.insert(buf.end(), data.begin(), data.end()); return true; } template bool done_sending(ParentPtr) { MESSAGE("done_sending called"); return true; } template void continue_reading(ParentPtr) { FAIL("continue_reading called"); } template size_t consume(ParentPtr, span data, span) { recv_buf_->clear(); recv_buf_->insert(recv_buf_->begin(), data.begin(), data.end()); MESSAGE("Received " << recv_buf_->size() << " bytes in dummy_application"); return recv_buf_->size(); } static void handle_error(sec code) { FAIL("handle_error called with " << CAF_ARG(code)); } template static void abort(ParentPtr, const error& reason) { FAIL("abort called with " << CAF_ARG(reason)); } private: byte_buffer_ptr recv_buf_; byte_buffer_ptr send_buf_; }; } // namespace BEGIN_FIXTURE_SCOPE(fixture) CAF_TEST(receive) { auto mgr = make_socket_manager( recv_socket_guard.release(), &mpx, shared_recv_buf, shared_send_buf); CHECK_EQ(mgr->init(config), none); mpx.apply_updates(); CHECK_EQ(mpx.num_socket_managers(), 2u); CHECK_EQ(static_cast(write(send_socket_guard.socket(), as_bytes(make_span(hello_manager)))), hello_manager.size()); MESSAGE("wrote " << hello_manager.size() << " bytes."); run(); CHECK_EQ(string_view(reinterpret_cast(shared_recv_buf->data()), shared_recv_buf->size()), hello_manager); } CAF_TEST(send) { auto mgr = make_socket_manager( recv_socket_guard.release(), &mpx, shared_recv_buf, shared_send_buf); CHECK_EQ(mgr->init(config), none); mpx.apply_updates(); CHECK_EQ(mpx.num_socket_managers(), 2u); mgr->register_writing(); mpx.apply_updates(); while (handle_io_event()) ; recv_buf.resize(hello_manager.size()); auto res = read(send_socket_guard.socket(), make_span(recv_buf)); MESSAGE("received " << res << " bytes"); recv_buf.resize(res); CHECK_EQ(string_view(reinterpret_cast(recv_buf.data()), recv_buf.size()), hello_manager); } END_FIXTURE_SCOPE()