Skip to content

Commit c504cae

Browse files
committed
demo/translation: new demo program
1 parent 89525ef commit c504cae

File tree

6 files changed

+510
-0
lines changed

6 files changed

+510
-0
lines changed

demo/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ subdir('lua')
77
subdir('net')
88
subdir('spawn')
99
subdir('systemd')
10+
subdir('translation')
1011
subdir('uring')
1112
subdir('was')
1213
subdir('zlib')

demo/translation/Translate.cxx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
// Copyright CM4all GmbH
3+
// author: Max Kellermann <max.kellermann@ionos.com>
4+
5+
#include "AllocatorPtr.hxx"
6+
#include "translation/Protocol.hxx"
7+
#include "translation/PReader.hxx"
8+
#include "translation/String.hxx"
9+
#include "net/ConnectSocket.hxx"
10+
#include "net/LocalSocketAddress.hxx"
11+
#include "net/SocketError.hxx"
12+
#include "net/SocketProtocolError.hxx"
13+
#include "net/UniqueSocketDescriptor.hxx"
14+
#include "util/SpanCast.hxx"
15+
#include "util/StringSplit.hxx"
16+
#include "util/PrintException.hxx"
17+
18+
#include <fmt/core.h>
19+
20+
#include <sysexits.h> // for EX_*
21+
22+
using std::string_view_literals::operator""sv;
23+
24+
static void
25+
AppendPacket(std::string &request, const TranslationHeader &header, std::string_view payload) noexcept
26+
{
27+
request += ToStringView(ReferenceAsBytes(header));
28+
request += payload;
29+
}
30+
31+
static std::string
32+
ParseRequest(std::span<const char * const> args)
33+
{
34+
std::string request;
35+
36+
// Add implicit BEGIN packet
37+
static constexpr TranslationHeader begin_header{
38+
.length = 0,
39+
.command = TranslationCommand::BEGIN,
40+
};
41+
AppendPacket(request, begin_header, {});
42+
43+
// Parse and add command line packets
44+
for (const char *arg : args) {
45+
std::string_view arg_view = arg;
46+
auto [cmd_str, payload] = Split(arg_view, '=');
47+
if (payload.data() == nullptr)
48+
throw std::runtime_error(fmt::format("Invalid packet format (expected COMMAND=PAYLOAD): {:?}", arg));
49+
50+
TranslationCommand command = ParseTranslationCommand(cmd_str);
51+
52+
TranslationHeader header{
53+
.length = static_cast<uint16_t>(payload.size()),
54+
.command = command,
55+
};
56+
AppendPacket(request, header, payload);
57+
}
58+
59+
// Add END packet
60+
static constexpr TranslationHeader end_header{
61+
.length = 0,
62+
.command = TranslationCommand::END,
63+
};
64+
AppendPacket(request, end_header, {});
65+
66+
return request;
67+
}
68+
69+
static void
70+
DumpPacket(TranslationCommand command, std::span<const std::byte> payload)
71+
{
72+
if (payload.empty())
73+
fmt::print("{}\n", ToString(command));
74+
else
75+
fmt::print("{} = {:?}\n", ToString(command), ToStringView(payload));
76+
}
77+
78+
static void
79+
ReadAndProcessResponse(SocketDescriptor socket, AllocatorPtr alloc)
80+
{
81+
TranslatePacketReader reader;
82+
83+
while (true) {
84+
std::array<std::byte, 4096> buffer;
85+
ssize_t bytes_read = socket.Read(buffer);
86+
if (bytes_read <= 0) [[unlikely]] {
87+
if (bytes_read < 0)
88+
throw MakeSocketError("Failed to read from socket");
89+
else
90+
throw SocketClosedPrematurelyError{};
91+
}
92+
93+
auto received = std::span{buffer}.first(bytes_read);
94+
while (true) {
95+
const auto consumed = reader.Feed(alloc, received);
96+
if (consumed == 0)
97+
// Need more data
98+
break;
99+
100+
received = received.subspan(consumed);
101+
102+
if (reader.IsComplete()) {
103+
const auto command = reader.GetCommand();
104+
DumpPacket(command, reader.GetPayload());
105+
106+
if (command == TranslationCommand::END)
107+
return;
108+
}
109+
}
110+
}
111+
}
112+
113+
114+
int
115+
main(int argc, char **argv)
116+
try {
117+
if (argc < 2) {
118+
fmt::print(stderr,
119+
"Usage: {} SOCKET_PATH [COMMAND=PAYLOAD] ...\nExample: {} /tmp/translation.sock HOST=example.com URI=/path"sv,
120+
argv[0], argv[0]);
121+
return EX_USAGE;
122+
}
123+
124+
const char *const socket_path = argv[1];
125+
const auto request = ParseRequest({argv + 2, static_cast<size_t>(argc - 2)});
126+
127+
auto socket = CreateConnectSocket(LocalSocketAddress{socket_path}, SOCK_STREAM);
128+
socket.FullWrite(AsBytes(request));
129+
130+
Allocator allocator_instance;
131+
AllocatorPtr alloc{allocator_instance};
132+
ReadAndProcessResponse(socket, alloc);
133+
134+
return EXIT_SUCCESS;
135+
} catch (...) {
136+
PrintException(std::current_exception());
137+
return EXIT_FAILURE;
138+
}

demo/translation/meson.build

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
executable(
2+
'Translate',
3+
'Translate.cxx',
4+
include_directories: inc,
5+
dependencies: [
6+
fmt_dep,
7+
net_dep,
8+
translation_dep,
9+
util_dep,
10+
],
11+
)

0 commit comments

Comments
 (0)