18#include <boost/beast/http/message.hpp>
19#include <boost/beast/http/empty_body.hpp>
20#include <boost/beast/http/parser.hpp>
21#include <boost/beast/http/write.hpp>
22#include <boost/beast/http/read.hpp>
23#include <boost/beast/ssl/ssl_stream.hpp>
24#include <boost/beast/core/tcp_stream.hpp>
26#include <promise-cpp/promise.hpp>
40 class Session :
public std::enable_shared_from_this<Session>
53 std::optional<std::variant<SslServerContext, boost::asio::ssl::context>>& sslContext,
55 std::function<
void(
Error&&)> onError,
56 std::weak_ptr<Router> router,
68 template <
typename BodyT>
72 template <
typename... Forwards,
typename RequestBodyT>
74 :
session_(session.shared_from_this())
78 :
session_(session.shared_from_this())
82 :
session_(session.shared_from_this())
86 :
session_(session.shared_from_this())
122 template <
typename T>
125 response_.body(std::forward<T>(toAssign));
148 template <
typename T>
151 response_.contentType(std::forward<T>(type));
160 template <
typename T>
163 response_.setHeader(field, std::forward<T>(value));
166 template <
typename T>
169 return setHeader(field, std::forward<T>(value));
192 template <
typename RequestBodyT2>
196 response_.enableCors(req, std::move(cors));
220 template <
typename T = BodyT,
bool Overr
ideSfinae = false>
222 !(std::is_same_v<T, RangeFileBody> || std::is_same_v<T, boost::beast::http::empty_body>) ||
233 promise_ = std::make_unique<promise::Promise>(promise::newPromise());
234 serializer_ = std::make_unique<boost::beast::http::serializer<false, BodyT>>(
response_.response());
242 template <
typename T = BodyT>
251 promise_ = std::make_unique<promise::Promise>(promise::newPromise());
252 serializer_ = std::make_unique<boost::beast::http::serializer<false, BodyT>>(
response_.response());
260 template <
typename T = BodyT>
263 using namespace boost::beast::http;
266 setHeader(field::content_type,
"multipart/byteranges; boundary=" +
response_.body().boundary());
269 const auto first =
response_.body().firstRange().first;
270 const auto second =
response_.body().firstRange().second;
271 const auto fileSize =
response_.body().fileSize();
274 field::content_range,
275 std::string{
"bytes "} + std::to_string(first) +
"-" + std::to_string(second) +
"/" +
276 std::to_string(fileSize));
278 status(status::partial_content);
279 return commit<BodyT, true>();
289 if (!wwwAuthenticate.empty())
290 response_.setHeader(boost::beast::http::field::www_authenticate, wwwAuthenticate);
291 response_.status(boost::beast::http::status::unauthorized);
338 boost::beast::http::async_write_header(
341 [self = this->shared_from_this()](boost::beast::error_code ec, std::size_t) {
343 self->promise_->fail(ec);
345 self->promise_->resolve(
false);
356 boost::beast::http::async_write_some(
359 [self = this->shared_from_this()](boost::beast::error_code ec, std::size_t bytesTransferred) {
360 if (!ec && !self->serializer_->is_done())
361 return self->writeChunk();
365 self->session_->close();
366 self->promise_->reject(
Error{.
error = ec, .additionalInfo =
"Failed to send response"});
370 if (self->onChunk_ && !self->onChunk_(bytesTransferred))
375 self->promise_->resolve(self->session_->onWriteComplete(
376 self->serializer_->get().need_eof(), ec, bytesTransferred));
378 catch (std::exception
const& exc)
380 self->promise_->fail(
381 Error{.
error = exc.what(), .additionalInfo =
"Failed to send response"});
393 std::unique_ptr<boost::beast::http::serializer<false, BodyT>>
serializer_;
396 template <
typename BodyT,
typename OriginalBodyT,
typename... Forwards>
397 [[nodiscard]] std::shared_ptr<SendIntermediate<BodyT>>
400 return std::shared_ptr<SendIntermediate<BodyT>>(
404 template <
typename BodyT>
405 [[nodiscard]] std::shared_ptr<SendIntermediate<BodyT>>
send(boost::beast::http::response<BodyT>&& res)
410 template <
typename BodyT>
416 template <
typename BodyT>
428 send(std::string message, std::chrono::seconds timeout = std::chrono::seconds{10});
435 template <
typename BodyT>
441 template <
typename OriginalBodyT,
typename... Forwards>
443 :
session_{session.shared_from_this()}
444 ,
req_{[&session, &forwardArgs...]() -> decltype(
req_) {
445 if constexpr (std::is_same_v<BodyT, boost::beast::http::empty_body>)
446 throw std::runtime_error(
"Attempting to read with empty_body type.");
448 return boost::beast::http::request_parser<BodyT>{
449 std::move(*session.
parser()), std::forward<Forwards>(forwardArgs)...};
452 , originalExtensions_{std::move(req).ejectExtensions()}
455 , overallTimeout_{std::nullopt}
472 req_.body_limit(limit);
484 req_.body_limit(std::stoull(std::string{limit}));
495 req_.body_limit(boost::none);
508 onChunk_ = onChunkFunc;
527 session_->withStreamDo([
this](
auto&
stream) {
528 boost::beast::get_lowest_layer(
stream).expires_after(*overallTimeout_);
531 promise_ = std::make_unique<promise::Promise>(promise::newPromise());
544 overallTimeout_ = timeout;
554 session_->withStreamDo([
this](
auto&
stream) {
555 if (!overallTimeout_)
557 boost::beast::http::async_read_some(
561 [self = this->shared_from_this()](boost::beast::error_code ec, std::size_t bytesReceived) {
562 if (!ec && !self->req_.is_done())
563 return self->readChunk();
567 self->session_->close();
568 self->promise_->reject(Error{.error = ec});
571 if (self->onChunk_ && !self->onChunk_(self->session_->buffer(), bytesReceived))
579 catch (std::exception
const& exc)
581 using namespace std::string_literals;
583 ->send(self->session_->standardResponseProvider().makeStandardResponse(
585 boost::beast::http::status::internal_server_error,
586 "An exception was thrown in the body read completion handler: "s + exc.what()))
595 boost::beast::http::request_parser<BodyT>
req_;
597 std::function<bool(boost::beast::flat_buffer&, std::size_t)>
onChunk_;
607 void readLimit(std::size_t bytesPerSecond);
614 void writeLimit(std::size_t bytesPerSecond);
627 template <
typename BodyT,
typename OriginalBodyT,
typename... Forwards>
628 [[nodiscard]] std::shared_ptr<ReadIntermediate<BodyT>>
631 return std::shared_ptr<ReadIntermediate<BodyT>>(
641 template <
typename BodyT = boost::beast::http::empty_body,
typename RequestBodyT,
typename... Forwards>
645 res.
setHeader(boost::beast::http::field::server,
"Roar+" BOOST_BEAST_VERSION_STRING);
646 if (routeOptions().cors)
650 template <
typename BodyT = boost::beast::http::empty_body>
654 res.
setHeader(boost::beast::http::field::server,
"Roar+" BOOST_BEAST_VERSION_STRING);
655 if (routeOptions().cors)
666 template <
typename FunctionT>
669 std::visit(std::forward<FunctionT>(func), stream());
697 bool isSecure()
const;
705 void sendStandardResponse(boost::beast::http::status status, std::string_view additionalInfo =
"");
710 void sendStrictTransportSecurityResponse();
719 template <
typename OriginalBodyT>
723 return promise::newPromise([
this, &req, &timeout](promise::Defer d) {
724 read<VoidBody>(std::move(req))
725 ->useFixedTimeout(timeout)
727 .then([d](
auto&,
auto const&) {
730 .fail([d](Error
const& e) {
737 void setupRouteOptions(RouteOptions options);
741 void performSslHandshake();
742 bool onWriteComplete(
bool expectsClose, boost::beast::error_code ec, std::size_t);
743 std::variant<Detail::StreamType, boost::beast::ssl_stream<Detail::StreamType>>& stream();
744 std::shared_ptr<boost::beast::http::request_parser<boost::beast::http::empty_body>>& parser();
745 boost::beast::flat_buffer& buffer();
746 StandardResponseProvider
const& standardResponseProvider();
747 void startup(
bool immediate =
true);
750 struct Implementation;
751 std::unique_ptr<Implementation>
impl_;
This factory creates sessions.
Definition factory.hpp:23
This class extends the boost::beast::http::request<BodyT> with additional convenience.
Definition request.hpp:52
Definition response.hpp:29
Response & enableCors(Request< RequestBodyT > const &req, std::optional< CorsSettings > cors=std::nullopt)
Sets cors headers.
Definition response.hpp:326
Response & setHeader(boost::beast::http::field field, std::string const &value)
Can be used to set a header field.
Definition response.hpp:269
Response & enableCorsEverything()
Definition response.hpp:378
A route represents the mapping of a verb+path to a user provided function.
Definition route.hpp:21
Definition session.hpp:41
Response< BodyT > prepareResponse()
Definition session.hpp:651
std::variant< Detail::StreamType, boost::beast::ssl_stream< Detail::StreamType > > & stream()
Definition session.cpp:260
std::shared_ptr< boost::beast::http::request_parser< boost::beast::http::empty_body > > & parser()
Definition session.cpp:265
std::shared_ptr< SendIntermediate< BodyT > > send(Response< BodyT > &&res)
Definition session.hpp:411
std::shared_ptr< SendIntermediate< BodyT > > send(boost::beast::http::response< BodyT > &&res)
Definition session.hpp:405
std::unique_ptr< Implementation > impl_
Definition session.hpp:751
static constexpr uint64_t defaultBodyLimit
Definition session.hpp:47
std::shared_ptr< SendIntermediate< BodyT > > send(Request< OriginalBodyT > const &req, Forwards &&... forwards)
Definition session.hpp:398
StandardResponseProvider const & standardResponseProvider()
Definition session.cpp:275
Detail::PromiseTypeBind< Detail::PromiseTypeBindThen<>, Detail::PromiseTypeBindFail< Error const & > > awaitClientClose(Request< OriginalBodyT > req, std::chrono::milliseconds timeout=std::chrono::seconds{3})
Some clients insist on closing the connection on their own even if they received a response....
Definition session.hpp:721
ROAR_PIMPL_SPECIAL_FUNCTIONS(Session)
Response< BodyT > prepareResponse(Request< RequestBodyT > const &req, Forwards &&... forwardArgs)
Prepares a response with some header values already set.
Definition session.hpp:642
std::shared_ptr< ReadIntermediate< BodyT > > read(Request< OriginalBodyT > req, Forwards &&... forwardArgs)
Read data from the client.
Definition session.hpp:629
void withStreamDo(FunctionT &&func)
std::visit for the underlying beast stream. Either ssl_stream or tcp_stream.
Definition session.hpp:667
boost::beast::flat_buffer & buffer()
Definition session.cpp:270
bool isSecure() const
Returns whether or not this is an encrypted session.
Definition session.cpp:230
static constexpr uint64_t defaultHeaderLimit
Definition session.hpp:46
void close()
Calls shutdown on the socket.
Definition session.cpp:306
static constexpr std::chrono::seconds sessionTimeout
Definition session.hpp:48
std::shared_ptr< SendIntermediate< BodyT > > sendWithAllAcceptedCors()
Definition session.hpp:417
Definition forward.hpp:11
Definition forward.hpp:29
auto ref(T &thing)
Definition promise_compat.hpp:20
auto cref(T const &thing)
Definition promise_compat.hpp:26
Definition authorization.hpp:10
Definition promise_compat.hpp:10
Definition promise_compat.hpp:67
Definition promise_compat.hpp:63
Definition promise_compat.hpp:70
Definition request.hpp:34
Holds errors that are produced asynchronously anywhere.
Definition error.hpp:20
std::variant< boost::system::error_code, std::string > error
Definition error.hpp:21
Options that modify the server behavior for a given route.
Definition proto_route.hpp:18