Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Cookie.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Characters/String2Int.h"
8#include "Stroika/Foundation/Containers/Set.h"
10
11#include "Cookie.h"
12
13using namespace Stroika::Foundation;
16using namespace Stroika::Foundation::IO;
19using namespace Stroika::Foundation::Streams;
20
22
23/*
24 ********************************************************************************
25 ********************************* HTTP::Cookie *********************************
26 ********************************************************************************
27 */
28Cookie::Cookie (const String& name, const String& value, const Mapping<String, String>& attributes)
29 : Cookie{name, value}
30{
31 for (const auto& i : attributes) {
32 AddAttribute (i.fKey, i.fValue);
33 }
34}
35
37{
39 if (fExpires) {
40 result.Add (kExpiresAttributeLabel, fExpires->Format (Time::DateTime::kISO8601Format)); // not sure this is right???
41 }
42 if (fMaxAge) {
43 result.Add (kMaxAgeAttributeLabel, "{}"_f(*fMaxAge));
44 }
45 if (fDomain) {
46 result.Add (kDomainAttributeLabel, *fDomain);
47 }
48 if (fPath) {
49 result.Add (kPathAttributeLabel, *fPath);
50 }
51 if (fSecure) {
52 result.Add (kSecureAttributeLabel, String{});
53 }
54 if (fHttpOnly) {
55 result.Add (kHttpOnlyAttributeLabel, String{});
56 }
57 if (fOtherAttributes) {
58 result += *fOtherAttributes;
59 }
60 return result;
61}
62
63void Cookie::AddAttribute (const String& aEqualsBAttributePair)
64{
65 if (auto i = aEqualsBAttributePair.Find ('=')) {
66 AddAttribute (aEqualsBAttributePair.SubString (0, *i), aEqualsBAttributePair.SubString (0, *i + 1));
67 }
68 else {
69 AddAttribute (aEqualsBAttributePair);
70 }
71}
72
73void Cookie::AddAttribute (const String& key, const String& value)
74{
75 using Time::DateTime;
76 // ordered so most likely first
77 if (key == kPathAttributeLabel) {
78 fPath = value;
79 }
80 else if (key == kDomainAttributeLabel) {
81 fDomain = value;
82 }
83 else if (key == kExpiresAttributeLabel) {
84 fExpires = DateTime::Parse (value, DateTime::kRFC1123Format);
85 }
86 else if (key == kMaxAgeAttributeLabel) {
87 fMaxAge = String2Int<int> (value);
88 }
89 else if (key == kSecureAttributeLabel) {
90 fSecure = true;
91 }
92 else if (key == kHttpOnlyAttributeLabel) {
93 fHttpOnly = true;
94 }
95 else {
96 if (fOtherAttributes) {
97 fOtherAttributes->Add (key, value);
98 }
99 else {
100 fOtherAttributes = Mapping<String, String>{KeyValuePair<String, String>{key, value}};
101 }
102 }
103}
104
105template <>
106String Cookie::As<String> () const
107{
108 // @todo re-read spec more carefully about character encoding...
109 StringBuilder sb;
110 sb << fKey << "="sv << fValue;
111 for (const KeyValuePair<String, String>& kvp : GetAttributes ()) {
112 sb << "; "sv << kvp.fKey;
113 static const Set<String> kNoValueAttributes_{kSecureAttributeLabel, kHttpOnlyAttributeLabel};
114 if (not kNoValueAttributes_.Contains (kvp.fKey)) {
115 sb << "="sv << kvp.fValue;
116 }
117 }
118 return String{};
119}
120
122{
123 Require (src.IsSeekable ());
124 auto skipWS = [&] () {
125 while (optional<Character> c = src.ReadBlocking ()) {
126 if (not c->IsWhitespace ()) {
127 src.Seek (eFromCurrent, -1);
128 return;
129 }
130 }
131 };
132 // scan up to target char; leave stream after that character, but return string just before it
133 auto skipUpTo = [&] (Character targetChar, String* s) {
134 StringBuilder sb;
135 while (optional<Character> c = src.ReadBlocking ()) {
136 if (*c == targetChar) {
137 break;
138 }
139 else {
140 sb.Append (*c);
141 }
142 }
143 *s = sb.str ();
144 };
145 // same as skipUpTo, but with 2 possible characters
146 auto skipUpTo2 = [&] (Character targetChar, Character targetChar2, String* s) {
147 StringBuilder sb;
148 while (optional<Character> c = src.ReadBlocking ()) {
149 if (*c == targetChar or *c == targetChar2) {
150 break;
151 }
152 else {
153 sb.Append (*c);
154 }
155 }
156 *s = sb.str ();
157 };
158 auto prevChar = [&] () {
159 src.Seek (eFromCurrent, -1);
160 auto c = src.ReadBlocking ();
161 Assert (c.has_value ());
162 return *c;
163 };
164 skipWS ();
165 String key;
166 skipUpTo ('=', &key);
167 String value;
168 skipUpTo (';', &value);
169 Mapping<String, String> attributes;
170 while (not src.IsAtEOF ()) {
171 skipWS ();
172 String k2;
173 String val2;
174 skipUpTo2 ('=', ';', &k2);
175 if (prevChar () == '=') {
176 skipUpTo (';', &val2);
177 }
178 if (not key.empty ()) {
179 attributes.Add (k2, val2);
180 }
181 }
182 return Cookie{key, value, attributes};
183}
184
185Cookie Cookie::Parse (const String& src)
186{
187 return Parse (BinaryToText::Reader::New (src));
188}
189
190/*
191 ********************************************************************************
192 ***************************** HTTP::CookieList *********************************
193 ********************************************************************************
194 */
196 : cookies{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Mapping<String, String> {
197 const CookieList* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &CookieList::cookies);
198 return thisObj->fCookieDetails_.As<Mapping<String, String>> ();
199 },
200 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] auto* property, const Mapping<String, String>& basicCookies) {
201 CookieList* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &CookieList::cookies);
202 thisObj->fCookieDetails_ = basicCookies.Map<Iterable<Cookie>> ([] (const auto& i) { return Cookie{i.fKey, i.fValue}; });
203 }}
204 , cookieDetails{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Collection<Cookie> {
205 const CookieList* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &CookieList::cookieDetails);
206 return thisObj->fCookieDetails_;
207 },
208 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] auto* property, const Collection<Cookie>& cookies) {
209 CookieList* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &CookieList::cookieDetails);
210 thisObj->fCookieDetails_ = cookies;
211 }}
212{
213}
214
216{
217 return String::Join (fCookieDetails_.Map<Iterable<String>> ([] (const auto& i) { return i.fKey + "="sv + i.fValue; }), "; "sv);
218}
219
220CookieList CookieList::Parse (const String& cookieValueArg)
221{
222 Collection<Cookie> results;
223 for (const auto& keyValuePair : cookieValueArg.Tokenize ({';'})) {
224 results += Cookie::Parse (keyValuePair);
225 }
226 return results;
227}
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual void Append(span< const CHAR_T > s)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static String Join(const Iterable< String > &list, const String &separator=", "sv)
Definition String.cpp:1692
nonvirtual String SubString(SZ from) const
nonvirtual Containers::Sequence< String > Tokenize() const
Definition String.cpp:1234
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
Definition String.inl:681
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:190
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual optional< ElementType > ReadBlocking() const
ReadBlocking () reads either a single element, or fills in argument intoBuffer - but never blocks (no...
nonvirtual SeekOffsetType Seek(SeekOffsetType offset) const
nonvirtual bool IsAtEOF() const
check if the stream is currently at EOF
nonvirtual bool IsSeekable() const
Returns true iff this object was constructed with a seekable input stream rep.
Definition Stream.inl:44
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...