Commit 27301958 authored by Dominik Charousset's avatar Dominik Charousset

Implement BASP handshake and extend unit tests

parent c4336998
......@@ -43,7 +43,8 @@ public:
// -- interface functions ----------------------------------------------------
template <class Parent>
error init(Parent&) {
error init(Parent& parent) {
system_ = &parent.system();
return none;
}
......@@ -83,6 +84,10 @@ public:
return state_;
}
actor_system& system() const noexcept {
return *system_;
}
private:
// -- message handling -------------------------------------------------------
......@@ -94,6 +99,9 @@ private:
// -- member variables -------------------------------------------------------
/// Stores a pointer to the parent actor system.
actor_system* system_ = nullptr;
/// Stores what we are expecting to receive next.
connection_state state_ = connection_state::await_magic_number;
......
......@@ -38,6 +38,7 @@ enum class ec : uint8_t {
missing_handshake,
version_mismatch,
unimplemented,
app_identifiers_mismatch,
};
/// @relates ec
......
......@@ -18,8 +18,8 @@
#pragma once
#include <string>
#include <cstdint>
#include <string>
namespace caf {
namespace net {
......@@ -43,12 +43,12 @@ enum class message_type : uint8_t {
/// Tries to resolve a path on the receiving node.
///
/// ![](resolve_request.png)
resolve_request= 0x02,
resolve_request = 0x02,
/// Transmits the result of a path lookup.
///
/// ![](resolve_response.png)
resolve_response= 0x03,
resolve_response = 0x03,
/// 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
......
......@@ -21,8 +21,10 @@
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
......@@ -46,9 +48,9 @@ expected<std::vector<byte>> application::serialize(actor_system& sys,
}
error application::handle(span<const byte> bytes) {
switch(state_) {
switch (state_) {
case connection_state::await_magic_number: {
if (bytes.size() !=sizeof(uint32_t))
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);
......@@ -58,9 +60,33 @@ error application::handle(span<const byte> bytes) {
return none;
}
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;
}
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;
}
case connection_state::await_header: {
......
......@@ -38,6 +38,7 @@ const char* ec_names[] = {
"missing_handshake",
"version_mismatch",
"unimplemented",
"app_identifiers_mismatch",
};
} // namespace
......
......@@ -29,6 +29,7 @@
#include "caf/net/basp/constants.hpp"
#include "caf/net/basp/ec.hpp"
#include "caf/none.hpp"
#include "caf/uri.hpp"
using namespace caf;
using namespace caf::net;
......@@ -44,24 +45,58 @@ struct fixture : test_coordinator_fixture<> {
fixture() {
REQUIRE_OK(app.init(*this));
uri mars_uri;
REQUIRE_OK(parse("tcp://mars", mars_uri));
mars = make_node_id(mars_uri);
}
template <class... Ts>
void set_input(const Ts&...xs) {
serializer_impl<buffer_type> sink{nullptr, input};
buffer_type to_buf(const Ts&... xs) {
buffer_type buf;
serializer_impl<buffer_type> sink{system(), buf};
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 output;
node_id mars;
basp::application app;
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(application_tests, fixture)
CAF_TEST(invalid magic number) {
......@@ -71,13 +106,51 @@ CAF_TEST(invalid magic number) {
basp::ec::invalid_magic_number);
}
CAF_TEST(handshake sequence) {
basp::application app;
REQUIRE_OK(app.init(*this));
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_magic_number);
set_input(basp::magic_number);
CAF_TEST(missing handshake) {
handle_magic_number();
CAF_CHECK_EQUAL(app.state(), basp::connection_state::await_handshake_header);
set_input(basp::header{basp::message_type::heartbeat, 0, 0});
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));
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);
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()
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