Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Headers.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_HTTP_Headers_h_
5#define _Stroika_Foundation_IO_Network_HTTP_Headers_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
10#include "Stroika/Foundation/Common/Common.h"
11#include "Stroika/Foundation/Common/Property.h"
12#include "Stroika/Foundation/Containers/Association.h"
13#include "Stroika/Foundation/Containers/Collection.h"
14#include "Stroika/Foundation/Containers/Mapping.h"
15#include "Stroika/Foundation/Containers/Set.h"
18#include "Stroika/Foundation/IO/Network/HTTP/CacheControl.h"
19#include "Stroika/Foundation/IO/Network/HTTP/ContentEncoding.h"
20#include "Stroika/Foundation/IO/Network/HTTP/Cookie.h"
21#include "Stroika/Foundation/IO/Network/HTTP/ETag.h"
22#include "Stroika/Foundation/IO/Network/HTTP/IfNoneMatch.h"
23#include "Stroika/Foundation/IO/Network/HTTP/KeepAlive.h"
24#include "Stroika/Foundation/IO/Network/HTTP/TransferEncoding.h"
26
27/**
28 */
30
31 using Characters::String;
32 using Common::KeyValuePair;
33 using Containers::Association;
34 using Containers::Collection;
35 using Containers::Mapping;
36 using Containers::Set;
37 using DataExchange::InternetMediaType;
38 using Traversal::Iterable;
39
40 /**
41 * standard HTTP headers one might want to access/retrieve
42 */
43 namespace HeaderName {
44
45 constexpr string_view kAcceptEncoding = "Accept-Encoding"sv;
46 constexpr string_view kAccessControlAllowCredentials = "Access-Control-Allow-Credentials"sv;
47 constexpr string_view kAccessControlAllowOrigin = "Access-Control-Allow-Origin"sv;
48 constexpr string_view kAccessControlAllowHeaders = "Access-Control-Allow-Headers"sv;
49 constexpr string_view kAccessControlAllowMethods = "Access-Control-Allow-Methods"sv;
50 constexpr string_view kAccessControlRequestHeaders = "Access-Control-Request-Headers"sv;
51 constexpr string_view kAccessControlMaxAge = "Access-Control-Max-Age"sv;
52 constexpr string_view kAllow = "Allow"sv;
53 constexpr string_view kAuthorization = "Authorization"sv;
54 constexpr string_view kCacheControl = "Cache-Control"sv;
55 constexpr string_view kContentDisposition = "Content-Disposition"sv;
56 constexpr string_view kContentEncoding = "Content-Encoding"sv;
57 constexpr string_view kContentLength = "Content-Length"sv;
58 constexpr string_view kContentType = "Content-Type"sv;
59 constexpr string_view kConnection = "Connection"sv;
60 constexpr string_view kCookie = "Cookie"sv;
61 constexpr string_view kDate = "Date"sv;
62 constexpr string_view kETag = "ETag"sv;
63 constexpr string_view kExpect = "Expect"sv;
64 constexpr string_view kExpires = "Expires"sv;
65 constexpr string_view kHost = "Host"sv;
66 constexpr string_view kIfNoneMatch = "If-None-Match"sv;
67 constexpr string_view kIfModifiedSince = "If-Modified-Since"sv;
68 constexpr string_view kKeepAlive = "Keep-Alive"sv;
69 constexpr string_view kLastModified = "Last-Modified"sv;
70 constexpr string_view kLocation = "Location"sv;
71 constexpr string_view kOrigin = "Origin"sv;
72 constexpr string_view kReferrer = "Referer"sv; // intentionally spelled this way - misspelled in the HTTP RFC
73 constexpr string_view kServer = "Server"sv;
74 constexpr string_view kSetCookie = "Set-Cookie"sv;
75 constexpr string_view kSOAPAction = "SOAPAction"sv;
76 constexpr string_view kTransferEncoding = "Transfer-Encoding"sv;
77 constexpr string_view kUserAgent = "User-Agent"sv;
78 constexpr string_view kVary = "Vary"sv;
79
80 }
81
82 /**
83 * \note from https://www.ietf.org/rfc/rfc2616.txt
84 * The field-names given are not limited to the set of standard
85 * request-header fields defined by this specification. Field names are
86 * case-insensitive.
87 */
88 constexpr auto kHeaderNameEqualsComparer = String::EqualsComparer{Characters::eCaseInsensitive};
89 constexpr auto kHeaderNameInOrderComparer = String::LessComparer{Characters::eCaseInsensitive};
90
91 /**
92 * \brief roughly equivalent to Association<String,String>, except that the class is smart about certain
93 * keys and will automatically fold them together.
94 *
95 * \note From https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2
96 *
97 * The order in which header fields with differing field names are
98 * received is not significant. However, it is good practice to send
99 * header fields that contain control data first, such as Host on
100 * requests and Date on responses, so that implementations can decide
101 * when not to handle a message as early as possible. A server MUST NOT
102 * apply a request to the target resource until the entire request
103 * header section is received, since later header fields might include
104 * conditionals, authentication credentials, or deliberately misleading
105 * duplicate header fields that would impact request processing.
106 *
107 * A sender MUST NOT generate multiple header fields with the same field
108 * name in a message unless either the entire field value for that
109 * header field is defined as a comma-separated list [i.e., #(values)]
110 * or the header field is a well-known exception (as noted below).
111 *
112 * A recipient MAY combine multiple header fields with the same field
113 * name into one "field-name: field-value" pair, without changing the
114 * semantics of the message, by appending each subsequent field value to
115 * the combined field value in order, separated by a comma. The order
116 * in which header fields with the same field name are received is
117 * therefore significant to the interpretation of the combined field
118 * value; a proxy MUST NOT change the order of these field values when
119 * forwarding a message.
120 *
121 * Note: In practice, the "Set-Cookie" header field ([RFC6265]) often
122 * appears multiple times in a response message and does not use the
123 * list syntax, violating the above requirements on multiple header
124 * fields with the same name. Since it cannot be combined into a
125 * single field-value, recipients ought to handle "Set-Cookie" as a
126 * special case while processing header fields. (See Appendix A.2.3
127 * of [Kri2001] for details.)
128 */
129 class Headers {
130 public:
131 /**
132 * ExtendableProperty allow the value of some headers to be overridden by some containing object.
133 * When copying a Header, we don't reference that external object any longer. So copying must
134 * by value, and copy that current value.
135 */
136 Headers ();
137 Headers (Headers&& src);
138 Headers (const Headers& src);
139 explicit Headers (const Iterable<pair<String, String>>& src);
141
142 public:
143 nonvirtual Headers& operator= (Headers&& rhs) noexcept;
144 nonvirtual Headers& operator= (const Headers& rhs);
145
146#if qStroika_Foundation_Debug_AssertExternallySynchronizedChecker_Enabled
147 public:
148 /**
149 * Allow users of the Headers object to have it share a 'assure externally synchronized' context.
150 */
152#endif
153
154 public:
155 /**
156 * For now - this returns 0 or one value.
157 * @todo - this should return ALL matching if there are multiple.
158 */
159 nonvirtual optional<String> LookupOne (const String& name) const;
160
161 public:
162 /**
163 * Some HTTP headers can appear multiple times (such as Set-Cookie). Return all with the given name.
164 */
165 nonvirtual Collection<String> LookupAll (const String& name) const;
166
167 public:
168 /**
169 * Add (or sometimes replace) the given header name/value pair. Whether this replaces
170 * or adds depends on the particular headerName. Some which are recognized to appear just once
171 * are replaced (such as Cookie); Some are recognized as appearing multiple times (such as Set-Cookie)
172 * and these are appended. Use Set/Remove to avoid ambiguity.
173 */
174 nonvirtual void Add (const String& headerName, const String& value);
175 nonvirtual void Add (const KeyValuePair<String, String>& hrdAndValue);
176 nonvirtual void Add (const pair<String, String>& hrdAndValue);
177
178 public:
179 /**
180 * \brief not the same as assignment - only for headers set in argument, replace those in this header object.
181 */
182 nonvirtual void AddAll (const Headers& headers);
183
184 public:
185 /**
186 */
187 nonvirtual void operator+= (const pair<String, String>& hrdAndValue);
188 nonvirtual void operator+= (const KeyValuePair<String, String>& hrdAndValue);
189 nonvirtual void operator+= (const Headers& headers);
190
191 public:
192 /**
193 * Remove ALL occurrences of the given header name.
194 * For the two-arg overload, remove the given header with teh given value.
195 *
196 * Returns the number of items removed.
197 */
198 nonvirtual size_t Remove (const String& headerName);
199 nonvirtual size_t Remove (const String& headerName, const String& value);
200
201 public:
202 /**
203 * Set to any string value, or to nullopt to clear the option.
204 */
205 nonvirtual void Set (const String& headerName, const optional<String>& value);
206
207 public:
208 /**
209 * \see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
210 */
212
213 public:
214 /**
215 * Property with the optional<String> value of the Access-Control-Allow-Origin header.
216 * \see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
217 *
218 * This can be nullopt (meaning not present), "*", or a hostname[:port]
219 */
221
222 public:
223 /**
224 * Property with the optional<CacheControl> value of the Allow header.
225 * \see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow
226 *
227 * This property automatically parses the header into an set of strings (because order here doesn't matter)
228 */
230
231 public:
232 /**
233 * \brief typically, the value will be missing, or "Bearer xxx"
234 * \see https://datatracker.ietf.org/doc/html/rfc7235 (not great reference)
235 * \note confusing name authorization due to HTTP authorization name - really refers to mix of authentication/authorization (AUTH)
236 */
238
239 public:
240 /**
241 * \brief HTTP Response header controlling how clients will cache this response.
242 *
243 * Property with the optional<CacheControl> value of the Cache-Control header.
244 *
245 * From https://tools.ietf.org/html/rfc7234#page-5
246 *
247 * Although caching is an entirely OPTIONAL feature
248 * of HTTP, it can be assumed that reusing a cached response is
249 * desirable and that such reuse is the default behavior when no
250 * requirement or local configuration prevents it. Therefore, HTTP
251 * cache requirements are focused on preventing a cache from either
252 * storing a non-reusable response or reusing a stored response
253 * inappropriately, rather than mandating that caches always store and
254 * reuse particular responses.
255 *
256 * https://tools.ietf.org/html/rfc2616#section-13.4
257 *
258 * Unless specifically constrained by a cache-control (section 14.9)
259 * directive, a caching system MAY always store a successful response
260 * (see section 13.8) as a cache entry, MAY return it without validation
261 * if it is fresh, and MAY return it after successful validation. If
262 * there is neither a cache validator nor an explicit expiration time
263 * associated with a response, we do not expect it to be cached, but
264 * certain caches MAY violate this expectation (for example, when little
265 * or no network connectivity is available).
266 *
267 * But then contradicting the above
268 * https://tools.ietf.org/html/rfc2616#section-9.5
269 * (about POST)
270 * Responses to this method are not cacheable, unless the response
271 * includes appropriate Cache-Control or Expires header fields
272 *
273 * similarly for PUT/POST/DELETE (from same document) - those appear to not be cacheable.
274 */
276
277 public:
278 /**
279 * Value of the HTTP 1.1 and earlier Connection header (connection property).
280 */
282 eKeepAlive,
283 eClose,
284 };
285
286 public:
287 /**
288 * Property with the optional<ConnectionValue> value of the Connection header.
289 *
290 * Header mostly just used for HTTP 1.1 and earlier.
291 */
293
294 public:
295 /**
296 * \see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
297 *
298 * \note - docs not super clear, but it appears there CAN be more than one encoding, and they are done in order, one after the other
299 */
301
302 public:
303 /**
304 * Property with the optional<uint64_t> value of the Content-Length header.
305 * \note this refers to the (possibly encoded) size in bytes of the HTTP payload body (so if zipped its the size of the zipfile not the size after unzip).
306 *
307 * \par Example Usage
308 * \code
309 * optional<uint64_t> contentLength = fHeaders_.contentLength;
310 * \endcode
311 */
313
314 public:
315 /**
316 * Property with the optional<InternetMediaType> value of the Content-Type header.
317 *
318 * \par Example Usage
319 * \code
320 * optional<InternetMediaType> contentType = fHeaders_.contentType;
321 * fHeaders_.contentType = nullopt; // remove the content-type header
322 * \endcode
323 */
325
326 public:
327 /**
328 * \brief The HTTP Content-Disposition header indicates whether content should be displayed inline in the browser as a web page or part of a web page or downloaded as an attachment locally
329 *
330 * \par Example Usage
331 * \code
332 * fHeaders_.contentDisposition = "inline"; // default value
333 * fHeaders_.contentDisposition = "attachment"; // file should be downloaded
334 * fHeaders_.contentDisposition = "attachment; filename=\"foo.jpg\"";
335 * \endcode
336 */
338
339 public:
340 /**
341 * \brief The Date header: when the message was sent (RFC 9110 section 6.6.1), held in UTC (a DateTime assigned is converted).
342 *
343 * The header is read and written with DateTime::kHTTPDateFormat - written as "Sun, 06 Nov 1994 08:49:37 GMT", and read in
344 * any of HTTP's date forms. One that cannot be read is taken as no Date header.
345 */
347
348 public:
349 /**
350 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie
351 *
352 * if the list is empty, this corresponds to no header present
353 */
355
356 public:
357 /**
358 * Property with the optional<ETag> value of the ETag header.
359 */
361
362 public:
363 /**
364 * Property with the optional<String> value of the Host header.
365 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
366 * This is a request-only Header.
367 */
369
370 public:
371 /**
372 * Property with the optional<IfNoneMatch> value of the IF-None-Match header.
373 */
375
376 public:
377 /**
378 * https://tools.ietf.org/html/rfc2068#section-19.7.1.1
379 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive
380 */
382
383 public:
384 /**
385 * Property with the optional<URI> value of the Location header.
386 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location
387 */
389
390 public:
391 /**
392 * Property with the optional<URI> value of the Origin header.
393 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
394 * This is a request-only Header sent with CORS requests, as well as with POST requests
395 */
397
398 public:
399 /**
400 * Property with the optional<String> value of the Server header.
401 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server
402 */
404
405 public:
406 /**
407 * Encoded in HTTP as a SEQUENCE of separate HTTP Headers;
408 * empty list amounts to no headers present
409 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
410 */
412
413 public:
414 /**
415 * Property with the optional<Set<TransferEncoding>> value of the Transfer-Encoding header.
416 *
417 * This is very typically OMITTED, and when present in a Response Header, its typically value will just be TransferEncoding::kChunked
418 * because the default - TransferEncoding::eIdentity is understood when the header is missing.
419 *
420 * \note - this is generally NOT to be used for compression (though it can be).
421 *
422 * \note because a TransferEncodings object with the single entry eIdentity is the default, that state is treated as equivalent to missing (so assign of such an array to this property results in it being missing)
423 */
425
426 public:
427 /**
428 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary
429 * This is a response-only Header.
430 */
432
433 public:
434 /**
435 * Returns the combined set of headers (list Key:Value pairs). Note this may not be returned in
436 * the same order and exactly losslessly identically to what was passed in.
437 *
438 * \note - if Mapping<String,String> is the target type, and if setCookie headers are present, some maybe omitted in the
439 * resulting Mapping<>
440 *
441 * Supported T types:
442 * o Association<String,String>
443 * o Mapping<String,String>
444 * o Collection<KeyValuePair<String,String>>
445 * o Iterable<KeyValuePair<String,String>>
446 */
447 template <Common::IAnyOf<Association<String, String>, Mapping<String, String>, Collection<KeyValuePair<String, String>>, Iterable<KeyValuePair<String, String>>> T =
448 Iterable<KeyValuePair<String, String>>>
449 nonvirtual T As () const;
450
451 public:
452 /**
453 * @see Characters::ToString ();
454 */
455 nonvirtual String ToString () const;
456
457 public:
458 /**
459 */
460 nonvirtual bool operator== (const Headers& rhs) const;
461
462 private:
463 enum class AddOrSet {
464 eAdd,
465 eSet,
466 eRemove
467 };
468 // UpdateBuiltin_ returns true iff headerName was a parsed/builtin type, and false for 'extra' headers: to find out # elts changed, use nChanges optional parameter
469 nonvirtual bool UpdateBuiltin_ (AddOrSet flag, const String& headerName, const optional<String>& value, size_t* nRemovals = nullptr);
470 nonvirtual void SetExtras_ (const String& headerName, const optional<String>& value);
471
472 private:
473 // Could have properties lookup once when loading and store here. Or could have
474 // them dynamically lookup in fExtraHeaders_. Just put the ones here in special variables
475 // that are very commonly checked for, so their check/update will be a bit quicker.
477 optional<String> fAuthorization_;
478 optional<ContentEncodings> fAcceptEncodings_; // request header only
479 optional<CacheControl> fCacheControl_;
480 optional<ContentEncodings> fContentEncoding_;
481 optional<uint64_t> fContentLength_; // must access through property to access extended property handlers (except root getter/setter)
482 optional<InternetMediaType> fContentType_;
483 optional<CookieList> fCookieList_; // store optional cuz often missing, and faster init
485 optional<HTTP::ETag> fETag_; // must access through property to access extended property handlers (except root getter/setter)
486 optional<String> fHost_;
487 optional<IfNoneMatch> fIfNoneMatch_;
488 optional<CookieList> fSetCookieList_; // store optional cuz often missing, and faster init
489 optional<TransferEncodings> fTransferEncoding_; // must access through property to access extended property handlers (except root getter/setter)
492 };
493 template <>
494 Association<String, String> Headers::As () const;
495 template <>
496 Mapping<String, String> Headers::As () const;
497 template <>
498 Collection<KeyValuePair<String, String>> Headers::As () const;
499 template <>
500 Iterable<KeyValuePair<String, String>> Headers::As () const;
501
502}
503
504/*
505 ********************************************************************************
506 ***************************** Implementation Details ***************************
507 ********************************************************************************
508 */
509#include "Headers.inl"
510
511#endif /*_Stroika_Foundation_IO_Network_HTTP_Headers_h_*/
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
ExtendableProperty is a Property which has callbacks associated with it, to be notified when it is ac...
Definition Property.h:491
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
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
Common::Property< optional< String > > contentDisposition
The HTTP Content-Disposition header indicates whether content should be displayed inline in the brows...
Definition Headers.h:337
nonvirtual optional< String > LookupOne(const String &name) const
Definition Headers.cpp:415
Common::ExtendableProperty< optional< HTTP::ETag > > ETag
Definition Headers.h:360
Common::Property< optional< URI > > location
Definition Headers.h:388
Common::ExtendableProperty< optional< uint64_t > > contentLength
Definition Headers.h:312
Common::Property< optional< String > > accessControlAllowOrigin
Definition Headers.h:220
Common::Property< optional< InternetMediaType > > contentType
Definition Headers.h:324
Common::Property< optional< Time::DateTime > > date
The Date header: when the message was sent (RFC 9110 section 6.6.1), held in UTC (a DateTime assigned...
Definition Headers.h:346
nonvirtual size_t Remove(const String &headerName)
Definition Headers.cpp:506
Common::Property< optional< String > > host
Definition Headers.h:368
nonvirtual void Add(const String &headerName, const String &value)
Definition Headers.cpp:528
Common::Property< optional< ContentEncodings > > contentEncoding
Definition Headers.h:300
nonvirtual Collection< String > LookupAll(const String &name) const
Definition Headers.cpp:478
Common::Property< optional< CacheControl > > cacheControl
HTTP Response header controlling how clients will cache this response.
Definition Headers.h:275
Common::Property< optional< Containers::Set< String > > > vary
Definition Headers.h:431
Common::Property< CookieList > cookie
Definition Headers.h:354
Common::Property< optional< HTTP::KeepAlive > > keepAlive
Definition Headers.h:381
Common::Property< optional< URI > > origin
Definition Headers.h:396
Common::Property< optional< ContentEncodings > > acceptEncoding
Definition Headers.h:211
Common::Property< optional< String > > authorization
typically, the value will be missing, or "Bearer xxx"
Definition Headers.h:237
Common::Property< optional< ConnectionValue > > connection
Definition Headers.h:292
Common::Property< optional< Containers::Set< String > > > allow
Definition Headers.h:229
Common::Property< CookieList > setCookie
Definition Headers.h:411
Common::Property< optional< IfNoneMatch > > ifNoneMatch
Definition Headers.h:374
Common::ExtendableProperty< optional< TransferEncodings > > transferEncoding
Definition Headers.h:424
Common::Property< optional< String > > server
Definition Headers.h:403
nonvirtual void AddAll(const Headers &headers)
not the same as assignment - only for headers set in argument, replace those in this header object.
Definition Headers.cpp:537
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:234
very similar to ThreeWayComparer but returns true if less
Definition String.h:1884