mirror of
https://github.com/boostorg/mysql.git
synced 2025-05-12 14:11:41 +00:00
message_parser tests 1
This commit is contained in:
parent
6e5d1c21a7
commit
f06d0cd775
@ -40,7 +40,7 @@ inline void boost::mysql::detail::message_parser::parse_message(
|
||||
// Deserialize the header
|
||||
packet_header header;
|
||||
deserialization_context ctx (
|
||||
boost::asio::buffer(buff.pending_first(), HEADER_SIZE),
|
||||
boost::asio::buffer(buff.pending_first() - HEADER_SIZE, HEADER_SIZE),
|
||||
capabilities(0) // unaffected by capabilities
|
||||
);
|
||||
errc err = deserialize(ctx, header);
|
||||
@ -50,7 +50,6 @@ inline void boost::mysql::detail::message_parser::parse_message(
|
||||
// Process the sequence number
|
||||
if (state_.is_first_frame)
|
||||
{
|
||||
state_.is_first_frame = false;
|
||||
state_.seqnum_first = header.sequence_number;
|
||||
state_.seqnum_last = header.sequence_number;
|
||||
}
|
||||
@ -79,6 +78,7 @@ inline void boost::mysql::detail::message_parser::parse_message(
|
||||
{
|
||||
buff.remove_current_message_last(HEADER_SIZE);
|
||||
}
|
||||
state_.is_first_frame = false;
|
||||
state_.reading_header = false;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ class message_parser
|
||||
bool is_first_frame {true};
|
||||
std::uint8_t seqnum_first {};
|
||||
std::uint8_t seqnum_last {};
|
||||
bool reading_header {false};
|
||||
bool reading_header {true};
|
||||
std::size_t remaining_bytes {0};
|
||||
bool more_frames_follow {false};
|
||||
bool has_seqnum_mismatch {false};
|
||||
@ -62,7 +62,9 @@ public:
|
||||
// Attempts to process a message from buff and puts it into msg.
|
||||
// If a message is read, returns true, and msg.message_first and msg.message_last
|
||||
// will contain offsets into buff's reserved area. Otherwise, required_size will contain
|
||||
// the number of bytes needed to complete the message part we're parsing.
|
||||
// the number of bytes needed to complete the message part we're parsing.
|
||||
// Doesn't cause buffer reallocations, and doesn't change the contents
|
||||
// of buff's reserved area.
|
||||
inline void parse_message(read_buffer& buff, result& res) noexcept;
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,11 @@ public:
|
||||
inline boost::asio::const_buffer get_next_message(std::uint8_t& seqnum, error_code& ec) noexcept;
|
||||
|
||||
// Reads some messages from stream, until there is at least one
|
||||
// or an error happens. On success, has_message() returns true
|
||||
// and get_next_message() returns the parsed message.
|
||||
// May relocate the buffer, modifying buffer_first(). If !keep_messages,
|
||||
// the reserved area bytes will be removed before the actual read; otherwise,
|
||||
// they won't be touched (but may be reallocated).
|
||||
template <class Stream>
|
||||
void read_some(Stream& stream, error_code& ec, bool keep_messages = false);
|
||||
|
||||
|
@ -29,6 +29,7 @@ target_link_libraries(
|
||||
add_executable(
|
||||
boost_mysql_unittests
|
||||
unit/detail/channel/read_buffer.cpp
|
||||
unit/detail/channel/message_parser.cpp
|
||||
|
||||
# unit/detail/auth/auth_calculator.cpp
|
||||
# unit/detail/auxiliar/static_string.cpp
|
||||
|
43
test/common/assert_buffer_equals.hpp
Normal file
43
test/common/assert_buffer_equals.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_TEST_COMMON_ASSERT_BUFFER_EQUALS_HPP
|
||||
#define BOOST_MYSQL_TEST_COMMON_ASSERT_BUFFER_EQUALS_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
struct buffer_printer
|
||||
{
|
||||
boost::asio::const_buffer buff;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, buffer_printer buff)
|
||||
{
|
||||
os << "{ ";
|
||||
for (std::size_t i = 0; i < buff.buff.size(); ++i)
|
||||
{
|
||||
os << static_cast<int>(static_cast<const std::uint8_t*>(buff.buff.data())[i]) << ", ";
|
||||
}
|
||||
return os << "}";
|
||||
}
|
||||
|
||||
} // test
|
||||
} // mysql
|
||||
} // boost
|
||||
|
||||
#define BOOST_MYSQL_ASSERT_BUFFER_EQUALS(b1, b2) \
|
||||
BOOST_TEST( \
|
||||
::std::memcmp(b1.data(), b2.data(), b1.size()) == 0, \
|
||||
#b1 " != " #b2 ": " << ::boost::mysql::test::buffer_printer{b1} << " != " << ::boost::mysql::test::buffer_printer{b2})
|
||||
|
||||
#endif
|
161
test/unit/detail/channel/message_parser.cpp
Normal file
161
test/unit/detail/channel/message_parser.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization_context.hpp>
|
||||
#include <boost/mysql/detail/channel/read_buffer.hpp>
|
||||
#include <boost/mysql/detail/channel/message_parser.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/unit_test_suite.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "assert_buffer_equals.hpp"
|
||||
|
||||
using boost::mysql::detail::message_parser;
|
||||
using boost::mysql::detail::read_buffer;
|
||||
using boost::mysql::detail::packet_header;
|
||||
using boost::mysql::detail::int3;
|
||||
|
||||
/**
|
||||
process_message
|
||||
fragmented msg 1
|
||||
header pìece
|
||||
header piece
|
||||
completes header
|
||||
body piece
|
||||
body piece
|
||||
completes body
|
||||
fragmented msg 2
|
||||
header
|
||||
body
|
||||
fragmented msg 3
|
||||
header + body part
|
||||
end of body + next header + next body part
|
||||
full message: header + body
|
||||
several messages, then fragmented one
|
||||
header 1 + body 1 + header 2 + body 2 + header 3 + body fragment 3
|
||||
several messages
|
||||
header 1 + body 1 + header 2 + body 2
|
||||
2-frame message
|
||||
header 1 + body 1 part
|
||||
body 1 part
|
||||
body 1 part + header 2 + body 2 part
|
||||
body 2 part
|
||||
3-frame message
|
||||
header 1 + body 1 part
|
||||
body 1 part
|
||||
body 1 part + header 2 + body 2 part
|
||||
body 2 part
|
||||
body 2 part + header 3 + body 3
|
||||
2-frame message in single read (not possible?)
|
||||
header 1 + body 1 + header 2 + body 2
|
||||
2-frame message with fragmented header
|
||||
header 1 piece
|
||||
header 1 piece + body 1 piece
|
||||
body 1 piece + header 2 piece
|
||||
coming from an already processed message (can we get rid of this?)
|
||||
coming from an error (can we get rid of this?)
|
||||
with reserved area
|
||||
2-frame message with mismatched seqnums
|
||||
2 different frames with "mismatched" seqnums
|
||||
*
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class parser_test
|
||||
{
|
||||
message_parser parser_;
|
||||
read_buffer buff_;
|
||||
std::vector<std::uint8_t> contents_;
|
||||
std::size_t bytes_written_ {0};
|
||||
const std::uint8_t* buffer_first_;
|
||||
public:
|
||||
parser_test(std::vector<std::uint8_t> contents, std::size_t buffsize = 512) :
|
||||
buff_(buffsize), contents_(std::move(contents)), buffer_first_(buff_.first()) {}
|
||||
read_buffer& buffer() noexcept { return buff_; }
|
||||
message_parser::result parse_bytes(std::size_t num_bytes)
|
||||
{
|
||||
message_parser::result res;
|
||||
std::memcpy(buff_.free_first(), contents_.data() + bytes_written_, num_bytes);
|
||||
bytes_written_ += num_bytes;
|
||||
buff_.move_to_pending(num_bytes);
|
||||
parser_.parse_message(buff_, res);
|
||||
return res;
|
||||
}
|
||||
void check_message(const std::vector<std::uint8_t>& contents)
|
||||
{
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(
|
||||
boost::asio::buffer(buff_.current_message_first() - contents.size(), contents.size()),
|
||||
boost::asio::buffer(contents)
|
||||
);
|
||||
}
|
||||
void check_buffer_stability()
|
||||
{
|
||||
BOOST_TEST(buff_.first() == buffer_first_);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::uint8_t> create_message(std::uint8_t seqnum, std::vector<std::uint8_t> body)
|
||||
{
|
||||
std::uint32_t body_size = body.size();
|
||||
packet_header header { int3{body_size}, seqnum };
|
||||
body.resize(body_size + 4);
|
||||
std::memmove(body.data() + 4, body.data(), body_size);
|
||||
boost::mysql::detail::serialization_context ctx (boost::mysql::detail::capabilities(), body.data());
|
||||
boost::mysql::detail::serialize(ctx, header);
|
||||
return body;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(message_parser_parse_message)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fragmented_header_and_body)
|
||||
{
|
||||
// message to be parsed
|
||||
parser_test fixture (create_message(0, { 0x01, 0x02, 0x03 }));
|
||||
|
||||
// 1 byte in the header received
|
||||
auto res = fixture.parse_bytes(1);
|
||||
BOOST_TEST(!res.has_message);
|
||||
BOOST_TEST(res.required_size == 3);
|
||||
|
||||
// Another 2 bytes received
|
||||
res = fixture.parse_bytes(2);
|
||||
BOOST_TEST(!res.has_message);
|
||||
BOOST_TEST(res.required_size == 1);
|
||||
|
||||
// Header fully received
|
||||
res = fixture.parse_bytes(1);
|
||||
BOOST_TEST(!res.has_message);
|
||||
BOOST_TEST(res.required_size == 3);
|
||||
|
||||
// 1 byte in body received
|
||||
res = fixture.parse_bytes(1);
|
||||
BOOST_TEST(!res.has_message);
|
||||
BOOST_TEST(res.required_size == 2);
|
||||
|
||||
// body fully received (single frame messages keep header as an optimization)
|
||||
res = fixture.parse_bytes(2);
|
||||
fixture.check_message({ 0x01, 0x02, 0x03 });
|
||||
BOOST_TEST(res.has_message);
|
||||
BOOST_TEST(res.message.size == 3);
|
||||
BOOST_TEST(res.message.seqnum_first == 0);
|
||||
BOOST_TEST(res.message.seqnum_last == 0);
|
||||
BOOST_TEST(!res.message.has_seqnum_mismatch);
|
||||
|
||||
// Buffer did not reallocate
|
||||
fixture.check_buffer_stability();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
73
test/unit/detail/channel/message_reader.cpp
Normal file
73
test/unit/detail/channel/message_reader.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/detail/channel/message_reader.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/unit_test_suite.hpp>
|
||||
|
||||
/**
|
||||
process_message
|
||||
fragmented msg 2
|
||||
header
|
||||
body
|
||||
fragmented msg 3
|
||||
header + body part
|
||||
end of body + next header + next body part
|
||||
full message: header + body
|
||||
several messages, then fragmented one
|
||||
header 1 + body 1 + header 2 + body 2 + header 3 + body fragment 3
|
||||
several messages
|
||||
header 1 + body 1 + header 2 + body 2
|
||||
2-frame message
|
||||
header 1 + body 1 part
|
||||
body 1 part
|
||||
body 1 part + header 2 + body 2 part
|
||||
body 2 part
|
||||
3-frame message
|
||||
header 1 + body 1 part
|
||||
body 1 part
|
||||
body 1 part + header 2 + body 2 part
|
||||
body 2 part
|
||||
body 2 part + header 3 + body 3
|
||||
2-frame message in single read (not possible?)
|
||||
header 1 + body 1 + header 2 + body 2
|
||||
2-frame message with fragmented header
|
||||
header 1 piece
|
||||
header 1 piece + body 1 piece
|
||||
body 1 piece + header 2 piece
|
||||
coming from an already processed message (can we get rid of this?)
|
||||
coming from an error (can we get rid of this?)
|
||||
with reserved area
|
||||
2-frame message with mismatched seqnums
|
||||
2 different frames with "mismatched" seqnums
|
||||
|
||||
next_message
|
||||
passed number seqnum mismatch
|
||||
intermediate frame seqnum mismatch
|
||||
OK, there is next message
|
||||
OK, there isn't next message
|
||||
read_some
|
||||
has already a message
|
||||
message that fits in the buffer
|
||||
message that fits in the buffer, but segmented in two
|
||||
message that doesn't fit in the buffer (segmented)
|
||||
there is a previous message, keep_messages = false
|
||||
there is a previous message, keep_messages = true
|
||||
error in a read
|
||||
read_one
|
||||
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -15,28 +15,14 @@
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include "assert_buffer_equals.hpp"
|
||||
|
||||
using boost::mysql::detail::read_buffer;
|
||||
using boost::asio::const_buffer;
|
||||
using boost::asio::buffer;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct buffer_printer
|
||||
{
|
||||
const_buffer buff;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, buffer_printer buff)
|
||||
{
|
||||
os << "{ ";
|
||||
for (std::size_t i = 0; i < buff.buff.size(); ++i)
|
||||
{
|
||||
os << static_cast<int>(static_cast<const std::uint8_t*>(buff.buff.data())[i]) << ", ";
|
||||
}
|
||||
return os << "}";
|
||||
}
|
||||
|
||||
// Records the buffer first pointer and size to verify the buffer
|
||||
// didn't do any re-allocation
|
||||
@ -61,11 +47,6 @@ public:
|
||||
|
||||
} // anon namespace
|
||||
|
||||
#define BOOST_MYSQL_BUFF_TEST(b1, b2) \
|
||||
BOOST_TEST( \
|
||||
std::memcmp(b1.data(), b2.data(), b1.size()) == 0, \
|
||||
#b1 " != " #b2 ": " << buffer_printer{b1} << " != " << buffer_printer{b2})
|
||||
|
||||
|
||||
static void check_buffer(
|
||||
read_buffer& buff,
|
||||
@ -125,9 +106,9 @@ static void check_buffer(
|
||||
buff.size() - free_offset
|
||||
);
|
||||
|
||||
BOOST_MYSQL_BUFF_TEST(buff.reserved_area(), buffer(reserved));
|
||||
BOOST_MYSQL_BUFF_TEST(buff.current_message(), buffer(current_message));
|
||||
BOOST_MYSQL_BUFF_TEST(buff.pending_area(), buffer(pending));
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(buff.reserved_area(), buffer(reserved));
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(buff.current_message(), buffer(current_message));
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(buff.pending_area(), buffer(pending));
|
||||
}
|
||||
|
||||
static void check_empty_buffer(read_buffer& buff)
|
||||
|
@ -14,7 +14,7 @@ from collections import namedtuple
|
||||
# Script to get file headers (copyright notices
|
||||
# and include guards) okay and up to date
|
||||
|
||||
REPO_BASE = path.abspath(path.join(path.dirname(__file__), '..', '..'))
|
||||
REPO_BASE = path.abspath(path.join(path.dirname(path.realpath(__file__)), '..', '..'))
|
||||
BASE_FOLDERS = [
|
||||
'cmake',
|
||||
'doc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user