Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Frameworks/WebServer/Connection.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Framework_WebServer_Connection_h_
5#define _Stroika_Framework_WebServer_Connection_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <optional>
10
12#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Common/Property.h"
15#include "Stroika/Foundation/IO/Network/HTTP/KeepAlive.h"
18#include "Stroika/Foundation/Streams/TextToBinary.h"
20
21#include "Stroika/Frameworks/WebServer/InterceptorChain.h"
22#include "Stroika/Frameworks/WebServer/Message.h"
23
24/*
25 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
26 */
27
29
30 using namespace Stroika::Foundation;
31 using namespace Stroika::Foundation::IO;
33
36
38
39 /**
40 * Write out files to %TEMP% dir, with logs of the details of the HTTP conversation, for debugging
41 * HTTP conversations.
42 */
43//#define qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog 1
44#ifndef qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog
45#if defined(qStroika_Framework_WebServer_Connection_DetailedMessagingLog)
46#define qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog qStroika_Framework_WebServer_Connection_DetailedMessagingLog
47#else
48#define qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog 0
49#endif
50#endif
51
52 /**
53 * This has a slight cost, so you might want to compile it out of the implementation.
54 * At least useful when debugging.
55 */
56//#define qStroika_Frameworks_WebServer_Connection_TrackExtraStats 0
57#ifndef qStroika_Frameworks_WebServer_Connection_TrackExtraStats
58#if defined(qStroika_Framework_WebServer_Connection_TrackExtraStats)
59#define qStroika_Frameworks_WebServer_Connection_TrackExtraStats qStroika_Framework_WebServer_Connection_TrackExtraStats
60#else
61#define qStroika_Frameworks_WebServer_Connection_TrackExtraStats 1
62#endif
63#endif
64
65// DEPRECATED NAMES - since 3.0d25 (to be removed in v3.0a1): Frameworks, plural, like every other qStroika_Frameworks_ name
66#ifndef qStroika_Framework_WebServer_Connection_DetailedMessagingLog
67#define qStroika_Framework_WebServer_Connection_DetailedMessagingLog qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog
68#endif
69#ifndef qStroika_Framework_WebServer_Connection_TrackExtraStats
70#define qStroika_Framework_WebServer_Connection_TrackExtraStats qStroika_Frameworks_WebServer_Connection_TrackExtraStats
71#endif
72
73 /**
74 * \brief A Connection object represents the state (and socket) for an ongoing, active, HTTP Connection, managed by the ConnectionManager class
75 *
76 * This tends to get used internally by the ConnectionManager, but you can use it directly. For example:
77 *
78 * \par Example Usage
79 * \code
80 * Connection conn{acceptedSocketConnection,
81 * Connection::Options{.fInterceptorChain = Sequence<Interceptor>{
82 * Interceptor{
83 * [=](Message* m) {
84 * Response& response = m->rwResponse ();
85 * response.rwHeaders().server = "stroika-ssdp-server-demo";
86 * response.write (Stroika::Frameworks::UPnP::Serialize (d, dd));
87 * response.contentType = DataExchange::InternetMediaTypes::kXML;
88 * }}}}};
89 * conn.SetRemainingConnectionMessages (Connection::Remaining{0, 0}); // disable keep-alives
90 * conn.ReadAndProcessMessage ();
91 * \endcode
92 *
93 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
94 */
96 public:
97 struct Options {
98 /**
99 * This is largely required (though can be provided later). Without it, you don't get notified about progress of the HTTP connection.
100 */
101 InterceptorChain fInterceptorChain;
102
103 /**
104 * These are the default/baseline response headers which will be provided on all responses (except possibly GET)
105 */
106 Headers fDefaultResponseHeaders;
107
108 /**
109 * These are the default response headers for GET requests (if not provided, defaults to fDefaultResponseHeaders)
110 */
111 optional<Headers> fDefaultGETResponseHeaders;
112
113 /**
114 * \see WebServer::Response::autoComputeETagResponse
115 */
116 optional<bool> fAutoComputeETagResponse;
117
118 /**
119 * \see WebServer::Response::automaticTransferChunkSize (default is usually fine)
120 */
121 optional<size_t> fAutomaticTransferChunkSize;
122
123 /**
124 * \see WebServer::Request::bodyEncoding;
125 * This set is intersected with REQUEST.acceptEncoding headers to select encoding to use in Response bodyEncoding
126 * (if nullopt, auto-computed based on what is supported in Stroika)
127 */
128 optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings;
129 };
130
131 public:
132 /**
133 */
134 Connection () = delete;
135 Connection (const Connection&) = delete;
136 [[deprecated ("Since Stroika v3.0d7 - use the Options object with basically the same values")]] explicit Connection (
137 const ConnectionOrientedStreamSocket::Ptr& s, const InterceptorChain& interceptorChain = {}, const Headers& defaultResponseHeaders = {},
138 const optional<Headers>& defaultGETResponseHeaders = nullopt, const optional<bool> autoComputeETagResponse = nullopt);
139 Connection (const ConnectionOrientedStreamSocket::Ptr& s, const Options& options);
140
141 public:
142 ~Connection ();
143
144 public:
145 nonvirtual Connection& operator= (const Connection&) = delete;
146
147 public:
148 /**
149 * This returns the (two way) connection oriented stream socket (ptr) used by this connection.
150 */
152
153 public:
154 /**
155 * Access a (read-only) reference of the underlying connection request
156 */
158
159 public:
160 /**
161 * Access a (read-only) reference of the underlying connection request
162 */
164
165 public:
166 /**
167 * Access a (read-only) reference to the underlying (modifiable) connection response (meaning you cannot assign to the response itself, but you can modify the response object)
168 */
170
171 public:
172 /**
173 * Mostly for debugging, but also for ongoing operational diagnostics (late season debugging ;-)).
174 *
175 * \note these are stats about the state of this connection, not connections in general
176 */
177 struct Stats {
178 /**
179 * Unique (at a given time) 'ID' which can be used to track the connection stats across calls to get stats.
180 * (note fUniqueID is opaque integer)
181 */
183
184 /**
185 * Connection being processed, with data present (if false, then connection in list of polled connections waiting for data to be available)
186 */
187 optional<bool> fActive;
188
189 /**
190 * When the connection object was created
191 */
192 TimePointSeconds fCreatedAt;
193
194#if qStroika_Frameworks_WebServer_Connection_TrackExtraStats
195 /**
196 * A given connection can be used for mutliple messages. Track what message number this is on this connection.
197 *
198 * \note for weird cases (pathological?) - this can be larger than the actual message number. If you have several
199 * failed calls to ReadHeaders(), for example, which should basically never happen (except for abuse).
200 */
202
203 /**
204 * @brief Experimental state information - dont count on details or names. Subject to change.
205 * This is intended to be used for debugging and diagnostics.
206 */
207 enum class State : uint8_t {
208 /**
209 * This means the socket was created in response to an Accept() returning, but we never got any bytes of data
210 * available on the socket (so far).
211 */
212 eNew,
213
214 /**
215 * In the middle of reading/parsing headers (data available, actively processing)
216 */
218
219 /**
220 *
221 */
222 ePausedIncompleteHeaders,
223
224 /**
225 * @brief done reading (header part), now processing the message (interceptor chain)
226 */
228
229 /**
230 * @brief Finished processing message, and in process of flushing response, and checking/updating response headers.
231 */
232 eFlushing,
233
234 /**
235 * @brief Re-using connection for next message (keep-alive).
236 */
238
239 /**
240 * @brief Done with connection and ready to close it down (maybe cleanly closing, or maybe with exception)
241 */
242 eClosing,
243
245 };
246 State fState{State::eNew};
247
248 /**
249 */
250 optional<Traversal::Range<TimePointSeconds>> fMostRecentMessage;
251
252 /**
253 */
254 optional<thread::id> fHandlingThread;
255
256 /**
257 * \brief the address of the client which is talking to the server
258 */
259 optional<SocketAddress> fRemotePeerAddress;
260
261 /**
262 * \brief last request
263 */
264 optional<String> fRequestWebMethod;
265
266 /**
267 * \brief last requested URI (always relative uri)
268 */
269 optional<URI> fRequestURI;
270#endif
271
272 /**
273 * @see Characters::ToString ();
274 */
275 nonvirtual String ToString () const;
276 };
277
278 public:
279 /**
280 * \brief retrieve stats about this connection, like threads used, start/end times. NB: INTERNALLY SYNCRONIZED
281 *
282 * \note \em Thread-Safety <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
283 */
285
286 public:
287 /**
288 */
289 enum ReadAndProcessResult {
290 eTryAgainLater, // Could mean success or some kinds of failure (like incomplete header/data), but try again later (so keep-alive results in this)
291 eClose,
292 };
293
294 public:
295 /**
296 * Return eTryAgainLater if 'keep alive' (or otherwise should try again - like incomplete input).
297 */
298 nonvirtual ReadAndProcessResult ReadAndProcessMessage () noexcept;
299
300 public:
301 /**
302 * \note set Remaining::fMessages := 0 to prevent keep-alives.
303 *
304 * \par Example Usage
305 * \code
306 * conn.remainingConnectionLimits = KeepAlive{0, 0}; // disable keep-alives
307 * \endcode
308 */
309 Common::Property<optional<HTTP::KeepAlive>> remainingConnectionLimits;
310
311#if qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog
312 private:
313 nonvirtual void WriteLogConnectionMsg_ (const String& msg) const;
314#endif
315
316 public:
317 /**
318 * @see Characters::ToString ();
319 */
320 nonvirtual String ToString (bool abbreviatedOutput = true) const;
321
322 private:
323 struct MyMessage_ : Message {
325 const Headers& defaultResponseHeaders, const optional<bool> autoComputeETagResponse);
326
327 // Only valid until the end of a successful ReadHeaders
329
330 // If result bad, throw exception
331 enum ReadHeadersResult {
332 eIncompleteButMoreMayBeAvailable,
333 eIncompleteDeadEnd,
334 eCompleteGood
335 };
336 nonvirtual ReadHeadersResult ReadHeaders (
337#if qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog
338 const function<void (const String&)>& logMsg
339#endif
340 );
341 };
342
343 private:
344 const InterceptorChain fInterceptorChain_;
345 const Headers fDefaultResponseHeaders_;
346 const optional<Headers> fDefaultGETResponseHeaders_;
347 const optional<bool> fAutoComputeETagResponse_;
348 const optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings_;
351 const TimePointSeconds fConnectionStartedAt_{};
352 unique_ptr<MyMessage_> fMessage_; // always there, but ptr so it can be replaced
353 optional<HTTP::KeepAlive> fRemaining_;
354#if qStroika_Frameworks_WebServer_Connection_TrackExtraStats
355 enum class State_Flag_ : uint8_t {
356 eNew,
357 eReadingHeaders_Started,
358 eFinishedReadingHeaders_Success,
359 eFinishedReadingHeaders_Incomplete,
360 eFinishedReadingHeaders_Failed,
361 eInterceptorChain_Start,
362 eInterceptorChain_Complete,
363 eFlushing_Start,
364 eFlushing_Done,
365 eAborting,
366 };
367 atomic<State_Flag_> fState_{State_Flag_::eNew}; // always increases during a single ReadHeaders invocation (but it reversts between).
368 atomic<unsigned int> fReadAndProcessMessageNumber_{0};
369 atomic<bool> fKeepAlive_{true};
370 struct Stats2Capture_ {
371 optional<TimePointSeconds> fMessageStart;
372 optional<TimePointSeconds> fMessageCompleted;
373 optional<SocketAddress> fPeer;
374 optional<String> fWebMethod;
375 optional<URI> fRequestURI;
376 optional<thread::id> fHandlingThread;
377 };
378 static_assert (is_default_constructible_v<Stats2Capture_>);
380#endif
381#if qStroika_Frameworks_WebServer_Connection_DetailedMessagingLog
382 Streams::OutputStream::Ptr<Character> fLogConnectionState_;
383#endif
384 };
385
386}
387
388/*
389 ********************************************************************************
390 ***************************** Implementation Details ***************************
391 ********************************************************************************
392 */
393#include "Connection.inl"
394
395#endif /*_Stroika_Framework_WebServer_Connection_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
roughly equivalent to Association<String,String>, except that the class is smart about certain keys a...
Definition Headers.h:129
InputOutputStream is single stream object that acts much as a InputStream::Ptr and an OutputStream::P...
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
A Connection object represents the state (and socket) for an ongoing, active, HTTP Connection,...
Common::Property< optional< HTTP::KeepAlive > > remainingConnectionLimits
const Common::ReadOnlyProperty< Stats > stats
retrieve stats about this connection, like threads used, start/end times. NB: INTERNALLY SYNCRONIZED
const Common::ReadOnlyProperty< ConnectionOrientedStreamSocket::Ptr > socket
nonvirtual ReadAndProcessResult ReadAndProcessMessage() noexcept
const Common::ReadOnlyProperty< const Request & > request
const Common::ReadOnlyProperty< const Response & > response
nonvirtual String ToString(bool abbreviatedOutput=true) const
optional< URI > fRequestURI
last requested URI (always relative uri)
State
Experimental state information - dont count on details or names. Subject to change....
@ eProcessingInterceptorChain
done reading (header part), now processing the message (interceptor chain)
@ eClosing
Done with connection and ready to close it down (maybe cleanly closing, or maybe with exception)
@ eReadyForNextMessage
Re-using connection for next message (keep-alive).
@ eFlushing
Finished processing message, and in process of flushing response, and checking/updating response head...
optional< SocketAddress > fRemotePeerAddress
the address of the client which is talking to the server