Stroika Library 3.0d23
 
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_Framework_WebServer_Connection_DetailedMessagingLog 1
44#ifndef qStroika_Framework_WebServer_Connection_DetailedMessagingLog
45#define qStroika_Framework_WebServer_Connection_DetailedMessagingLog 0
46#endif
47
48 /**
49 * This has a slight cost, so you might want to compile it out of the implementation.
50 * At least useful when debugging.
51 */
52//#define qStroika_Framework_WebServer_Connection_TrackExtraStats 0
53#ifndef qStroika_Framework_WebServer_Connection_TrackExtraStats
54#define qStroika_Framework_WebServer_Connection_TrackExtraStats 1
55#endif
56
57 /**
58 * \brief A Connection object represents the state (and socket) for an ongoing, active, HTTP Connection, managed by the ConnectionManager class
59 *
60 * This tends to get used internally by the ConnectionManager, but you can use it directly. For example:
61 *
62 * \par Example Usage
63 * \code
64 * Connection conn{acceptedSocketConnection,
65 * Connection::Options{.fInterceptorChain = Sequence<Interceptor>{
66 * Interceptor{
67 * [=](Message* m) {
68 * Response& response = m->rwResponse ();
69 * response.rwHeaders().server = "stroika-ssdp-server-demo";
70 * response.write (Stroika::Frameworks::UPnP::Serialize (d, dd));
71 * response.contentType = DataExchange::InternetMediaTypes::kXML;
72 * }}}}};
73 * conn.SetRemainingConnectionMessages (Connection::Remaining{0, 0}); // disable keep-alives
74 * conn.ReadAndProcessMessage ();
75 * \endcode
76 *
77 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
78 */
80 public:
81 struct Options {
82 /**
83 * This is largely required (though can be provided later). Without it, you don't get notified about progress of the HTTP connection.
84 */
85 InterceptorChain fInterceptorChain;
86
87 /**
88 * These are the default/baseline response headers which will be provided on all responses (except possibly GET)
89 */
90 Headers fDefaultResponseHeaders;
91
92 /**
93 * These are the default response headers for GET requests (if not provided, defaults to fDefaultResponseHeaders)
94 */
95 optional<Headers> fDefaultGETResponseHeaders;
96
97 /**
98 * \see WebServer::Response::autoComputeETagResponse
99 */
100 optional<bool> fAutoComputeETagResponse;
101
102 /**
103 * \see WebServer::Response::automaticTransferChunkSize (default is usually fine)
104 */
105 optional<size_t> fAutomaticTransferChunkSize;
106
107 /**
108 * \see WebServer::Request::bodyEncoding;
109 * This set is intersected with REQUEST.acceptEncoding headers to select encoding to use in Response bodyEncoding
110 * (if nullopt, auto-computed based on what is supported in Stroika)
111 */
112 optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings;
113 };
114
115 public:
116 /**
117 */
118 Connection () = delete;
119 Connection (const Connection&) = delete;
120 [[deprecated ("Since Stroika v3.0d7 - use the Options object with basically the same values")]] explicit Connection (
121 const ConnectionOrientedStreamSocket::Ptr& s, const InterceptorChain& interceptorChain = {}, const Headers& defaultResponseHeaders = {},
122 const optional<Headers>& defaultGETResponseHeaders = nullopt, const optional<bool> autoComputeETagResponse = nullopt);
123 Connection (const ConnectionOrientedStreamSocket::Ptr& s, const Options& options);
124
125 public:
126 ~Connection ();
127
128 public:
129 nonvirtual Connection& operator= (const Connection&) = delete;
130
131 public:
132 /**
133 * This returns the (two way) connection oriented stream socket (ptr) used by this connection.
134 */
136
137 public:
138 /**
139 * Access a (read-only) reference of the underlying connection request
140 */
142
143 public:
144 /**
145 * Access a (read-only) reference of the underlying connection request
146 */
148
149 public:
150 /**
151 * 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)
152 */
154
155 public:
156 /**
157 * Mostly for debugging, but also for ongoing operational diagnostics (late season debugging ;-)).
158 *
159 * \note these are stats about the state of this connection, not connections in general
160 */
161 struct Stats {
162 /**
163 * Unique (at a given time) 'ID' which can be used to track the connection stats across calls to get stats.
164 * (note fUniqueID is opaque integer)
165 */
167
168 /**
169 * Connection being processed, with data present (if false, then connection in list of polled connections waiting for data to be available)
170 */
171 optional<bool> fActive;
172
173 /**
174 * When the connection object was created
175 */
176 TimePointSeconds fCreatedAt;
177
178#if qStroika_Framework_WebServer_Connection_TrackExtraStats
179 /**
180 * A given connection can be used for mutliple messages. Track what message number this is on this connection.
181 *
182 * \note for weird cases (pathological?) - this can be larger than the actual message number. If you have several
183 * failed calls to ReadHeaders(), for example, which should basically never happen (except for abuse).
184 */
186
187 /**
188 * @brief Experimental state information - dont count on details or names. Subject to change.
189 * This is intended to be used for debugging and diagnostics.
190 */
191 enum class State : uint8_t {
192 /**
193 * This means the socket was created in response to an Accept() returning, but we never got any bytes of data
194 * available on the socket (so far).
195 */
196 eNew,
197
198 /**
199 * In the middle of reading/parsing headers (data available, actively processing)
200 */
202
203 /**
204 *
205 */
206 ePausedIncompleteHeaders,
207
208 /**
209 * @brief done reading (header part), now processing the message (interceptor chain)
210 */
212
213 /**
214 * @brief Finished processing message, and in process of flushing response, and checking/updating response headers.
215 */
216 eFlushing,
217
218 /**
219 * @brief Re-using connection for next message (keep-alive).
220 */
222
223 /**
224 * @brief Done with connection and ready to close it down (maybe cleanly closing, or maybe with exception)
225 */
226 eClosing,
227
229 };
230 State fState{State::eNew};
231
232 /**
233 */
234 optional<Traversal::Range<TimePointSeconds>> fMostRecentMessage;
235
236 /**
237 */
238 optional<thread::id> fHandlingThread;
239
240 /**
241 * \brief the address of the client which is talking to the server
242 */
243 optional<SocketAddress> fRemotePeerAddress;
244
245 /**
246 * \brief last request
247 */
248 optional<String> fRequestWebMethod;
249
250 /**
251 * \brief last requested URI (always relative uri)
252 */
253 optional<URI> fRequestURI;
254#endif
255
256 /**
257 * @see Characters::ToString ();
258 */
259 nonvirtual String ToString () const;
260 };
261
262 public:
263 /**
264 * \brief retrieve stats about this connection, like threads used, start/end times. NB: INTERNALLY SYNCRONIZED
265 *
266 * \note \em Thread-Safety <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
267 */
269
270 public:
271 /**
272 */
273 enum ReadAndProcessResult {
274 eTryAgainLater, // Could mean success or some kinds of failure (like incomplete header/data), but try again later (so keep-alive results in this)
275 eClose,
276 };
277
278 public:
279 /**
280 * Return eTryAgainLater if 'keep alive' (or otherwise should try again - like incomplete input).
281 */
282 nonvirtual ReadAndProcessResult ReadAndProcessMessage () noexcept;
283
284 public:
285 /**
286 * \note set Remaining::fMessages := 0 to prevent keep-alives.
287 *
288 * \par Example Usage
289 * \code
290 * conn.remainingConnectionLimits = KeepAlive{0, 0}; // disable keep-alives
291 * \endcode
292 */
293 Common::Property<optional<HTTP::KeepAlive>> remainingConnectionLimits;
294
295#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
296 private:
297 nonvirtual void WriteLogConnectionMsg_ (const String& msg) const;
298#endif
299
300 public:
301 /**
302 * @see Characters::ToString ();
303 */
304 nonvirtual String ToString (bool abbreviatedOutput = true) const;
305
306 private:
307 struct MyMessage_ : Message {
309 const Headers& defaultResponseHeaders, const optional<bool> autoComputeETagResponse);
310
311 // Only valid until the end of a successful ReadHeaders
313
314 // If result bad, throw exception
315 enum ReadHeadersResult {
316 eIncompleteButMoreMayBeAvailable,
317 eIncompleteDeadEnd,
318 eCompleteGood
319 };
320 nonvirtual ReadHeadersResult ReadHeaders (
321#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
322 const function<void (const String&)>& logMsg
323#endif
324 );
325 };
326
327 private:
328 const InterceptorChain fInterceptorChain_;
329 const Headers fDefaultResponseHeaders_;
330 const optional<Headers> fDefaultGETResponseHeaders_;
331 const optional<bool> fAutoComputeETagResponse_;
332 const optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings_;
335 const TimePointSeconds fConnectionStartedAt_{};
336 unique_ptr<MyMessage_> fMessage_; // always there, but ptr so it can be replaced
337 optional<HTTP::KeepAlive> fRemaining_;
338#if qStroika_Framework_WebServer_Connection_TrackExtraStats
339 enum class State_Flag_ : uint8_t {
340 eNew,
341 eReadingHeaders_Started,
342 eFinishedReadingHeaders_Success,
343 eFinishedReadingHeaders_Incomplete,
344 eFinishedReadingHeaders_Failed,
345 eInterceptorChain_Start,
346 eInterceptorChain_Complete,
347 eFlushing_Start,
348 eFlushing_Done,
349 eAborting,
350 };
351 atomic<State_Flag_> fState_{State_Flag_::eNew}; // always increases during a single ReadHeaders invocation (but it reversts between).
352 atomic<unsigned int> fReadAndProcessMessageNumber_{0};
353 atomic<bool> fKeepAlive_{true};
354 struct Stats2Capture_ {
355 optional<TimePointSeconds> fMessageStart;
356 optional<TimePointSeconds> fMessageCompleted;
357 optional<SocketAddress> fPeer;
358 optional<String> fWebMethod;
359 optional<URI> fRequestURI;
360 optional<thread::id> fHandlingThread;
361 };
362 static_assert (is_default_constructible_v<Stats2Capture_>);
364#endif
365#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
366 Streams::OutputStream::Ptr<Character> fLogConnectionState_;
367#endif
368 };
369
370}
371
372/*
373 ********************************************************************************
374 ***************************** Implementation Details ***************************
375 ********************************************************************************
376 */
377#include "Connection.inl"
378
379#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