Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Frameworks/WebServer/Response.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_WebServer_Response_h_
5#define _Stroika_Framework_WebServer_Response_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <vector>
10
14#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Common/Property.h"
17#include "Stroika/Foundation/Cryptography/Digest/Digester.h"
21#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
22#include "Stroika/Foundation/IO/Network/HTTP/Response.h"
23#include "Stroika/Foundation/IO/Network/HTTP/Status.h"
29#include "Stroika/Foundation/Streams/SharedMemoryStream.h"
30
31/*
32 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
33 *
34 * TODO:
35 * @todo REDO THE HTTPRESPONSE USING A BINARY OUTPUT STREAM.
36 * INTERNALLY - based on code page - construct a TEXTOUTPUTSTREAM wrapping that binary output stream!!!
37 * (partly done - but more todo)
38 */
39
41
42 using namespace Stroika::Foundation;
44
49 using HTTP::Status;
50 using Memory::BLOB;
51
52 /*
53 * \note Set headers (with this->rwHeaders()...) as early as practical (before calling write or Flush).
54 * \note To use chunked transfer encoding, specify automaticTransferChunkSize
55 * \code
56 * response->rwHeaders ().automaticTransferChunkSize = 1024; // will begin chunking when this is exceeded on some write call
57 * ... then do
58 * response->write (...);
59 * \endcode
60 *
61 * \note Satisfies Concepts:
62 * o static_assert (not copyable<Response>);
63 * o static_assert (movable<Response>);
64 *
65 * TODO:
66 * @todo Support http://stroika-bugs.sophists.com/browse/STK-727 - HTTP Chunked Transfer Trailers. We do support
67 * chunked transfers, but require all the headers set first.
68 *
69 * \note When todo compressed responses
70 *
71 * This can be handled a lot of ways:
72 * > let the user specify by setting header flags
73 * > automatically (based on accept-encoding headers)
74 * > Based on the size of the response (no point compressing a single byte response)
75 *
76 * Since this decision requires data from the 'request' (accept-encoding headers) - its not made here. The caller (Connection)
77 * specifies this through the bodyEncoding property.
78 *
79 * \todo Add API "write-content-encoded" - so caller can pass in pre-content-encoded content (e.g. an existing .zip file data)
80 *
81 * \note Technically, its possible to want to specify the Content-Length as a parameter/property. But we prohibit
82 * this for simplicity sake (since 3.0d7) - since its much simpler to just always let it be computed as needed.
83 *
84 * We want NO Content-Length if using chunked transfer:
85 * "The Content-Length header field MUST NOT be sent if these two lengths are different (i.e., if a Transfer-Encoding
86 * header field is present)"
87 * https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
88 *
89 * And if no chunked transfer, we are accumulating all the byte before sending anyhow - so we know the length.
90 *
91 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
92 */
93 class Response : public IO::Network::HTTP::Response {
94 private:
95 using inherited = IO::Network::HTTP::Response;
96
97 public:
98 /**
99 */
100 Response () = delete;
101 Response (const Response&) = delete;
102 Response (Response&& src);
103 Response (const IO::Network::Socket::Ptr& s, const Streams::OutputStream::Ptr<byte>& outStream,
104 const optional<HTTP::Headers>& initialHeaders = nullopt);
105
106 public:
107 /**
108 * Response must be completed (OK or Abort ()) before being destroyed
109 */
110 ~Response () = default;
111
112 public:
113 nonvirtual Response& operator= (const Response&) = delete;
114 nonvirtual Response& operator= (Response&&) noexcept;
115
116 public:
117 /**
118 * If true, and if possible, and not already present, an eTag value will be automatically added to the response headers.
119 *
120 * As of Stroika 2.1b10, this is not done for 'chunked' transfer responses, because we don't yet support trailers.
121 *
122 * \pre this->state == ePreparingHeaders (before first write to body) to set
123 */
124 Common::Property<bool> autoComputeETag;
125
126 public:
127 /**
128 * \brief defaults to automatically chunking responses larger than a modest fixed size.
129 *
130 * When todo chunked encoding?
131 *
132 * You can always do chunked encoding for a response. But when is it best?
133 *
134 * You could do
135 * > one chunk per write
136 * > one chunk (not transfer-encoding: chunked) - full-response buffering
137 * > Some fixed buffer size, and when that's exceeded, chunk
138 *
139 * In Stroika v3.0d7 and later, the caller may specify a target chunk-size, and when writes exceed this size,
140 * the transfer will be chunked automatically. Set this size to kNoChunkedTransfer to prevent chunking (nullopt just means default).
141 *
142 * \note This chunk-size threshold may refer to the compressed size, or uncompressed size as is convenient to the implementer, and is just
143 * a guideline, not strictly followed (except for the kNoChunkedTransfer special case where no chunking takes place).
144 *
145 * \note When combined with compression, due to chunking of the compression algorithm, this may not result in very uniformly
146 * sized chunks.
147 *
148 * \pre this->state == ePreparingHeaders (before first write to body) to set, but can always be read
149 *
150 * \pre value == nullopt or value > 0 (zero chunk size wouldn't make sense)
151 */
152 Common::Property<optional<size_t>> automaticTransferChunkSize;
153
154 public:
155 /**
156 * \brief sentinel value for automaticTransferChunkSize property - to disable chunked transfers
157 */
158 static constexpr size_t kNoChunkedTransfer = numeric_limits<size_t>::max ();
159
160 public:
161 /**
162 * \brief default value for automaticTransferChunkSize
163 *
164 * \note dont count on this value. Its subject to change. Maybe compute 'total message size' internally and compare with network FRAG size
165 * to do transfer-coding at a size that is just right to avoid extra packets.
166 */
167 static constexpr size_t kAutomaticTransferChunkSize_Default = 16 * 1024;
168
169 public:
170 /**
171 * \brief this corresponds to either headers().contentEncoding() or the compression part of headers().transferEncoding()
172 *
173 * \note - this is typically NOT set directly by users, and is set by the connection/message, based on the request
174 * 'Accept-Encoding' headers.
175 *
176 * \pre this->headersCanBeSet() to set property
177 * \pre all provided encodings, the library is built to support (caller should check)
178 */
179 Common::Property<optional<HTTP::ContentEncodings>> bodyEncoding;
180
181 public:
182 /**
183 * \brief returns true iff headers().transferMode().Contains (HTTP::TransferMode::kChunked) - but checks for nulls etc...
184 *
185 * Note - can change depending on other settings, like whether write() has been called, or automaticTransferChunkSize()
186 *
187 * \note CANNOT change after responseStatusSent()
188 */
189 Common::ReadOnlyProperty<bool> chunkedTransferMode;
190
191 public:
192 /**
193 * Conversion applied to String objects to convert to bytes emitted to stream output.
194 * This value depends on the codePage property.
195 *
196 * \see http://stroika-bugs.sophists.com/browse/STK-983
197 */
198 Common::ReadOnlyProperty<Characters::CodeCvt<>> codeCvt;
199
200 public:
201 /*
202 * Note - the code page is only applied to string/text conversions and content-types which are know text-based content types.
203 * For ContentTypes
204 * o text / * {avoid comment-character}
205 * o application/json
206 * and any other content type that returns true to InternetMediaType::IsA (InternetMediaTypes::Wildcards::kText) the codepage is added to the content-type as in:
207 * "text/html; charset=UTF-8"
208 *
209 * codePage.Set ()
210 * \pre this->headersCanBeSet()
211 * \pre TotalBytesWritten == 0
212 *
213 * \note SEE http://stroika-bugs.sophists.com/browse/STK-983
214 *
215 * \note - if DataExchange::InternetMediaTypeRegistry::sThe->IsA (InternetMediaTypes::Wildcards::kText, fContentType_), then
216 * the character set will be automatically folded into the used contentType. To avoid this,
217 * Use UpdateHeader() to modify the contenttype field directly.
218 *
219 * \pre this->headersCanBeSet() to set property
220 */
221 Common::Property<Characters::CodePage> codePage;
222
223 public:
224 /*
225 * \brief Common::Property <optional<InternetMediaType>> contentType is a short-hand for headers().contentType (or rwHeaders().contentType);
226 *
227 * \pre this->headersCanBeSet() to set property
228 *
229 * NOTE - if DataExchange::InternetMediaTypeRegistry::sThe->IsA (InternetMediaTypes::Wildcards::kText, contentType), then
230 * the character set will be automatically folded into the used contentType (on WRITES to the property - not reads).
231 * @todo revisit this - I think I always use character set if you use write API taking strings)--LGP 2024-06-22
232 */
233 Common::Property<optional<InternetMediaType>> contentType;
234
235 public:
236 /**
237 * some responses will not have an entity body, like a response to a HEAD, method for example. A Stroika
238 * response has an entityBody iff the user has called a 'write' method on response?? _ NO WAHT ABOUT HEAD
239 *
240 * roughly:
241 * (not fHeadMode_ and this->status () != HTTP::StatusCodes::kNotModified and contentLength() > 0)
242 *
243 * \see https://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7
244 */
245 Common::ReadOnlyProperty<bool> hasEntityBody;
246
247 public:
248 /**
249 * Check this (readonly) property before updating headers. For now, this is the same as this->state == ePreparingHeaders
250 *
251 * \note - even HTTP 1.1 allows for headers to be sent AFTER we've started sending chunks, so this interpretation MAY
252 * change over time, but the current implementation only allows setting headers while in the preparingHeaders state
253 * (so in other words, before any calls to Flush or write).
254 */
255 Common::ReadOnlyProperty<bool> headersCanBeSet;
256
257 public:
258 /**
259 * Once set to true, this cannot be set false. It defaults to false;
260 *
261 * \pre not this->responseStatusSent()
262 */
263 Common::Property<bool> headMode;
264
265 public:
266 /**
267 * \brief rarely used, but used in 'Created' 201 responses, and redirects
268 */
269 Common::Property<optional<URI>> location;
270
271 public:
272 /**
273 * Returns true iff the response has been aborted with a call to response.Abort ()
274 * \note - responseAborted() implies responseCompleted();
275 */
276 Common::ReadOnlyProperty<bool> responseAborted;
277
278 public:
279 /**
280 * Returns true once the response has been completed and fully flushed. No further calls to write() are allowed at that point.
281 * \note - responseCompleted() doesn't mean correctly - could be responseAborted too.
282 */
283 Common::ReadOnlyProperty<bool> responseCompleted;
284
285 public:
286 /**
287 * Returns true once the response status code has been sent (so most things cannot be changed after this); note responseStatus and
288 * the initial bunch of headers (excluding trailers) all set at the same time (so this also checks for all non-trial headers being sent).
289 */
290 Common::ReadOnlyProperty<bool> responseStatusSent;
291
292 public:
293 /**
294 * \note about states - certain properties (declared here and inherited) - like rwHeaders, and writes to properties like (XXX) cannot be done
295 * unless the current state is ePreparingHeaders; and these are generally checked with assertions.
296 *
297 * \note state ordering corresponds to a progression - so new states always correspond to larger numbers, and states
298 * never go backwards
299 */
300 enum class State : uint8_t {
301 ePreparingHeaders, // A newly constructed Response starts out ePreparingHeaders state
302 eHeadersSent, // headers have now been sent over the wire
303 eCompleted, // and finally to Completed
304
305 Stroika_Define_Enum_Bounds (ePreparingHeaders, eCompleted)
306 };
307
308 public:
309 /**
310 * The state may be changed by calls to Abort (), End (), Flush (), Redirect (), and more...
311 *
312 * \note as the design of the HTTP server changes, the list of States may change, so better to check properties
313 * like headersCanBeSet, or responseStatusSent, rather than checking the state explicitly.
314 */
316
317 public:
318 /**
319 * This begins sending the parts of the message which have already been accumulated to the client.
320 * Its illegal to modify anything in the headers etc - after this - but additional writes can happen
321 * IFF you first set the respose.transferEncoding mode to TransferEncoding::kChunked, or because of automaticTransferChunkSize
322 *
323 * This does NOT End the response, and it CAN be called arbitrarily many times (even after the response has completed - though
324 * its pointless then).
325 *
326 * This can be called in any state.
327 *
328 * \note some of this restriction on headers is due to lack of support for trailers - which maybe supported by this class at some point --LGP 2024-06-29
329 */
330 nonvirtual void Flush ();
331
332 public:
333 /**
334 * This method enforces that the given request has been handled. Its illegal to write to this request object again, or modify
335 * any aspect of it (except for calling Abort on it).
336 *
337 * End () does nothing if it was already completed.
338 *
339 * This returns true if the response was ended normally (even if it ended prior to this call) and false if the response was
340 * aborted (abort this->Abort() called) - even if the abort was after the response status was sent.
341 *
342 * \note An internal failure in End (say because the outgoing socket was closed) will internally mark the Response
343 * as aborted (and completed), as if a call to Abort() had been done
344 *
345 * \post this->responseCompleted () (even if exiting the routine via exception)
346 */
347 nonvirtual bool End ();
348
349 public:
350 /**
351 * This can be called anytime, but has no effect if the status = eCompleted. It has the effect of throwing away all
352 * unsent data, and closing the associated socket.
353 *
354 * Can be called in any state.
355 * \post this->responseCompleted ()
356 */
357 nonvirtual void Abort ();
358
359 public:
360 /**
361 * \brief End processing of this response, and direct the client to retry the request at the given url.
362 *
363 * \pre this->headersCanBeSet
364 * \post this->responseCompleted ()
365 */
366 nonvirtual void Redirect (const URI& url);
367
368 public:
369 /**
370 * Depending on modes, write MAY or MAY NOT call Flush () sending the headers. So callers
371 * should set any headers before calling write (or printf which calls write).
372 *
373 * Note for string and wchar_t* writes, this uses this->codePage to encode the characters.
374 *
375 * write (TypedBLOB) is short-hand for setting the contentType, and then writing the BLOB.
376 *
377 * \pre not this->responseCompleted ()
378 * \pre not this->responseStatusSent () or (this->headers ().transferEncoding ()->Contains (HTTP::TransferEncoding::kChunked)))
379 */
380 nonvirtual void write (const span<const byte>& b);
381 nonvirtual void write (const TypedBLOB& b);
382 template <Characters::IConvertibleToString T>
383 nonvirtual void write (T&& s);
384 template <typename CHAR_T, typename... ARGS>
385 nonvirtual void write (const FormatString<CHAR_T>& f, ARGS&&... args);
386
387 public:
388 // @todo deprecate lowercase write() - lowercase implies stdC++ like functionality and not true here
389 template <typename T>
390 nonvirtual void Write (T t)
391 {
392 write (t);
393 }
394
395 public:
396 // @todo deprecate lowercase write() - lowercase implies stdC++ like functionality and not true here
397 template <typename T>
398 nonvirtual void WriteLn (T t)
399 {
400 writeln (t);
401 }
402
403 public:
404 /**
405 * writeln () does a write, followed by writing a CRLF
406 * @todo add overloads like write()
407 */
408 nonvirtual void writeln (const String& e);
409
410 public:
411 /**
412 * @see Characters::ToString ();
413 */
414 nonvirtual String ToString () const;
415
416 private:
417 nonvirtual void FlushNextChunkIfNeeded_ ();
418
419 private:
420 nonvirtual void StateTransition_ (State to);
421
422 private:
423 nonvirtual void ApplyBodyEncodingIfNeeded_ ();
424
425 private:
426 /*
427 * argument rawBytes already compressed if appropriate - just emits/flushes the chunk
428 */
429 nonvirtual void WriteChunk_ (span<const byte> rawBytes);
430
431 private:
432 nonvirtual InternetMediaType AdjustContentTypeForCodePageIfNeeded_ (const InternetMediaType& ct) const;
433
434 private:
436
437 private:
439 Streams::OutputStream::Ptr<byte> fProtocolOutputStream_; // socket stream - either regular socket, or SSL socket stream
440#if !qCompilerAndStdLib_enum_with_bitLength_opequals_Buggy
441 State fState_ : 3 {State::ePreparingHeaders};
442 bool fHeadMode_ : 1 {false};
443 bool fAborted_ : 1 {false};
444#else
445 State fState_{State::ePreparingHeaders};
446 bool fHeadMode_{false};
447 bool fAborted_{false};
448#endif
449 optional<size_t> fAutoTransferChunkSize_{nullopt};
450 optional<HTTP::ContentEncodings> fBodyEncoding_; // either contentEncoding or transferEncodings for compression
451 Streams::InputOutputStream::Ptr<byte> fBodyRawStream_; // write (span<const byte>) appends to this
452 size_t fBodyRawStreamLength_{}; // same as fBodyRawStream_.GetWriteOffset (), but accessible after fBodyRawStream_.CloseWrite ()
453 size_t fBodyRowStreamLengthWhenLastChunkGenerated_{};
454 Streams::InputStream::Ptr<byte> fBodyCompressedStream_; // if not null, implies a bodyEncoding, and this is a typically smaller compressed version of fBodyRawStream_
455 Streams::BufferedOutputStream::Ptr<byte> fUseOutStream_; // wrapper on fProtocolOutputStream_ to provide buffering
456 Characters::CodePage fCodePage_{Characters::WellKnownCodePages::kUTF8};
457 mutable optional<Characters::CodeCvt<>> fCodeCvt_; // for now, cache derived from fCodePage_ --LGP 2024-06-30
458 optional<ETagDigester_> fETagDigester_; // dual use - if present, then flag for autoComputeETag mode as well
459
460 public:
461 [[deprecated ("Since Stroika v3.0d6 - use write with _f strings")]] void printf (const wchar_t* format, ...);
462 [[deprecated ("Since Stroika v3.0d7 use span overload)")]] void write (const byte* s, const byte* e)
463 {
464 Require (s <= e);
465 write (span<const byte>{s, e});
466 }
467 [[deprecated ("Since Stroika v3.0d6 - use write with _f strings")]] void write (const wchar_t* s, const wchar_t* e)
468 {
469 write (span<const wchar_t>{s, e});
470 }
471 };
472 template <>
473 void Response::write (const String& e);
474
475}
476
477/*
478 ********************************************************************************
479 ***************************** Implementation Details ***************************
480 ********************************************************************************
481 */
482#include "Response.inl"
483
484#endif /*_Stroika_Framework_WebServer_Response_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
IncrementalDigester<ALGORITHM> () is the low level way to call Digest algorithms, appropriate for str...
Definition Digester.h:135
TypedBLOB is a named tuple<Memory::BLOB, optional<InternetMediaType>> - with friendlier names,...
Definition TypedBLOB.h:48
a smart pointer wrapper (like shared_ptr <_IRep>).
Definition Socket.h:178
InputOutputStream is single stream object that acts much as a InputStream::Ptr and an OutputStream::P...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
CONTAINER::value_type * End(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the end of the co...
void Abort(const Traversal::Iterable< Ptr > &threads)
foreach Thread t: t.Abort ()
Definition Thread.cpp:987
Roughly equivalent to std::wformat_string, except that it can be constructed from 'char' string,...