VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
server.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/**
25 * @file server.h
26 * @brief Type-safe method-model server (handler side) for VLink RPC.
27 *
28 * @details
29 * @c Server<ReqT, RespT, SecT> is the handler side of the VLink method model.
30 * It registers a callback that is invoked for each incoming request and
31 * optionally fills a response.
32 *
33 * @par Method Model Overview
34 * @code
35 * Client<Req,Resp> Server<Req,Resp>
36 * | Transport Back-end |
37 * |-- invoke(req) -------> | |
38 * | serialize(req) |-- request delivery ---------> |
39 * | | |--> callback(req, resp)
40 * | | <-- reply(resp) ------------- |
41 * | deserialize(resp) | <-- response delivery ------- |
42 * |<-- resp ---------------- |
43 * @endcode
44 *
45 * @par Three Listen Modes
46 * | Method | When to use |
47 * | ----------------------------------------- | -------------------------------------- |
48 * | @c listen(ReqCallback) | Fire-and-forget. |
49 * | @c listen(ReqRespCallback) | Synchronous reply inside the callback. |
50 * | @c listen_for_reply(ReqAsyncRespCallback) | Async reply via @c reply(). |
51 *
52 * @par Synchronous Reply Example
53 * @code
54 * Server<Req, Resp> server("dds://my_service");
55 * server.listen([](const Req& req, Resp& resp) {
56 * resp.result = process(req); // fill resp inside callback
57 * });
58 * @endcode
59 *
60 * @par Asynchronous Reply Example
61 * @code
62 * Server<Req, Resp> server("dds://my_service");
63 * uint64_t saved_req_id = 0;
64 * server.listen_for_reply([&saved_req_id](uint64_t req_id, const Req& req) {
65 * saved_req_id = req_id; // save request ID for later
66 * });
67 * // ... later, from any thread:
68 * server.reply(saved_req_id, Resp{...});
69 * @endcode
70 *
71 * @par Fire-and-forget Example (no response)
72 * @code
73 * Server<Req> server("dds://my_service"); // RespT defaults to EmptyType
74 * server.listen([](const Req& req) {
75 * handle(req);
76 * });
77 * @endcode
78 *
79 * @note Calling @c listen() / @c listen_for_reply() more than once is fatal.
80 * @c reply() must only be called after @c listen_for_reply(); calling it
81 * after a synchronous @c listen() triggers a fatal log.
82 *
83 * @tparam ReqT Request message type. Must satisfy @c Serializer::is_supported().
84 * @tparam RespT Response message type. Defaults to @c Traits::EmptyType (no response).
85 * @tparam SecT Security mode; defaults to @c SecurityType::kWithoutSecurity.
86 */
87
88#pragma once
89
90#include <functional>
91#include <memory>
92#include <string>
93
94#include "./impl/server_impl.h"
95#include "./node.h"
96
97namespace vlink {
98
99/**
100 * @class Server
101 * @brief Type-safe server for the VLink method (RPC) communication model.
102 *
103 * @tparam ReqT Request type.
104 * @tparam RespT Response type (defaults to @c Traits::EmptyType -- no response).
105 * @tparam SecT Security mode.
106 */
107template <typename ReqT, typename RespT = Traits::EmptyType, SecurityType SecT = SecurityType::kWithoutSecurity>
108class Server : public Node<ServerImpl, SecT> {
109 public:
110 /** @brief Unique-pointer alias. */
111 using UniquePtr = std::unique_ptr<Server<ReqT, RespT, SecT>>;
112
113 /** @brief Shared-pointer alias. */
114 using SharedPtr = std::shared_ptr<Server<ReqT, RespT, SecT>>;
115
116 /** @brief Fire-and-forget callback -- no response (@c RespT must be @c EmptyType). */
117 using ReqCallback = std::function<void(const ReqT&)>;
118
119 /** @brief Synchronous callback -- response filled in-place inside the callback. */
120 using ReqRespCallback = std::function<void(const ReqT&, RespT&)>;
121
122 /**
123 * @brief Asynchronous callback -- response sent later via @c reply(req_id, resp).
124 *
125 * @details
126 * The first parameter is the opaque request ID that must be passed to
127 * @c reply() to deliver the response to the waiting client.
128 */
129 using ReqAsyncRespCallback = std::function<void(uint64_t, const ReqT&)>;
130
131 /** @brief Node role identifier (@c kServer). */
132 static constexpr ImplType kImplType = kServer;
133
134 /** @brief @c true when @c RespT is not @c EmptyType (server has a response). */
135 static constexpr bool kHasResp = !std::is_same_v<RespT, Traits::EmptyType>;
136
137 /** @brief Serializer type for @c ReqT. */
139
140 /** @brief Serializer type for @c RespT. */
142
143 static_assert(Serializer::is_supported(kReqType), "<ReqT> is not a supported Serializer type.");
144 static_assert(!kHasResp || Serializer::is_supported(kRespType), "<RespT> is not a supported Serializer type.");
145
146 /**
147 * @brief Creates a @c Server on the heap wrapped in a @c unique_ptr.
148 *
149 * @param url_str Service URL string (e.g. @c "dds://my_service").
150 * @param type @c kWithInit to call @c init() immediately (default).
151 * @return @c UniquePtr owning the new server.
152 */
153 [[nodiscard]] static UniquePtr create_unique(const std::string& url_str, InitType type = InitType::kWithInit);
154
155 /**
156 * @brief Creates a @c Server on the heap wrapped in a @c shared_ptr.
157 *
158 * @param url_str Service URL string.
159 * @param type @c kWithInit to call @c init() immediately (default).
160 * @return @c SharedPtr owning the new server.
161 */
162 [[nodiscard]] static SharedPtr create_shared(const std::string& url_str, InitType type = InitType::kWithInit);
163
164 /**
165 * @brief Constructs a server from a typed transport configuration object.
166 *
167 * @details
168 * Accepts any @c Conf-derived configuration. A compile-time @c static_assert
169 * verifies the configuration supports the server role.
170 *
171 * @tparam ConfT @c Conf-derived configuration type.
172 * @param conf Populated configuration object.
173 * @param type @c kWithInit to call @c init() immediately (default).
174 */
175 // NOLINTNEXTLINE(modernize-use-constraints)
176 template <typename ConfT, typename = std::enable_if_t<std::is_base_of_v<Conf, ConfT>>>
177 explicit Server(const ConfT& conf, InitType type = InitType::kWithInit);
178
179 /**
180 * @brief Constructs a server from a URL string.
181 *
182 * @param url_str Service URL (e.g. @c "someip://30490/0x1/my_method").
183 * @param type @c kWithInit to call @c init() immediately (default).
184 */
185 explicit Server(const std::string& url_str, InitType type = InitType::kWithInit);
186
187 /**
188 * @brief Registers a fire-and-forget request callback (no response).
189 *
190 * @details
191 * Only valid when @c RespT == @c EmptyType (enforced by @c static_assert).
192 * The callback is invoked for every incoming request; no reply is sent.
193 * Calling @c listen() more than once is a fatal error.
194 *
195 * @param callback @c void(const ReqT&) invoked for each request.
196 * @return @c true if registration succeeded; @c false on error.
197 */
198 bool listen(ReqCallback&& callback);
199
200 /**
201 * @brief Registers a synchronous request/response callback.
202 *
203 * @details
204 * Only valid when @c kHasResp is @c true (enforced by @c static_assert).
205 * The callback must fill @p resp before returning. The framework
206 * serialises and sends the response immediately after the callback returns.
207 *
208 * @param callback @c void(const ReqT&, RespT&) -- fills @c resp in-place.
209 * @return @c true if registration succeeded; @c false on error.
210 */
211 bool listen(ReqRespCallback&& callback);
212
213 /**
214 * @brief Registers an asynchronous request callback (reply sent later).
215 *
216 * @details
217 * Only valid when @c kHasResp is @c true (enforced by @c static_assert).
218 * The callback receives an opaque @c req_id. The handler must eventually
219 * call @c reply(req_id, resp) from any thread to send the response.
220 *
221 * @param callback @c void(uint64_t req_id, const ReqT&) -- stores @c req_id for later.
222 * @return @c true if registration succeeded; @c false on error.
223 */
224 bool listen_for_reply(ReqAsyncRespCallback&& callback);
225
226 /**
227 * @brief Sends an asynchronous response for a previously received request.
228 *
229 * @details
230 * Must only be called after @c listen_for_reply() (calling after a synchronous
231 * @c listen() triggers a fatal log). The @p req_id must match the value
232 * passed to the async callback; an unrecognised ID is silently ignored by
233 * the transport.
234 *
235 * @param req_id Opaque request identifier received in the async callback.
236 * @param resp Response value to serialise and send back to the client.
237 * @return @c true if the transport accepted the response; @c false on error.
238 */
239 bool reply(uint64_t req_id, const RespT& resp);
240
241 private:
242 [[nodiscard]] bool has_clients() const;
243
244 bool listen_bytes(NodeImpl::ReqRespCallback&& callback);
245
246 template <bool HasPtrT>
247 bool reply_bytes(uint64_t req_id, const Bytes& resp_data, bool is_sync, Bytes* resp_data_ptr = nullptr);
248};
249
250/**
251 * @class SecurityServer
252 * @brief Convenience alias for @c Server with message security enabled.
253 *
254 * @details
255 * Equivalent to @c Server<ReqT, RespT, SecurityType::kWithSecurity>.
256 * Each incoming request is decrypted before dispatch to the callback, and
257 * each outgoing response is encrypted before transmission.
258 *
259 * @tparam ReqT Request type.
260 * @tparam RespT Response type (defaults to @c Traits::EmptyType).
261 */
262template <typename ReqT, typename RespT = Traits::EmptyType>
263class SecurityServer : public Server<ReqT, RespT, SecurityType::kWithSecurity> {
264 public:
266};
267
268} // namespace vlink
269
Base CRTP template for all VLink communication nodes.
Abstract base class for all transport-specific server (RPC responder) implementations.