VLink  2.0.0
A high-performance communication middleware
server.h
浏览该文件的文档.
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 Handler-side primitive of the VLink method (RPC) communication model.
27  *
28  * @details
29  * @c Server<ReqT, RespT, SecT> registers a handler for inbound RPC requests
30  * issued by matching @c Client instances. Three handler styles are
31  * supported: fire-and-forget (no response), synchronous request/response,
32  * and deferred asynchronous reply. Codec selection for both the request
33  * and the response payload is resolved at compile time.
34  *
35  * The class is a thin, header-only template wrapper around @c ServerImpl.
36  *
37  * @par Request / Response Sequence
38  * @verbatim
39  * Client<Req,Resp> Transport Back-end Server<Req,Resp>
40  * ----------------- ------------------ -----------------
41  * | invoke(req) | |
42  * |----------------------------->| serialised request |
43  * | |--------------------------->|
44  * | | | handler
45  * | | | fills resp
46  * | | | (sync mode)
47  * | | | -- or --
48  * | | | store req_id
49  * | | | reply(req_id, resp)
50  * | | | (async mode)
51  * | | serialised response |
52  * | |<---------------------------|
53  * | deserialised response | |
54  * |<-----------------------------| |
55  * @endverbatim
56  *
57  * @par Three Listen-Handler Variants
58  * | Method | Handler signature | Semantics |
59  * | ----------------------------------------- | ------------------------------ | ----------------------------- |
60  * | @c listen(ReqCallback) | @c void(const ReqT&) | Fire-and-forget; no response. |
61  * | @c listen(ReqRespCallback) | @c void(const ReqT&, RespT&) | Synchronous fill of @c resp. |
62  * | @c listen_for_reply(ReqAsyncRespCallback) | @c void(uint64_t, const Req&) | Deferred reply via @c reply. |
63  *
64  * @par Synchronous Reply Example
65  * @code
66  * vlink::Server<Req, Resp> svr("dds://compute/sum");
67  * svr.listen([](const Req& q, Resp& r) {
68  * r.value = q.a + q.b;
69  * });
70  * @endcode
71  *
72  * @par Deferred Asynchronous Reply Example
73  * @code
74  * vlink::Server<Req, Resp> svr("dds://compute/sum");
75  * uint64_t pending = 0;
76  * svr.listen_for_reply([&](uint64_t id, const Req& q) {
77  * pending = id;
78  * schedule_async_work(q);
79  * });
80  *
81  * // ...some time later, from any thread:
82  * svr.reply(pending, Resp{result});
83  * @endcode
84  *
85  * @par Fire-and-Forget Example
86  * @code
87  * vlink::Server<Req> svr("dds://logger/push"); // RespT defaults to EmptyType
88  * svr.listen([](const Req& q) { write_log(q); });
89  * @endcode
90  *
91  * @note Calling @c listen() or @c listen_for_reply() more than once is a
92  * fatal error. @c reply() must only be used following
93  * @c listen_for_reply(); calling it after a synchronous @c listen()
94  * triggers a fatal log.
95  *
96  * @see client.h, node.h, serializer.h, base/functional.h
97  */
98 
99 #pragma once
100 
101 #include <memory>
102 #include <string>
103 #include <type_traits>
104 
105 #include "./base/functional.h"
106 #include "./impl/server_impl.h"
107 #include "./node.h"
108 
109 namespace vlink {
110 
111 /**
112  * @class Server
113  * @brief Type-safe RPC handler for the VLink method communication model.
114  *
115  * @details
116  * Inherits the full @c Node API and adds handler-side operations:
117  * three @c listen() variants and the deferred @c reply() entry point. The
118  * transport implementation (@c ServerImpl) is selected by the URL scheme or
119  * by the typed configuration object supplied at construction time.
120  *
121  * @tparam ReqT Request message type. Must satisfy @c Serializer::is_supported().
122  * @tparam RespT Response message type. Defaults to @c Traits::EmptyType (no response).
123  * @tparam SecT Security mode; defaults to @c SecurityType::kWithoutSecurity.
124  */
125 template <typename ReqT, typename RespT = Traits::EmptyType, SecurityType SecT = SecurityType::kWithoutSecurity>
126 class Server : public Node<ServerImpl, SecT> {
127  public:
128  using UniquePtr = std::unique_ptr<Server<ReqT, RespT, SecT>>; ///< Owning unique-pointer alias.
129  using SharedPtr = std::shared_ptr<Server<ReqT, RespT, SecT>>; ///< Owning shared-pointer alias.
130  using ReqCallback = Function<void(const ReqT&)>; ///< Fire-and-forget handler signature.
131  using ReqRespCallback = Function<void(const ReqT&, RespT&)>; ///< Synchronous fill-response handler.
132 
133  /**
134  * @brief Handler signature for deferred asynchronous replies.
135  *
136  * @details
137  * Receives the opaque @c req_id assigned by the framework alongside the
138  * incoming request. The handler must eventually call @c reply(req_id, resp)
139  * from any thread to deliver the response.
140  */
141  using ReqAsyncRespCallback = Function<void(uint64_t, const ReqT&)>;
142 
143  static constexpr ImplType kImplType = kServer; ///< Node role tag (@c kServer).
144  static constexpr bool kHasResp = !std::is_same_v<RespT, Traits::EmptyType>; ///< @c true when response produced.
145  static constexpr Serializer::Type kReqType = Serializer::get_type_of<ReqT>(); ///< Codec for the request.
146  static constexpr Serializer::Type kRespType = Serializer::get_type_of<RespT>(); ///< Codec for the response.
147 
148  static_assert(Serializer::is_supported(kReqType), "<ReqT> is not a supported Serializer type.");
149  static_assert(!kHasResp || Serializer::is_supported(kRespType), "<RespT> is not a supported Serializer type.");
150 
151  /**
152  * @brief Heap-allocates a @c Server and wraps it in a @c std::unique_ptr.
153  *
154  * @param url_str Service URL such as @c "dds://my_service".
155  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
156  * @return Owning @c UniquePtr to the new server.
157  */
158  [[nodiscard]] static UniquePtr create_unique(const std::string& url_str, InitType type = InitType::kWithInit);
159 
160  /**
161  * @brief Heap-allocates a @c Server and wraps it in a @c std::shared_ptr.
162  *
163  * @param url_str Service URL string.
164  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
165  * @return Owning @c SharedPtr to the new server.
166  */
167  [[nodiscard]] static SharedPtr create_shared(const std::string& url_str, InitType type = InitType::kWithInit);
168 
169  /**
170  * @brief Constructs a server from a typed transport configuration object.
171  *
172  * @details
173  * Accepts any @c Conf-derived configuration. A compile-time check
174  * enforces that the configuration permits the server role.
175  *
176  * @tparam ConfT Concrete configuration type derived from @c Conf.
177  * @param conf Populated configuration aggregate.
178  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
179  */
180  // NOLINTNEXTLINE(modernize-use-constraints)
181  template <typename ConfT, typename = std::enable_if_t<std::is_base_of_v<Conf, ConfT>>>
182  explicit Server(const ConfT& conf, InitType type = InitType::kWithInit);
183 
184  /**
185  * @brief Constructs a server from a URL string.
186  *
187  * @param url_str Service URL such as @c "someip://30490/0x1/my_method".
188  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
189  */
190  explicit Server(const std::string& url_str, InitType type = InitType::kWithInit);
191 
192  /**
193  * @brief Installs a fire-and-forget request handler.
194  *
195  * @details
196  * Only available when @c RespT == @c EmptyType (enforced by
197  * @c static_assert). The handler is invoked for every received request
198  * and no response is produced.
199  *
200  * @param callback @c void(const ReqT&) handler.
201  * @return @c true if registration succeeded.
202  */
203  bool listen(ReqCallback&& callback);
204 
205  /**
206  * @brief Installs a synchronous request/response handler.
207  *
208  * @details
209  * Only available when @c kHasResp is @c true. The handler must populate
210  * @c resp before returning; the framework serialises @c resp immediately
211  * and emits the reply on the underlying transport.
212  *
213  * @param callback @c void(const ReqT&, RespT&) handler that fills @c resp.
214  * @return @c true if registration succeeded.
215  */
216  bool listen(ReqRespCallback&& callback);
217 
218  /**
219  * @brief Installs a handler that defers the reply via @c reply().
220  *
221  * @details
222  * Only available when @c kHasResp is @c true. The handler receives an
223  * opaque @c req_id and the deserialised request. The handler must
224  * eventually invoke @c reply(req_id, resp) -- from any thread -- to send
225  * the response back to the waiting client.
226  *
227  * @param callback @c void(uint64_t, const ReqT&) handler.
228  * @return @c true if registration succeeded.
229  */
230  bool listen_for_reply(ReqAsyncRespCallback&& callback);
231 
232  /**
233  * @brief Emits the asynchronous response for a previously received request.
234  *
235  * @details
236  * Must be paired with @c listen_for_reply(); calling @c reply() after a
237  * synchronous @c listen() triggers a fatal log. The @c req_id must match
238  * the value passed to the async handler. Unknown IDs may be silently
239  * rejected by the active transport.
240  *
241  * @param req_id Opaque identifier received in the async handler.
242  * @param resp Response value to serialise and emit.
243  * @return @c true if the transport accepted the response.
244  */
245  bool reply(uint64_t req_id, const RespT& resp);
246 
247  private:
248  [[nodiscard]] bool has_clients() const;
249 
250  bool listen_bytes(NodeImpl::ReqRespCallback&& callback);
251 
252  template <bool HasPtrT>
253  bool reply_bytes(uint64_t req_id, const Bytes& resp_data, bool is_sync, Bytes* resp_data_ptr = nullptr);
254 };
255 
256 /**
257  * @class SecurityServer
258  * @brief Convenience alias of @c Server with per-message encryption enabled.
259  *
260  * @details
261  * Equivalent to @c Server<ReqT, RespT, SecurityType::kWithSecurity>. Each
262  * incoming request is decrypted before dispatch to the handler, and each
263  * outgoing response is encrypted before transmission.
264  *
265  * @tparam ReqT Request message type.
266  * @tparam RespT Response message type. Defaults to @c Traits::EmptyType.
267  */
268 template <typename ReqT, typename RespT = Traits::EmptyType>
269 class SecurityServer : public Server<ReqT, RespT, SecurityType::kWithSecurity> {
270  public:
271  using UniquePtr = std::unique_ptr<SecurityServer<ReqT, RespT>>; ///< Owning unique-pointer alias.
272  using SharedPtr = std::shared_ptr<SecurityServer<ReqT, RespT>>; ///< Owning shared-pointer alias.
273 
274  /**
275  * @brief Heap-allocates a @c SecurityServer and wraps it in a @c std::unique_ptr.
276  *
277  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
278  * @param url_str Service URL string.
279  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
280  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
281  * @return Owning @c UniquePtr to the new secure server.
282  */
283  // NOLINTNEXTLINE(modernize-use-constraints)
284  template <typename SecurityConfigT = Security::Config>
285  [[nodiscard]] static UniquePtr create_unique(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
287 
288  /**
289  * @brief Heap-allocates a @c SecurityServer and wraps it in a @c std::shared_ptr.
290  *
291  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
292  * @param url_str Service URL string.
293  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
294  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
295  * @return Owning @c SharedPtr to the new secure server.
296  */
297  // NOLINTNEXTLINE(modernize-use-constraints)
298  template <typename SecurityConfigT = Security::Config>
299  [[nodiscard]] static SharedPtr create_shared(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
301 
302  /**
303  * @brief Constructs a @c SecurityServer from a typed configuration object.
304  *
305  * @tparam ConfT Configuration type derived from @c Conf.
306  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
307  * @param conf Populated configuration aggregate.
308  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
309  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
310  */
311  // NOLINTNEXTLINE(modernize-use-constraints)
312  template <typename ConfT, typename SecurityConfigT = Security::Config,
313  typename = std::enable_if_t<std::is_base_of_v<Conf, ConfT>>>
314  explicit SecurityServer(const ConfT& conf, SecurityConfigT&& sec_cfg = {}, InitType type = InitType::kWithInit);
315 
316  /**
317  * @brief Constructs a @c SecurityServer from a URL string and installs the security configuration.
318  *
319  * @details
320  * Builds the base @c Server in @c kWithoutInit mode, installs @p sec_cfg
321  * via @c enable_security(), then calls @c init() unless deferred. When
322  * @c enable_security() fails to produce a usable @c NodeImpl::security the
323  * subsequent @c init() will fail.
324  *
325  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
326  * @param url_str Service URL string.
327  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
328  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
329  */
330  // NOLINTNEXTLINE(modernize-use-constraints)
331  template <typename SecurityConfigT = Security::Config>
332  explicit SecurityServer(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
334 };
335 
336 } // namespace vlink
337 
338 #include "./internal/server-inl.h"
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Common CRTP base for every VLink communication primitive.
Transport-neutral base class for every method-model server implementation.