roar
Loading...
Searching...
No Matches
response.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <roar/cors.hpp>
6#include <roar/request.hpp>
7#include <roar/error.hpp>
9
10#include <boost/beast/http/message.hpp>
11#include <boost/beast/http/string_body.hpp>
12#include <boost/beast/http/empty_body.hpp>
13#include <promise-cpp/promise.hpp>
14
15#ifdef ROAR_ENABLE_NLOHMANN_JSON
16# include <nlohmann/json.hpp>
17#endif
18
19#include <type_traits>
20#include <numeric>
21#include <string>
22#include <optional>
23#include <iterator>
24
25namespace Roar
26{
27 template <typename BodyT>
29 {
30 public:
31 using body_type = BodyT;
32
34 : response_{}
35 {
36 response_.version(11);
37 }
38
39 explicit Response(boost::beast::http::response<BodyT>&& res)
40 : response_{std::move(res)}
41 {
42 response_.version(11);
43 }
44
45 template <typename... Forwards>
46 explicit Response(Forwards... forwards)
47 : response_{std::piecewise_construct, std::forward_as_tuple(std::forward<Forwards>(forwards)...)}
48 {
49 response_.version(11);
50 }
51
57 Response& status(boost::beast::http::status status)
58 {
59 response_.result(status);
60 return *this;
61 }
62
68 Response& setCookie(Cookie const& cookie)
69 {
70 response_.set(boost::beast::http::field::set_cookie, cookie.toSetCookieString());
71 return *this;
72 }
73
79 auto& body()
80 {
81 return response_.body();
82 }
83
91 {
92 response_.keep_alive(keepAlive);
93 return *this;
94 }
95
103 template <typename T>
104 Response& body(T&& toAssign)
105 {
106#ifdef ROAR_ENABLE_NLOHMANN_JSON
107 if constexpr (std::is_same_v<std::decay_t<T>, nlohmann::json>)
108 response_.body() = std::forward<T>(toAssign).dump();
109 else
110#endif
111 response_.body() = std::forward<T>(toAssign);
113 return *this;
114 }
115 auto body() const
116 {
117 return response_.body();
118 }
119
126 Response& chunked(bool activate = true)
127 {
128 response_.chunked(activate);
129 return *this;
130 }
131 bool chunked() const
132 {
133 return response_.chunked();
134 }
135
142 Response& contentType(std::string const& type)
143 {
144 return setHeader(boost::beast::http::field::content_type, type);
145 }
146 std::optional<std::string> contentType() const
147 {
148 if (auto it = response_.find(boost::beast::http::field::content_type); it != std::end(response_))
149 return std::string{it->value()};
150 return std::nullopt;
151 }
152
153 Response& contentLength(std::size_t length)
154 {
155 return setHeader(boost::beast::http::field::content_length, std::to_string(length));
156 }
157 std::optional<std::size_t> contentLength() const
158 {
159 if (auto it = response_.find(boost::beast::http::field::content_length); it != std::end(response_))
160 return std::stoull(std::string{it->value()});
161 return std::nullopt;
162 }
163
164 auto begin()
165 {
166 return response_.begin();
167 }
168 auto begin() const
169 {
170 return response_.begin();
171 }
172 auto end()
173 {
174 return response_.end();
175 }
176 auto end() const
177 {
178 return response_.end();
179 }
180 auto cbegin() const
181 {
182 return response_.cbegin();
183 }
184 auto cend() const
185 {
186 return response_.cend();
187 }
188
189 auto at(boost::beast::http::field field) const
190 {
191 return response_.at(field);
192 }
193
194 void clear()
195 {
196 response_.clear();
197 }
198
199 auto equal_range(boost::beast::http::field field) const
200 {
201 return response_.equal_range(field);
202 }
203
204 auto erase(boost::beast::http::field field)
205 {
206 return response_.erase(field);
207 }
208 auto find(boost::beast::http::field field) const
209 {
210 return response_.find(field);
211 }
212 bool hasContentLength() const
213 {
214 return response_.has_content_length();
215 }
216 bool hasField(boost::beast::http::field field) const
217 {
218 return response_.has_field(field);
219 }
220 bool hasKeepAlive() const
221 {
222 return response_.has_keep_alive();
223 }
224 auto payloadSize() const
225 {
226 return response_.payload_size();
227 }
228 auto reason() const
229 {
230 return response_.reason();
231 }
232 auto result() const
233 {
234 return response_.result();
235 }
236 auto resultInt() const
237 {
238 return response_.result_int();
239 }
240 void set(boost::beast::http::field field, std::string const& value)
241 {
242 response_.set(field, value);
243 }
244 auto target() const
245 {
246 return response_.target();
247 }
248 auto version() const
249 {
250 return response_.version();
251 }
252
259 Response& contentType(char const* type)
260 {
261 return setHeader(boost::beast::http::field::content_type, type);
262 }
263
269 Response& setHeader(boost::beast::http::field field, std::string const& value)
270 {
271 response_.set(field, value);
272 return *this;
273 }
274
275 std::optional<std::string> getHeader(boost::beast::http::field field) const
276 {
277 if (auto it = response_.find(field); it != std::end(response_))
278 return std::string{it->value()};
279 return std::nullopt;
280 }
281
287 Response& setHeader(boost::beast::http::field field, char const* value)
288 {
289 response_.set(field, value);
290 return *this;
291 }
292
299 {
300 response_.prepare_payload();
301 return *this;
302 }
303
304 private:
305 auto joinList(std::vector<std::string> const& list)
306 {
307 return list.empty() ? std::string{}
308 : std::accumulate(
309 std::next(std::begin(list)),
310 std::end(list),
311 list.front(),
312 [](std::string accum, std::string const& elem) {
313 return std::move(accum) + "," + elem;
314 });
315 };
316
317 public:
325 template <typename RequestBodyT>
326 Response& enableCors(Request<RequestBodyT> const& req, std::optional<CorsSettings> cors = std::nullopt)
327 {
328 if (!cors)
329 cors = makePermissiveCorsSettings(req.method());
330
331 response_.set(
332 boost::beast::http::field::access_control_allow_origin,
333 cors->allowedOrigin(std::string{req[boost::beast::http::field::origin]}));
334
335 // Preflight requests require both methods and allowHeaders to be set.
336 if (req.method() == boost::beast::http::verb::options)
337 {
338 std::string requestedMethod;
339 if (req.find(boost::beast::http::field::access_control_request_method) != std::end(req))
340 requestedMethod = std::string{req[boost::beast::http::field::access_control_request_method]};
341
342 if (const auto&& allowedMethods = cors->methodAllowSelection({std::move(requestedMethod)});
343 !allowedMethods.empty())
344 response_.set(boost::beast::http::field::access_control_allow_methods, joinList(allowedMethods));
345 }
346
347 std::vector<std::string> requestedHeaders;
348 if (req.find(boost::beast::http::field::access_control_request_headers) != std::end(req))
349 requestedHeaders =
350 req.splitCommaSeperatedHeaderEntry(boost::beast::http::field::access_control_request_headers);
351
352 if (const auto&& allowedHeaders = cors->headerAllowSelection(requestedHeaders); !allowedHeaders.empty())
353 response_.set(boost::beast::http::field::access_control_allow_headers, joinList(allowedHeaders));
354 else
355 response_.set(boost::beast::http::field::access_control_allow_headers, "*");
356
357 if (cors->allowCredentials.has_value())
358 response_.set(
359 boost::beast::http::field::access_control_allow_credentials,
360 *cors->allowCredentials ? "true" : "false");
361
362 if (!cors->exposeHeaders.empty())
363 {
364 response_.set(
365 boost::beast::http::field::access_control_expose_headers,
366 std::accumulate(
367 std::next(std::begin(cors->exposeHeaders)),
368 std::end(cors->exposeHeaders),
369 std::string{boost::beast::http::to_string(cors->exposeHeaders.front())},
370 [](std::string accum, auto const& field) {
371 return std::move(accum) + "," + std::string{boost::beast::http::to_string(field)};
372 }));
373 }
374
375 return *this;
376 }
377
379 {
380 response_.set(boost::beast::http::field::access_control_allow_origin, "*");
381 response_.set(boost::beast::http::field::access_control_allow_methods, "*");
382 response_.set(boost::beast::http::field::access_control_allow_headers, "*");
383 response_.set(boost::beast::http::field::access_control_allow_credentials, "true");
384 return *this;
385 }
386
390 boost::beast::http::response<BodyT>&& response() &&
391 {
392 return std::move(response_);
393 }
394
398 boost::beast::http::response<BodyT>& response() &
399 {
400 return response_;
401 }
402
403 private:
404 boost::beast::http::response<BodyT> response_;
405 };
406}
Definition cookie.hpp:18
std::string toSetCookieString() const
Definition cookie.cpp:159
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
Definition response.hpp:29
Response()
Definition response.hpp:33
auto body() const
Definition response.hpp:115
Response & contentType(std::string const &type)
For setting of the content type.
Definition response.hpp:142
auto version() const
Definition response.hpp:248
auto target() const
Definition response.hpp:244
std::optional< std::string > contentType() const
Definition response.hpp:146
bool hasKeepAlive() const
Definition response.hpp:220
std::optional< std::size_t > contentLength() const
Definition response.hpp:157
Response(Forwards... forwards)
Definition response.hpp:46
auto cbegin() const
Definition response.hpp:180
bool hasField(boost::beast::http::field field) const
Definition response.hpp:216
auto begin()
Definition response.hpp:164
boost::beast::http::response< BodyT > response_
Definition response.hpp:404
Response & status(boost::beast::http::status status)
Sets the response status code.
Definition response.hpp:57
Response & setHeader(boost::beast::http::field field, char const *value)
Can be used to set a header field.
Definition response.hpp:287
Response & body(T &&toAssign)
This function can be used to assign something to the body.
Definition response.hpp:104
Response(boost::beast::http::response< BodyT > &&res)
Definition response.hpp:39
std::optional< std::string > getHeader(boost::beast::http::field field) const
Definition response.hpp:275
auto & body()
Retrieve the response body object.
Definition response.hpp:79
Response & enableCors(Request< RequestBodyT > const &req, std::optional< CorsSettings > cors=std::nullopt)
Sets cors headers.
Definition response.hpp:326
Response & setCookie(Cookie const &cookie)
Sets a set-cookie header entry.
Definition response.hpp:68
auto at(boost::beast::http::field field) const
Definition response.hpp:189
auto cend() const
Definition response.hpp:184
Response & preparePayload()
Sets header values that are implicit by the body (like Content-Lenght).
Definition response.hpp:298
auto equal_range(boost::beast::http::field field) const
Definition response.hpp:199
bool chunked() const
Definition response.hpp:131
auto find(boost::beast::http::field field) const
Definition response.hpp:208
Response & setHeader(boost::beast::http::field field, std::string const &value)
Can be used to set a header field.
Definition response.hpp:269
auto resultInt() const
Definition response.hpp:236
bool hasContentLength() const
Definition response.hpp:212
Response & contentLength(std::size_t length)
Definition response.hpp:153
auto erase(boost::beast::http::field field)
Definition response.hpp:204
boost::beast::http::response< BodyT > & response() &
Accessor for the underlying boost response.
Definition response.hpp:398
Response & chunked(bool activate=true)
(De)Activate chunked encoding.
Definition response.hpp:126
auto begin() const
Definition response.hpp:168
auto end() const
Definition response.hpp:176
auto payloadSize() const
Definition response.hpp:224
Response & enableCorsEverything()
Definition response.hpp:378
BodyT body_type
Definition response.hpp:31
boost::beast::http::response< BodyT > && response() &&
Ejects the underlying boost response.
Definition response.hpp:390
auto end()
Definition response.hpp:172
void clear()
Definition response.hpp:194
auto result() const
Definition response.hpp:232
auto reason() const
Definition response.hpp:228
auto joinList(std::vector< std::string > const &list)
Definition response.hpp:305
Response & keepAlive(bool keepAlive=true)
Set keep alive.
Definition response.hpp:90
Response & contentType(char const *type)
For setting of the content type.
Definition response.hpp:259
void set(boost::beast::http::field field, std::string const &value)
Definition response.hpp:240
Definition authorization.hpp:10
CorsSettings makePermissiveCorsSettings(std::string method)
Reflects all requested headers and origin or sets the Kleene star. Is as permissive as possible....
Definition cors.hpp:53