Commit 04d918ed authored by Dominik Charousset's avatar Dominik Charousset

Implement scaffold for BASP application protocol

parent b7b6ebe4
...@@ -8,7 +8,10 @@ file(GLOB_RECURSE LIBCAF_NET_HDRS "caf/*.hpp") ...@@ -8,7 +8,10 @@ file(GLOB_RECURSE LIBCAF_NET_HDRS "caf/*.hpp")
# list cpp files excluding platform-dependent files # list cpp files excluding platform-dependent files
set(LIBCAF_NET_SRCS set(LIBCAF_NET_SRCS
src/actor_proxy_impl.cpp src/actor_proxy_impl.cpp
src/application.cpp
src/connection_state.cpp
src/datagram_socket.cpp src/datagram_socket.cpp
src/ec.cpp
src/endpoint_manager.cpp src/endpoint_manager.cpp
src/header.cpp src/header.cpp
src/host.cpp src/host.cpp
...@@ -21,8 +24,8 @@ set(LIBCAF_NET_SRCS ...@@ -21,8 +24,8 @@ set(LIBCAF_NET_SRCS
src/socket.cpp src/socket.cpp
src/socket_manager.cpp src/socket_manager.cpp
src/stream_socket.cpp src/stream_socket.cpp
src/tcp_stream_socket.cpp
src/tcp_accept_socket.cpp src/tcp_accept_socket.cpp
src/tcp_stream_socket.cpp
) )
add_custom_target(libcaf_net) add_custom_target(libcaf_net)
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "caf/actor_addr.hpp"
#include "caf/byte.hpp"
#include "caf/error.hpp"
#include "caf/net/basp/connection_state.hpp"
#include "caf/net/basp/header.hpp"
#include "caf/net/basp/message_type.hpp"
#include "caf/net/endpoint_manager.hpp"
#include "caf/node_id.hpp"
#include "caf/span.hpp"
namespace caf {
namespace net {
namespace basp {
class application {
public:
// -- member types -----------------------------------------------------------
// -- interface functions ----------------------------------------------------
template <class Parent>
error init(Parent&) {
return none;
}
template <class Parent>
void write_message(Parent& parent,
std::unique_ptr<endpoint_manager::message> ptr) {
header hdr{message_type::actor_message,
static_cast<uint32_t>(ptr->payload.size()),
ptr->msg->mid.integer_value()};
auto bytes = to_bytes(hdr);
parent.write_packet(make_span(bytes), ptr->payload);
}
template <class Parent>
error handle_data(Parent&, span<const byte> bytes) {
return handle(bytes);
}
template <class Parent>
void resolve(Parent&, const std::string&, actor) {
// TODO: implement me
}
template <class Transport>
void timeout(Transport&, atom_value, uint64_t) {
// nop
}
void handle_error(sec) {
// nop
}
static expected<std::vector<byte>> serialize(actor_system& sys,
const type_erased_tuple& x);
private:
// -- message handling -------------------------------------------------------
error handle(span<const byte> bytes);
error handle(header hdr, span<const byte> payload);
error handle_handshake(header hdr, span<const byte> payload);
// -- member variables -------------------------------------------------------
/// Stores what we are expecting to receive next.
connection_state state_ = connection_state::shutdown;
/// Caches the last header;we need to store it when waiting for the payload.
header hdr_;
/// Stores our own ID.
node_id id_;
/// Stores the ID of our peer.
node_id peer_id_;
/// Keeps track of which local actors our peer monitors.
std::unordered_set<actor_addr> monitored_actors_;
};
} // namespace basp
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
namespace caf {
namespace net {
namespace basp {
/// @addtogroup BASP
/// Stores the state of a connection in a `basp::application`.
enum class connection_state {
/// Indicates that we have just accepted or opened a connection and await the
/// magic number.
await_magic_number,
/// Indicates that we successfully checked the magic number and now wait for
/// the handshake header.
await_handshake_header,
/// Indicates that we received the header for the handshake and now wait for
/// the payload.
await_handshake_payload,
/// Indicates that a connection is established and this node is waiting for
/// the next BASP header.
await_header,
/// Indicates that this node has received a header with non-zero payload and
/// is waiting for the data.
await_payload,
/// Indicates that we are about to close this connection.
shutdown,
};
/// @relates connection_state
std::string to_string(connection_state x);
/// @}
} // namespace basp
} // namespace net
} // namespace caf
...@@ -28,7 +28,10 @@ namespace basp { ...@@ -28,7 +28,10 @@ namespace basp {
/// The current BASP version. /// The current BASP version.
/// @note BASP is not backwards compatible. /// @note BASP is not backwards compatible.
constexpr uint64_t version = 4; constexpr uint64_t version = 1;
/// The very first thing clients send before the first header.
constexpr uint32_t magic_number = 0xCAFC0DE5;
/// @} /// @}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include <string>
#include "caf/fwd.hpp"
namespace caf {
namespace net {
namespace basp {
/// BASP-specific error codes.
enum class ec : uint8_t {
invalid_magic_number = 1,
unexpected_number_of_bytes,
unexpected_payload,
missing_payload,
illegal_state,
invalid_handshake,
missing_handshake,
version_mismatch,
unimplemented,
};
/// @relates ec
std::string to_string(ec x);
/// @relates ec
error make_error(ec x);
} // namespace basp
} // namespace net
} // namespace caf
...@@ -18,12 +18,15 @@ ...@@ -18,12 +18,15 @@
#pragma once #pragma once
#include <array>
#include <cstdint> #include <cstdint>
#include "caf/byte.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
#include "caf/meta/hex_formatted.hpp" #include "caf/meta/hex_formatted.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
#include "caf/net/basp/message_type.hpp" #include "caf/net/basp/message_type.hpp"
#include "caf/span.hpp"
namespace caf { namespace caf {
namespace net { namespace net {
...@@ -37,7 +40,7 @@ struct header : detail::comparable<header> { ...@@ -37,7 +40,7 @@ struct header : detail::comparable<header> {
uint64_t operation_data; uint64_t operation_data;
constexpr header() noexcept constexpr header() noexcept
: type(message_type::client_handshake), payload_len(0), operation_data(0) { : type(message_type::handshake), payload_len(0), operation_data(0) {
// nop // nop
} }
...@@ -51,12 +54,18 @@ struct header : detail::comparable<header> { ...@@ -51,12 +54,18 @@ struct header : detail::comparable<header> {
header& operator=(const header&) noexcept = default; header& operator=(const header&) noexcept = default;
int compare(const header& other) const noexcept; int compare(header other) const noexcept;
/// @pre `bytes.size() == header_size`
static header from_bytes(span<const byte> bytes);
}; };
/// Size of a BASP header in serialized form /// Size of a BASP header in serialized form
constexpr size_t header_size = 13; constexpr size_t header_size = 13;
/// @relates header
std::array<byte, header_size> to_bytes(header x);
/// @relates header /// @relates header
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, header& x) { typename Inspector::result_type inspect(Inspector& f, header& x) {
......
...@@ -33,46 +33,41 @@ enum class message_type : uint8_t { ...@@ -33,46 +33,41 @@ enum class message_type : uint8_t {
/// Sends supported BASP version and node information to the server. /// Sends supported BASP version and node information to the server.
/// ///
/// ![](client_handshake.png) /// ![](client_handshake.png)
client_handshake = 0x00, handshake = 0x00,
/// Sends supported BASP version and node information to the client.
///
/// ![](server_handshake.png)
server_handshake = 0x01,
/// Transmits an actor-to-actor messages. /// Transmits an actor-to-actor messages.
/// ///
/// ![](direct_message.png) /// ![](direct_message.png)
actor_message = 0x02, actor_message = 0x01,
/// Tries to resolve a path on the receiving node. /// Tries to resolve a path on the receiving node.
/// ///
/// ![](resolve_request.png) /// ![](resolve_request.png)
resolve_request= 0x03, resolve_request= 0x02,
/// Transmits the result of a path lookup. /// Transmits the result of a path lookup.
/// ///
/// ![](resolve_response.png) /// ![](resolve_response.png)
resolve_response= 0x04, resolve_response= 0x03,
/// Informs the receiving node that the sending node has created a proxy /// Informs the receiving node that the sending node has created a proxy
/// instance for one of its actors. Causes the receiving node to attach a /// instance for one of its actors. Causes the receiving node to attach a
/// functor to the actor that triggers a down_message on termination. /// functor to the actor that triggers a down_message on termination.
/// ///
/// ![](monitor_message.png) /// ![](monitor_message.png)
monitor_message = 0x05, monitor_message = 0x04,
/// Informs the receiving node that it has a proxy for an actor that has been /// Informs the receiving node that it has a proxy for an actor that has been
/// terminated. /// terminated.
/// ///
/// ![](down_message.png) /// ![](down_message.png)
down_message = 0x06, down_message = 0x05,
/// Used to generate periodic traffic between two nodes in order to detect /// Used to generate periodic traffic between two nodes in order to detect
/// disconnects. /// disconnects.
/// ///
/// ![](heartbeat.png) /// ![](heartbeat.png)
heartbeat = 0x07, heartbeat = 0x06,
}; };
/// @relates message_type /// @relates message_type
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/basp/application.hpp"
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/net/basp/constants.hpp"
#include "caf/net/basp/ec.hpp"
#include "caf/none.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/type_erased_tuple.hpp"
namespace caf {
namespace net {
namespace basp {
expected<std::vector<byte>> application::serialize(actor_system& sys,
const type_erased_tuple& x) {
std::vector<byte> result;
serializer_impl<std::vector<byte>> sink{sys, result};
if (auto err = message::save(sink, x))
return err;
return result;
}
error application::handle(span<const byte> bytes) {
switch(state_) {
case connection_state::await_magic_number: {
if (bytes.size() !=sizeof(uint32_t))
return ec::unexpected_number_of_bytes;
auto xptr = reinterpret_cast<const uint32_t*>(bytes.data());
auto x = detail::from_network_order(*xptr);
if (x != magic_number)
return ec::invalid_magic_number;
state_ = connection_state::await_handshake_header;
return none;
}
case connection_state::await_handshake_header: {
return none;
}
case connection_state::await_handshake_payload: {
return none;
}
case connection_state::await_header: {
if (bytes.size() != header_size)
return ec::unexpected_number_of_bytes;
hdr_ = header::from_bytes(bytes);
if (hdr_.payload_len == 0)
return handle(hdr_, span<const byte>{});
else
state_ = connection_state::await_payload;
return none;
}
case connection_state::await_payload: {
if (bytes.size() != hdr_.payload_len)
return ec::unexpected_number_of_bytes;
return handle(hdr_, bytes);
}
default:
return ec::illegal_state;
}
}
error application::handle(header, span<const byte>) {
return ec::unimplemented;
}
error application::handle_handshake(header hdr, span<const byte> payload) {
if (hdr.type != message_type::handshake)
return ec::missing_handshake;
if (hdr.operation_data != version)
return ec::version_mismatch;
binary_deserializer source{nullptr, payload};
node_id peer_id;
std::vector<std::string> app_ids;
if (auto err = source(peer_id, app_ids))
return err;
// TODO: verify peer_id and app_ids
peer_id_ = std::move(peer_id);
state_ = connection_state::await_header;
return none;
}
} // namespace basp
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/basp/connection_state.hpp"
namespace caf {
namespace net {
namespace basp {
namespace {
const char* connection_state_names[] = {
"await_magic_number",
"await_handshake_header",
"await_handshake_payload",
"await_header",
"await_payload",
"shutdown",
};
} // namespace
std::string to_string(connection_state x) {
return connection_state_names[static_cast<uint8_t>(x)];
}
} // namespace basp
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/basp/ec.hpp"
#include "caf/atom.hpp"
#include "caf/error.hpp"
namespace caf {
namespace net {
namespace basp {
namespace {
const char* ec_names[] = {
"none",
"invalid_magic_number",
"unexpected_number_of_bytes",
"unexpected_payload",
"missing_payload",
"illegal_state",
"invalid_handshake",
"missing_handshake",
"version_mismatch",
"unimplemented",
};
} // namespace
std::string to_string(ec x) {
return ec_names[static_cast<uint8_t>(x)];
}
error make_error(ec x) {
return {static_cast<uint8_t>(x), atom("basp")};
}
} // namespace basp
} // namespace net
} // namespace caf
...@@ -20,12 +20,39 @@ ...@@ -20,12 +20,39 @@
#include <cstring> #include <cstring>
#include "caf/detail/network_order.hpp"
namespace caf { namespace caf {
namespace net { namespace net {
namespace basp { namespace basp {
int header::compare(const header& other) const noexcept { int header::compare(header other) const noexcept {
return memcmp(this, &other, sizeof(header)); auto x = to_bytes(*this);
auto y = to_bytes(other);
return memcmp(x.data(), y.data(), header_size);
}
header header::from_bytes(span<const byte> bytes) {
CAF_ASSERT(bytes.size() == header_size);
header result;
auto ptr = bytes.data();
result.type = *reinterpret_cast<const message_type*>(ptr);
auto payload_len = *reinterpret_cast<const uint32_t*>(ptr + 1);
result.payload_len = detail::from_network_order(payload_len);
auto operation_data = *reinterpret_cast<const uint64_t*>(ptr + 5);
result.operation_data = detail::from_network_order(operation_data);
return result;
}
std::array<byte, header_size> to_bytes(header x) {
std::array<byte, header_size> result;
auto ptr = result.data();
*ptr = static_cast<byte>(x.type);
auto payload_len = detail::to_network_order(x.payload_len);
memcpy(ptr + 1, &payload_len, sizeof(payload_len));
auto operation_data = detail::to_network_order(x.operation_data);
memcpy(ptr + 5, &operation_data, sizeof(operation_data));
return result;
} }
} // namespace basp } // namespace basp
......
...@@ -27,8 +27,8 @@ namespace basp { ...@@ -27,8 +27,8 @@ namespace basp {
namespace { namespace {
string_view message_type_names[] = { string_view message_type_names[] = {
"client_handshake", "server_handshake", "actor_message", "resolve_request", "handshake", "actor_message", "resolve_request", "resolve_response",
"resolve_response", "monitor_message", "down_message", "heartbeat", "monitor_message", "down_message", "heartbeat",
}; };
} // namespace } // namespace
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE application
#include "caf/net/basp/application.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
namespace {
struct fixture {
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(application_tests, fixture)
CAF_TEST(todo) {
// implement me
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -29,7 +29,7 @@ using namespace caf; ...@@ -29,7 +29,7 @@ using namespace caf;
using namespace caf::net; using namespace caf::net;
CAF_TEST(serialization) { CAF_TEST(serialization) {
basp::header x{basp::message_type::server_handshake, 42, 4}; basp::header x{basp::message_type::handshake, 42, 4};
std::vector<byte> buf; std::vector<byte> buf;
{ {
serializer_impl<std::vector<byte>> sink{nullptr, buf}; serializer_impl<std::vector<byte>> sink{nullptr, buf};
...@@ -45,6 +45,6 @@ CAF_TEST(serialization) { ...@@ -45,6 +45,6 @@ CAF_TEST(serialization) {
} }
CAF_TEST(to_string) { CAF_TEST(to_string) {
basp::header x{basp::message_type::server_handshake, 42, 4}; basp::header x{basp::message_type::handshake, 42, 4};
CAF_CHECK_EQUAL(to_string(x), "basp::header(server_handshake, 42, 4)"); CAF_CHECK_EQUAL(to_string(x), "basp::header(handshake, 42, 4)");
} }
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