Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
ConnectionManager.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Framework_WebServer_ConnectionManager_h_
5#define _Stroika_Framework_WebServer_ConnectionManager_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <list>
10#include <memory>
11
12#include "Stroika/Foundation/Common/Property.h"
13#include "Stroika/Foundation/Containers/Collection.h"
14#include "Stroika/Foundation/Containers/KeyedCollection.h"
15#include "Stroika/Foundation/Containers/Set.h"
19#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
20#include "Stroika/Foundation/IO/Network/Listener.h"
21#include "Stroika/Foundation/IO/Network/SocketAddress.h"
23#if qCompilerAndStdLib_function_dependency_too_complex_Buggy
25#endif
26
27#include "Stroika/Frameworks/WebServer/CORS.h"
28#include "Stroika/Frameworks/WebServer/Connection.h"
29#include "Stroika/Frameworks/WebServer/Request.h"
30#include "Stroika/Frameworks/WebServer/Response.h"
31#include "Stroika/Frameworks/WebServer/Router.h"
32
33/**
34 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
35 */
36
38
39 using namespace Stroika::Foundation;
43 using Containers::Set;
46 using Time::Duration;
48
49 /**
50 * This class is a useful helper for managing a set of connections. You can start it and stop it
51 * (it maintains internal threads). And you can hand it Connections, along with a set of handlers,
52 * and it will monitor the connections, and when any is ready with more input, it will assign the
53 * appropriate handler to handle the request, and produce the response.
54 *
55 * \note The connection manager supports HTTP keep-alives, to keep the connection open.
56 *
57 * \note Default ordering for interceptors:
58 * interceptors += earlyInterceptors;
59 * interceptors += beforeInterceptors;
60 * interceptors += router;
61 * interceptors += afterInterceptors;
62 *
63 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
64 *
65 * TODO:
66 * @todo We could allow updating most of these parameters. Some easily (like ServerHeader). Some would require stopping the
67 * connections, and rebuilding the list of threads etc (cuz must redo bindings). For now, KISS,
68 * and only allow modifications as needed.
69 */
71 public:
72 /**
73 * \brief ConnectionManager::Options specify things like default headers, caching policies, binding flags (not bindings), thread count
74 */
75 struct Options {
76
77 /**
78 * This is the max number of TCP connections the webserver will allow to keep around, before starting
79 * to reject new connections.
80 *
81 * \note NYI tracking and rejecting extra connections - just used as a hint for other values
82 */
83 optional<unsigned int> fMaxConnections;
84
85 /**
86 * This is basically the number of threads to use. It can be automatically inferred from fMaxConnections
87 * but can be specified explicitly.
88 */
89 optional<unsigned int> fMaxConcurrentlyHandledConnections;
90
91 /**
92 * If bindFlags omitted, it defaults to kDefault_BindFlags
93 */
94 optional<Socket::BindFlags> fBindFlags;
95
96 /**
97 * fDefaultResponseHeaders may be used to specify initial{default} values for some HTTP Response headers.
98 * It can be used for specifying any non-standard HTTP headers to add to all responded (like X-Foobar: value).
99 * It can be used to specify a default 'Server' header.
100 * This is equivalent to adding an interceptor early in the interceptor chain, and calling
101 * response.rwHeaders = *fDefaultResponseHeaders;
102 *
103 * Probably not a good idea to specify things like contentLength, etc here. That may produce bad results.
104 * @todo consider listing a stronger set of requirements for what headings can and cannot be set?)
105 *
106 * This is also a good place to add defaults like:
107 * "Content-Type": DataExchange::InternetMediaTypes::kOctetStream (which was done automatically by Stroika before 2.1b10)
108 */
109 optional<Headers> fDefaultResponseHeaders;
110
111 /**
112 * fDefaultGETResponseHeaders - like fDefaultResponseHeaders - may be used to specify initial{default} values for some HTTP headers,
113 * but it is only applied to GET requests (in addition to those applied from fDefaultResponseHeaders which applied to all responses).
114 *
115 * An example of something that makes sense to apply here would be Cache-Control settings (since these are unneeded on other HTTP methods
116 * typically).
117 */
118 optional<Headers> fDefaultGETResponseHeaders;
119
120 /**
121 * Options for how the HTTP Server handles CORS (mostly HTTP OPTIONS requests)
122 */
123 optional<CORSOptions> fCORS;
124
125 /**
126 * \brief sets the initial value for each Response. Harmless except for the slight performance cost (wont always work) - see Response::autoComputeETag
127 *
128 * defaults to kDefault_AutoComputeETagResponse
129 */
131
132 /**
133 * This feature causes sockets to automatically flush their data - and avoid connection reset - when possible.
134 * This makes the closing socket process more costly and slow, so is optional, but is on by default because it makes
135 * communications more reliable.
136 *
137 * Turn this on - especially - if you see connection reset when clients talk to the Stroika web-server (TCP RST sent).
138 *
139 * \note - this defaults to 2 seconds (kDefault_AutomaticTCPDisconnectOnClose)
140 */
142
143 /**
144 * @see Socket::SetLinger () - SO_LINGER
145 */
146 optional<int> fLinger;
147
148 /**
149 * Is the TCP_NODELAY algorithm being used? Generally - this increases latency, for the benefit of better bandwidth utilization.
150 *
151 * See https://brooker.co.za/blog/2024/05/09/nagle.html for hints about if its right for you?
152 */
153 optional<bool> fTCPNoDelay;
154
155 /**
156 * mostly for debugging - so easier to segregate names of threads if you have
157 * multiple thread pools/connection managers
158 */
159 optional<String> fThreadPoolName;
160
161 /**
162 * Statistics are available (from the statistics()) property whether this is true or false. But this
163 * controls whether or not extra statistics are collected and made available.
164 *
165 * Note collecting statistics costs a modest amount of memory and time.
166 */
168
169 /**
170 * This controls the time used to compute field statistics().fConnections.fConnectionsPiningForTheFjords
171 */
173
174 /**
175 * The number of new TCP connections the kernel will buffer before the application has a chance to accept.
176 *
177 * This can typically be left unset, and defaults to be based on fMaxConnections.
178 *
179 * The default for tcp backlog is a little less than max # of connections. What makes
180 * sense depends on ratio of incoming connections to the lifetime of those calls. If high, make this more than
181 * number of connections. If low, then can be less than max# of connections.
182 *
183 * @see http://man7.org/linux/man-pages/man2/listen.2.html
184 */
185 optional<unsigned int> fTCPBacklog;
186
187 /**
188 * \brief controls whether to use Transfer-Coding: chunked or not
189 *
190 * \see WebServer::Connection::Options::fAutomaticTransferChunkSize
191 * \see WebServer::Response::automaticTransferChunkSize
192 */
194
195 /**
196 * \brief override the set of compression encodings the WebServer supports (default is all the Stroika implementation is built to support)
197 *
198 * \see WebServer::Connection::Options::fSupportedCompressionEncodings
199 */
200 optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings;
201
202 static constexpr unsigned int kDefault_MaxConnections{25};
203 static constexpr Socket::BindFlags kDefault_BindFlags{};
204 static inline const Headers kDefault_Headers{Iterable<KeyValuePair<String, String>>{{IO::Network::HTTP::HeaderName::kServer, "Stroika/3.0"sv}}};
205 static inline const CORSOptions kDefault_CORS{[] () { return kDefault_CORSOptions; }()};
206 static constexpr bool kDefault_AutoComputeETagResponse{true};
207 static constexpr Duration kDefault_AutomaticTCPDisconnectOnClose{2.0s};
208 static constexpr optional<int> kDefault_Linger{nullopt}; // intentionally optional-valued
209 static constexpr bool kDefault_TCPNoDelay{true}; // https://brooker.co.za/blog/2024/05/09/nagle.html (defaults to NO DELAY - disable Nagle - because we are carefully to only write once when the response is fully read)
210 };
211 static const Options kDefaultOptions;
212
213 public:
214 /**
215 */
216 ConnectionManager (const SocketAddress& bindAddress, const Sequence<Route>& routes, const Options& options = kDefaultOptions);
217 ConnectionManager (const Traversal::Iterable<SocketAddress>& bindAddresses, const Sequence<Route>& routes, const Options& options = kDefaultOptions);
218 ConnectionManager (const ConnectionManager&) = delete;
219#if qStroika_Foundation_Debug_DefaultTracingOn
220 ~ConnectionManager ();
221#else
222 ~ConnectionManager () = default;
223#endif
224
225 public:
226 nonvirtual ConnectionManager& operator= (const ConnectionManager&) = delete;
227
228 private:
230 decltype ([] (const Connection::Stats& t) { return t.fSocketID; })>;
231
232 public:
234
235 public:
236 /**
237 * Get the list of interceptors after the private ConnectionManager interceptors (e.g. router).
238 * @see beforeInterceptors
239 *
240 * @see earlyInterceptors, beforeInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
241 *
242 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
243 */
245
246 public:
247 /**
248 * Get the list of interceptors before the private ConnectionManager interceptors (e.g. router).
249 *
250 * @see earlyInterceptors, afterInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
251 *
252 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
253 */
255
256 public:
257 /**
258 * \brief Return the socket addresses the webserver (connection manager) is listening on (to serve web content).
259 */
261
262 public:
263 /**
264 * Each connection maybe active (currently engaged in the threadpool processing) or inactive (waited on via select/epoll).
265 * To see just summary statistics, use the statistics() property () of this class.
266 */
268
269 public:
270 /**
271 * This defaults to @DefaultFaultInterceptor, but can be set to 'missing' or any other fault handler. Not also - that
272 * all interceptors can engage in fault handling. This is just meant to provide a simple one-stop-shop for how to
273 * handle faults in one place.
274 */
276
277 public:
278 /**
279 * Get/Set the list of interceptors early interceptors. These default to:
280 * earlyInterceptors += ServerHeadersInterceptor_{serverHeader, corsSupportMode};
281 * if (defaultFaultHandler) {
282 * earlyInterceptors += *defaultFaultHandler;
283 * }
284 *
285 * @see beforeInterceptors, afterInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
286 *
287 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
288 */
290
291 public:
292 /**
293 * Returns the 'effective' options after applying defaults, not (generally) the original options.
294 */
296
297 public:
298 /**
299 * Basic statistics about the state of the ConnectionManager (webserver).
300 */
301 struct Statistics {
302 /**
303 */
304 struct ThreadPool : Execution::ThreadPool::Statistics {
305 size_t fThreadEntryCount{};
306
307 /**
308 * See Characters::ToString ()
309 */
310 nonvirtual Characters::String ToString () const;
311 };
312 ThreadPool fThreadPool;
313
314 /**
315 * NOTE - call 'connections' property to get full details... - not summary stats
316 */
318 /**
319 * Number of sockets with TCP connections between the server web server clients.
320 */
322
323 /**
324 * The subset of open connections where there is current activity, such as reading headers,
325 * or processing the body of the request, or sending the response (a thread allocated to doing work for this connection).
326 */
328
329 /**
330 * Each connection that still exists has been open for a given amount of time. Median of those.
331 */
333
334 /**
335 * Each connection will in general serve many requests (due to connection keep-alives). This computes
336 * the median duration of currently open requests. @todo CLARIFY
337 *
338 * \note REQUIRES qStroika_Framework_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
339 */
341
342 /**
343 * Each connection will in general serve many requests (due to connection keep-alives). This computes
344 * the median duration of currently active connection requests.
345 *
346 * \note REQUIRES qStroika_Framework_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
347 */
349
350 /**
351 * Count the number of currently running requests which have taken longer than options.fConnectionPiningForTheFjordsDelay
352 *
353 * This can help to detect deadlocked/or problematically slow web service APIs.
354 *
355 * If this number is non-zero, try the connections () property, and look through for connections with problematic times.
356 *
357 * \note REQUIRES qStroika_Framework_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
358 */
360
361 /**
362 * See Characters::ToString ()
363 */
364 nonvirtual Characters::String ToString () const;
365 };
366
367 /*
368 * \brief statistics about current and recent connections and request durations.
369 *
370 * \see connections() property for more details about currently connected sockets.
371 */
372 ConnectionStatistics fConnections;
373
374 /**
375 * See Characters::ToString ()
376 */
377 nonvirtual Characters::String ToString () const;
378 };
379
380 public:
381 /**
382 * Then this can be used to fetch the current thread pool statistics.
383 *
384 * \note set options.fCollectStatistics = true to use this most effectively - some information maybe missing if this is not set.
385 */
387
388 private:
389 nonvirtual Statistics ComputeStatistics_ () const;
390
391 public:
392 /**
393 * These 'before' and 'after' values are relative to the router, which towards the end of the chain.
394 *
395 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
396 */
398 ePrependsToEarly,
399 ePrepend,
400 eAppend,
401 eAfterBeforeInterceptors,
402 };
403 using InterceptorAddRelativeTo::eAfterBeforeInterceptors;
404 using InterceptorAddRelativeTo::eAppend;
405 using InterceptorAddRelativeTo::ePrepend;
406 using InterceptorAddRelativeTo::ePrependsToEarly;
407
408 public:
409 /**
410 */
411 nonvirtual void AddInterceptor (const Interceptor& i, InterceptorAddRelativeTo relativeTo);
412
413 public:
414 /**
415 */
416 nonvirtual void RemoveInterceptor (const Interceptor& i);
417
418 public:
419 /**
420 */
421 nonvirtual void AbortConnection (const shared_ptr<Connection>& conn);
422
423 private:
424 nonvirtual void DeriveConnectionDefaultOptionsFromEffectiveOptions_ ();
425
426 private:
427 nonvirtual void onConnect_ (const ConnectionOrientedStreamSocket::Ptr& s);
428
429 private:
430 nonvirtual void WaitForReadyConnectionLoop_ ();
431
432 private:
433 // Inactive connections are those we are waiting (select/epoll) for incoming data; these are stored in fInactiveSockSetPoller_
434 nonvirtual Collection<shared_ptr<Connection>> GetInactiveConnections_ () const;
435
436 private:
437 nonvirtual void ReplaceInEarlyInterceptor_ (const optional<Interceptor>& oldValue, const optional<Interceptor>& newValue);
438
439 private:
440 Options fEffectiveOptions_;
441 Traversal::Iterable<SocketAddress> fBindings_; // just to return bindings API
442#if qCompilerAndStdLib_function_dependency_too_complex_Buggy
443 // BWA not too bad cuz ConnectionManager(const ConnectionManager&)=delete and op= as well.
444 shared_ptr<Connection::Options> fUseDefaultConnectionOptions_BWA_{Memory::MakeSharedPtr<Connection::Options> ()};
445 Connection::Options& fUseDefaultConnectionOptions_{*fUseDefaultConnectionOptions_BWA_};
446#else
447 Connection::Options fUseDefaultConnectionOptions_;
448#endif
453 Execution::Synchronized<optional<Duration>> fAutomaticTCPDisconnectOnClose_;
454 Router fRouter_;
455
456 // Active connections are those actively in the readheaders/readbody, dispatch/handle code
458
459 struct MyWaitForIOReady_Traits_ {
460 using HighLevelType = shared_ptr<Connection>;
461 static inline auto GetSDKPollable (const HighLevelType& t)
462 {
463 return t->socket ().GetNativeSocket ();
464 }
465 };
466 // No need to lock fInactiveSockSetPoller_ since its internally synchronized;
467 Execution::UpdatableWaitForIOReady<shared_ptr<Connection>, MyWaitForIOReady_Traits_> fInactiveSockSetPoller_{};
468
469 /*
470 * SUBTLE DATA MEMBER ORDERING NOTE!
471 * We count on the THREADS that run and manipulate all the above data members are all listed AFTER those data
472 * members in this object. This is just for the convenient ordering that imposes on construction and destruction:
473 * the threads (declared below) are automatically shutdown on destruction before the data they reference (above)
474 *
475 * Same with the listener, as this is basically a thread invoking calls on the above data members.
476 */
477
478 // we may eventually want two thread pools - one for managing bookkeeping/monitoring harvests, and one for actually handling
479 // connections. Or maybe a single thread for the bookkeeping, and the pool for handling ongoing connections?
480 //
481 // But for now - KISS
482 //
483 // Note - for now - we don't even handle 'accepting' connections in the threadpool!!! - just one thread
484 Execution::ThreadPool fActiveConnectionThreads_;
485
486 Execution::Thread::CleanupPtr fWaitForReadyConnectionThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
487
488 // Note: this must be declared after the threadpool so its shutdown on destruction before the thread pool, and doesn't try to launch
489 // new tasks into an already destroyed threadpool.
490 IO::Network::Listener fListener_;
491 };
492
493 inline const ConnectionManager::Options ConnectionManager::kDefaultOptions;
494
495}
496
497/*
498 ********************************************************************************
499 ***************************** Implementation Details ***************************
500 ********************************************************************************
501 */
502#include "ConnectionManager.inl"
503
504#endif /*_Stroika_Framework_WebServer_ConnectionManager_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
Simple wrapper on WaitForIOReady (POSIX select/poll/etc API) - except it allows for the list if polle...
roughly equivalent to Association<String,String>, except that the class is smart about certain keys a...
Definition Headers.h:129
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
Common::ReadOnlyProperty< Statistics > statistics
Common::ReadOnlyProperty< ConnectionStatsCollection > connections
Common::ReadOnlyProperty< Traversal::Iterable< SocketAddress > > bindings
Return the socket addresses the webserver (connection manager) is listening on (to serve web content)...
Common::Property< Sequence< Interceptor > > earlyInterceptors
Common::Property< Sequence< Interceptor > > beforeInterceptors
Common::Property< optional< Interceptor > > defaultErrorHandler
Common::ReadOnlyProperty< const Options & > options
Common::Property< Sequence< Interceptor > > afterInterceptors
ConnectionManager::Options specify things like default headers, caching policies, binding flags (not ...
optional< bool > fAutoComputeETagResponse
sets the initial value for each Response. Harmless except for the slight performance cost (wont alway...
optional< Containers::Set< HTTP::ContentEncoding > > fSupportedCompressionEncodings
override the set of compression encodings the WebServer supports (default is all the Stroika implemen...
optional< size_t > fAutomaticTransferChunkSize
controls whether to use Transfer-Coding: chunked or not