Commit 27301958 authored by Dominik Charousset's avatar Dominik Charousset

Implement BASP handshake and extend unit tests

parent c4336998
...@@ -43,7 +43,8 @@ public: ...@@ -43,7 +43,8 @@ public:
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
template <class Parent> template <class Parent>
error init(Parent&) { error init(Parent& parent) {
system_ = &parent.system();
return none; return none;
} }
...@@ -83,6 +84,10 @@ public: ...@@ -83,6 +84,10 @@ public:
return state_; return state_;
} }
actor_system& system() const noexcept {
return *system_;
}
private: private:
// -- message handling ------------------------------------------------------- // -- message handling -------------------------------------------------------
...@@ -94,6 +99,9 @@ private: ...@@ -94,6 +99,9 @@ private:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
/// Stores a pointer to the parent actor system.
actor_system* system_ = nullptr;
/// Stores what we are expecting to receive next. /// Stores what we are expecting to receive next.
connection_state state_ = connection_state::await_magic_number; connection_state state_ = connection_state::await_magic_number;
......
...@@ -38,6 +38,7 @@ enum class ec : uint8_t { ...@@ -38,6 +38,7 @@ enum class ec : uint8_t {
missing_handshake, missing_handshake,
version_mismatch, version_mismatch,
unimplemented, unimplemented,
app_identifiers_mismatch,
}; };
/// @relates ec /// @relates ec
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#pragma once #pragma once
#include <string>
#include <cstdint> #include <cstdint>
#include <string>
namespace caf { namespace caf {
namespace net { namespace net {
...@@ -43,12 +43,12 @@ enum class message_type : uint8_t { ...@@ -43,12 +43,12 @@ enum class message_type : uint8_t {
/// 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= 0x02, 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= 0x03, 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
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
#include <vector> #include <vector>
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/network_order.hpp" #include "caf/detail/network_order.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
...@@ -46,9 +48,9 @@ expected<std::vector<byte>> application::serialize(actor_system& sys, ...@@ -46,9 +48,9 @@ expected<std::vector<byte>> application::serialize(actor_system& sys,
} }
error application::handle(span<const byte> bytes) { error application::handle(span<const byte> bytes) {
switch(state_) { switch (state_) {
case connection_state::await_magic_number: { case connection_state::await_magic_number: {
if (bytes.size() !=sizeof(uint32_t)) if (bytes.size() != sizeof(uint32_t))
return ec::unexpected_number_of_bytes; return ec::unexpected_number_of_bytes;
auto xptr = reinterpret_cast<const uint32_t*>(bytes.data()); auto xptr = reinterpret_cast<const uint32_t*>(bytes.data());
auto x = detail::from_network_order(*xptr); auto x = detail::from_network_order(*xptr);
...@@ -58,9 +60,33 @@ error application::handle(span<const byte> bytes) { ...@@ -58,9 +60,33 @@ error application::handle(span<const byte> bytes) {
return none; return none;
} }
case connection_state::await_handshake_header: { case connection_state::await_handshake_header: {
if (bytes.size() != header_size)
return ec::unexpected_number_of_bytes;
hdr_ = header::from_bytes(bytes);
if (hdr_.type != message_type::handshake)
return ec::missing_handshake;
if (hdr_.operation_data != version)
return ec::version_mismatch;
if (hdr_.payload_len == 0)
return ec::missing_payload;
state_ = connection_state::await_handshake_payload;
return none; return none;
} }
case connection_state::await_handshake_payload: { case connection_state::await_handshake_payload: {
node_id nid;
std::vector<std::string> app_ids;
binary_deserializer source{system(), bytes};
if (auto err = source(nid, app_ids))
return err;
if (!nid || app_ids.empty())
return ec::invalid_handshake;
auto ids = get_or(system().config(), "middleman.app-identifiers",
defaults::middleman::app_identifiers);
auto predicate = [=](const std::string& x) {
return std::find(ids.begin(), ids.end(), x) != ids.end();
};
if (std::none_of(app_ids.begin(), app_ids.end(), predicate))
return ec::app_identifiers_mismatch;
return none; return none;
} }
case connection_state::await_header: { case connection_state::await_header: {
......
...@@ -38,6 +38,7 @@ const char* ec_names[] = { ...@@ -38,6 +38,7 @@ const char* ec_names[] = {
"missing_handshake", "missing_handshake",
"version_mismatch", "version_mismatch",
"unimplemented", "unimplemented",
"app_identifiers_mismatch",
}; };
} // namespace } // namespace
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "caf/net/basp/constants.hpp" #include "caf/net/basp/constants.hpp"
#include "caf/net/basp/ec.hpp" #include "caf/net/basp/ec.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/uri.hpp"
using namespace caf; using namespace caf;
using namespace caf::net; using namespace caf::net;
...@@ -44,24 +45,58 @@ struct fixture : test_coordinator_fixture<> { ...@@ -44,24 +45,58 @@ struct fixture : test_coordinator_fixture<> {
fixture() { fixture() {
REQUIRE_OK(app.init(*this)); REQUIRE_OK(app.init(*this));
uri mars_uri;
REQUIRE_OK(parse("tcp://mars", mars_uri));
mars = make_node_id(mars_uri);
} }
template <class... Ts> template <class... Ts>
void set_input(const Ts&...xs) { buffer_type to_buf(const Ts&... xs) {
serializer_impl<buffer_type> sink{nullptr, input}; buffer_type buf;
serializer_impl<buffer_type> sink{system(), buf};
REQUIRE_OK(sink(xs...)); REQUIRE_OK(sink(xs...));
return buf;
}
template <class... Ts>
void set_input(const Ts&... xs) {
input = to_buf(xs...);
}
void handle_magic_number() {
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_magic_number);
set_input(basp::magic_number);
REQUIRE_OK(app.handle_data(*this, input));
}
void handle_handshake() {
CAF_CHECK_EQUAL(app.state(),
basp::connection_state::await_handshake_header);
auto payload = to_buf(mars, defaults::middleman::app_identifiers);
set_input(basp::header{basp::message_type::handshake,
static_cast<uint32_t>(payload.size()),
basp::version});
REQUIRE_OK(app.handle_data(*this, input));
CAF_CHECK_EQUAL(app.state(),
basp::connection_state::await_handshake_payload);
REQUIRE_OK(app.handle_data(*this, payload));
}
actor_system& system() {
return sys;
} }
buffer_type input; buffer_type input;
buffer_type output; buffer_type output;
node_id mars;
basp::application app; basp::application app;
}; };
} // namespace } // namespace
CAF_TEST_FIXTURE_SCOPE(application_tests, fixture) CAF_TEST_FIXTURE_SCOPE(application_tests, fixture)
CAF_TEST(invalid magic number) { CAF_TEST(invalid magic number) {
...@@ -71,13 +106,51 @@ CAF_TEST(invalid magic number) { ...@@ -71,13 +106,51 @@ CAF_TEST(invalid magic number) {
basp::ec::invalid_magic_number); basp::ec::invalid_magic_number);
} }
CAF_TEST(handshake sequence) { CAF_TEST(missing handshake) {
basp::application app; handle_magic_number();
REQUIRE_OK(app.init(*this)); CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_magic_number); set_input(basp::header{basp::message_type::heartbeat, 0, 0});
set_input(basp::magic_number); CAF_CHECK_EQUAL(app.handle_data(*this, input), basp::ec::missing_handshake);
}
CAF_TEST(version mismatch) {
handle_magic_number();
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
set_input(basp::header{basp::message_type::handshake, 0, 0});
CAF_CHECK_EQUAL(app.handle_data(*this, input), basp::ec::version_mismatch);
}
CAF_TEST(missing payload in handshake) {
handle_magic_number();
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
set_input(basp::header{basp::message_type::handshake, 0, basp::version});
CAF_CHECK_EQUAL(app.handle_data(*this, input), basp::ec::missing_payload);
}
CAF_TEST(invalid handshake) {
handle_magic_number();
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
node_id no_nid;
std::vector<std::string> no_ids;
auto payload = to_buf(no_nid, no_ids);
set_input(basp::header{basp::message_type::handshake,
static_cast<uint32_t>(payload.size()), basp::version});
REQUIRE_OK(app.handle_data(*this, input)); REQUIRE_OK(app.handle_data(*this, input));
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_payload);
CAF_CHECK_EQUAL(app.handle_data(*this, payload), basp::ec::invalid_handshake);
}
CAF_TEST(app identifier mismatch) {
handle_magic_number();
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header); CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
std::vector<std::string> wrong_ids{"YOLO!!!"};
auto payload = to_buf(mars, wrong_ids);
set_input(basp::header{basp::message_type::handshake,
static_cast<uint32_t>(payload.size()), basp::version});
REQUIRE_OK(app.handle_data(*this, input));
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_payload);
CAF_CHECK_EQUAL(app.handle_data(*this, payload),
basp::ec::app_identifiers_mismatch);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
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