Skip to content

Commit

Permalink
Merge pull request #395
Browse files Browse the repository at this point in the history
  • Loading branch information
Neverlord committed May 9, 2024
2 parents c529c38 + 1bc0d93 commit 04ee6fd
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2.8.0-dev.64 | 2024-05-09 14:11:32 +0200

* Implement new decode functions for the JSON format (Dominik Charousset, Corelight)

2.8.0-dev.61 | 2024-05-06 14:26:27 -0700

* Properly install config.hh header (again) (Dominik Charousset, Corelight)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.8.0-dev.61
2.8.0-dev.64
1 change: 1 addition & 0 deletions libbroker/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ set(BROKER_TEST_SRC
broker/data.test.cc
broker/detail/peer_status_map.test.cc
broker/domain_options.test.cc
broker/envelope.test.cc
broker/error.test.cc
broker/filter_type.test.cc
broker/format/bin.test.cc
Expand Down
5 changes: 5 additions & 0 deletions libbroker/broker/data_envelope.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
#include "broker/error.hh"
#include "broker/expected.hh"
#include "broker/format/bin.hh"
#include "broker/internal/json.hh"
#include "broker/internal/native.hh"
#include "broker/internal/type_id.hh"
#include "broker/topic.hh"

#include <caf/binary_serializer.hpp>
#include <caf/byte_buffer.hpp>
#include <caf/expected.hpp>
#include <caf/json_object.hpp>
#include <caf/json_value.hpp>

using namespace std::literals;

Expand Down
37 changes: 37 additions & 0 deletions libbroker/broker/envelope.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "broker/error.hh"
#include "broker/expected.hh"
#include "broker/format/bin.hh"
#include "broker/internal/json.hh"
#include "broker/internal/logger.hh"
#include "broker/internal/type_id.hh"
#include "broker/p2p_message_type.hh"
Expand All @@ -20,6 +21,9 @@
#include <caf/byte_buffer.hpp>
#include <caf/detail/ieee_754.hpp>
#include <caf/detail/network_order.hpp>
#include <caf/expected.hpp>
#include <caf/json_object.hpp>
#include <caf/json_value.hpp>

namespace broker {

Expand Down Expand Up @@ -148,6 +152,39 @@ expected<envelope_ptr> envelope::deserialize(const std::byte* data,
}
}

expected<envelope_ptr> envelope::deserialize_json(const char* data,
size_t size) {
// Parse the JSON text into a JSON object.
auto val = caf::json_value::parse_shallow(std::string_view{data, size});
if (!val)
return error{ec::invalid_json};
auto obj = val->to_object();
// Type-checking.
if (obj.value("type").to_string() != "data-message")
return error{ec::deserialization_failed};
// Read the topic.
auto topic = obj.value("topic").to_string();
if (topic.empty())
return error{ec::deserialization_failed};
auto stl_topic = std::string_view{topic.data(), topic.size()};
// Try to convert the JSON structure into our binary serialization format.
std::vector<std::byte> buf;
buf.reserve(512); // Allocate some memory to avoid small allocations.
if (auto err = internal::json::data_message_to_binary(obj, buf))
return err;
// Turn the binary data into a data envelope. TTL and sender/receiver are
// not part of the JSON representation, so we use defaults values.
auto res = data_envelope::deserialize(endpoint_id::nil(), endpoint_id::nil(),
defaults::ttl, stl_topic, buf.data(),
buf.size());
// Note: must manually "unbox" the expected to convert from
// expected<data_envelope_ptr> to expected<envelope_ptr>.
if (res)
return *res;
else
return res.error();
}

data_envelope_ptr envelope::as_data() const {
BROKER_ASSERT(type() == envelope_type::data);
return {new_ref, static_cast<const data_envelope*>(this)};
Expand Down
4 changes: 4 additions & 0 deletions libbroker/broker/envelope.hh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public:
/// write format.
static expected<envelope_ptr> deserialize(const std::byte* data, size_t size);

/// Attempts to deserialize an envelope from the given message in Broker's
/// JSON format.
static expected<envelope_ptr> deserialize_json(const char* data, size_t size);

/// @pre `type == envelope_type::data`
data_envelope_ptr as_data() const;

Expand Down
127 changes: 127 additions & 0 deletions libbroker/broker/envelope.test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include "broker/data_envelope.hh"

#include "broker/broker-test.test.hh"

using namespace broker;
using namespace std::literals;

namespace {

// A data message that has one of everything.
constexpr std::string_view json = R"_({
"type": "data-message",
"topic": "/foo/bar",
"@data-type": "vector",
"data": [
{
"@data-type": "none",
"data": {}
},
{
"@data-type": "boolean",
"data": true
},
{
"@data-type": "count",
"data": 42
},
{
"@data-type": "integer",
"data": 23
},
{
"@data-type": "real",
"data": 12.48
},
{
"@data-type": "string",
"data": "this is a string"
},
{
"@data-type": "address",
"data": "2001:db8::"
},
{
"@data-type": "subnet",
"data": "255.255.255.0/24"
},
{
"@data-type": "port",
"data": "8080/tcp"
},
{
"@data-type": "timestamp",
"data": "2014-07-09T10:16:44.000"
},
{
"@data-type": "timespan",
"data": "23s"
},
{
"@data-type": "enum-value",
"data": "foo"
},
{
"@data-type": "set",
"data": [
{
"@data-type": "integer",
"data": 1
},
{
"@data-type": "integer",
"data": 2
},
{
"@data-type": "integer",
"data": 3
}
]
},
{
"@data-type": "table",
"data": [
{
"key": {
"@data-type": "string",
"data": "first-name"
},
"value": {
"@data-type": "string",
"data": "John"
}
},
{
"key": {
"@data-type": "string",
"data": "last-name"
},
"value": {
"@data-type": "string",
"data": "Doe"
}
}
]
}
]
})_";

} // namespace

