TEC
A lightweight C++ library enabling safe, efficient execution in multithreaded and concurrent systems.
Loading...
Searching...
No Matches
tec_socket_server_nd.hpp
Go to the documentation of this file.
1// Time-stamp: <Last changed 2026-02-20 16:26:40 by magnolia>
2/*----------------------------------------------------------------------
3------------------------------------------------------------------------
4Copyright (c) 2020-2026 The Emacs Cat (https://github.com/olddeuteronomy/tec).
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17------------------------------------------------------------------------
18----------------------------------------------------------------------*/
26#pragma once
27
28#include <cerrno>
29#include <functional>
30
31#include <sys/socket.h>
32
33#include "tec/tec_def.hpp" // IWYU pragma: keep
34#include "tec/tec_print.hpp"
35#include "tec/tec_status.hpp"
36#include "tec/tec_trace.hpp"
37#include "tec/tec_serialize.hpp"
43
44
45namespace tec {
46
64template <typename TParams>
65class SocketServerNd : public SocketServer<TParams>
66{
67public:
68 using Params = TParams;
69 using Lock = std::lock_guard<std::mutex>;
70 using ID = NetData::ID;
72
83
93 using HandlerFunc = std::function<void (ServerNd*, DataInOut)>;
94
95private:
96 struct Slot
97 {
98 ServerNd* server;
99 HandlerFunc handler;
100
101 explicit Slot(ServerNd* _server, HandlerFunc _handler)
102 : server{_server}
103 , handler{std::move(_handler)}
104 {}
105 ~Slot() = default;
106 };
107
108 std::unordered_map<ID, std::unique_ptr<Slot>> slots_;
109 std::mutex mtx_slots_;
110
111public:
112
117 explicit SocketServerNd(const Params& params)
118 : SocketServer<Params>(params)
119 {
120 // Register echo request handler (ID=0).
121 this->template register_handler<SocketServerNd>(
122 this, 0, &SocketServerNd::echo);
123 }
124
125 virtual ~SocketServerNd() = default;
126
127
140 template <typename Derived>
141 void register_handler(Derived* server, ID id, void (Derived::*handler)(DataInOut dio))
142 {
143 // Ensure Derived is actually derived from ServerNd.
144 static_assert(std::is_base_of_v<ServerNd, Derived>,
145 "Derived must inherit from tec::SocketServerNd");
146 Lock lk{mtx_slots_};
147 TEC_ENTER("SocketServerNd::register_handler");
148 // Remove existing handler.
149 if (auto slot = slots_.find(id); slot != slots_.end()) {
150 slots_.erase(id);
151 }
152 // Set the slot.
153 slots_[id] = std::make_unique<Slot>(
154 server,
155 [handler](ServerNd* server, DataInOut dio) {
156 // Safely downcast to Derived.
157 auto derived = dynamic_cast<Derived*>(server);
158 (derived->*handler)(dio);
159 });
160 TEC_TRACE("NetData handler ID={} registered.", id);
161 }
162
163protected:
164
171 virtual Status dispatch(ID id, DataInOut dio)
172 {
173 TEC_ENTER("SocketServerNd::dispatch");
174 Status status;
175 bool found{false};
176 //
177 // Find and call the corresponding handler.
178 //
179 if (auto slot = slots_.find(id); slot != slots_.end()) {
180 found = true;
181 slot->second->handler(slot->second->server, dio);
182 }
183 if (!found) {
184 auto errmsg = format("Handler for ID={} not found.", id);
185 status = {ENOTSUP, errmsg, Error::Kind::NotImplemented};
186 TEC_TRACE("{}", status);
187 }
188 else {
189 status = *dio.status;
190 TEC_TRACE("Dispatched with {}.", status);
191 }
192 return status;
193 }
194
205 virtual void reply_error(Status status, NetData::ID request_id, SocketNd* sock)
206 {
207 TEC_ENTER("SocketServerNd::reply_error");
208 NetData nd;
209 nd.header.id = request_id;
210 nd.header.status = static_cast<decltype(nd.header.status)>(status.code.value_or(0xffff));
211 SocketNd::send_nd(&nd, sock);
212 TEC_TRACE("Replied with errcode={}.", nd.header.status);
213 }
214
215 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216 *
217 * Pre- and postprocessing
218 *
219 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
220
227 {
228 if (this->params_.compression) {
229 NdCompress cmpr(this->params_.compression,
230 this->params_.compression_level,
231 this->params_.compression_min_size);
232 return cmpr.compress(*nd);
233 }
234 return {};
235 }
236
243 {
244 if (nd->header.get_compression()) {
245 NdCompress cmpr;
246 return cmpr.uncompress(*nd);
247 }
248 return {};
249 }
250
256 {
257 TEC_ENTER("SocketServerNd::preprocess");
258 return uncompress(nd);
259 }
260
266 {
267 TEC_ENTER("SocketServerNd::postprocess");
268 return compress(nd);
269 }
270
285 void on_net_data(const Socket* s) override
286 {
287 TEC_ENTER("SocketServerNd::on_net_data");
288 SocketNd sock(*s);
289 NetData nd_in;
290 //
291 // Read input NetData.
292 //
293 Status status = SocketNd::recv_nd(&nd_in, &sock);
294 if (status) {
295 //
296 // Preprocess input inplace.
297 //
298 status = preprocess(&nd_in);
299 //
300 // Dispatch NetData.
301 //
302 if (status) {
303 NetData nd_out;
304 DataInOut dio{&status, &sock, &nd_in, &nd_out};
305 status = dispatch(nd_in.header.id, dio);
306 if (status) {
307 //
308 // Postprocess output inplace.
309 //
310 status = postprocess(&nd_out);
311 if (status) {
312 //
313 // Send output to a client.
314 //
315 status = SocketNd::send_nd(&nd_out, &sock);
316 }
317 }
318 }
319 }
320 else if (status.code == EBADMSG) {
321 //
322 // Not a valid NetData --- fallback to raw string handling.
323 //
325 return;
326 }
327 //
328 // Send an error to the client if not OK.
329 //
330 if (!status) {
331 reply_error(status, nd_in.header.id, &sock);
332 }
333 }
334
335 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
336 *
337 * Default handler (ID=0)
338 *
339 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
340
345 virtual void echo(DataInOut dio)
346 {
347 TEC_ENTER("SocketServerNd::echo");
348 //
349 // It just copies input to output.
350 //
351 dio.nd_out->copy_from(*dio.nd_in);
352 *dio.status = {};
353 }
354
355};
356
357} // namespace tec
Compression wrapper for NetData objects with pluggable backends.
Definition tec_nd_compress.hpp:89
virtual Status compress(NetData &nd) const
Compresses the payload of a NetData object in-place (if configured)
Definition tec_nd_compress.hpp:161
virtual Status uncompress(NetData &nd) const
Decompresses the payload of a NetData object in-place (if compressed)
Definition tec_nd_compress.hpp:202
Lightweight binary serialization container optimized for network communication.
Definition tec_net_data.hpp:51
void copy_from(const NetData &src)
Performs deep copy of content from another NetData instance.
Definition tec_net_data.hpp:101
Header header
Global message header.
Definition tec_net_data.hpp:54
NetData-protocol-aware TCP server with request dispatching and optional compression.
Definition tec_socket_server_nd.hpp:66
virtual Status compress(NetData *nd)
Applies compression to the NetData message if enabled in parameters.
Definition tec_socket_server_nd.hpp:226
void register_handler(Derived *server, ID id, void(Derived::*handler)(DataInOut dio))
Registers a request handler for a specific NetData message ID.
Definition tec_socket_server_nd.hpp:141
SocketServerNd(const Params &params)
Constructs server and automatically registers default echo handler (ID=0)
Definition tec_socket_server_nd.hpp:117
virtual Status preprocess(NetData *nd)
Pre-processes incoming message (currently only decompression)
Definition tec_socket_server_nd.hpp:255
virtual Status uncompress(NetData *nd)
Decompresses the NetData message if header indicates compression.
Definition tec_socket_server_nd.hpp:242
std::function< void(ServerNd *, DataInOut)> HandlerFunc
Signature of request handler functions.
Definition tec_socket_server_nd.hpp:93
virtual Status dispatch(ID id, DataInOut dio)
Finds and invokes the handler registered for the given message ID.
Definition tec_socket_server_nd.hpp:171
void on_net_data(const Socket *s) override
Main entry point for handling new data on a client connection.
Definition tec_socket_server_nd.hpp:285
virtual void echo(DataInOut dio)
Default handler (ID=0) — echoes the received message back.
Definition tec_socket_server_nd.hpp:345
virtual Status postprocess(NetData *nd)
Post-processes outgoing message (currently only compression)
Definition tec_socket_server_nd.hpp:265
virtual void reply_error(Status status, NetData::ID request_id, SocketNd *sock)
Sends a minimal error reply containing only status code.
Definition tec_socket_server_nd.hpp:205
Generic BSD socket server template using actor pattern with configurable parameters.
Definition tec_socket_server.hpp:82
Params params_
Server configuration parameters (address, port, buffer size, threading mode, etc.)
Definition tec_socket_server.hpp:89
virtual void on_string(const Socket *sock)
Default handler for character-stream / line-based protocols.
Definition tec_socket_server.hpp:517
#define TEC_ENTER(name)
Logs an entry message for a named context (e.g., function).
Definition tec_trace.hpp:211
#define TEC_TRACE(...)
Logs a formatted trace message.
Definition tec_trace.hpp:222
@ NotImplemented
Not implemented.
int16_t status
Status code (application-specific).
Definition tec_nd_types.hpp:145
constexpr int get_compression() const
Extract compression algorithm type.
Definition tec_nd_types.hpp:176
uint16_t id
User-defined root object identifier.
Definition tec_nd_types.hpp:144
uint16_t ID
Unique identifier type for serialized objects/messages.
Definition tec_nd_types.hpp:59
Specialized socket wrapper optimized for sending/receiving NetData protocol messages.
Definition tec_socket_nd.hpp:63
static Status send_nd(const NetData *nd, const SocketNd *sock)
Sends a complete NetData message (header + payload) over the socket.
Definition tec_socket_nd.hpp:96
static Status recv_nd(NetData *nd, const SocketNd *sock)
Receives one complete NetData message (header + payload)
Definition tec_socket_nd.hpp:148
Container carrying context for one request/response cycle.
Definition tec_socket_server_nd.hpp:77
SocketNd * sock
[in] connection to the client
Definition tec_socket_server_nd.hpp:79
Status * status
[in/out] current operation status — can be modified by handler
Definition tec_socket_server_nd.hpp:78
NetData * nd_in
[in] received and already uncompressed message
Definition tec_socket_server_nd.hpp:80
NetData * nd_out
[out] message to be sent back (usually filled by handler)
Definition tec_socket_server_nd.hpp:81
Lightweight wrapper around a connected socket file descriptor.
Definition tec_socket.hpp:272
std::optional< TCode > code
Optional error code.
Definition tec_status.hpp:112
Common definitions and utilities for the tec namespace.
Compression wrapper for NetData objects with pluggable backends.
Lightweight binary serialization optimized for network communication.
Provides variadic print and format utilities for the tec namespace. Implemented for compatibility wit...
std::string format(const T &arg)
Formats a single argument into a string.
Definition tec_print.hpp:171
The base interface for serializable objects.
Generic BSD socket parameters and helpers.
NetData BSD socket operations.
Generic BSD socket server template using actor pattern with configurable parameters.
Defines error handling types and utilities for the tec namespace.
Provides a thread-safe tracing utility for debugging in the tec namespace.