Flow 1.0.2
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#include "flow/log/log.hpp"
25#include "flow/util/util.hpp"
28#include <boost/unordered_set.hpp>
29#include <boost/enable_shared_from_this.hpp>
30#include <queue>
31
32namespace flow::net_flow
33{
34// Types.
35
36/**
37 * A server socket able to listen on a single Flow port for incoming connections and return peer
38 * sockets (Peer_socket objects) to the local user once such connections are established.
39 *
40 * ### Life cycle of a Server_socket ###
41 * A given Server_socket can only arise by calling Node::listen().
42 * Node generates a new Server_socket and returns it (factory pattern). Server_socket is not
43 * instantiable otherwise. A Server_socket cannot be deleted explicitly by the user and will only
44 * be returned via `boost::shared_ptr<>`; when both the Node and all user code no longer refers to
45 * it, the Server_socket will be destroyed.
46 *
47 * Once a Flow-protocol user has a Server_socket object, that object represents a socket in one of the
48 * following basic states:
49 *
50 * - Listening.
51 * - accept() may or may not return peer socket ("Acceptable").
52 * - Closing.
53 * - accept() will never return peer socket (never Acceptable).
54 * - Closed.
55 * - accept() will never return peer socket (never Acceptable).
56 *
57 * Exactly the following state transitions are possible for a given Server_socket returned by Node:
58 *
59 * - start => Listening
60 * - start => Closing
61 * - start => Closed
62 * - Listening => Closing
63 * - Listening => Closed
64 * - Closing => Closed
65 *
66 * Note, in particular, that Closed is final; socket cannot move from Closed to Listening. If after
67 * an error or valid closing you want to reestablish a server, obtain a new Server_socket
68 * from Node's factory. Same rationale as for equivalent design decision in Peer_socket.
69 *
70 * Closing means either this side or other side initiated the closing of this server socket for any
71 * reason, but Node is still finishing up operations related to that in the background (such as
72 * closing in-progress peer connections). Closed means Node has finished any such operations and
73 * has disowned the Server_socket.
74 *
75 * In either case the only operation Server_socket supports (accept() and derivatives thereof) is
76 * impossible in Closing and Closed. Therefore the two states are distinguished for
77 * diagnostic/informational purposes only. Generally one should only accept() when Acceptable, and
78 * accept() will return an error if the state is Closing or Closed.
79 *
80 * Note that a Closing or Closed Server_socket does NOT mean that already accept()ed Peer_socket objects
81 * will be in any way affected.
82 *
83 * @todo Implement `Server_socket::close()` functionality -- at least the equivalent of
84 * Peer_socket::close_abruptly().
85 *
86 * ### Accept queue ###
87 * Server_socket, like a TCP server socket, stores a queue of fully established peer
88 * connections. A Peer_socket is placed onto this internal queue only once its state() ==
89 * State::S_OPEN (i.e., the connection handshake is finished/successful). Peer_socket objects are then
90 * obtained from this queue via the Server_socket::accept() call and its derivatives.
91 *
92 * ### Efficiently accept()ing ###
93 * The sync_accept() method is efficient, in that it uses no processor
94 * cycles until Acceptable is achieved (i.e., it sleeps until that point). The non-blocking
95 * accept() method doesn't sleep/block, however. For a program using it to be efficient it should
96 * sleep until Acceptable and only then call accept(), when a Peer_socket is certainly
97 * available for immediate acceptance. Moreover, a complex program is likely to want to perform
98 * this sleep-and-conditional-wake on a set of several Server_sockets (and/or other sockets)
99 * simultaneously (similarly to `select()`, `epoll*()`, etc.). Use class Event_set for this purpose.
100 *
101 * ### Thread safety ###
102 * Same as for Node. (Briefly: all operations safe for simultaneous execution on separate or the same object.)
103 *
104 * @internal
105 *
106 * Implementation notes
107 * --------------------
108 * See Peer_socket documentation header. Similar comments apply here.
109 *
110 * To prevent node.cpp from being unmanageably large (and also because it makes sense),
111 * implementations for Node methods that deal only with an individual Server_socket objects reside in
112 * server_socket.cpp (even though they are members of Node, since, again, the logic is all forwarded to Node).
113 *
114 * @todo Limit Server_socket listen queue/set length.
115 *
116 * @todo Rename `State` to `Phase`, as with similar to-do in class Peer_socket doc header.
117 */
120 // Endow us with shared_ptr<>s ::Ptr and ::Const_ptr (syntactic sugar).
121 public util::Shared_ptr_alias_holder<boost::shared_ptr<Server_socket>>,
122 // Allow access to Ptr(this) from inside Server_socket methods. Just call shared_from_this().
123 public boost::enable_shared_from_this<Server_socket>,
124 public log::Log_context,
125 private boost::noncopyable
126{
127public:
128 // Types.
129
130 /// State of a Server_socket.
131 enum class State
132 {
133 /// Future or current accept()s may be possible. A socket in this state may be Acceptable.
134 S_LISTENING,
135 /// No accept()s are or will be possible, but Node is still finishing up the closing operation.
136 S_CLOSING,
137 /// No accept()s are or will be possible, AND Node has disowned the Server_socket.
138 S_CLOSED
139 };
140
141 /// Equivalent to Peer_socket::Ptr, but can't use that due to C++'s circular reference nonsense.
142 using Peer_socket_ptr = boost::shared_ptr<Peer_socket>;
143
144 // Constructors/destructor.
145
146 /// Boring `virtual` destructor. Note that deletion is to be handled exclusively via `shared_ptr`, never explicitly.
147 ~Server_socket() override;
148
149 // Methods.
150
151 /**
152 * Current State of the socket.
153 * @return Current state of the socket.
154 */
155 State state() const;
156
157 /**
158 * Node that produced this Server_socket.
159 * @return Pointer to (guaranteed valid) Node; 0 if state is S_CLOSED.
160 */
161 Node* node() const;
162
163 /**
164 * The local Flow-protocol port on which this server is or was listening. For a given Server_socket, this
165 * will always return the same value, even if CLOSED. However, when CLOSED, the port may be
166 * unused or taken by another socket.
167 *
168 * @return See above.
169 */
170 flow_port_t local_port() const;
171
172 /**
173 * Non-blocking accept: obtain socket for the least recently established not-yet-obtained peer
174 * connection on this server. There is a queue (FIFO) of not-yet-claimed connections, and this
175 * returns the one at the front of the queue.
176 *
177 * If state() is State::S_CLOSING or State::S_CLOSED (i.e., not State::S_LISTENING), this will return null (and an
178 * error), even if connections have been queued up. Rationale: BSD sockets act similarly: cannot
179 * succeed with `accept(s)`, if `s` is not listening; also internal implementation is simpler.
180 * Anti-rationale: our API is much more amenable to allowing accept()s in that situation; and it
181 * would mirror the fact that Peer_socket::receive() can succeed in `S_OPEN+S_DISCONNECTING` state.
182 * Why rationale > anti-rationale: it's a judgment call, and I'm going with simplicity of
183 * implementation at least for now.
184 *
185 * @todo Reconsider allowing successful accept() in State::S_CLOSING state?
186 *
187 * @param err_code
188 * See flow::Error_code docs for error reporting semantics.
189 * Note: no available connections is not, in itself, an error. So it's quite possible for
190 * null to be returned but `*err_code` is success.
191 * @return A Peer_socket `sock` with `sock->state() == Peer_socket::State::S_OPEN`. If no
192 * connections are available (including if `bool(*err_code) == true`), returns null pointer.
193 */
194 Peer_socket_ptr accept(Error_code* err_code = 0);
195
196 /**
197 * Blocking (synchronous) version of accept(). Acts just like accept(), except that if `*this` is
198 * not immediately Acceptable (i.e., accept() would return null and no error), waits until it is
199 * Acceptable (accept() would return either non-null, or null and an error) and returns
200 * `accept(err_code)`. In `reactor_pattern` mode (see arg doc), if it were to otherwise return a non-null
201 * `Peer_socket::Ptr`, it instead leaves the ready peer socket available for subsequence acceptance and
202 * returns null.
203 *
204 * If a timeout is specified, and this timeout expires before socket is
205 * Acceptable, acts like accept() executed on an un-Acceptable server socket.
206 *
207 * ### Error handling ###
208 * These are the possible outcomes.
209 * 1. Connection succeeds before the given timeout expires (or succeeds, if no timeout given).
210 * Socket is at least Writable at time of return. If `!reactor_pattern` then the new socket is returned,
211 * and no error is returned via `*err_code`; otherwise the socket is left available for acceptance, while
212 * null is returned, and (similarly to non-reactor-pattern mode) no error is returned via `*err_code`.
213 * 2. Connection fails before the given timeout expires (or fails, if no timeout given). null
214 * is returned, `*err_code` is set to reason for connection failure unless null.
215 * (If `err_code` null, Runtime_error thrown.) The code error::Code::S_WAIT_INTERRUPTED means the
216 * wait was interrupted (similarly to POSIX's `EINTR`).
217 * 3. A user timeout is given, and the connection does not succeed before it expires.
218 * Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT.
219 * (Rationale: consistent with sync_receive(), sync_send() behavior.)
220 *
221 * Note that -- if `!reactor_pattern` -- it is NOT possible to return null and no error.
222 *
223 * Tip: Typical types you might use for `max_wait`: `boost::chrono::milliseconds`,
224 * `boost::chrono::seconds`, `boost::chrono::high_resolution_clock::duration`.
225 *
226 * @see The version of sync_accept() with no timeout.
227 * @tparam Rep
228 * See boost::chrono::duration documentation (and see above tip).
229 * @tparam Period
230 * See boost::chrono::duration documentation (and see above tip).
231 * @param max_wait
232 * The maximum amount of time from now to wait before giving up on the wait and returning.
233 * `"duration<Rep, Period>::max()"` will eliminate the time limit and cause indefinite wait
234 * (i.e., no timeout).
235 * @param reactor_pattern
236 * If and only if `true`, and the call successfully waited until server socket became Acceptable, then
237 * we return a null pointer and leave the peer socket ready to be accepted by the caller. If `false`, then
238 * in that same situation the socket is accepted and returned. The parameter doesn't affect any other
239 * situations.
240 * @param err_code
241 * See flow::Error_code docs for error reporting semantics.
242 * @return Reference-counted pointer to new Server_socket; or an empty pointer (essentially null).
243 * Reminder that in `reactor_pattern` mode this may be null, yet indeed a socket is Acceptable
244 * (the presence or lack of an error indicates whether that's the case).
245 */
246 template<typename Rep, typename Period>
247 Peer_socket_ptr sync_accept(const boost::chrono::duration<Rep, Period>& max_wait,
248 bool reactor_pattern = false,
249 Error_code* err_code = 0);
250
251 /**
252 * Equivalent to `sync_accept(duration::max(), reactor_pattern, err_code)`; i.e., sync_accept() with no user
253 * timeout.
254 *
255 * @param err_code
256 * See other sync_accept().
257 * @param reactor_pattern
258 * See other sync_accept().
259 * @return See other sync_accept().
260 */
261 Peer_socket_ptr sync_accept(bool reactor_pattern = false, Error_code* err_code = 0);
262
263 /**
264 * The error code that perviously caused state() to become State::S_CLOSED, or success code if state
265 * is not CLOSED. Note that once it returns non-success, the returned value subsequently will always be the same.
266 *
267 * @return Ditto.
268 */
270
271protected:
272 // Constructors/destructor.
273
274 /**
275 * Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily
276 * meaningful values.
277 *
278 * @param logger
279 * The Logger implementation to use subsequently.
280 * @param child_sock_opts
281 * Pointer to a per-socket options `struct` to copy and store; for each Peer_socket resulting
282 * from this Server_socket, the options will be a copy of this copy. If null pointer, then
283 * instead the enclosing Node's global per-socket options will be used to produce the
284 * copy.
285 */
286 explicit Server_socket(log::Logger* logger, const Peer_socket_options* child_sock_opts);
287
288private:
289 // Friends.
290
291 /**
292 * See rationale for `friend`ing Node in class Server_socket documentation header.
293 * @see Node.
294 */
295 friend class Node;
296
297 // Types.
298
299 /**
300 * Short-hand for reentrant mutex type. We explicitly rely on reentrant behavior, so this isn't "just in case."
301 *
302 * @todo This doc header for Server_socket::Mutex should specify what specific behavior requires mutex reentrance, so
303 * that for example one could reevaluate whether there's a sleeker code pattern that would avoid it.
304 */
306
307 /// Short-hand for RAII lock guard of #Mutex.
309
310 /* Methods: as few as possible. Logic should reside in class Node as discussed (though
311 * implementation may be in server_socket.cpp). See rationale in class Server_socket
312 * documentation header. */
313
314 /**
315 * Same as sync_accept() but uses a #Fine_clock-based #Fine_duration non-template type
316 * for implementation convenience and to avoid code bloat to specify timeout.
317 *
318 * @param wait_until
319 * See `sync_accept(timeout)`. This is the absolute time point corresponding to that.
320 * `"duration<Rep, Period>::max()"` maps to the value `Fine_time_pt()` (Epoch) for this argument.
321 * @param reactor_pattern
322 * See sync_accept().
323 * @param err_code
324 * See sync_accept().
325 * @return See sync_accept().
326 */
327 Peer_socket_ptr sync_accept_impl(const Fine_time_pt& wait_until, bool reactor_pattern, Error_code* err_code);
328
329 // Data.
330
331 /**
332 * Either null or the pointer to a copy of the template Peer_socket_options intended for resulting
333 * Peer_socket objects. Null means Peer_socket should use Node::options() as the template instead.
334 *
335 * Must be deleted in destructor if not null.
336 */
338
339 /// See state(). Should be set before user gets access to `*this`. Must not be modified by non-W threads.
341
342 /**
343 * See node(). Should be set before user gets access to `*this` and not changed, except to 0 when
344 * state is S_CLOSED. Must not be modified by non-W threads.
345 */
347
348 /**
349 * See local_port(). Should be set before user gets access to `*this` and not changed afterwards.
350 * @todo Make #m_local_port `const`?
351 */
353
354 /**
355 * Queue of passively opened sockets in Peer_socket::Int_state::S_ESTABLISHED internal state that have not yet been
356 * claimed by the user via `*accept()`. `back()` is the next socket to be accepted (i.e., the one
357 * that established connection longest ago). This is not the more restricted `queue<>`,
358 * because sometimes we want to remove things from the middle of it on error. It is not a `list<>`, because
359 * we sometimes (on error) need to erase items from the middle which requires a lookup by stored #Peer_socket_ptr
360 * value which would be linear-time in a plain `list<>`.
361 *
362 * Write-accessible from thread W and user threads (in `accept()`) and must be protected by a mutex.
363 *
364 * @see Peer_socket::m_originating_serv for the closely related inverse.
365 */
367
368 /**
369 * The #Error_code causing this server's move from LISTENING state (if this has occurred); otherwise a
370 * clear (success) #Error_code. This starts as success and may move to one non-success value
371 * and then never change after that. Graceful closing of the server via `close()` is indeed counted as a
372 * non-success value for #m_disconnect_cause.
373 *
374 * As in TCP net-stacks, one cannot recover from a transmission error or termination on the socket
375 * (the "error" `EWOULDBLOCK`/`EAGAIN` does not count), which is why this can only go from success ->
376 * non-success and never change after that. (boost.asio error would be `would_block` or `try_again` when
377 * I said `EWOUDLBLOCK`/`EAGAIN` informally there.)
378 *
379 * How to report this to the user: attempting to `*accept()` or other operations while
380 * #m_disconnect_cause is not success => the operation returns this #Error_code to the user.
381 * Note that even already queued acceptable sockets (#m_unaccepted_socks) will no longer be
382 * available for acceptance.
383 *
384 * This should be success in LISTENING state and failure otherwise.
385 *
386 * ### Thread safety ###
387 * Since user threads will access this at least via accept(), while thread W may
388 * set it when implementing the server socket close, this must be protected by a mutex.
389 */
391
392 /**
393 * This object's mutex. The protected items are #m_state, #m_node,
394 * #m_unaccepted_socks, `sock->m_originating_serv` for each `sock` in #m_unaccepted_socks, and
395 * #m_disconnect_cause.
396 */
397 mutable Mutex m_mutex;
398
399 /**
400 * Set of passively opening sockets in pre-ESTABLISHED (so SYN_RCVD?) internal state (and thus
401 * are not yet ready to be given out via `*accept()`). This gains meaning only in thread W. This
402 * should NOT be accessed outside of thread W and is not protected my a mutex.
403 *
404 * Once a socket is acceptable (ESTABLISHED), it is moved from this to #m_unaccepted_socks, where
405 * it can be claimed by the user. Thus the user never accesses this, and it is maintained by
406 * thread W only.
407 *
408 * @see Peer_socket::m_originating_serv for the closely related inverse.
409 */
410 boost::unordered_set<Peer_socket_ptr> m_connecting_socks;
411}; // class Server_socket
412
413// Free functions: in *_fwd.hpp.
414
415// However the following refer to inner type(s) and hence must be declared here and not _fwd.hpp.
416
417/**
418 * Prints string representation of given socket state to given standard `ostream` and returns the
419 * latter.
420 *
421 * @relatesalso Server_socket
422 *
423 * @param os
424 * Stream to print to.
425 * @param state
426 * Value to serialize.
427 * @return `os`.
428 */
429std::ostream& operator<<(std::ostream& os, Server_socket::State state);
430
431// Template implementations.
432
433template<typename Rep, typename Period>
434Server_socket::Peer_socket_ptr Server_socket::sync_accept(const boost::chrono::duration<Rep, Period>& max_wait,
435 bool reactor_pattern, Error_code* err_code)
436{
437 assert(max_wait.count() > 0);
438 return sync_accept_impl(util::chrono_duration_from_now_to_fine_time_pt(max_wait), reactor_pattern, err_code);
439}
440
441} // namespace flow::net_flow
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Definition: log.hpp:1619
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...
Peer_socket_ptr sync_accept(const boost::chrono::duration< Rep, Period > &max_wait, bool reactor_pattern=false, Error_code *err_code=0)
Blocking (synchronous) version of accept().
util::Mutex_recursive Mutex
Short-hand for reentrant mutex type.
boost::unordered_set< Peer_socket_ptr > m_connecting_socks
Set of passively opening sockets in pre-ESTABLISHED (so SYN_RCVD?) internal state (and thus are not y...
Server_socket(log::Logger *logger, const Peer_socket_options *child_sock_opts)
Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily meani...
boost::shared_ptr< Peer_socket > Peer_socket_ptr
Equivalent to Peer_socket::Ptr, but can't use that due to C++'s circular reference nonsense.
Mutex m_mutex
This object's mutex.
Error_code disconnect_cause() const
The error code that perviously caused state() to become State::S_CLOSED, or success code if state is ...
Peer_socket_options const *const m_child_sock_opts
Either null or the pointer to a copy of the template Peer_socket_options intended for resulting Peer_...
State m_state
See state(). Should be set before user gets access to *this. Must not be modified by non-W threads.
util::Linked_hash_set< Peer_socket_ptr > m_unaccepted_socks
Queue of passively opened sockets in Peer_socket::Int_state::S_ESTABLISHED internal state that have n...
Peer_socket_ptr sync_accept_impl(const Fine_time_pt &wait_until, bool reactor_pattern, Error_code *err_code)
Same as sync_accept() but uses a Fine_clock-based Fine_duration non-template type for implementation ...
State
State of a Server_socket.
State state() const
Current State of the socket.
flow_port_t m_local_port
See local_port().
~Server_socket() override
Boring virtual destructor. Note that deletion is to be handled exclusively via shared_ptr,...
flow_port_t local_port() const
The local Flow-protocol port on which this server is or was listening.
util::Lock_guard< Mutex > Lock_guard
Short-hand for RAII lock guard of Mutex.
Node * node() const
Node that produced this Server_socket.
Error_code m_disconnect_cause
The Error_code causing this server's move from LISTENING state (if this has occurred); otherwise a cl...
Peer_socket_ptr accept(Error_code *err_code=0)
Non-blocking accept: obtain socket for the least recently established not-yet-obtained peer connectio...
An empty interface, consisting of nothing but a default virtual destructor, intended as a boiler-plat...
Definition: util.hpp:45
Convenience class template that endows the given subclass T with nested aliases Ptr and Const_ptr ali...
Flow module containing the API and implementation of the Flow network protocol, a TCP-inspired stream...
Definition: node.cpp:25
uint16_t flow_port_t
Logical Flow port type (analogous to a UDP/TCP port in spirit but in no way relevant to UDP/TCP).
std::ostream & operator<<(std::ostream &os, const Congestion_control_selector::Strategy_choice &strategy_choice)
Serializes a Peer_socket_options::Congestion_control_strategy_choice enum to a standard ostream – the...
Definition: cong_ctl.cpp:146
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::unique_lock< Mutex > Lock_guard
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of tha...
Definition: util_fwd.hpp:265
boost::recursive_mutex Mutex_recursive
Short-hand for reentrant, exclusive mutex.
Definition: util_fwd.hpp:218
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:503
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:408
A set of low-level options affecting a single Peer_socket.
Definition: options.hpp:36