TEST(JSON can be deserialized to a data message) {
auto maybe_envelope = envelope::deserialize_json(json.data(), json.size());
REQUIRE(maybe_envelope);
}

TEST(an invalid JSON payload results in an error) {
auto no_json = "this is not json!"sv;
auto maybe_envelope = envelope::deserialize_json(no_json.data(),
no_json.size());
CHECK(!maybe_envelope);
}

TEST(a JSON payload that does not contain a broker data results in an error) {
std::string_view obj = R"_({"foo": "bar"})_";
auto maybe_envelope = envelope::deserialize_json(obj.data(), obj.size());
CHECK(!maybe_envelope);
}
2 changes: 2 additions & 0 deletions libbroker/broker/error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ enum class ec : uint8_t {
redundant_connection,
/// Broker encountered a
logic_error = 40,
/// Broker failed to parse a JSON object.
invalid_json,
};
// --ec-enum-end

Expand Down
32 changes: 32 additions & 0 deletions libbroker/broker/format/json.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
#include "broker/format/json.hh"

#include "broker/defaults.hh"
#include "broker/error.hh"
#include "broker/expected.hh"
#include "broker/internal/json.hh"
#include "broker/variant.hh"

#include <caf/expected.hpp>
#include <caf/json_object.hpp>
#include <caf/json_value.hpp>

#include <cassert>
#include <chrono>
#include <ctime>
Expand Down Expand Up @@ -40,4 +50,26 @@ size_t encode_to_buf(timestamp value, std::array<char, 32>& buf) {
return pos;
}

error decode(std::string_view str, variant& result) {
// Parse the JSON text into a JSON object.
auto val = caf::json_value::parse_shallow(str);
if (!val)
return error{ec::invalid_json};
auto obj = val->to_object();
// Try to convert the JSON structure into our binary serialization format.
std::vector<std::byte> buf;
buf.reserve(512); // Allocate some memory to avoid small allocations.
if (auto err = internal::json::data_message_to_binary(obj, buf))
return err;
// Turn the binary data into a data envelope. TTL and sender/receiver are
// not part of the JSON representation, so we use defaults values.
auto res = data_envelope::deserialize(endpoint_id::nil(), endpoint_id::nil(),
defaults::ttl, topic::reserved,
buf.data(), buf.size());
if (!res)
return res.error();
result = (*res)->value();
return {};
}

} // namespace broker::format::json::v1
7 changes: 7 additions & 0 deletions libbroker/broker/format/json.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "broker/config.hh"
#include "broker/data.hh"
#include "broker/fwd.hh"
#include "broker/message.hh"

#include <algorithm>
Expand Down Expand Up @@ -385,4 +386,10 @@ OutIter encode(const data_message& msg, OutIter out) {
return out;
}

/// Tries to decode a JSON object from `str`. On success, the result is stored
/// in `result` and the functions a default-constructed `error`. Otherwise, the
/// function returns a non-empty error and leaves `result` in an unspecified
/// state.
error decode(std::string_view str, variant& result);

} // namespace broker::format::json::v1

0 comments on commit 04ee6fd

Please sign in to comment.