24#include <netinet/in.h>
25#include <sys/socket.h>
39static void check(
bool ok,
const std::string& what) {
40 std::cout << (
ok ?
" ok " :
" FAIL ") << what << std::endl;
50static std::vector<uint8_t>
frame(uint8_t hi, uint8_t lo,
size_t payload_len, uint8_t fill) {
51 const uint32_t len =
static_cast<uint32_t
>(payload_len + 4);
52 std::vector<uint8_t> f(8 + payload_len, fill);
53 std::memcpy(f.data(), &len, 4);
64 std::atomic<bool>
run{
true};
65 std::atomic<uint16_t>
port{0};
68 void start(std::vector<uint8_t> blob,
bool close_after_write) {
69 th = std::thread([
this, blob = std::move(blob), close_after_write]() {
70 int srv = socket(AF_INET, SOCK_STREAM, 0);
72 setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt));
73 struct sockaddr_in
a{};
74 a.sin_family = AF_INET;
75 a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
77 if (bind(srv,
reinterpret_cast<struct sockaddr*
>(&
a),
sizeof(
a)) < 0) {
81 socklen_t alen =
sizeof(
a);
82 getsockname(srv,
reinterpret_cast<struct sockaddr*
>(&
a), &alen);
84 port.store(ntohs(
a.sin_port));
86 struct timeval tv{.tv_sec = 5, .tv_usec = 0};
87 setsockopt(srv, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv));
88 int c = accept(srv,
nullptr,
nullptr);
94 while (sent < blob.size()) {
95 ssize_t
n =
send(c, blob.data() + sent, blob.size() - sent, MSG_NOSIGNAL);
98 sent +=
static_cast<size_t>(
n);
100 if (close_after_write) {
105 std::this_thread::sleep_for(std::chrono::milliseconds(20));
110 while (
port.load() == 0)
111 std::this_thread::sleep_for(std::chrono::milliseconds(5));
122 std::cout <<
"=== ElodinClient read_packet framing ===" << std::endl;
127 std::vector<uint8_t> blob;
128 const auto big =
frame(0x20, 0x01, 200, 0xAA);
129 const auto small =
frame(0x50, 0x00, 8, 0xBB);
130 blob.insert(blob.end(), big.begin(), big.end());
131 blob.insert(blob.end(), small.begin(), small.end());
134 db.
start(blob,
false);
137 std::cerr <<
"SKIP: cannot connect to the fake db" << std::endl;
143 const ssize_t r1 = c.
read_packet(buf,
sizeof(buf));
145 "oversized packet is skipped, not an error (got " + std::to_string(r1) +
")");
147 const ssize_t r2 = c.
read_packet(buf,
sizeof(buf));
148 check(r2 == 16,
"the packet AFTER an oversized one still reads (got " + std::to_string(r2) +
150 check(r2 == 16 && buf[5] == 0x50 && buf[6] == 0x00,
151 "and it is framed correctly — stream stayed aligned");
159 constexpr size_t kMax = 64;
161 const auto exact =
frame(0x50, 0x00,
kMax - 8, 0xCC);
163 const auto over_by_four =
frame(0x50, 0x00,
kMax - 4, 0xDD);
164 std::vector<uint8_t> blob;
165 blob.insert(blob.end(), exact.begin(), exact.end());
166 blob.insert(blob.end(), over_by_four.begin(), over_by_four.end());
169 db.
start(blob,
false);
174 const ssize_t r1 = c.
read_packet(buf,
sizeof(buf));
175 check(r1 ==
static_cast<ssize_t
>(
kMax),
"a packet that exactly fills the buffer is read");
177 const ssize_t r2 = c.
read_packet(buf,
sizeof(buf));
178 check(r2 == 0,
"packet_len == max_len is skipped, not written 4 bytes past the buffer");
193 "reads the packet the peer sent before closing");
195 check(
r < 0,
"a read past EOF fails");
200 std::cout << (
g_failures ?
"FAILED" :
"PASSED") << std::endl;
Standalone Elodin database client.
bool is_connected() const
Check if connected.
bool connect(const std::string &host, uint16_t port=2240)
Connect to Elodin database.
ssize_t read_packet(uint8_t *packet_buffer, size_t max_len)
Read complete packet from Elodin (header + payload).
static void send(void *ctx, const uint8_t *bytes, uint16_t len)
*brief create JSON pointer *sa reference_tokens const string_t & a
*brief user defined string literal for JSON values *sa std::size_t n
Fake elodin-db: accept one connection, write blob, then hold the socket open.
void start(std::vector< uint8_t > blob, bool close_after_write)
std::atomic< bool > closed_early
std::atomic< uint16_t > port
static void check(bool ok, const std::string &what)
static std::vector< uint8_t > frame(uint8_t hi, uint8_t lo, size_t payload_len, uint8_t fill)
One wire packet: len(4, LE) then len bytes of body, where the first 4 body bytes are ty/packet_id(2)/...
static const size_t kMax
sensorBatchReady: when a sensor board ships its queued chunks.