Flow 1.0.0
Flow project: Full implementation reference.
server_socket.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
23
25{
26
27/**
28 * A net_flow::Server_socket that adds integration with boost.asio. See net_flow::Node for a discussion of the topic of
29 * boost.asio integration. A net_flow::asio::Server_socket *is* a net_flow::Server_socket with all that functionality,
30 * plus APIs (mainly async_accept()) that accomplish boost.asio-style asynchronous operations with a
31 * standard `boost::asio::io_service`.
32 *
33 * As of this writing, `asio::Server_socket::Ptr`s are generated by `net_flow::Node::listen()` and any derivatives.
34 * The underlying `io_service` (a/k/a flow::util::Task_engine)
35 * is carried forward from the `net_flow::asio::Node` factory object that generated and returned the `Ptr`. It can be
36 * overwritten via set_async_task_engine().
37 */
40 // Note: can't derive util::Shared_ptr_alias_holder<>, because superclass already did it (doc header disallows this).
41{
42public:
43 // Types.
44
45 /// Short-hand for `shared_ptr` to Server_socket.
46 using Ptr = boost::shared_ptr<Server_socket>;
47
48 // Constructors/destructor.
49
50 /// Boring `virtual` destructor as in superclass. See notes there.
51 ~Server_socket() override;
52
53 // Methods.
54
55 /**
56 * Pointer (possibly null) for the flow::util::Task_engine used by any coming async I/O calls and inherited by any
57 * subsequently generated Peer_socket objects.
58 *
59 * One, this is used by `this->async_accept()` I/O calls, both
60 * synchronously and during the async phases of such calls, whenever placing a user-supplied handler routine
61 * onto an `Task_engine`. Whatever async_task_engine() returns at that time is the `Task_engine` used.
62 *
63 * Two, when sync_accept() creates Peer_socket, at that moment this is used. Hence, the initial value
64 * returned by Peer_socket::async_task_engine() is thus inherited from
65 * the factory Server_socket's async_task_engine() result.
66 *
67 * ### Thread safety (operating on a given Node) ###
68 *
69 * - set_async_task_engine() is the only way to change the returned value, upon construction.
70 * - It is not safe to call set_async_task_engine() at the same time as async_task_engine().
71 * - Define "any async op" as Server_socket::async_accept() and all its variants, including actions it takes
72 * in the background (asynchronously).
73 * - async_task_engine() must return the same value -- and *not* null -- throughout "any async op."
74 * - Define "any async inheriting op" as any (non-blocking) Server_socket::accept() method (which creates a
75 * Peer_socket.
76 * - async_task_engine() must return the same value -- possibly null -- throughout "any async inheriting op."
77 * - Put simply, a null `Task_engine` can be inherited by a generated socket, but a null cannot be used when
78 * performing an async op. Furthermore, set_async_task_engine() is unsafe through any of those and through
79 * async_task_engine() itself.
80 *
81 * Informal tip: Typically you'd never call set_async_task_engine(), hence there is no problem. If you DO need to
82 * call it, even then normally it's easy to ensure one does this before any actual async calls are made.
83 * Trouble only begins if one calls set_async_task_engine() "in the middle" of operating the socket.
84 * There is no way to add outside locking to make that work, either, due to async "tails" of some calls.
85 *
86 * @return Null or non-null pointer to `Task_engine`.
87 */
89
90 /**
91 * Read-only version of async_task_engine().
92 *
93 * @return Reference to read-only `*(async_task_engine())`.
94 */
96
97 /**
98 * Overwrites the value to be returned by next async_task_engine().
99 *
100 * See async_task_engine() doc header before using this.
101 *
102 * @param target_async_task_engine
103 * See async_task_engine().
104 */
105 void set_async_task_engine(util::Task_engine* target_async_task_engine);
106
107 /**
108 * boost.asio-style asynchronous version that essentially performs
109 * net_flow::Server_socket::sync_accept() in the background and invokes the given handler via the saved
110 * `Task_engine *(async_task_engine())`, as if by `Task_engine::post()`.
111 *
112 * The semantics are identical to the similar `sync_accept()`, except that the operation does not block the
113 * calling thread; and the results are delivered to the supplied handler `on_result` instead of to the caller.
114 *
115 * @tparam Rep
116 * See net_flow::Server_socket::accept().
117 * @tparam Period
118 * See net_flow::Server_socket::accept().
119 * @tparam Handler
120 * A type such that if `Handler h`, then a function equivalent to `{ h(err_code, sock); }` can
121 * be `post()`ed onto an `Task_engine`, with `const Error_code& err_code` and
122 * `net_flow::asio::Peer_socket sock`.
123 * @param on_result
124 * Handler to be executed asynchronously within the saved `Task_engine`.
125 * The error code and socket values passed to it, in that order, are identical
126 * to those out-arg/returned values in net_flow::Server_socket::sync_accept().
127 * Note: Use `bind_executor(S, F)` to bind your handler to the util::Strand `S`.
128 * @param reactor_pattern
129 * See net_flow::Server_socket::sync_accept().
130 * @param max_wait
131 * See net_flow::Server_socket::sync_accept().
132 */
133 template<typename Rep, typename Period, typename Handler>
134 void async_accept(const boost::chrono::duration<Rep, Period>& max_wait,
135 bool reactor_pattern,
136 const Handler& on_result);
137
138 /**
139 * Equivalent to `async_accept(duration::max(), false, on_result)`; i.e., `async_accept()`
140 * with no timeout and in normal -- non-reactor-pattern -- handler mode.
141 *
142 * @tparam Handler
143 * See other async_accept().
144 * @param on_result
145 * See other async_accept().
146 */
147 template<typename Handler>
148 void async_accept(const Handler& on_result);
149
150 /**
151 * Equivalent to `async_accept(max_wait, false, on_result)`; i.e., `async_accept()`
152 * with in normal -- non-reactor-pattern -- handler mode.
153 *
154 * @tparam Handler
155 * See other async_accept().
156 * @param on_result
157 * See other async_accept().
158 * @param max_wait
159 * See other async_accept().
160 */
161 template<typename Rep, typename Period, typename Handler>
162 void async_accept(const boost::chrono::duration<Rep, Period>& max_wait,
163 const Handler& on_result);
164
165 /**
166 * Equivalent to `async_accept(duration::max(), reactor_pattern, on_result)`; i.e., `async_accept()`
167 * with no timeout.
168 *
169 * @tparam Handler
170 * See other async_accept().
171 * @param on_result
172 * See other async_accept().
173 * @param reactor_pattern
174 * See other async_accept().
175 */
176 template<typename Handler>
177 void async_accept(bool reactor_pattern, const Handler& on_result);
178
179 /**
180 * Convenience method that polymorphically casts from `net_flow::Server_socket::Ptr` to
181 * subclass pointer `net_flow::asio::Server_socket::Ptr`.
182 * Behavior undefined if `serv` is not actually of the latter type.
183 * With this, one needn't do verbose `static_[pointer_]cast<>` or similar conversions. For example:
184 * `auto serv = asio::Server_socket::cast(node.listen(...));` where `node` is a `net_flow::asio::Node&`,
185 * and `serv` is auto-typed as `net_flow::asio::Server_socket::Ptr`.
186 *
187 * @param serv
188 * Handle to a `serv` such that the true underlying concrete type is net_flow::asio::Server_socket.
189 * @return See above.
190 */
192
193private:
194 // Friends.
195
196 /// Similarly to the equivalent `friend` in net_flow::Peer_socket.
197 friend class Node;
198 /// As of this writing, it is needed for Node::serv_create_forward_plus_ctor_args().
199 friend class net_flow::Node;
200
201 // Types.
202
203 /// Short-hand for the `Task_engine`-compatible accept `Handler` concrete type for class-internal code.
204 using Handler_func = Function<void (const Error_code& err_code, Peer_socket::Ptr new_sock)>;
205
206 // Constructors.
207
208 /**
209 * Constructs object.
210 *
211 * @param logger
212 * See superclass.
213 * @param child_sock_opts
214 * See superclass.
215 */
216 explicit Server_socket(log::Logger* logger, const Peer_socket_options* child_sock_opts);
217
218 // Methods.
219
220 /**
221 * De-templated implementation of all `async_accept()` methods.
222 *
223 * @param on_result
224 * `handler_func(on_result)`, where `on_result` is the user's `async_*()` method arg.
225 * @param wait_until
226 * See `max_wait` arg on the originating `async_accept()` method. This is absolute timeout time point
227 * derived from it; zero-valued if no timeout.
228 * @param reactor_pattern
229 * See `async_accept()`.
230 */
231 void async_accept_impl(Handler_func&& on_result, const Fine_time_pt& wait_until, bool reactor_pattern);
232
233 /**
234 * Returns a functor that essentially performs `post()` `on_result` onto `*async_task_engine()` in a way suitable
235 * for a boost.asio-compatible async-op.
236 *
237 * ### Rationale ###
238 * See asio::Peer_socket::handler_func().
239 *
240 * @tparam Handler
241 * See async_accept().
242 * @param on_result
243 * See async_accept().
244 * @return Function to call from any context that will properly `post()` `on_result();` onto `*async_task_engine()`.
245 */
246 template<typename Handler>
247 Handler_func handler_func(Handler&& on_result);
248
249 // Data.
250
251 /// See async_task_engine().
253}; // class asio::Server_socket
254
255// Free functions: in *_fwd.hpp.
256
257// Template implementations.
258
259template<typename Rep, typename Period, typename Handler>
260void Server_socket::async_accept(const boost::chrono::duration<Rep, Period>& max_wait,
261 bool reactor_pattern,
262 const Handler& on_result)
263{
266 reactor_pattern);
267}
268
269template<typename Handler>
270void Server_socket::async_accept(const Handler& on_result)
271{
272 async_accept_impl(Handler_func(on_result), Fine_time_pt(), false);
273}
274
275template<typename Handler>
276void Server_socket::async_accept(bool reactor_pattern, const Handler& on_result)
277{
278 async_accept_impl(Handler_func(on_result), Fine_time_pt(), reactor_pattern);
279}
280
281template<typename Rep, typename Period, typename Handler>
282void Server_socket::async_accept(const boost::chrono::duration<Rep, Period>& max_wait,
283 const Handler& on_result)
284{
287 false);
288}
289
290template<typename Handler>
292{
293 using boost::asio::post;
294 using boost::asio::bind_executor;
295 using boost::asio::get_associated_executor;
296
297 /* This mirrors Peer_socket::handler_func() exactly (just different signature on on_result()). Comments light.
298 * @todo Maybe there's a way to generalize this with template param-packs or something. */
299
300 return [this, on_result = std::move(on_result)]
301 (const Error_code& err_code, Peer_socket::Ptr new_sock)
302 mutable
303 {
304 const auto executor = get_associated_executor(on_result);
305 post(*(async_task_engine()),
306 bind_executor(executor,
307 [err_code, new_sock, on_result = std::move(on_result)]
308 {
309 on_result(err_code, new_sock);
310 }));
311 };
312} // Server_socket::handler_func()
313
314} // namespace flow::net_flow::asio
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1291
An object of this class is a single Flow-protocol networking node, in the sense that: (1) it has a di...
Definition: node.hpp:937
A server socket able to listen on a single Flow port for incoming connections and return peer sockets...
A subclass of net_flow::Node that adds the ability to easily and directly use net_flow sockets in gen...
Definition: node.hpp:236
boost::shared_ptr< Peer_socket > Ptr
Short-hand for shared_ptr to Peer_socket.
Definition: peer_socket.hpp:46
A net_flow::Server_socket that adds integration with boost.asio.
void async_accept_impl(Handler_func &&on_result, const Fine_time_pt &wait_until, bool reactor_pattern)
De-templated implementation of all async_accept() methods.
Server_socket(log::Logger *logger, const Peer_socket_options *child_sock_opts)
Constructs object.
void set_async_task_engine(util::Task_engine *target_async_task_engine)
Overwrites the value to be returned by next async_task_engine().
~Server_socket() override
Boring virtual destructor as in superclass. See notes there.
const util::Task_engine & async_task_engine_cref() const
Read-only version of async_task_engine().
static Ptr cast(net_flow::Server_socket::Ptr serv)
Convenience method that polymorphically casts from net_flow::Server_socket::Ptr to subclass pointer n...
util::Task_engine * async_task_engine()
Pointer (possibly null) for the flow::util::Task_engine used by any coming async I/O calls and inheri...
void async_accept(const boost::chrono::duration< Rep, Period > &max_wait, bool reactor_pattern, const Handler &on_result)
boost.asio-style asynchronous version that essentially performs net_flow::Server_socket::sync_accept(...
Handler_func handler_func(Handler &&on_result)
Returns a functor that essentially performs post() on_result onto *async_task_engine() in a way suita...
boost::shared_ptr< Server_socket > Ptr
Short-hand for shared_ptr to Server_socket.
Function< void(const Error_code &err_code, Peer_socket::Ptr new_sock)> Handler_func
Short-hand for the Task_engine-compatible accept Handler concrete type for class-internal code.
util::Task_engine * m_target_task_engine
See async_task_engine().
boost::shared_ptr< Server_socket > Ptr
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
Contains classes that add boost.asio integration to the main Flow-protocol classes such as net_flow::...
Definition: node.cpp:25
Fine_time_pt chrono_duration_from_now_to_fine_time_pt(const boost::chrono::duration< Rep, Period > &dur)
Helper that takes a non-negative duration of arbitrary precision/period and converts it to Fine_durat...
Definition: util.hpp:42
boost::asio::io_service Task_engine
Short-hand for boost.asio event service, the central class of boost.asio.
Definition: util_fwd.hpp:135
boost::system::error_code Error_code
Short-hand for a boost.system error code (which basically encapsulates an integer/enum error code and...
Definition: common.hpp:502
Fine_clock::time_point Fine_time_pt
A high-res time point as returned by Fine_clock::now() and suitable for precise time math in general.
Definition: common.hpp:407
A set of low-level options affecting a single Peer_socket.
Definition: options.hpp:36