Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
cookie_parser.h
1#pragma once
2#include <iomanip>
3#include <memory>
4#include "crow/utility.h"
5#include "crow/http_request.h"
6#include "crow/http_response.h"
7
8namespace crow
9{
10 // Any middleware requires following 3 members:
11
12 // struct context;
13 // storing data for the middleware; can be read from another middleware or handlers
14
15 // before_handle
16 // called before handling the request.
17 // if res.end() is called, the operation is halted.
18 // (still call after_handle of this middleware)
19 // 2 signatures:
20 // void before_handle(request& req, response& res, context& ctx)
21 // if you only need to access this middlewares context.
22 // template <typename AllContext>
23 // void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
24 // you can access another middlewares' context by calling `all_ctx.template get<MW>()'
25 // ctx == all_ctx.template get<CurrentMiddleware>()
26
27 // after_handle
28 // called after handling the request.
29 // void after_handle(request& req, response& res, context& ctx)
30 // template <typename AllContext>
31 // void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
32
34 {
35 // Cookie stores key, value and attributes
36 struct Cookie
37 {
38 enum class SameSitePolicy
39 {
40 Strict,
41 Lax,
42 None
43 };
44
45 template<typename U>
46 Cookie(const std::string& key, U&& value):
47 Cookie()
48 {
49 key_ = key;
50 value_ = std::forward<U>(value);
51 }
52
53 Cookie(const std::string& key):
54 Cookie(key, "") {}
55
56 // format cookie to HTTP header format
57 std::string dump() const
58 {
59 const static char* HTTP_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT";
60
61 std::stringstream ss;
62 ss << key_ << '=';
63 ss << (value_.empty() ? "\"\"" : value_);
64 dumpString(ss, !domain_.empty(), "Domain=", domain_);
65 dumpString(ss, !path_.empty(), "Path=", path_);
66 dumpString(ss, secure_, "Secure");
67 dumpString(ss, httponly_, "HttpOnly");
68 if (expires_at_)
69 {
70 ss << DIVIDER << "Expires="
71 << std::put_time(expires_at_.get(), HTTP_DATE_FORMAT);
72 }
73 if (max_age_)
74 {
75 ss << DIVIDER << "Max-Age=" << *max_age_;
76 }
77 if (same_site_)
78 {
79 ss << DIVIDER << "SameSite=";
80 switch (*same_site_)
81 {
82 case SameSitePolicy::Strict:
83 ss << "Strict";
84 break;
85 case SameSitePolicy::Lax:
86 ss << "Lax";
87 break;
88 case SameSitePolicy::None:
89 ss << "None";
90 break;
91 }
92 }
93 return ss.str();
94 }
95
96 const std::string& name()
97 {
98 return key_;
99 }
100
101 template<typename U>
102 Cookie& value(U&& value)
103 {
104 value_ = std::forward<U>(value);
105 return *this;
106 }
107
108 // Expires attribute
109 Cookie& expires(const std::tm& time)
110 {
111 expires_at_ = std::unique_ptr<std::tm>(new std::tm(time));
112 return *this;
113 }
114
115 // Max-Age attribute
116 Cookie& max_age(long long seconds)
117 {
118 max_age_ = std::unique_ptr<long long>(new long long(seconds));
119 return *this;
120 }
121
122 // Domain attribute
123 Cookie& domain(const std::string& name)
124 {
125 domain_ = name;
126 return *this;
127 }
128
129 // Path attribute
130 Cookie& path(const std::string& path)
131 {
132 path_ = path;
133 return *this;
134 }
135
136 // Secured attribute
137 Cookie& secure()
138 {
139 secure_ = true;
140 return *this;
141 }
142
143 // HttpOnly attribute
144 Cookie& httponly()
145 {
146 httponly_ = true;
147 return *this;
148 }
149
150 // SameSite attribute
151 Cookie& same_site(SameSitePolicy ssp)
152 {
153 same_site_ = std::unique_ptr<SameSitePolicy>(new SameSitePolicy(ssp));
154 return *this;
155 }
156
157 Cookie(const Cookie& c):
158 key_(c.key_),
159 value_(c.value_),
160 domain_(c.domain_),
161 path_(c.path_),
162 secure_(c.secure_),
163 httponly_(c.httponly_)
164 {
165 if (c.max_age_)
166 max_age_ = std::unique_ptr<long long>(new long long(*c.max_age_));
167
168 if (c.expires_at_)
169 expires_at_ = std::unique_ptr<std::tm>(new std::tm(*c.expires_at_));
170
171 if (c.same_site_)
172 same_site_ = std::unique_ptr<SameSitePolicy>(new SameSitePolicy(*c.same_site_));
173 }
174
175 private:
176 Cookie() = default;
177
178 static void dumpString(std::stringstream& ss, bool cond, const char* prefix,
179 const std::string& value = "")
180 {
181 if (cond)
182 {
183 ss << DIVIDER << prefix << value;
184 }
185 }
186
187 private:
188 std::string key_;
189 std::string value_;
190 std::unique_ptr<long long> max_age_{};
191 std::string domain_ = "";
192 std::string path_ = "";
193 bool secure_ = false;
194 bool httponly_ = false;
195 std::unique_ptr<std::tm> expires_at_{};
196 std::unique_ptr<SameSitePolicy> same_site_{};
197
198 static constexpr const char* DIVIDER = "; ";
199 };
200
201
202 struct context
203 {
204 std::unordered_map<std::string, std::string> jar;
205
206 std::string get_cookie(const std::string& key) const
207 {
208 auto cookie = jar.find(key);
209 if (cookie != jar.end())
210 return cookie->second;
211 return {};
212 }
213
214 template<typename U>
215 Cookie& set_cookie(const std::string& key, U&& value)
216 {
217 cookies_to_add.emplace_back(key, std::forward<U>(value));
218 return cookies_to_add.back();
219 }
220
221 Cookie& set_cookie(Cookie cookie)
222 {
223 cookies_to_add.push_back(std::move(cookie));
224 return cookies_to_add.back();
225 }
226
227 private:
228 friend struct CookieParser;
229 std::vector<Cookie> cookies_to_add;
230 };
231
232 void before_handle(request& req, response& res, context& ctx)
233 {
234 // TODO(dranikpg): remove copies, use string_view with c++17
235 int count = req.headers.count("Cookie");
236 if (!count)
237 return;
238 if (count > 1)
239 {
240 res.code = 400;
241 res.end();
242 return;
243 }
244 std::string cookies = req.get_header_value("Cookie");
245 size_t pos = 0;
246 while (pos < cookies.size())
247 {
248 size_t pos_equal = cookies.find('=', pos);
249 if (pos_equal == cookies.npos)
250 break;
251 std::string name = cookies.substr(pos, pos_equal - pos);
252 name = utility::trim(name);
253 pos = pos_equal + 1;
254 if (pos == cookies.size())
255 break;
256
257 size_t pos_semicolon = cookies.find(';', pos);
258 std::string value = cookies.substr(pos, pos_semicolon - pos);
259
260 value = utility::trim(value);
261 if (value[0] == '"' && value[value.size() - 1] == '"')
262 {
263 value = value.substr(1, value.size() - 2);
264 }
265
266 ctx.jar.emplace(std::move(name), std::move(value));
267
268 pos = pos_semicolon;
269 if (pos == cookies.npos)
270 break;
271 pos++;
272 }
273 }
274
275 void after_handle(request& /*req*/, response& res, context& ctx)
276 {
277 for (const auto& cookie : ctx.cookies_to_add)
278 {
279 res.add_header("Set-Cookie", cookie.dump());
280 }
281 }
282 };
283
284 /*
285 App<CookieParser, AnotherJarMW> app;
286 A B C
287 A::context
288 int aa;
289
290 ctx1 : public A::context
291 ctx2 : public ctx1, public B::context
292 ctx3 : public ctx2, public C::context
293
294 C depends on A
295
296 C::handle
297 context.aaa
298
299 App::context : private CookieParser::context, ...
300 {
301 jar
302
303 }
304
305 SimpleApp
306 */
307} // namespace crow
The main namespace of the library. In this namespace is defined the most important classes and functi...
An HTTP request.
Definition http_request.h:36
HTTP response.
Definition http_response.h:34
int code
The Status code for the response.
Definition http_response.h:40
void end()
Set the response completion flag and call the handler (to send the response).
Definition http_response.h:237