roar
Loading...
Searching...
No Matches
request.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <boost/beast/http/message.hpp>
11#include <boost/beast/http/empty_body.hpp>
12#include <boost/algorithm/string/classification.hpp>
13#include <boost/algorithm/string/constants.hpp>
14#include <boost/algorithm/string/split.hpp>
15#include <boost/algorithm/string/trim.hpp>
16#include <boost/beast/websocket/rfc6455.hpp>
17#include <boost/lexical_cast.hpp>
18
19#ifdef ROAR_ENABLE_NLOHMANN_JSON
20# include <nlohmann/json.hpp>
21#endif
22
23#include <optional>
24#include <string>
25#include <vector>
26#include <unordered_map>
27#include <deque>
28
29namespace Roar
30{
31 namespace Detail
32 {
34 {
35 std::optional<std::vector<std::string>> regexMatches_;
36 std::string path_;
37 std::unordered_map<std::string, std::string> query_;
38 std::string host_;
39 std::string port_;
40 };
41 }
42
48 template <typename BodyT>
49 class Request
50 : public boost::beast::http::request<BodyT>
52 {
53 public:
56
58 : boost::beast::http::request<BodyT>{}
60 {}
61
67 explicit Request(beast_request req)
68 : boost::beast::http::request<BodyT>{std::move(req)}
70 {
72 }
73
80 : boost::beast::http::request<BodyT>{std::move(req)}
81 , Detail::RequestExtensions{std::move(extensions)}
82 {
84 }
85
91 std::string path() const
92 {
93 return path_;
94 }
95
96 void target(std::string_view target)
97 {
98 static_cast<beast_request*>(this)->target(target);
100 }
101 std::string_view target() const
102 {
103 return static_cast<beast_request const*>(this)->target();
104 }
105
106 Request<BodyT>& host(std::string&& host)
107 {
108 host_ = std::move(host);
109 this->set(boost::beast::http::field::host, host_);
110 return *this;
111 }
112 Request<BodyT>& host(std::string const& host)
113 {
114 host_ = host;
115 this->set(boost::beast::http::field::host, host_);
116 return *this;
117 }
118 Request<BodyT>& host(std::string_view host)
119 {
120 host_ = std::string{host};
121 this->set(boost::beast::http::field::host, host_);
122 return *this;
123 }
125 {
126 host_ = std::string{host};
127 this->set(boost::beast::http::field::host, host_);
128 return *this;
129 }
130 std::string host() const
131 {
132 return host_;
133 }
134
135 Request<BodyT>& port(std::string&& port)
136 {
137 port_ = std::move(port);
138 return *this;
139 }
140 Request<BodyT>& port(std::string const& port)
141 {
142 port_ = port;
143 return *this;
144 }
145 Request<BodyT>& port(std::string_view port)
146 {
147 port_ = std::string{port};
148 return *this;
149 }
150 Request<BodyT>& port(unsigned short port)
151 {
152 port_ = std::to_string(port);
153 return *this;
154 }
156 {
157 port_ = std::string{port};
158 return *this;
159 }
160 std::string port() const
161 {
162 return port_;
163 }
164
165 Request<BodyT>& setHeader(boost::beast::http::field field, std::string value)
166 {
167 this->set(field, std::move(value));
168 return *this;
169 }
170
176 auto const& query() const
177 {
178 return query_;
179 }
180
186 auto const& pathMatches() const
187 {
188 return regexMatches_;
189 }
190
196 Request<BodyT>& pathMatches(std::vector<std::string>&& matches)
197 {
198 regexMatches_ = std::move(matches);
199 return *this;
200 }
201
207 template <typename FieldT>
208 std::vector<std::string> splitCommaSeperatedHeaderEntry(FieldT&& field) const
209 {
210 std::vector<std::string> splitList;
211 boost::algorithm::split(
212 splitList,
213 std::string{this->at(std::forward<FieldT>(field))},
214 boost::algorithm::is_any_of(","),
215 boost::algorithm::token_compress_on);
216
217 for (auto& element : splitList)
218 boost::algorithm::trim(element);
219
220 return splitList;
221 }
222
230 {
231 return boost::beast::websocket::is_upgrade(*this);
232 }
233
234#ifdef ROAR_ENABLE_NLOHMANN_JSON
241 template <typename Body = BodyT>
242 std::enable_if_t<std::is_same_v<Body, boost::beast::http::string_body>, nlohmann::json> json() const
243 {
244 return nlohmann::json::parse(this->body());
245 }
246
247 template <typename Body = BodyT>
248 std::enable_if_t<std::is_same_v<Body, boost::beast::http::string_body>, Request<BodyT>&>
249 json(nlohmann::json const& j, int indent = -1)
250 {
251 this->body() = j.dump(indent);
252 this->set(boost::beast::http::field::content_type, "application/json");
253 this->prepare_payload();
254 return *this;
255 }
256#endif
263 {
264 return {
265 .regexMatches_ = std::move(regexMatches_),
266 .path_ = std::move(path_),
267 .query_ = std::move(query_),
268 .host_ = std::move(host_),
269 .port_ = std::move(port_)};
270 }
271
278 bool expectsContinue() const
279 {
280 auto iter = this->find(boost::beast::http::field::expect);
281 if (iter == std::end(*this))
282 return false;
283 else
284 return iter->value() == "100-continue";
285 }
286
292 void expectsContinue(bool enable)
293 {
294 if (enable)
295 this->set(boost::beast::http::field::expect, "100-continue");
296 else
297 this->erase(boost::beast::http::field::expect);
298 }
299
305 std::optional<std::size_t> contentLength() const
306 {
307 auto contentLength = this->find("Content-Length");
308 if (contentLength == std::end(*this))
309 return std::nullopt;
310 else
311 return boost::lexical_cast<std::size_t>(contentLength->value());
312 }
313
319 void contentLength(std::size_t length)
320 {
321 this->set(boost::beast::http::field::content_length, std::to_string(length));
322 }
323
329 std::optional<Ranges> ranges() const
330 {
331 auto iter = this->find(boost::beast::http::field::range);
332 if (iter == std::end(*this))
333 return std::nullopt;
334 return Ranges::fromString(std::string{iter->value()});
335 }
336
340 std::optional<AuthorizationScheme> authorizationScheme() const
341 {
342 auto iter = this->find(boost::beast::http::field::authorization);
343 if (iter == std::end(*this))
344 return std::nullopt;
345 auto spacePos = iter->value().find(' ');
346 if (spacePos == std::string::npos)
347 return std::nullopt;
348 return authorizationSchemeFromString(iter->value().substr(0, spacePos));
349 }
350
354 std::optional<BasicAuth> basicAuth() const
355 {
356 auto iter = this->find(boost::beast::http::field::authorization);
357 auto value = iter->value();
358 if (iter == std::end(*this))
359 return std::nullopt;
360 auto spacePos = value.find(' ');
361 if (spacePos == std::string::npos)
362 return std::nullopt;
363 if (value.substr(0, spacePos) != "Basic")
364 return std::nullopt;
365 return BasicAuth::fromBase64(value.substr(spacePos + 1, value.size() - spacePos - 1));
366 }
367
369 {
370 this->set(boost::beast::http::field::authorization, "Basic " + auth.toBase64());
371 return *this;
372 }
373
377 std::optional<DigestAuth> digestAuth() const
378 {
379 auto iter = this->find(boost::beast::http::field::authorization);
380 auto value = iter->value();
381 if (iter == std::end(*this))
382 return std::nullopt;
383 auto spacePos = value.find(' ');
384 if (spacePos == std::string::npos)
385 return std::nullopt;
386 if (value.substr(0, spacePos) != "Digest")
387 return std::nullopt;
388 return DigestAuth::fromParameters(value.substr(spacePos + 1));
389 }
390
392 {
393 this->set(boost::beast::http::field::authorization, "Digest " + auth.toParameters());
394 return *this;
395 }
396
400 std::optional<std::string> bearerAuth() const
401 {
402 auto iter = this->find(boost::beast::http::field::authorization);
403 if (iter == std::end(*this))
404 return std::nullopt;
405 auto spacePos = iter->value().find(' ');
406 if (spacePos == std::string::npos)
407 return std::nullopt;
408 if (iter->value().substr(0, spacePos) != "Bearer")
409 return std::nullopt;
410 return std::string{iter->value().substr(spacePos + 1)};
411 }
412
413 Request<BodyT>& bearerAuth(std::string const& token)
414 {
415 this->set(boost::beast::http::field::authorization, "Bearer " + token);
416 return *this;
417 }
418
424 std::unordered_map<std::string, std::string> getCookies() const
425 {
426 auto [begin, end] = this->equal_range(boost::beast::http::field::cookie);
427 std::unordered_map<std::string, std::string> cookies;
428 for (; begin != end; ++begin)
429 cookies.merge(Cookie::parseCookies(begin->value()));
430 return cookies;
431 }
432
433 private:
438 {
439 auto path = std::string{this->target()};
440 auto queryPos = path.find_last_of('?');
441 if (queryPos != std::string::npos)
442 {
443 query_ = parseQuery(std::string_view(path).substr(queryPos + 1, path.size() - queryPos - 1));
444 path.erase(queryPos);
445 }
446 using std::swap;
447 swap(path, path_);
448 }
449
456 std::unordered_map<std::string, std::string> parseQuery(std::string_view queryString) const
457 {
458 std::vector<std::string> splitBySeperator;
459 boost::algorithm::split(
460 splitBySeperator, queryString, boost::algorithm::is_any_of("&;"), boost::algorithm::token_compress_off);
461
462 std::unordered_map<std::string, std::string> query;
463 for (auto const& pair : splitBySeperator)
464 {
465 auto pos = pair.find('=');
466 if (pos == std::string::npos)
467 query[pair] = "";
468 else
469 query[pair.substr(0, pos)] = pair.substr(pos + 1, pair.length() - pos - 1);
470 }
471 return query;
472 }
473 };
474
476}
static std::unordered_map< std::string, std::string > parseCookies(std::string const &cookieHeaderEntry)
Definition cookie.cpp:33
This class extends the boost::beast::http::request<BodyT> with additional convenience.
Definition request.hpp:52
std::vector< std::string > splitCommaSeperatedHeaderEntry(FieldT &&field) const
Retrieves a header which value is a typically comma seperated list as a vector of string.
Definition request.hpp:208
bool isWebsocketUpgrade() const
Is this a websocket upgrade request?
Definition request.hpp:229
Request< BodyT > & bearerAuth(std::string const &token)
Definition request.hpp:413
std::unordered_map< std::string, std::string > getCookies() const
Gets all cookies.
Definition request.hpp:424
void parseTarget()
Extracts path and query from boost::beast::http::request::target()
Definition request.hpp:437
Request< BodyT > & port(std::string_view port)
Definition request.hpp:145
bool expectsContinue() const
Returns true if the request expects a continue response.
Definition request.hpp:278
auto const & query() const
Returns the query part of the url as an unordered_map<string, string>.
Definition request.hpp:176
Request< BodyT > & host(char const *host)
Definition request.hpp:124
std::optional< Ranges > ranges() const
Extracts the Range header.
Definition request.hpp:329
std::optional< std::size_t > contentLength() const
Returns the content length header.
Definition request.hpp:305
Request< BodyT > & host(std::string_view host)
Definition request.hpp:118
Request()
Definition request.hpp:57
Request< BodyT > & digestAuth(DigestAuth const &auth)
Definition request.hpp:391
void contentLength(std::size_t length)
Sets the content length header.
Definition request.hpp:319
Detail::RequestExtensions ejectExtensions() &&
Rips extensions out of this request object intended to be implanted into another request object.
Definition request.hpp:262
Request< BodyT > & setHeader(boost::beast::http::field field, std::string value)
Definition request.hpp:165
std::string host() const
Definition request.hpp:130
std::optional< BasicAuth > basicAuth() const
Returns a basic auth object if the authorization scheme is "Basic".
Definition request.hpp:354
Request(beast_request req)
Construct a new Request object from a beast request to upgrade it.
Definition request.hpp:67
std::string path() const
Returns only the path of the url.
Definition request.hpp:91
std::string_view target() const
Definition request.hpp:101
std::optional< AuthorizationScheme > authorizationScheme() const
Returns the authorization scheme of the Authorization header if it exists.
Definition request.hpp:340
Request< BodyT > & port(std::string &&port)
Definition request.hpp:135
std::string port() const
Definition request.hpp:160
void target(std::string_view target)
Definition request.hpp:96
Request< BodyT > & port(std::string const &port)
Definition request.hpp:140
Request< BodyT > & host(std::string &&host)
Definition request.hpp:106
Request< BodyT > & host(std::string const &host)
Definition request.hpp:112
Request< BodyT > & port(char const *port)
Definition request.hpp:155
boost::beast::http::request< BodyT > beast_request
Definition request.hpp:55
auto const & pathMatches() const
Retrieves regex matches for this request with the registered route.
Definition request.hpp:186
std::optional< DigestAuth > digestAuth() const
Returns a digest auth object with all digest auth information.
Definition request.hpp:377
Request< BodyT > & pathMatches(std::vector< std::string > &&matches)
Sets regex matches for this request with the registered route.
Definition request.hpp:196
Request< BodyT > & basicAuth(BasicAuth const &auth)
Definition request.hpp:368
void expectsContinue(bool enable)
enables/disables the expect continue header.
Definition request.hpp:292
std::optional< std::string > bearerAuth() const
Returns token data of bearer authentication method.
Definition request.hpp:400
Request(beast_request req, Detail::RequestExtensions &&extensions)
Construct a new Request object from a beast request and takes extensions of a previous request object...
Definition request.hpp:79
Request< BodyT > & port(unsigned short port)
Definition request.hpp:150
std::unordered_map< std::string, std::string > parseQuery(std::string_view queryString) const
Transforms the query part from string to map.
Definition request.hpp:456
Definition authorization.hpp:10
AuthorizationScheme authorizationSchemeFromString(std::string_view)
Definition authorization.cpp:9
Definition forward.hpp:4
Definition basic_auth.hpp:10
static std::optional< BasicAuth > fromBase64(std::string_view base64Encoded)
Definition basic_auth.cpp:7
std::string toBase64() const
Definition basic_auth.cpp:24
Definition request.hpp:34
std::string host_
Definition request.hpp:38
std::unordered_map< std::string, std::string > query_
Definition request.hpp:37
std::optional< std::vector< std::string > > regexMatches_
Definition request.hpp:35
std::string port_
Definition request.hpp:39
std::string path_
Definition request.hpp:36
Definition digest_auth.hpp:10
std::string toParameters() const
Definition digest_auth.cpp:34
static std::optional< DigestAuth > fromParameters(std::string_view parameterList)
Definition digest_auth.cpp:67
static std::optional< Ranges > fromString(std::string const &str)
Definition ranges.cpp:37