Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
Cookie.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 <>
107{
108 // @todo re-read spec more carefully about character encoding...
110 sb << fKey << "="sv << fValue;
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) {
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
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);
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{
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...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual bool Contains(Character c, CompareOptions co=eWithCase) const
Definition String.inl:697
nonvirtual bool IsWhitespace() const
Definition String.cpp:1783
static String Join(const Iterable< String > &list, const String &separator=", "sv)
Definition String.cpp:1693
nonvirtual String SubString(SZ from) const
nonvirtual Containers::Sequence< String > Tokenize() const
Definition String.cpp:1235
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
Definition String.inl:685
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
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 ...