Stroika Library 3.0d25
 
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 unsigned int fMaxConnections{kDefault_MaxConnections};
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:
229#if qCompilerAndStdLib_lambdas_in_unevaluatedContext_warning_Buggy
230 // a named key extractor: g++ <= 14 gives the lambda's type internal linkage, and warns (-Wsubobject-linkage) of the
231 // field connections, whose type uses it
232 struct StatsSocketID_ {
233 auto operator() (const Connection::Stats& t) const
234 {
235 return t.fSocketID;
236 }
237 };
239#else
241 decltype ([] (const Connection::Stats& t) { return t.fSocketID; })>;
242#endif
243
244 public:
246
247 public:
248 /**
249 * Get the list of interceptors after the private ConnectionManager interceptors (e.g. router).
250 * @see beforeInterceptors
251 *
252 * @see earlyInterceptors, beforeInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
253 *
254 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
255 */
257
258 public:
259 /**
260 * Get the list of interceptors before the private ConnectionManager interceptors (e.g. router).
261 *
262 * @see earlyInterceptors, afterInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
263 *
264 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
265 */
267
268 public:
269 /**
270 * \brief Return the socket addresses the webserver (connection manager) is listening on (to serve web content).
271 */
273
274 public:
275 /**
276 * Each connection maybe active (currently engaged in the threadpool processing) or inactive (waited on via select/epoll).
277 * To see just summary statistics, use the statistics() property () of this class.
278 */
280
281 public:
282 /**
283 * This defaults to @DefaultFaultInterceptor, but can be set to 'missing' or any other fault handler. Not also - that
284 * all interceptors can engage in fault handling. This is just meant to provide a simple one-stop-shop for how to
285 * handle faults in one place.
286 */
288
289 public:
290 /**
291 * Get/Set the list of interceptors early interceptors. These default to:
292 * earlyInterceptors += ServerHeadersInterceptor_{serverHeader, corsSupportMode};
293 * if (defaultFaultHandler) {
294 * earlyInterceptors += *defaultFaultHandler;
295 * }
296 *
297 * @see beforeInterceptors, afterInterceptors, AddInterceptor, RemoveInterceptor to maintain the list of interceptors
298 *
299 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
300 */
302
303 public:
304 /**
305 * Returns the 'effective' options after applying defaults, not (generally) the original options.
306 */
308
309 public:
310 /**
311 * Basic statistics about the state of the ConnectionManager (webserver).
312 */
313 struct Statistics {
314 /**
315 */
316 struct ThreadPool : Execution::ThreadPool::Statistics {
317 size_t fThreadEntryCount{};
318
319 /**
320 * See Characters::ToString ()
321 */
322 nonvirtual Characters::String ToString () const;
323 };
324 ThreadPool fThreadPool;
325
326 /**
327 * NOTE - call 'connections' property to get full details... - not summary stats
328 */
330 /**
331 * Number of sockets with TCP connections between the server web server clients.
332 */
334
335 /**
336 * The subset of open connections where there is current activity, such as reading headers,
337 * or processing the body of the request, or sending the response (a thread allocated to doing work for this connection).
338 */
340
341 /**
342 * Each connection that still exists has been open for a given amount of time. Median of those.
343 */
345
346 /**
347 * Each connection will in general serve many requests (due to connection keep-alives). This computes
348 * the median duration of currently open requests. @todo CLARIFY
349 *
350 * \note REQUIRES qStroika_Frameworks_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
351 */
353
354 /**
355 * Each connection will in general serve many requests (due to connection keep-alives). This computes
356 * the median duration of currently active connection requests.
357 *
358 * \note REQUIRES qStroika_Frameworks_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
359 */
361
362 /**
363 * Count the number of currently running requests which have taken longer than options.fConnectionPiningForTheFjordsDelay
364 *
365 * This can help to detect deadlocked/or problematically slow web service APIs.
366 *
367 * If this number is non-zero, try the connections () property, and look through for connections with problematic times.
368 *
369 * \note REQUIRES qStroika_Frameworks_WebServer_Connection_TrackExtraStats (maybe @todo use options.fCollectStats)
370 */
372
373 /**
374 * See Characters::ToString ()
375 */
376 nonvirtual Characters::String ToString () const;
377 };
378
379 /*
380 * \brief statistics about current and recent connections and request durations.
381 *
382 * \see connections() property for more details about currently connected sockets.
383 */
384 ConnectionStatistics fConnections;
385
386 /**
387 * See Characters::ToString ()
388 */
389 nonvirtual Characters::String ToString () const;
390 };
391
392 public:
393 /**
394 * Then this can be used to fetch the current thread pool statistics.
395 *
396 * \note set options.fCollectStatistics = true to use this most effectively - some information maybe missing if this is not set.
397 */
399
400 private:
401 nonvirtual Statistics ComputeStatistics_ () const;
402
403 public:
404 /**
405 * These 'before' and 'after' values are relative to the router, which towards the end of the chain.
406 *
407 * \note ordering is earlyInterceptors => beforeInterceptors => router => afterInterceptors;
408 */
410 ePrependsToEarly,
411 ePrepend,
412 eAppend,
413 eAfterBeforeInterceptors,
414 };
415 using InterceptorAddRelativeTo::eAfterBeforeInterceptors;
416 using InterceptorAddRelativeTo::eAppend;
417 using InterceptorAddRelativeTo::ePrepend;
418 using InterceptorAddRelativeTo::ePrependsToEarly;
419
420 public:
421 /**
422 */
423 nonvirtual void AddInterceptor (const Interceptor& i, InterceptorAddRelativeTo relativeTo);
424
425 public:
426 /**
427 */
428 nonvirtual void RemoveInterceptor (const Interceptor& i);
429
430 public:
431 /**
432 */
433 nonvirtual void AbortConnection (const shared_ptr<Connection>& conn);
434
435 private:
436 nonvirtual void DeriveConnectionDefaultOptionsFromEffectiveOptions_ ();
437
438 private:
439 nonvirtual void onConnect_ (const ConnectionOrientedStreamSocket::Ptr& s);
440
441 private:
442 nonvirtual void WaitForReadyConnectionLoop_ ();
443
444 private:
445 // Inactive connections are those we are waiting (select/epoll) for incoming data; these are stored in fInactiveSockSetPoller_
446 nonvirtual Collection<shared_ptr<Connection>> GetInactiveConnections_ () const;
447
448 private:
449 nonvirtual void ReplaceInEarlyInterceptor_ (const optional<Interceptor>& oldValue, const optional<Interceptor>& newValue);
450
451 private:
452 Options fEffectiveOptions_;
453 Traversal::Iterable<SocketAddress> fBindings_; // just to return bindings API
454#if qCompilerAndStdLib_function_dependency_too_complex_Buggy
455 // BWA not too bad cuz ConnectionManager(const ConnectionManager&)=delete and op= as well.
456 shared_ptr<Connection::Options> fUseDefaultConnectionOptions_BWA_{Memory::MakeSharedPtr<Connection::Options> ()};
457 Connection::Options& fUseDefaultConnectionOptions_{*fUseDefaultConnectionOptions_BWA_};
458#else
459 Connection::Options fUseDefaultConnectionOptions_;
460#endif
465 Execution::Synchronized<optional<Duration>> fAutomaticTCPDisconnectOnClose_;
466 Router fRouter_;
467
468 // Active connections are those actively in the readheaders/readbody, dispatch/handle code
470
471 struct MyWaitForIOReady_Traits_ {
472 using HighLevelType = shared_ptr<Connection>;
473 static inline auto GetSDKPollable (const HighLevelType& t)
474 {
475 return t->socket ().GetNativeSocket ();
476 }
477 };
478 // No need to lock fInactiveSockSetPoller_ since its internally synchronized;
479 Execution::UpdatableWaitForIOReady<shared_ptr<Connection>, MyWaitForIOReady_Traits_> fInactiveSockSetPoller_{};
480
481 /*
482 * SUBTLE DATA MEMBER ORDERING NOTE!
483 * We count on the THREADS that run and manipulate all the above data members are all listed AFTER those data
484 * members in this object. This is just for the convenient ordering that imposes on construction and destruction:
485 * the threads (declared below) are automatically shutdown on destruction before the data they reference (above)
486 *
487 * Same with the listener, as this is basically a thread invoking calls on the above data members.
488 */
489
490 // we may eventually want two thread pools - one for managing bookkeeping/monitoring harvests, and one for actually handling
491 // connections. Or maybe a single thread for the bookkeeping, and the pool for handling ongoing connections?
492 //
493 // But for now - KISS
494 //
495 // Note - for now - we don't even handle 'accepting' connections in the threadpool!!! - just one thread
496 Execution::ThreadPool fActiveConnectionThreads_;
497
498 Execution::Thread::CleanupPtr fWaitForReadyConnectionThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
499
500 // Note: this must be declared after the threadpool so its shutdown on destruction before the thread pool, and doesn't try to launch
501 // new tasks into an already destroyed threadpool.
502 IO::Network::Listener fListener_;
503 };
504
505 inline const ConnectionManager::Options ConnectionManager::kDefaultOptions;
506
507}
508
509/*
510 ********************************************************************************
511 ***************************** Implementation Details ***************************
512 ********************************************************************************
513 */
514#include "ConnectionManager.inl"
515
516#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:234
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