Skip to content

Commit

Permalink
Zero-byte buffers are now ignored properly
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianReimold committed Mar 4, 2024
1 parent 0b84c7d commit 5d48735
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 13 deletions.
20 changes: 10 additions & 10 deletions ecaludp/include_with_udpcap/ecaludp/socket_udpcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ namespace ecaludp
// API "Passthrough" (and a bit conversion to asio types)
/////////////////////////////////////////////////////////////////
public:
bool is_valid() const;
bool bind(const asio::ip::udp::endpoint& sender_endpoint);
bool is_bound() const;
asio::ip::udp::endpoint local_endpoint();
bool set_receive_buffer_size(int size);
bool join_multicast_group(const asio::ip::address_v4& group_address);
bool leave_multicast_group(const asio::ip::address_v4& group_address);
void set_multicast_loopback_enabled(bool enabled);
bool is_multicast_loopback_enabled() const;
void close();
ECALUDP_EXPORT bool is_valid() const;
ECALUDP_EXPORT bool bind(const asio::ip::udp::endpoint& sender_endpoint);
ECALUDP_EXPORT bool is_bound() const;
ECALUDP_EXPORT asio::ip::udp::endpoint local_endpoint();
ECALUDP_EXPORT bool set_receive_buffer_size(int size);
ECALUDP_EXPORT bool join_multicast_group(const asio::ip::address_v4& group_address);
ECALUDP_EXPORT bool leave_multicast_group(const asio::ip::address_v4& group_address);
ECALUDP_EXPORT void set_multicast_loopback_enabled(bool enabled);
ECALUDP_EXPORT bool is_multicast_loopback_enabled() const;
ECALUDP_EXPORT void close();

/////////////////////////////////////////////////////////////////
// Receiving
Expand Down
20 changes: 17 additions & 3 deletions ecaludp/src/protocol/datagram_builder_v5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,37 @@ namespace ecaludp

constexpr size_t header_size = sizeof(ecaludp::v5::Header);

size_t total_size = 0;
// Create a new buffer_sequence that doesn't contain zero-sized buffers
std::vector<asio::const_buffer> buffer_sequence_without_zero_sized_buffers;
buffer_sequence_without_zero_sized_buffers.reserve(buffer_sequence.size());
for (const auto& buffer : buffer_sequence)
{
if (buffer.size() > 0)
{
buffer_sequence_without_zero_sized_buffers.push_back(buffer);
}
}

// Calculate the total size of all buffers
size_t total_size = 0;
for (const auto& buffer : buffer_sequence_without_zero_sized_buffers)
{
total_size += buffer.size();
}

if ((total_size + header_size) <= max_datagram_size)
{
// Small enough! We can send the entire payload in one datagram
DatagramList datagram_list;
datagram_list.reserve(1);
datagram_list.emplace_back(create_non_fragmented_datagram(buffer_sequence, magic_header_bytes));
datagram_list.emplace_back(create_non_fragmented_datagram(buffer_sequence_without_zero_sized_buffers, magic_header_bytes));

return datagram_list;
}
else
{
return create_fragmented_datagram_list(buffer_sequence, max_datagram_size, magic_header_bytes);
// Too big! We need to fragment the payload
return create_fragmented_datagram_list(buffer_sequence_without_zero_sized_buffers, max_datagram_size, magic_header_bytes);
}
}

Expand Down
62 changes: 62 additions & 0 deletions tests/ecaludp_private_test/src/fragmentation_v5_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,68 @@ TEST_F(FragmentationV5Test, MultiBufferFragmentation)
}
}

// Test if a tailing zero-buffer will create a new fragment (it shouldn't)
TEST_F(FragmentationV5Test, MultiBufferWithTailingZeroBuffer)
{
auto message_to_send_1 = std::make_shared<std::string>("Hello World!");
auto message_to_send_2 = std::make_shared<std::string>("");
auto message_to_send_3 = std::make_shared<std::string>("");

// Create an asio buffer from the string
asio::const_buffer message_to_send_buffer_1 = asio::buffer(*message_to_send_1);
asio::const_buffer message_to_send_buffer_2 = asio::buffer(*message_to_send_2);
asio::const_buffer message_to_send_buffer_3 = asio::buffer(*message_to_send_3);

// Compute the UDP Datagram size. The goal is to fill exactly 2 datagrams with the "Hello World" message
ASSERT_EQ(0, message_to_send_1->size() % 2); // Quickly check that the message has a size dividable by 2
size_t max_datagram_size = sizeof(ecaludp::v5::Header) + (message_to_send_1->size() / 2);

// Let the datagram builder create fragments for the buffer with the computed max datagram size
auto datagram_list = ecaludp::v5::create_datagram_list({message_to_send_buffer_1, message_to_send_buffer_2}, max_datagram_size, {'E', 'C', 'A', 'L'});

// The datagram list must have exactly 3 entries: 1 fragment info and 2 fragments. The empty message must have disappeared
ASSERT_EQ(datagram_list.size(), 3);

// The size of the datagram list is the size of the buffer plus the size of the header
ASSERT_EQ(datagram_list[0].size(), sizeof(ecaludp::v5::Header)); // This is the fragment info
ASSERT_EQ(datagram_list[1].size(), sizeof(ecaludp::v5::Header) + message_to_send_1->size() / 2); // This is the first fragment
ASSERT_EQ(datagram_list[2].size(), sizeof(ecaludp::v5::Header) + message_to_send_1->size() / 2); // This is the second fragment

// Copy the datagram list to a couple of binary buffers
auto binary_buffer_1 = to_binary_buffer(datagram_list[0]);
auto binary_buffer_2 = to_binary_buffer(datagram_list[1]);
auto binary_buffer_3 = to_binary_buffer(datagram_list[2]);

// Create the reassembly
ecaludp::v5::Reassembly reassembly;

// Create a fake sender endpoint as shared_ptr
auto sender_endpoint = std::make_shared<asio::ip::udp::endpoint>();
sender_endpoint->address(asio::ip::address::from_string("127.0.0.1"));
sender_endpoint->port(1234);

// Reassemble all datagrams
{
ecaludp::Error error (ecaludp::Error::GENERIC_ERROR);

reassembly.handle_datagram(binary_buffer_1, sender_endpoint, error);
reassembly.handle_datagram(binary_buffer_2, sender_endpoint, error);
auto reassembled_message = reassembly.handle_datagram(binary_buffer_3, sender_endpoint, error);

// The reassembly must have succeeded
ASSERT_EQ(error, ecaludp::Error::OK);

// The message must not be nullptr
ASSERT_NE(reassembled_message, nullptr);

// The message must have the same size as the original buffer
ASSERT_EQ(reassembled_message->size(), message_to_send_1->size());

// The message must contain the same data as the original buffer
ASSERT_EQ(std::memcmp(reassembled_message->data(), message_to_send_1->data(), message_to_send_1->size()), 0);
}
}

TEST_F(FragmentationV5Test, Cleanup)
{
// Create 2 messages that are the same size
Expand Down

0 comments on commit 5d48735

Please sign in to comment.