Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Frameworks/WebServer/Connection.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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"
14#include "Stroika/Foundation/IO/Network/HTTP/KeepAlive.h"
17#include "Stroika/Foundation/Streams/TextToBinary.h"
19
20#include "Stroika/Frameworks/WebServer/InterceptorChain.h"
21#include "Stroika/Frameworks/WebServer/Message.h"
22
23/*
24 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
25 */
26
28
29 using namespace Stroika::Foundation;
30 using namespace Stroika::Foundation::IO;
32
34
36
37 /**
38 * Write out files to %TEMP% dir, with logs of the details of the HTTP conversation, for debugging
39 * HTTP conversations.
40 */
41//#define qStroika_Framework_WebServer_Connection_DetailedMessagingLog 1
42#ifndef qStroika_Framework_WebServer_Connection_DetailedMessagingLog
43#define qStroika_Framework_WebServer_Connection_DetailedMessagingLog 0
44#endif
45
46 /**
47 * This has a slight cost, so you might want to compile it out of the implementation.
48 * At least useful when debugging.
49 */
50//#define qStroika_Framework_WebServer_Connection_TrackExtraStats 0
51#ifndef qStroika_Framework_WebServer_Connection_TrackExtraStats
52#define qStroika_Framework_WebServer_Connection_TrackExtraStats 1
53#endif
54
55 /**
56 * \brief A Connection object represents the state (and socket) for an ongoing, active, HTTP Connection, managed by the ConnectionManager class
57 *
58 * This tends to get used internally by the ConnectionManager, but you can use it directly. For example:
59 *
60 * \par Example Usage
61 * \code
62 * Connection conn{acceptedSocketConnection,
63 * Connection::Options{.fInterceptorChain = Sequence<Interceptor>{
64 * Interceptor{
65 * [=](Message* m) {
66 * Response& response = m->rwResponse ();
67 * response.rwHeaders().server = "stroika-ssdp-server-demo";
68 * response.write (Stroika::Frameworks::UPnP::Serialize (d, dd));
69 * response.contentType = DataExchange::InternetMediaTypes::kXML;
70 * }}}}};
71 * conn.SetRemainingConnectionMessages (Connection::Remaining{0, 0}); // disable keep-alives
72 * conn.ReadAndProcessMessage ();
73 * \endcode
74 *
75 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
76 */
78 public:
79 struct Options {
80 /**
81 * This is largely required (though can be provided later). Without it, you don't get notified about progress of the HTTP connection.
82 */
83 InterceptorChain fInterceptorChain;
84
85 /**
86 * These are the default/baseline response headers which will be provided on all responses (except possibly GET)
87 */
88 Headers fDefaultResponseHeaders;
89
90 /**
91 * These are the default response headers for GET requests (if not provided, defaults to fDefaultResponseHeaders)
92 */
93 optional<Headers> fDefaultGETResponseHeaders;
94
95 /**
96 * \see WebServer::Response::autoComputeETagResponse
97 */
98 optional<bool> fAutoComputeETagResponse;
99
100 /**
101 * \see WebServer::Response::automaticTransferChunkSize (default is usually fine)
102 */
103 optional<size_t> fAutomaticTransferChunkSize;
104
105 /**
106 * \see WebServer::Request::bodyEncoding;
107 * This set is intersected with REQUEST.acceptEncoding headers to select encoding to use in Response bodyEncoding
108 * (if nullopt, auto-computed based on what is supported in Stroika)
109 */
110 optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings;
111 };
112
113 public:
114 /**
115 */
116 Connection () = delete;
117 Connection (const Connection&) = delete;
118 [[deprecated ("Since Stroika v3.0d7 - use the Options object with basically the same values")]] explicit Connection (
119 const ConnectionOrientedStreamSocket::Ptr& s, const InterceptorChain& interceptorChain = {}, const Headers& defaultResponseHeaders = {},
120 const optional<Headers>& defaultGETResponseHeaders = nullopt, const optional<bool> autoComputeETagResponse = nullopt);
121 Connection (const ConnectionOrientedStreamSocket::Ptr& s, const Options& options);
122
123 public:
124 ~Connection ();
125
126 public:
127 nonvirtual Connection& operator= (const Connection&) = delete;
128
129 public:
130 /**
131 * This returns the (two way) connection oriented stream socket (ptr) used by this connection.
132 */
134
135 public:
136 /**
137 * Access a (read-only) reference of the underlying connection request
138 */
140
141 public:
142 /**
143 * Access a (read-only) reference of the underlying connection request
144 */
146
147 public:
148 /**
149 * 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)
150 */
152
153 public:
154 /**
155 * Mostly for debugging, but also for ongoing operational diagnostics (late season debugging ;-)).
156 *
157 * \todo - https://stroika.atlassian.net/browse/STK-1025 - Stats should have STATE flag
158 * \todo - Consider ALSO adding a Request URI - which might be helpful for debugging when deadlocks happen.
159 */
160 struct Stats {
161 /**
162 * Unique (at a given time) 'ID' which can be used to track the connection stats across calls to get stats.
163 * (note fUniqueID is opaque integer)
164 */
166
167 /**
168 * Is this connection object currently 'connected' (socket level listen or accept returned).
169 */
170 optional<bool> fActive;
171
172 /**
173 * When the connection object was created
174 */
176
177#if qStroika_Framework_WebServer_Connection_TrackExtraStats
178 /**
179 */
180 optional<Traversal::Range<Time::TimePointSeconds>> fMostRecentMessage;
181
182 /**
183 */
184 optional<thread::id> fHandlingThread;
185#endif
186
187 /**
188 * @see Characters::ToString ();
189 */
190 nonvirtual String ToString () const;
191 };
192
193 public:
194 /**
195 * \brief retrieve stats about this connection, like threads used, start/end times. NB: INTERNALLY SYNCRONIZED
196 *
197 * \note \em Thread-Safety <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
198 */
200
201 public:
202 /**
203 */
204 enum ReadAndProcessResult {
205 eTryAgainLater, // Could mean success or some kinds of failure (like incomplete header/data), but try again later (so keep-alive results in this)
206 eClose,
207 };
208
209 public:
210 /**
211 * Return eTryAgainLater if 'keep alive' (or otherwise should try again - like incomplete input).
212 */
213 nonvirtual ReadAndProcessResult ReadAndProcessMessage () noexcept;
214
215 public:
216 /**
217 * \note set Remaining::fMessages := 0 to prevent keep-alives.
218 *
219 * \par Example Usage
220 * \code
221 * conn.remainingConnectionLimits = KeepAlive{0, 0}; // disable keep-alives
222 * \endcode
223 */
224 Common::Property<optional<HTTP::KeepAlive>> remainingConnectionLimits;
225
226#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
227 private:
228 nonvirtual void WriteLogConnectionMsg_ (const String& msg) const;
229#endif
230
231 public:
232 /**
233 * @see Characters::ToString ();
234 */
235 nonvirtual String ToString (bool abbreviatedOutput = true) const;
236
237 private:
238 struct MyMessage_ : Message {
240 const Headers& defaultResponseHeaders, const optional<bool> autoComputeETagResponse);
241
242 // Only valid until the end of a successful ReadHeaders
244
245 // If result bad, throw exception
246 enum ReadHeadersResult {
247 eIncompleteButMoreMayBeAvailable,
248 eIncompleteDeadEnd,
249 eCompleteGood
250 };
251 nonvirtual ReadHeadersResult ReadHeaders (
252#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
253 const function<void (const String&)>& logMsg
254#endif
255 );
256 };
257
258 private:
259 const InterceptorChain fInterceptorChain_;
260 const Headers fDefaultResponseHeaders_;
261 const optional<Headers> fDefaultGETResponseHeaders_;
262 const optional<bool> fAutoComputeETagResponse_;
263 const optional<Containers::Set<HTTP::ContentEncoding>> fSupportedCompressionEncodings_;
266 const Time::TimePointSeconds fConnectionStartedAt_{};
267 unique_ptr<MyMessage_> fMessage_; // always there, but ptr so it can be replaced
268 optional<HTTP::KeepAlive> fRemaining_;
269#if qStroika_Framework_WebServer_Connection_TrackExtraStats
270 // Sigh: atomic doesn't work with time_point, nor optional! - so use double and sentinel
271 static constexpr double kAtomicTimeSentinel_ = -1;
272 atomic<double> fStartHandleMessage_{kAtomicTimeSentinel_};
273 atomic<double> fCompletedHandleMessage_{kAtomicTimeSentinel_};
274 atomic<thread::id> fHandlingThread_; // thread::id{} sentinel
275#endif
276#if qStroika_Framework_WebServer_Connection_DetailedMessagingLog
277 Streams::OutputStream::Ptr<Character> fLogConnectionState_;
278#endif
279 };
280
281}
282
283/*
284 ********************************************************************************
285 ***************************** Implementation Details ***************************
286 ********************************************************************************
287 */
288#include "Connection.inl"
289
290#endif /*_Stroika_Framework_WebServer_Connection_h_*/
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...
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